Skip to content

Pandas入门

约定:

import numpy as np
import pandas as pd
from pandas import Series, DataFrame
import pandas_datareader as web

pandas数据结构介绍

Series

Series是一种一维的数组型对象,它包含了一个值序列(与NumPy中的类型相似),并且包含了数据标签,称为**索引(index)。 从另一个角度考虑Series,可以认为它是一个**长度固定且有序的字典,因为它将索引值和数据值按位置配对。索引在左边,值在右边。

obj = pd.Series([4, 7, -5, 3])
print(obj)
# 0    4
# 1    7
# 2   -5
# 3    3
# dtype: int64

print(obj.values)
# [ 4  7 -5  3

print(obj.index)
# RangeIndex(start=0, stop=4, step=1)

自定义index

obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
print(obj)
# d    4
# b    7
# a   -5
# c    3
# dtype: int64

print(obj.values)
# [ 4  7 -5  3]

print(obj.index)
# Index(['d', 'b', 'a', 'c'], dtype='object')

# 输出索引值为'a'的Series值
print(obj['a'])  
# -5

# 使用布尔值数组进行过滤Series值
print(obj[obj > 3])
# d    4
# b    7
# dtype: int64

# 对Series值进行算术运算
print(obj * 2)
# d     8
# b    14
# a   -10
# c     6
# dtype: int64

# 对Series值进行算术运算
print(np.exp(obj))
# d      54.598150
# b    1096.633158
# a       0.006738
# c      20.085537
# dtype: float64

# 更新Series数组值
obj['a'] = 9

# 输出指定索引值的Series值,注意,索引条件是列表
print(obj[['a', 'b', 'c']])
# a    9
# b    7
# c    3
# dtype: int64

# 注意,下面的判断是索引值,非Series值
print(obj)
print(7 in obj) 
# False
print('a' in obj)
# True

通过字典生成一个Series。NaN(not a number),这是pandas中标记缺失值或NA值的方式。

当把字典传递给Series构造函数时,产生的Series的索引将是排序好的字典键。可以将字典键按照你所想要的顺序传递给构造函数,从而使生成的Series的索引顺序符合预期。

看下例,通过字典sdata生成Series。

sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)

print(sdata)  
# {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}

print(obj3)
# Ohio      35000
# Texas     71000
# Oregon    16000
# Utah       5000
# dtype: int64

通过指定索引states去匹配字典sdata生成基于新索引states的Series。

states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = pd.Series(sdata, index=states)

print(obj4)
# California        NaN
# Ohio          35000.0
# Oregon        16000.0
# Texas         71000.0
# dtype: float64

对Series进行布尔值判断。

print(pd.isnull(obj4))
# California     True
# Ohio          False
# Oregon        False
# Texas         False
# dtype: bool

print(pd.notnull(obj4))
# California    False
# Ohio           True
# Oregon         True
# Texas          True
# dtype: bool

对Series进行布尔值判断。

print(obj4.isnull)
# <bound method Series.isnull of California        NaN
# Ohio          35000.0
# Oregon        16000.0
# Texas         71000.0
# dtype: float64>

print(obj4.notnull)
# <bound method Series.notnull of California        NaN
# Ohio          35000.0
# Oregon        16000.0
# Texas         71000.0
# dtype: float64>

Series的自动对齐索引,与数据库的join操作是非常相似。

print("obj3 \n", obj3)
# obj3 
#  Ohio      35000
# Texas     71000
# Oregon    16000
# Utah       5000
# dtype: int64

print("obj4 \n", obj4)
# obj4 
#  California        NaN
# Ohio          35000.0
# Oregon        16000.0
# Texas         71000.0
# dtype: float64

print("obj3+obj4 \n", obj3 + obj4)
# obj3+obj4
# California         NaN
# Ohio           70000.0
# Oregon         32000.0
# Texas         142000.0
# Utah               NaN
# dtype: float64

# 下面是obj3和obj4的值,帮助理解上面obj3 + obj4的操作。
# obj3                          obj4
# Ohio      35000               California        NaN
# Texas     71000               Ohio          35000.0
# Oregon    16000               Oregon        16000.0
# Utah       5000               Texas         71000.0
# dtype: int64                  dtype: float64

Series对象自身和其索引都有name属性。

obj4.name = 'population'
obj4.index.name = 'state'
print(obj4)
# state
# California        NaN
# Ohio          35000.0
# Oregon        16000.0
# Texas         71000.0
# Name: population, dtype: float64

替换Series的索引名。

obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
print(obj)
obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
print(obj)
# Bob      4
# Steve    7
# Jeff    -5
# Ryan     3
# dtype: int64

DataFrame

DataFrame表示的是矩阵的数据表,它包含已排序的列集合,每一列可以是不同的值类型(数值、字符串、布尔值等)。

DataFrame既有行索引也有列索引,它可以被视为一个共享相同索引的Series的字典,比如所有列共享同一个列索引。

在DataFrame中,数据被存储为一个以上的二维块,而不是列表、字典或其他一维数组的集合。

DataFrame是二维的,但可以利用**分层索引**在DataFrame中展现更高维度的数据。

从DataFrame中选取的列是数据的视图,而不是拷贝。因此,对Series的修改会映射到DataFrame中。如果需要复制,则应当显式地使用Series的copy方法。

由字典构成DataFrame

基于字典data产生的DataFrame会自动为Sereies分配索引,并且列会按照排序的顺序排列。

data = {
    'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
    'year': [2000, 2001, 2002, 2001, 2002, 2003],
    'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]
}

frame = pd.DataFrame(data)
print(frame)
#     state  year  pop
# 0    Ohio  2000  1.5
# 1    Ohio  2001  1.7
# 2    Ohio  2002  3.6
# 3  Nevada  2001  2.4
# 4  Nevada  2002  2.9
# 5  Nevada  2003  3.2


# 对于大型DataFrame, head方法将会只选出头部的若干行, 默认是前五行。
print(frame.head(3))
#   state  year  pop
# 0  Ohio  2000  1.5
# 1  Ohio  2001  1.7
# 2  Ohio  2002  3.6

如果指定了列的顺序,DataFrame的列将会按照指定顺序排列。

frame = pd.DataFrame(data, columns=['year', 'state', 'pop'])
print(frame)
#    year   state  pop
# 0  2000    Ohio  1.5
# 1  2001    Ohio  1.7
# 2  2002    Ohio  3.6
# 3  2001  Nevada  2.4
# 4  2002  Nevada  2.9
# 5  2003  Nevada  3.2

如果传的列(debt)不包含在字典(data)中,将会在结果中出现缺失值。

frame2 = pd.DataFrame(
    data,
    columns=['year', 'state', 'pop', 'debt'],
    index=['one', 'two', 'three', 'four', 'five', 'six']
)

print(frame2)
#        year   state  pop debt
# one    2000    Ohio  1.5  NaN
# two    2001    Ohio  1.7  NaN
# three  2002    Ohio  3.6  NaN
# four   2001  Nevada  2.4  NaN
# five   2002  Nevada  2.9  NaN
# six    2003  Nevada  3.2  NaN

选取行, 可以通过位置或行索引标签loc进行选取。

print(frame2.loc['three'])
# year     2002
# state    Ohio
# pop       3.6
# debt      NaN
# Name: three, dtype: object

DataFrame中的一列,可以按字典型标记或属性那样检索为Series。 frame2[colunm]对于任意列名均有效,但是frame2.column只在列名是有效的Python变量名时有效。 返回的Series与原DataFrame有相同的索引,且Series的name属性也会被合理地设置。

print(frame2['state'])
# one        Ohio
# two        Ohio
# three      Ohio
# four     Nevada
# five     Nevada
# six      Nevada
# Name: state, dtype: object

print(frame2.state)  # 属性型连接
# one        Ohio
# two        Ohio
# three      Ohio
# four     Nevada
# five     Nevada
# six      Nevada
# Name: state, dtype: object

列的引用是可以修改的。值的长度必须和DataFrame的长度相匹配,比如,下例中np.arange(6.)frame2['debt']的长度都是6。

frame2['debt'] = 16.5
print(frame2)
# Name: state, dtype: object
#        year   state  pop  debt
# one    2000    Ohio  1.5  16.5
# two    2001    Ohio  1.7  16.5
# three  2002    Ohio  3.6  16.5
# four   2001  Nevada  2.4  16.5
# five   2002  Nevada  2.9  16.5
# six    2003  Nevada  3.2  16.5

frame2['debt'] = np.arange(6.)
print(frame2)
#        year   state  pop  debt
# one    2000    Ohio  1.5   0.0
# two    2001    Ohio  1.7   1.0
# three  2002    Ohio  3.6   2.0
# four   2001  Nevada  2.4   3.0
# five   2002  Nevada  2.9   4.0
# six    2003  Nevada  3.2   5.0

如果将Series赋值给一列时,Series的索引将会按照DataFrame的索引重新排列,并在空缺的地方填充缺失值

val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])

frame2['debt'] = val

print(frame2)
#        year   state  pop  debt
# one    2000    Ohio  1.5   NaN
# two    2001    Ohio  1.7  -1.2
# three  2002    Ohio  3.6   NaN
# four   2001  Nevada  2.4  -1.5
# five   2002  Nevada  2.9  -1.7
# six    2003  Nevada  3.2   NaN

如果被赋值的列(eastern列)并不存在,则会生成一个新的列。frame2.state == 'Ohio'返回的是布尔值,赋值给eastern

frame2['eastern'] = frame2.state == 'Ohio'

print(frame2)
#        year   state  pop  debt  eastern
# one    2000    Ohio  1.5   NaN     True
# two    2001    Ohio  1.7  -1.2     True
# three  2002    Ohio  3.6   NaN     True
# four   2001  Nevada  2.4  -1.5    False
# five   2002  Nevada  2.9  -1.7    False
# six    2003  Nevada  3.2   NaN    False

print(frame2.eastern)
# one       True
# two       True
# three     True
# four     False
# five     False
# six      False
# Name: eastern, dtype: bool

del关键字可以像在字典中那样对DataFrame删除列。

del frame2['eastern']
print(frame2.columns)
# Index(['year', 'state', 'pop', 'debt'], dtype='object')

使用嵌套字典构建DataFrame

pandas会将字典的键作为列('Nevada', etc.),将内部字典的键作为行索引(2001, etc.)

pop = {
    'Nevada': {
        2001: 2.4,
        2002: 2.9
    },
    'Ohio': {
        2000: 1.5,
        2001: 1.7,
        2002: 3.6
    }
}

# 不指定索引,默认使用字典索引
frame3 = pd.DataFrame(pop)
print(frame3)
#       Nevada  Ohio
# 2001     2.4   1.7
# 2002     2.9   3.6
# 2000     NaN   1.5

# 指定字典某列作为索引
print(pd.DataFrame(pop, index=[2001, 2002, 2003]))
#       Nevada  Ohio
# 2001     2.4   1.7
# 2002     2.9   3.6
# 2003     NaN   NaN

# 指定不相干索引
print(pd.DataFrame(pop, index=['a', 'b', 'c']))
#    Nevada  Ohio
# a     NaN   NaN
# b     NaN   NaN
# c     NaN   NaN

转置操作(调换行和列)

print(frame3.T)
#         2001  2002  2000
# Nevada   2.4   2.9   NaN
# Ohio     1.7   3.6   1.5

使用含Series的字典构造DataFrame

frame3['Ohio'][:-1]是值为Ohio的Series的0~倒数第一个元素(不含),一共3个。 frame3['Nevada'][:2]是值为Nevada的Series的前2个元素。

pdata = {
    'Ohio': frame3['Ohio'][:-1],
    'Nevada': frame3['Nevada'][:2]
}

print(pd.DataFrame(pdata))
#       Ohio  Nevada
# 2001   1.7     2.4
# 2002   3.6     2.9

指定Dataframe frame3的列名。

frame3.index.name = 'year'
frame3.columns.name = 'state'

print(frame3)
# state  Nevada  Ohio
# year
# 2001      2.4   1.7
# 2002      2.9   3.6
# 2000      NaN   1.5

只输出Dataframe的值frame3.values,是一个二维数组。

print(frame3.values)
# [[2.4 1.7]
#  [2.9 3.6]
#  [nan 1.5]]

只输出Dataframe的值frame2.values,是一个二维数组。

print(frame2)
#        year   state  pop  debt
# one    2000    Ohio  1.5   NaN
# two    2001    Ohio  1.7  -1.2
# three  2002    Ohio  3.6   NaN
# four   2001  Nevada  2.4  -1.5
# five   2002  Nevada  2.9  -1.7
# six    2003  Nevada  3.2   NaN

print(frame2.values)
# [[2000 'Ohio' 1.5 nan]
#  [2001 'Ohio' 1.7 -1.2]
#  [2002 'Ohio' 3.6 nan]
#  [2001 'Nevada' 2.4 -1.5]
#  [2002 'Nevada' 2.9 -1.7]
#  [2003 'Nevada' 3.2 nan]]

索引对象

pandas中的**索引对象**是用于存储轴标签和其他元数据的(例如轴名称或标签)。

在构造Series或DataFrame时,你所使用的任意数组或标签序列都可以在内部转换为索引对象。 索引对象是不可变的。

除了类似数组,索引对象也像一个固定大小的集合。与Python集合不同,pandas索引对象可以包含重复标签。 因为一些操作会产生包含索引化数据的结果,理解索引如何工作还是很重要的。

下例演示了如何读取Dataframe的索引值。

obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index

print(obj)
# a    0
# b    1
# c    2
# dtype: int64

print(index)
# Index(['a', 'b', 'c'], dtype='object')

print(index[1:])
# Index(['b', 'c'], dtype='object')

下例演示了通过一个指定的Dataframe索引labels来生成Dataframe obj2

labels = pd.Index(np.arange(3))

print(labels)
# Int64Index([0, 1, 2], dtype='int64')

obj2 = pd.Series([1.5, -2.5, 0], index=labels)
print(obj2)
# 0    1.5
# 1   -2.5
# 2    0.0
# dtype: float64

print(obj2.index is labels)
# True

下例演示了不指定索引,默认使用字典索引来创建Dataframe。

pop = {
    'Nevada': {
        2001: 2.4,
        2002: 2.9
    },
    'Ohio': {
        2000: 1.5,
        2001: 1.7,
        2002: 3.6
    }
}


frame3 = pd.DataFrame(pop)
print(frame3)
#       Nevada  Ohio
# 2001     2.4   1.7
# 2002     2.9   3.6
# 2000     NaN   1.5

print(frame3)
# state  Nevada  Ohio
# year
# 2001      2.4   1.7
# 2002      2.9   3.6
# 2000      NaN   1.5

print(frame3.columns)
# Index(['Nevada', 'Ohio'], dtype='object', name='state')

print(frame3.index)
# Int64Index([2001, 2002, 2000], dtype='int64', name='year')

print('Ohio' in frame3.columns)
# True

print(2003 in frame3.index)
# False

pandas索引对象允许包含重复标签。根据重复标签进行筛选,会选取所有重复标签对应的数据。

dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])

print(dup_labels)
# Index(['foo', 'foo', 'bar', 'bar'], dtype='object')

一些常用索引对象的方法和属性。

obj1 = pd.Series(range(3), index=['a', 'b', 'c'])
index1 = obj1.index

obj2 = pd.Series(range(3), index=['c', 'f', 'g'])
index2 = obj2.index

print(index1)
# Index(['a', 'b', 'c'], dtype='object')

print(index2)
# Index(['c', 'f', 'g'], dtype='object')

append方法:将外部的索引对象粘贴到原索引后,产生一个新的索引。 接上例,把index2对象追加到index1对象。

print(index1.append(index2))
# Index(['a', 'b', 'c', 'c', 'f', 'g'], dtype='object')

difference方法: 计算2个索引的差集。

print(index1.difference(index2))
# Index(['a', 'b'], dtype='object')

intersection方法: 计算2个索引的交集。

print(index1.intersection(index2))
# Index(['c'], dtype='object')

union方法: 计算2个索引的并集(去重)。

print(index1.union(index2))
# Index(['a', 'b', 'c', 'f', 'g'], dtype='object')

isin方法: 计算表示每一个值是否在传值容器中,返回的是一个布尔数组。

print(index1.isin(index2))
# [False False  True]

delete方法: 将位置i(从0开始编号)的元素删除,并产生新的索引。

print(index1.delete('b'))
# IndexError: arrays used as indices must be of integer (or boolean) type

print(index1.delete(1))
# Index(['a', 'c'], dtype='object')

print(index1)
# Index(['a', 'b', 'c'], dtype='object')

drop方法: 根据传参删除指定索引值,并产生新的索引, 对比和delete的区别,delete方法是输入位置,drop方法是输入索引名称。

print(index2.drop(1))
# KeyError: '[1] not found in axis'

print(index2.drop('f'))
# Index(['c', 'g'], dtype='object')

print(index2)
# Index(['c', 'f', 'g'], dtype='object')

insert方法: 在位置i插入元素,并产生新的索引。

print(index1.insert(1, 'e'))  
# Index(['a', 'e', 'b', 'c'], dtype='object')

print(index1)  
# Index(['a', 'b', 'c'], dtype='object')

is_monotonic方法: 如果索引序列递增,则返回True

print(index1.is_monotonic) 
# True

print(index1.insert(1, 'e').is_monotonic) 
# False

is_unique方法: 如果索引序列唯一则返回True

print(index1.is_unique)  # True
print(index1.append(index2).is_unique)  # False

unique方法: 计算索引的唯一值序列(对比Union)。

print(index1.unique())
# Index(['a', 'b', 'c'], dtype='object')

print(index1.append(index2).unique())
# Index(['a', 'b', 'c', 'f', 'g'], dtype='object')

pandas基本功能

重建索引

Series调用reindex方法时,会将数据按照新的索引进行排列,如果某个索引值之前并不存在,则会引入缺失值。 下例中,对obj1reindexreindex方法会创建一个新索引对象obj2,索引值e之前并不存在,所以填入缺失值。 如果对obj1做reindex时指定method='ffill',会报错index must be monotonic increasing or decreasing

obj1 = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
print(obj1)
# d    4.5
# b    7.2
# a   -5.3
# c    3.6
# dtype: float64

obj2 = obj1.reindex(['a', 'b', 'c', 'd', 'e'])
print(obj2)
# a   -5.3
# b    7.2
# c    3.6
# d    4.5
# e    NaN
# dtype: float64

obj2 = obj1.reindex(['a', 'b', 'c', 'd', 'e'], method='ffill')
# ValueError: index must be monotonic increasing or decreasing

对于顺序数据,比如时间序列,在重建索引时可能会需要进行插值或填值。ffill方法在重建索引时插值,将值前向填充。

obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
print(obj3.reindex(range(6), method='ffill'))
# 0      blue
# 1      blue
# 2    purple
# 3    purple
# 4    yellow
# 5    yellow
# dtype: object

在DataFrame中,reindex可以改变行索引、列索引,也可以同时改变二者。当仅传入一个序列时,会重建行索引。 下例中,通过indexes创建Dataframe frame。 通过frame.reindex(['a', 'b', 'c', 'd'])重建行索引。 通过frame2.reindex(columns=['Ohio', 'Uta', 'California'])重建列索引。 缺失的索引列填入缺失值。

indexes = index = ['a', 'b', 'c']
states = ['Ohio', 'Texas', 'California']
frame = pd.DataFrame(
    np.arange(9).reshape(3, 3),
    index=indexes,
    columns=states
)

print(frame)
#    Ohio  Texas  California
# a     0      1           2
# b     3      4           5
# c     6      7           8

frame2 = frame.reindex(['a', 'b', 'c', 'd'])  # 重建行索引
print(frame2)
#    Ohio  Texas  California
# a   0.0    1.0         2.0
# b   3.0    4.0         5.0
# c   6.0    7.0         8.0
# d   NaN    NaN         NaN

frame3 = frame2.reindex(columns=['Ohio', 'Uta', 'California'])  # 重建列索引
print(frame3)
#    Ohio  Uta  California
# a   0.0  NaN         2.0
# b   3.0  NaN         5.0
# c   6.0  NaN         8.0
# d   NaN  NaN         NaN

使用loc进行更为简洁的行、列标签索引。下例通过筛选行索引和列索引产生新的Dataframe。

frame4 = frame.loc[['a', 'b'], states]
print(frame4)
#    Ohio  Texas  California
# a     0      1           2
# b     3      4           5

轴向索引删除条目

set_index(), dropna(), fillna(), reset_index(), drop(), replace()这些方法的inplace属性设为True时,这些方法会修改Series或DataFrame的尺寸或形状,*直接*操作原对象而不返回新对象。

obj = pd.Series(np.arange(5), index=['a', 'b', 'c', 'd', 'e'])
print(obj)
# a    0
# b    1
# c    2
# d    3
# e    4
# dtype: int64

obj1 = obj.drop('c')
print(obj1)
# a    0
# b    1
# d    3
# e    4
# dtype: int64
print(obj1.drop(['d', 'e']))
# a    0
# b    1
# dtype: int64

对比inplace=TrueFalse的区别。inplace=False时,obj的值没有变化。

obj = pd.Series(np.arange(5), index=['a', 'b', 'c', 'd', 'e'])

print(obj.drop('c', inplace=False))  # 说明生成了新对象
# a    0
# b    1
# d    3
# e    4
# dtype: int64

print(obj)
# a    0
# b    1
# c    2
# d    3
# e    4
# dtype: int64

obj.drop('c', inplace=True)输出是None,说明没有生成新对象,变化直接作用到obj上。

obj = pd.Series(np.arange(5), index=['a', 'b', 'c', 'd', 'e'])
print(obj)

print(obj.drop('c', inplace=True))  # 说明没有生成新对象
# None

print(obj)
# a    0
# b    1
# d    3
# e    4
# dtype: int64

下例演示了轴向的效果。 如果不指定轴向axis,drop()会默认沿axis=0进行,所以,在0轴上执行data.drop(['one', 'two'])会报错。 axis='columns与指定axis=1同样效果。

data = pd.DataFrame(
    np.arange(16).reshape(4, 4),
    index=['Ohio', 'Colorado', 'Utah', 'New York'],
    columns=['one', 'two', 'three', 'four']
)

print(data)
#           one  two  three  four
# Ohio        0    1      2     3
# Colorado    4    5      6     7
# Utah        8    9     10    11
# New York   12   13     14    15

# 沿0轴操作,删除符合条件的行记录
print(data.drop(['Ohio', 'Colorado']))
#           one  two  three  four
# Utah        8    9     10    11
# New York   12   13     14    15

print(data.drop(['one', 'two']))
# KeyError: "['one' 'two'] not found in axis"

# 沿1轴操作,删除符合条件的列记录
print(data.drop(['one', 'two'], axis=1))
#           three  four
# Ohio          2     3
# Colorado      6     7
# Utah         10    11
# New York     14    15

print(data.drop(['one', 'two'], axis='columns'))
#           three  four
# Ohio          2     3
# Colorado      6     7
# Utah         10    11
# New York     14    15

再通过下例体会一下inplace参数的不同效果。

data = pd.DataFrame(
    {
        'Name': ['Shobhit', 'vaibhav', 'vimal', 'Sourabh'],
        'class': [11, 12, 10, 9],
        'Age': [18, 20, 21, 17]
    }
)

print(data)
#       Name  class  Age
# 0  Shobhit     11   18
# 1  vaibhav     12   20
# 2    vimal     10   21
# 3  Sourabh      9   17

print(data.rename(columns={'Name': 'FirstName'}, inplace=False))
#   FirstName  class  Age
# 0   Shobhit     11   18
# 1   vaibhav     12   20
# 2     vimal     10   21
# 3   Sourabh      9   17

print(data)
#       Name  class  Age
# 0  Shobhit     11   18
# 1  vaibhav     12   20
# 2    vimal     10   21
# 3  Sourabh      9   17

print(data.rename(columns={'Name': 'FirstName'}, inplace=True))  # 没有生成新对象
# None

print(data)
#   FirstName  class  Age
# 0   Shobhit     11   18
# 1   vaibhav     12   20
# 2     vimal     10   21
# 3   Sourabh      9   17

索引、选择与过滤

Series的索引obj[...]与NumPy数组索引的功能类似,只不过Series的索引值可以不仅仅是整数。

下例中:

  • obj[1]通过索引位1检索,输出对应Series的值。
  • obj[1]通过索引位[1]检索,输出Series。
  • obj['b']通过索引值'b'检索,输出对应Series的值。
  • obj[['b']]通过索引值['b']检索,输出Series。
obj = pd.Series(['Shobhit', 'vaibhav', 'vimal', 'Sourabh'], index=['a', 'b', 'c', 'd'])

print(obj)
# a    Shobhit
# b    vaibhav
# c      vimal
# d    Sourabh
# dtype: object

print(obj[1])  # 通过索引位检索,输出对应Series的值
# vaibhav

print(obj[[1]])
# b    vaibhav
# dtype: object

print(obj['b'])  # 通过索引值检索,输出对应Series的值
# vaibhav

print(obj[['b']])  # 通过索引值检索,输出Series
# b    vaibhav
# dtype: object

下面一组的输出中,注意对比普通Python切片与Series的切片的差异。

obj = pd.Series(['Shobhit', 'vaibhav', 'vimal', 'Sourabh'], index=['a', 'b', 'c', 'd'])

print(obj[1])
# vaibhav

print(obj[[1]])
# b    vaibhav
# dtype: object

print(obj[1:3])
# b    vaibhav
# c      vimal
# dtype: object
print(obj['b':'d'])
# b    vaibhav
# c      vimal
# d    Sourabh
# dtype: object

Series的切片的值更新。 obj['b': 'c'] = 5是通过索引值进行更新,直接作用在objobj[1: 3] = 6是通过索引位置来更新obj

obj = pd.Series(['Shobhit', 'vaibhav', 'vimal', 'Sourabh'], index=['a', 'b', 'c', 'd'])

obj['b': 'c'] = 5
print(obj)
# a    Shobhit
# b          5
# c          5
# d    Sourabh
# dtype: object

obj[1: 3] = 6
print(obj)
# a    Shobhit
# b          6
# c          6
# d    Sourabh
# dtype: object

DataFrame的索引与切片。 data[['Three', 'Two']]选取指定列,注意输入列条件是列表['Three', 'Two']

data = pd.DataFrame(
    np.arange(16).reshape(4, 4),
    index=['Ohio', 'Colorado', 'Utah', 'New York'],
    columns=['One', 'Two', 'Three', 'Four']
)

print(data)
#           One  Two  Three  Four
# Ohio        0    1      2     3
# Colorado    4    5      6     7
# Utah        8    9     10    11
# New York   12   13     14    15

print(data['Two'])
# Ohio         1
# Colorado     5
# Utah         9
# New York    13
# Name: Two, dtype: int64

print(data[['Three', 'Two']])
#           Three  Two
# Ohio          2    1
# Colorado      6    5
# Utah         10    9
# New York     14   13
print(data[:2])
#           One  Two  Three  Four
# Ohio        0    1      2     3
# Colorado    4    5      6     7

嵌套:根据一个布尔值数组切片或选择数据。 data['Three'] > 5是一个布尔值序列。 data[data['Three'] > 5]输出条件为True的结果集。

print(data['Three'] > 5)
# Ohio        False
# Colorado     True
# Utah         True
# New York     True
# Name: Three, dtype: bool

print(data[data['Three'] > 5])
#           One  Two  Three  Four
# Colorado    4    5      6     7
# Utah        8    9     10    11
# New York   12   13     14    15

使用布尔值DataFrame进行索引,已经更新。 在下面这个例子中,这种索引方式使得DataFrame在语法上更像是NumPy二维数组。

print(data < 5)
#             One    Two  Three   Four
# Ohio       True   True   True   True
# Colorado   True  False  False  False
# Utah      False  False  False  False
# New York  False  False  False  False

data[data < 5] = 0
print(data)
#           One  Two  Three  Four
# Ohio        0    0      0     0
# Colorado    0    5      6     7
# Utah        8    9     10    11
# New York   12   13     14    15

使用loc和iloc选择数据。 使用标签名loc或标签位置iloc以NumPy风格的语法从DataFrame中选出Dataframe的行和列的子集。

data = pd.DataFrame(
    np.arange(16).reshape(4, 4),
    index=['Ohio', 'Colorado', 'Utah', 'New York'],
    columns=['One', 'Two', 'Three', 'Four']
)

print(data)
#           One  Two  Three  Four
# Ohio        0    1      2     3
# Colorado    4    5      6     7
# Utah        8    9     10    11
# New York   12   13     14    15

下例通过loc对标签名筛选行或列数据。例如,输出Colorado行标签的TwoThree这两列的值,以行记录的方式展现。

print(data.loc['Colorado', ['Two', 'Three']])  # 切片:
# Two      5
# Three    6
# Name: Colorado, dtype: int64

print(data.loc[:'Ohio', :'Two'])  # 切片: 0行,0,1列
#       One  Two
# Ohio    0    1

下例通过标签位置iloc进行类似的数据选择。 data.iloc[:3, :2][data > 4]按指定条件进行行、列筛选,符合条件[data > 4]的输出Dataframe值,不符合条件的输出NaN。

print(data.iloc[[0]])  # 0行
#       One  Two  Three  Four
# Ohio    0    1      2     3

print(data.iloc[[0], [1]])  # 切片: 0行,1列
#       Two
# Ohio    1

print(data.iloc[1:2, 1:2])  # 切片: 1行,2列
#       Two
# Ohio    1

print(data.iloc[2, [3, 0, 1]])  # 切片: 2行,依次取3,0,1列
# Four    11
# One      8
# Two      9
# Name: Utah, dtype: int64

print(data.iloc[:3, :2][data > 4])
#           One  Two
# Ohio      NaN  NaN
# Colorado  NaN  5.0
# Utah      8.0  9.0

整数索引

Pandas的Series的索引值是整数索引。

data = np.arange(3.)
ser = pd.Series(data)

print(ser)
# 0    0.0
# 1    1.0
# 2    2.0
# dtype: float64

print(ser[:1])
# 0    0.0
# dtype: float64

print(ser.loc[:1])  # loc用于标签名
# 0    0.0
# 1    1.0
# dtype: float64

print(ser.iloc[:1])  # iloc用于标签位置
# 0    0.0
# dtype: float64

data = ['1', 'b', 'e', 3]
ser = pd.Series(data)

print(ser)
# 0    1
# 1    b
# 2    e
# 3    3
# dtype: object

print(ser[:1])
# 0    1
# dtype: object

print(ser.loc[:1])
# 0    1
# 1    b
# dtype: object

print(ser.iloc[:1]) 
# 0    1
# dtype: object

对DataFrame的更新。

df1 = pd.DataFrame(np.arange(4).reshape((2, 2)), columns=list('ab'))

print(df1)
#    a  b
# 0  0  1
# 1  2  3

# 按标签名更新
df1.loc[1, :'b'] = np.nan

print(df1)
#      a    b
# 0  0.0  1.0
# 1  NaN  NaN

算术和数据对齐

Pandas支持在Series或者DataFrame对象之间进行算术运算。

例:两个Series做算术加法。

  • 返回的结果也是一个Series。
  • 返回结果的索引是每个Series的索引的并集。
  • 凡是没有在两个Series都出现的索引位置,内部数据对齐会填充缺失值NaN。缺失值会在后续的其它算术操作上产生影响。
  • 同时出现在两个Series的索引位置,Series的值做算术相加。
s1 = pd.Series(
    [7.3, -2.5, 3.4, 1.5],
    index=['a', 'c', 'd', 'e']
)

s2 = pd.Series(
    [-2.1, 3.6, -1.5, 4, 3.1],
    index=['a', 'c', 'e', 'f', 'g']
)

print(s1)
# a    7.3
# c   -2.5
# d    3.4
# e    1.5
# dtype: float64

print(s2)
# a   -2.1
# c    3.6
# e   -1.5
# f    4.0
# g    3.1
# dtype: float64

print(s1 + s2)
# a    5.2
# c    1.1
# d    NaN
# e    0.0
# f    NaN
# g    NaN
# dtype: float64

例:两个Dataframe做算术加法

  • 返回结果也是一个Dataframe。
  • 返回结果的行列索引是每个DataFrame的行列索引的并集。
  • 凡是没有在两个DataFrame都出现的位置就会被置为NaN。
  • 两个DataFrame都出现的位置,对Dataframe的值做算术加法。
df1 = pd.DataFrame(
    np.arange(9).reshape((3, 3)),
    columns=list('bcd'),
    index=['Ohio', 'Texas', 'Colorado']
)

df2 = pd.DataFrame(
    np.arange(12).reshape((4, 3)),
    columns=list('bde'),
    index=['Utah', 'Ohio', 'Texas', 'Oregon']
)

print(df1)
#           b  c  d
# Ohio      0  1  2
# Texas     3  4  5
# Colorado  6  7  8

print(df2)
#         b   d   e
# Utah    0   1   2
# Ohio    3   4   5
# Texas   6   7   8
# Oregon  9  10  11

print(df1 + df2)
#             b   c     d   e
# Colorado  NaN NaN   NaN NaN
# Ohio      3.0 NaN   6.0 NaN
# Oregon    NaN NaN   NaN NaN
# Texas     9.0 NaN  12.0 NaN
# Utah      NaN NaN   NaN NaN

在Series或者DataFrame对象之间进行算术操作时,有时需要对缺失值指定填充值,比如当轴标签在一个对象中存在,在另一个对象中不存在时,将缺失值填充为0。也就是说,如果在两个DataFrame都缺失,那么依然还会是NaN。

下例中a2是在df1df2都缺失的位置,所以即使fill_value=0,a2仍然是NaN。

df1 = pd.DataFrame(
    np.arange(4).reshape((2, 2)),
    columns=list('ab')
)

df2 = pd.DataFrame(
    np.arange(9).reshape((3, 3)),
    columns=list('bcd')
)

print(df1)
#    a  b
# 0  0  1
# 1  2  3

print(df2)
#    b  c  d
# 0  0  1  2
# 1  3  4  5
# 2  6  7  8

# 对df1和df2就算术和,对应没有行列共同的交集的地方填充空值NaN,有交集的地方求算术和。
print(df1.add(df2))
#     a    b   c   d
# 0 NaN  1.0 NaN NaN
# 1 NaN  6.0 NaN NaN
# 2 NaN  NaN NaN NaN

# 对df1和df2就算术和,对应没有行列共同的交集的地方填充空值0,有交集的地方求算术和。
print(df1.add(df2, fill_value=0))  # df2.add(df1, fill_value=0) 返回同样的结果
#      a    b    c    d
# 0  0.0  1.0  1.0  2.0
# 1  2.0  6.0  4.0  5.0
# 2  NaN  6.0  7.0  8.0

下例中b2是在df1df2都缺失的位置,所以即使fill_value=0,b2仍然是NaN。

df1 = pd.DataFrame(
    np.arange(4).reshape((2, 2)),
    columns=list('ab')
)

df2 = pd.DataFrame(
    np.arange(9).reshape((3, 3)),
    columns=list('acd')
)

print(df1)
#    a  b
# 0  0  1
# 1  2  3

print(df2)
#    a  c  d
# 0  0  1  2
# 1  3  4  5
# 2  6  7  8

print(df1.add(df2, fill_value=0))
#      a    b    c    d
# 0  0.0  1.0  1.0  2.0
# 1  5.0  3.0  4.0  5.0
# 2  6.0  NaN  7.0  8.0

下例中没有两个DataFrame共同缺失的情况。

df1 = pd.DataFrame(
    np.arange(4).reshape((2, 2)),
    columns=list('ab')
)

df2 = pd.DataFrame(
    np.arange(9).reshape((3, 3)),
    columns=list('abd')
)

print(df1)
#    a  b
# 0  0  1
# 1  2  3

print(df2)
#    a  b  d
# 0  0  1  2
# 1  3  4  5
# 2  6  7  8

print(df1.add(df2, fill_value=0))
#      a    b    d
# 0  0.0  2.0  2.0
# 1  5.0  7.0  5.0
# 2  6.0  7.0  8.0

下面是Series和DataFrame的算术方法。

  • add,radd:加法(+)
  • sub,rsub:减法(-)
  • div,rdiv:除法(/)
  • floordiv,rfloordiv:整除(//)
  • mul,rmul:乘法(*)
  • pow,rpow:幂次方(**)

上述每个方法都有一个以r开头的副本,这些副本方法的参数是翻转的。比如,求DataFrame当中所有元素的倒数1/df,可以写成df.rdiv(1)。

df1 = pd.DataFrame(
    np.arange(4).reshape((2, 2)),
    columns=list('ab')
)

df2 = pd.DataFrame(
    np.arange(9).reshape((3, 3)),
    columns=list('abd')
)

print(df1)
#    a  b
# 0  0  1
# 1  2  3

print(df2)
#    a  b  d
# 0  0  1  2
# 1  3  4  5
# 2  6  7  8

print(df1.radd(df2, fill_value=0))
#      a    b    d
# 0  0.0  2.0  2.0
# 1  5.0  7.0  5.0
# 2  6.0  7.0  8.0

print(df1.sub(df2, fill_value=0))
#      a    b    d
# 0  0.0  0.0 -2.0
# 1 -1.0 -1.0 -5.0
# 2 -6.0 -7.0 -8.0

print(df1.div(df2, fill_value=0))
#           a     b    d
# 0       NaN  1.00  0.0
# 1  0.666667  0.75  0.0
# 2  0.000000  0.00  0.0

print(df1.floordiv(df2, fill_value=0))
#      a    b    d
# 0  NaN  1.0  0.0
# 1  0.0  0.0  0.0
# 2  0.0  0.0  0.0

print(df1.mul(df2, fill_value=0))
#      a     b    d
# 0  0.0   1.0  0.0
# 1  6.0  12.0  0.0
# 2  0.0   0.0  0.0

print(df1.pow(df2, fill_value=0))
#      a     b    d
# 0  1.0   1.0  0.0
# 1  8.0  81.0  0.0
# 2  0.0   0.0  0.0

DataFrame和Series间的算术操作

DataFrame和Series间的算术操作与NumPy中不同维度数组间的操作类似。

不同维度NumPy数组间的算术操作

从arr中减去arr[0]时,减法沿0轴在每一行都进行了操作。这就是所谓的广播机制。

arr = np.arange(12).reshape((3, 4))

print(arr)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(arr[0])
# [0 1 2 3]

print(arr - arr[0])
# [[0 0 0 0]
#  [4 4 4 4]
#  [8 8 8 8]]

DataFrame和Series间算术操作

默认情况下,DataFrame和Series的数学操作中会将Series的索引和DataFrame的*列*进行匹配,并*广播到各行*.

如果一个索引值不在DataFrame的列中,也不在Series的索引中,则新对象会构建并集索引。

frame = pd.DataFrame(
    np.arange(12).reshape((4, 3)),
    columns=list('bde'),
    index=['Utah', 'Ohio', 'Texas', 'Oregon']
)

print(frame)
#         b   d   e
# Utah    0   1   2
# Ohio    3   4   5
# Texas   6   7   8
# Oregon  9  10  11

# 截取frame的第0行,列标签变成新Serise的索引。
series = frame.iloc[0]
print(series)
# b    0
# d    1
# e    2
# Name: Utah, dtype: int64


series2 = pd.Series(
    range(3),
    index=list('bef')
)

print(series2)
# b    0
# e    1
# f    2
# dtype: int64

# 截取frame的d列,行标签变成新Serise的索引。
series3 = frame['d']
print(series3)
# Utah       1
# Ohio       4
# Texas      7
# Oregon    10
# Name: d, dtype: int64

# 将Series的索引和DataFrame的列标签进行匹配,Series的值沿DataFrame的0轴广播到各个行。
print(frame - series)
# frame:                      series                           Result:
#         b   d   e           # b    0                         #         b  d  e
# Utah    0   1   2           # d    1                         # Utah    0  0  0
# Ohio    3   4   5           # e    2                         # Ohio    3  3  3
# Texas   6   7   8           # Name: Utah, dtype: int64       # Texas   6  6  6
# Oregon  9  10  11                                            # Oregon  9  9  9

# 将Series的索引和DataFrame的列标签进行匹配,Series的值沿DataFrame的0轴广播到各个行,缺失位置填充空值NaN。
print(frame - series2)
# frame:                      series2                          Result:
#         b   d   e           # b    0                         #           b   d     e   f
# Utah    0   1   2           # e    1                         # Utah    0.0 NaN   1.0 NaN
# Ohio    3   4   5           # f    2                         # Ohio    3.0 NaN   4.0 NaN
# Texas   6   7   8           # dtype: int64                   # Texas   6.0 NaN   7.0 NaN
# Oregon  9  10  11                                            # Oregon  9.0 NaN  10.0 NaN

# 改为在列上进行广播,在行上匹配,必须作用在某种算术方法上。下例中Series的值沿DataFrame的0轴广播到各个行(按index匹配进行行操作)。
print(frame.sub(series3, axis='index'))  # 或axis=0
# frame:                       series3                         Result:
#         b   d   e            # Utah       1                  #         b  d  e
# Utah    0   1   2            # Ohio       4                  # Utah   -1  0  1
# Ohio    3   4   5            # Texas      7                  # Ohio   -1  0  1
# Texas   6   7   8            # Oregon    10                  # Texas  -1  0  1
# Oregon  9  10  11            # Name: d, dtype: int64         # Oregon -1  0  1

函数应用和映射

NumPy的通用函数(逐元素数组方法)对pandas对象(DataFrame和Series)也有效。

frame = pd.DataFrame(
    np.random.randn(4, 3),
    columns=list('bde'),
    index=['Utah', 'Ohio', 'Texas', 'Oregon']
)

print(frame)
#                b         d         e
# Utah    2.737734 -0.379977  0.758933
# Ohio    0.847497  0.839583 -2.192021
# Texas  -0.907544 -0.457436 -1.907396
# Oregon  0.389362  0.250170  1.065889

# 对DataFrame对象计算绝对值。
print(np.abs(frame))
#                b         d         e
# Utah    2.737734  0.379977  0.758933
# Ohio    0.847497  0.839583  2.192021
# Texas   0.907544  0.457436  1.907396
# Oregon  0.389362  0.250170  1.065889

# f返回一个标量值
f = lambda x: x.max() - x.min()

# 沿0轴应用f(对每列的所有行元素进行f计算), 默认axis=0
print(frame.apply(f))
# b    3.645278
# d    1.297019
# e    3.257911
# dtype: float64

# 沿1轴应用f(对每行的所有列元素进行f计算)
print(frame.apply(f, axis=1))
# Utah      3.117711
# Ohio      3.039518
# Texas     1.449961
# Oregon    0.815720
# dtype: float64


# 定义函数f,返回带有多个值的Series。
def f(x):
    return pd.Series(
        [x.min(), x.max()],
        index=['min', 'max']
    )

print(frame.apply(f))
#             b         d         e
# min -0.907544 -0.457436 -2.192021
# max  2.737734  0.839583  1.065889


# 定义函数f,使用applymap方法格式化字符,将一个逐元素的函数应用到Series上。
f = lambda x: '%.2f' % x
print(frame.applymap(f))  # 
#             b      d      e
# Utah     2.74  -0.38   0.76
# Ohio     0.85   0.84  -2.19
# Texas   -0.91  -0.46  -1.91
# Oregon   0.39   0.25   1.07

print(frame['e'].map(f))
# Utah       0.76
# Ohio      -2.19
# Texas     -1.91
# Oregon     1.07
# Name: e, dtype: object

排序和排名

使用sort_index方法,按行或列索引进行字典型排序,返回一个新的排序好的Pandas对象。

Series排序

对Series进行索引排序和值排序。

obj = pd.Series(
    range(4),
    index=list('dabc')
)

print(obj)
# d    0
# a    1
# b    2
# c    3
# dtype: int64

print(obj.sort_index())
# a    1
# b    2
# c    3
# d    0
# dtype: int64

# print(obj.sort_values())
# d    0
# a    1
# b    2
# c    3
# dtype: int64

默认情况下,所有的缺失值都会被排序至Series的尾部。

obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])

print(obj)
# 0    4.0
# 1    NaN
# 2    7.0
# 3    NaN
# 4   -3.0
# 5    2.0
# dtype: float64

print(obj.sort_values())
# 4   -3.0
# 5    2.0
# 0    4.0
# 2    7.0
# 1    NaN
# 3    NaN
# dtype: float64

DataFrame排序

frame = pd.DataFrame(
    [[0, 1, 10, 3],
     [4, 5, 6, 21],
     [8, 9, 2, 21]],
    index=['three', 'one', 'five'],
    columns=list('dabc')
)

print(frame)
#        d  a   b   c
# three  0  1  10   3
# one    4  5   6  21
# five   8  9   2  21

print(frame.index)
# Index(['three', 'one', 'five'], dtype='object')

# 默认0轴,所有行进行索引首字母升序
print(frame.sort_index())
#        d  a   b   c
# five   8  9   2  21
# one    4  5   6  21
# three  0  1  10   3

# 指定0轴,所有行进行索引首字母升序
print(frame.sort_index(axis=0))
#        d  a   b   c
# five   8  9   2  21
# one    4  5   6  21
# three  0  1  10   3

# 指定0轴,所有行进行索引首字母降序
print(frame.sort_index(axis=0, ascending=False))
#        d  a   b   c
# three  0  1  10   3
# one    4  5   6  21
# five   8  9   2  21

# 指定1轴,所有列进列索引首字母升序
print(frame.sort_index(axis=1))
#        a   b   c  d
# three  1  10   3  0
# one    5   6  21  4
# five   9   2  21  8

# 指定1轴,所有列进列索引首字母降序
print(frame.sort_index(axis=1, ascending=False))
#        d   c   b  a
# three  0   3  10  1
# one    4  21   6  5
# five   8  21   2  9

# 按指定单列进行值排序(降序)
print(frame.sort_values(by=['c'], ascending=False))
#        d  a   b   c
# one    4  5   6  21
# five   8  9   2  21
# three  0  1  10   3

# 按指定多列进行值排序(降序),先对b降序,再对d降序
print(frame.sort_values(by=['c', 'd'], ascending=False)) 
#        d  a   b   c
# five   8  9   2  21
# one    4  5   6  21
# three  0  1  10   3

排名

**排名**是指对数组从1到有效数据点总数分配名次的操作。

  • Series和DataFrame的rank方法是实现排名的方法,
  • df.rank(ascending=False, method='max')
  • ascending:排名方式,默认从低到高,ascending=False 表示从高到低;
  • method:排名方式,包括:
    • average:默认,在相等分组中,为各个值分配平均排名,即相同值的和除以该值的个数,即为该值的名次。
    • min:使用整个分组的最小排名,即,对应相同值,取在顺序排名中最小的那个排名作为所有该值的排名。
    • max:使用整个分组的最大排名,即,对应相同值,取在顺序排名中最大的那个排名作为所有该值的排名。
    • first:按值再原始数据中出现顺序分配排名,谁出现的位置靠前,谁的排名靠前。
    • dense:类似min方法,但排名总是在组间增加1,而不是组中相同的元素数,即相同值的排名相同,其他依次加1即可。
  • 默认情况下,rank是通过“为各组分配一个平均排名”的方式破坏平级关系
# 按照每个元素的大小顺序给出一个平均排名
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])

print(obj)
# 0    7
# 1   -5
# 2    7
# 3    4
# 4    2
# 5    0
# 6    4
# dtype: int64

print(obj.rank())
# 0    6.5
# 1    1.0
# 2    6.5
# 3    4.5
# 4    3.0
# 5    2.0
# 6    4.5
# dtype: float64

# index  value rank
#     2     -5    1
#     6      0    2
#     5      2    3
#     4      4    4.5
#     7      4    4.5
#     1      7    6.5
#     3      7    6.5


# 根据元素的观察顺序进行分配。元素0和2没有使用平均排名6.5,它们被设成了6和7,因为数据中标签0位于标签2的前面。
print(obj.rank(method='first'))
# 0    6.0
# 1    1.0
# 2    7.0
# 3    4.0
# 4    3.0
# 5    2.0
# 6    5.0
# dtype: float64

# 按照max进行升序和降序
print(obj.rank(ascending=False, method='max'))
print(obj.rank(ascending=True, method='max'))
# Original Series   Max with inc         Max with dec
# 0     7           # 0    2.0 (最小)     # 0    7.0 (最大)
# 1    -5           # 1    7.0 (最大)     # 1    1.0 (最小)
# 2     7           # 2    2.0 (最小)     # 2    7.0 (最大)
# 3     4           # 3    4.0           # 3    5.0
# 4     2           # 4    5.0           # 4    3.0
# 5     0           # 5    6.0           # 5    2.0
# 6     4           # 6    4.0           # 6    5.0
# dtype: float64    # dtype: float64     # dtype: float64
frame = pd.DataFrame(
    {'b': [4.3, 7, -3, 2],
     'a': [0, 1, 0, 1],
     'c': [-2, 5, 8, -2]}
)

print(frame)
#      b  a  c
# 0  4.3  0 -2
# 1  7.0  1  5
# 2 -3.0  0  8
# 3  2.0  1 -2

# 沿1轴对DataFrame进行rank操作,即,每一行各元素进行rank。
print(frame.rank(axis='columns'))  # axis=1
#      b    a    c
# 0  3.0  2.0  1.0
# 1  3.0  1.0  2.0
# 2  1.0  2.0  3.0
# 3  3.0  2.0  1.0

含有重复标签的轴索引

尽管很多pandas函数(比如reindex)需要标签是唯一的,但这个并不是强制性的。 索引的is_unique属性可以检查标签是否唯一。

带有重复索引的情况下,一个索引标签会以序列方式返回多个条目。不重复的索引则会以标量值的形式返回单个条目,这可能会使代码更复杂。

obj = pd.Series(range(5), index=['a', 'b', 'a', 'c', 'b'])
print(obj)
# a    0
# b    1
# a    2
# c    3
# b    4
# dtype: int64

print(obj.is_unique)  
# True

print(obj.index.is_unique)  
# False

# 返回重复索引对应值的序列。
print(obj['a'])
# a    0
# a    2
# dtype: int64

df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
print(df)
#           0         1         2
# a -0.726164  0.531540 -0.521611
# a -1.539807 -0.710880 -0.992789
# b -0.975970 -0.470725  0.121958
# b -0.301495  1.072322 -1.542296

print(df.index.is_unique)  
# False

print(df.loc['b'])
#           0         1         2
# b -0.520008  0.052574  0.638529
# b -1.928705 -1.099534 -1.605296

描述性统计概述与计算

pandas包含了一些常用数学、统计学方法。其中大部分属于归约或汇总统计的类别,这些方法从DataFrame的行或列中抽取一个Series或一系列值(如总和或平均值)。

与NumPy数组中的类似方法相比,pandas内建了处理缺失值的功能。

  • 归约方法: sum()
  • 积累型方法: cumsun()
  • 既不是归约型方法也不是积累型方法: describe()
df = pd.DataFrame(
    [[1.4, np.nan],
     [7.1, -4.5],
     [np.nan, np.nan],
     [0.75, -1.3]],
    index=list('abcd'),
    columns=['one', 'two']
)

print(df)
#     one  two
# a  1.40  NaN
# b  7.10 -4.5
# c   NaN  NaN
# d  0.75 -1.3

# axis=0, 返回一个每列算术和的Series
print(df.sum())
# one    9.25
# two   -5.80
# dtype: float64

# axis=1且skipna=True, 返回一个每行和的Series, 忽略NA值, 填0。
print(df.sum(axis=1))
# a    1.40
# b    2.60
# c    0.00
# d   -0.55
# dtype: float64

# 不忽略NA值,填NaN。
print(df.sum(axis=1, skipna=False))
# a     NaN
# b    2.60
# c     NaN
# d   -0.55
# dtype: float64

# 只有1级索引,所以level=0和原索引没有区别,NaN填充0。
print(df.groupby(level=0).sum())
#     one  two
# a  1.40  0.0
# b  7.10 -4.5
# c  0.00  0.0
# d  0.75 -1.3

# 列one的最大值是在索引b, 列two的最大值是在索引d
print(df.idxmax())
# one    b
# two    d
# dtype: object

print(df.idxmin())
# one    d
# two    b
# dtype: object

# cumsun的意思是第n次的和是n-1次的和与n的和,one列d行的和就是one列a、b、c、d值的总和。
print(df.cumsum())
#     one  two
# a  1.40  NaN
# b  8.50 -4.5
# c   NaN  NaN
# d  9.25 -5.8

通过describe产生统计信息,注意,数值型和非数值型的describe的信息是不同的。

# 一次性产生多个汇总统计
print(df.describe())
#             one       two
# count  3.000000  2.000000
# mean   3.083333 -2.900000
# std    3.493685  2.262742
# min    0.750000 -4.500000
# 25%    1.075000 -3.700000
# 50%    1.400000 -2.900000
# 75%    4.250000 -2.100000
# max    7.100000 -1.300000

obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
print(obj)
# 0     a
# 1     a
# 2     b
# 3     c
# 4     a
# 5     a
# 6     b
# 7     c
# 8     a
# 9     a
# 10    b
# 11    c
# 12    a
# 13    a
# 14    b
# 15    c
# dtype: object

# 针对非数值型数据,describe产生另一种汇总统计
print(obj.describe())
# count     16
# unique     3
# top        a
# freq       8
# dtype: object

相关性和协方差

协方差与相关系数也是在时域分析时常见的两个概念,他们都是用来描述数据“像不像”的。

协方差的通俗理解:

  • 两个变量在变化过程中是同方向变化还是反方向变化?相同或者相反程度如何?
  • 你变大,同时我变大,说明两个变量是同向变化,这时协方差就是正的。
  • 你变大,同时我变小,说明两个变量是反向变化,这时协方差就是负的。
  • 从数值看,协方差的数值越大,两个变量同向程度也就越大。反之亦然。

相关系数的通俗理解:

  • 用X,Y的协方差除以X的标准差和Y的标准差。相关系数也可以看成协方差,一种提出了两个变量量纲影响、标准化后的特殊协方差。所以:也可以反映两个变量变化时是同向还是反向,如果同向变化就为正,反向变化就为负。
  • 由于是标准版后的协方差,相关系数消除了两个变量变化幅度的影响,而只是单纯反应两个变量每单位变化时的相似程度。

总结:

  • 对于两个变量X、Y,
  • 当他们的相关系数为1时,说明两个变量变化时的正向相似度最大。
  • 当他们的相关系数为-1时,说明两个变量变化的反向相似度最大。
  • 随着他们相关系数减小,两个变量变化时的相似度也变小,当相关系数为0时,两个变量的变化过程没有任何相似度,也即两个变量无关。
  • 当相关系数继续变小,小于0时,两个变量开始出现反向的相似度,随着相关系数继续变小,反向相似度会逐渐变大。

下面的例子使用 pandas-datareader:

在所有例子中,在计算相关性之前,数据点已经按标签进行了对齐。

下例需要通过pandas-datareader库从Yahoo! Finance上获取的包含股价和交易量的DataFrame。

import pandas_datareader.data as web

all_data = {

    ticker: web.get_data_yahoo(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']
}

price = pd.DataFrame(
    {
        ticker: data['Adj Close'] for ticker, data in all_data.items()
    }
)

volume = pd.DataFrame(
    {
        ticker: data['Volume'] for ticker, data in all_data.items()
    }
)

returns = price.pct_change()
print(returns.tail())
#                 AAPL       IBM      MSFT      GOOG
# Date
# 2021-08-09 -0.000342 -0.008424 -0.003904  0.007049
# 2021-08-10 -0.003354  0.000920 -0.006555  0.000685
# 2021-08-11  0.001786  0.005305  0.001781 -0.002947
# 2021-08-12  0.020773  0.006614  0.009967  0.005084
# 2021-08-13  0.001410  0.000769  0.010490  0.000119

Series的corr方法计算的是两个Series中重叠的、非NA的、按索引对齐的值的相关性。相应地,cov计算的是协方差

print(returns['MSFT'])
# Date
# 2016-08-15         NaN
# 2016-08-16   -0.005540
# 2016-08-17    0.002089
# 2016-08-18    0.000695
# 2016-08-19    0.000347
#                 ...
# 2021-08-09   -0.003904
# 2021-08-10   -0.006555
# 2021-08-11    0.001781
# 2021-08-12    0.009967
# 2021-08-13    0.010490
# Name: MSFT, Length: 1259, dtype: float64

# Series的corr方法计算的是两个Series中重叠的、非NA的、按索引对齐的值的相关性。
print(returns['MSFT'].corr(returns['IBM']))  
# 0.5175237180581937

# 等同写法,MSFT是一个有效的Python属性
print(returns.MSFT.corr(returns.IBM))  
# 0.5175237180581937

# Series的cov方法计算的是两个Series中值的协方差。
print(returns['MSFT'].cov(returns['IBM']))
# 0.0001452224236764915

DataFrame的corr和cov方法会分别以DataFrame的形式返回相关性和协方差矩阵。

print(returns.corr())
#           AAPL       IBM      MSFT      GOOG
# AAPL  1.000000  0.441111  0.735539  0.661961
# IBM   0.441111  1.000000  0.517524  0.484230
# MSFT  0.735539  0.517524  1.000000  0.775756
# GOOG  0.661961  0.484230  0.775756  1.000000

# 给corrwith方法,传入一个Series时,会返回一个含有为每列计算相关性值的Series
print(returns.corrwith(returns['IBM']))  
# AAPL    0.441111
# IBM     1.000000
# MSFT    0.517524
# GOOG    0.484230
# dtype: float64

# 给corrwith方法,传入一个DataFrame时,会计算匹配到列名的相关性数值。下面是计算交易量百分比变化的相关性
print(returns.corrwith(volume))  
# AAPL   -0.063111
# IBM    -0.103721
# MSFT   -0.056842
# GOOG   -0.119026
# dtype: float64

print(returns.cov())
#           AAPL       IBM      MSFT      GOOG
# AAPL  0.000361  0.000137  0.000240  0.000211
# IBM   0.000137  0.000268  0.000145  0.000133
# MSFT  0.000240  0.000145  0.000294  0.000224
# GOOG  0.000211  0.000133  0.000224  0.000282

唯一值、计数和成员属性

obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'a', 'b', 'b', 'c', 'c'])

print(obj)
# 0    c
# 1    a
# 2    d
# 3    a
# 4    a
# 5    a
# 6    b
# 7    b
# 8    c
# 9    c
# dtype: object

函数unique给出Series中的唯一值。

print(obj.unique())
# ['c' 'a' 'd' 'b']

print(obj.sort_values().unique())
# ['a' 'b' 'c' 'd']

# value_counts计算Series包含的值的个数
print(obj.value_counts())
# a    4
# c    3
# b    2
# d    1
# dtype: int64

# 这里value_counts不是Series的方法,是pandas顶层方法
print(pd.value_counts(obj.values, sort=True))
# a    4
# c    3
# b    2
# d    1
# dtype: int64

print(obj.isin(['b', 'c']))
# 0     True
# 1    False
# 2    False
# 3    False
# 4    False
# 5    False
# 6     True
# 7     True
# 8     True
# 9     True
# dtype: bool

# 将上面的结果作为列表输入的条件,输出为True的结果
print(obj[obj.isin(['b', 'c'])])
# 0    c
# 6    b
# 7    b
# 8    c
# 9    c
# dtype: object

参考: pandas.Index.get_indexer

obj1 = pd.Series(['c', 'a', 'd', 'a', 'a', 'a', 'b', 'b', 'c', 'c'])
obj2 = pd.Series(['c', 'a', 'b'])

print(pd.Index(obj1))
# Index(['c', 'a', 'd', 'a', 'a', 'a', 'b', 'b', 'c', 'c'], dtype='object')

print(pd.Index(obj2))
# Index(['c', 'a', 'b'], dtype='object')

# 这里0对应obj2里面的c在job1的位置,以此类推,生成新的索引列表
print(pd.Index(obj2).get_indexer(obj1))
# [ 0  1 -1  1  1  1  2  2  0  0]

计算DataFrame多个相关列的直方图。

data = pd.DataFrame(
    {
        'Que1': [1, 3, 4, 3, 4],
        'Que2': [2, 3, 1, 2, 3],
        'Que3': [1, 5, 2, 4, 4],
    }
)

print(data)
#    Que1  Que2  Que3
# 0     1     2     1
# 1     3     3     5
# 2     4     1     2
# 3     3     2     4
# 4     4     3     4

result = data.apply(pd.value_counts).fillna(0)

# 下面结果中的行标签是所有列中出现的不同值,数值则是这些不同值在每个列中出现的次数,例如:数字5只在Que3里面出现了一次
print(result)
#    Que1  Que2  Que3
# 1   1.0   1.0   1.0
# 2   0.0   2.0   1.0
# 3   2.0   2.0   0.0
# 4   2.0   0.0   2.0
# 5   0.0   0.0   1.0