第十六天
Pandas数值运算方法
Numpy的基本功能之一是快速对每个元素进行运算,即包括基本算术,也包括更复杂的运算。Pandas继承了Numpy的功能。
但是Pandas也实现了一些高效技巧:对于一元运算(像函数与三角函数),这些通用函数将在输出结果中保留索引和列标签;而对于二元运算(如加法和乘法),Pandas 在传递通用函数时会自动对齐索引进行计算。这就意味着,保存数据内容与组合不同来源的数据——两处在NumPy 数组中都容易出错的地方——变成了Pandas 的杀手锏。
一、通用函数:保留索引
因为Pandas 是建立在NumPy 基础之上的,所以NumPy 的通用函数同样适用于Pandas 的Series 和DataFrame 对象。
import pandas as pd
import numpy asnp
rng = np.random.RandomState(42)
ser = pd.Series(rng.randint(0, 10, 4))
ser
#
0 6
1 3
2 7
3 4
dtype: int64
df = pd.DataFrame(rng.randint(0, 10, (3, 4)),columns=['A','B','C','D'])
df
#
A B C D
0 6 9 2 6
1 7 4 3 7
2 7 2 5 4
如果对这两个对象的其中一个使用NumPy 通用函数,生成的结果是另一个保留索引的Pandas 对象:
np.exp(ser)
#
0 403.428793
1 20.085537
2 1096.633158
3 54.598150
dtype: float64
或者,再做一个比较复杂的运算:
np.sin(df * np.pi / 4)
#
A B C D
0 -1.000000 7.071068e-01 1.000000 -1.000000e+00
1 -0.707107 1.224647e-16 0.707107 -7.071068e-01
2 -0.707107 1.000000e+00 -0.707107 1.224647e-16
二、通用函数:索引对齐
当在两个Series 或DataFrame 对象上进行二元计算时,Pandas 会在计算过程中对齐两个对象的索引。当你处理不完整的数据时,这一点非常方便。
1.Series索引对齐
假如你要整合两个数据源的数据,其中一个是美国面积最大的三个州的面积数据,另一个是美国人口最多的三个州的人口数据。
area = pd.Series({'Alaska':1723337,'Texas':695662,'California':423967},name='area')
population = pd.Series({'California':38332521,'Texas':26448193,'New York':19651127},name='population')
population / area
Alaska NaN
California 90.413926
New York NaN
Texas 38.018740
dtype: float64
结果数组的索引是两个输入数组索引的并集。我们也可以用Python 标准库的集合运算法则来获得这个索引:
area.index | population.index
Index(['Alaska', 'California', 'New York', 'Texas'], dtype='object')
对于缺失位置的数据,Pandas 会用NaN 填充,表示“此处无数”。这是Pandas 表示缺失值的方法。
这种索引对齐方式是通过Python 内置的集合运算规则实现的,任何缺失值默认都用NaN 填充:
A = pd.Series([2, 4, 6], index=[0, 1, 2])
B = pd.Series([1, 3, 5], index=[1, 2, 3])
A + B
#
0 NaN
1 5.0
2 9.0
3 NaN
dtype: float64
如果用NaN 值不是我们想要的结果,那么可以用适当的对象方法代替运算符。
A.add(B, fill_value=0)
#
0 2.0
1 5.0
2 9.0
3 5.0
dtype: float64
2.DataFrame索引对齐
在计算两个DataFrame 时,类似的索引对齐规则也同样会出现在共同(并集)列中:
A = pd.DataFrame(rng.randint(0, 20, (2, 2)), columns=list('AB'))
A
#
A B
0 1 11
1 5 1
B = pd.DataFrame(rng.randint(0, 30, (3, 3)), columns=list('BAC'))
B
#
B A C
0 4 0 9
1 5 8 0
2 9 2 6
A + B
#
A B C
0 1.0 15.0 NaN
1 13.0 6.0 NaN
2 NaN NaN NaN
三、通用函数:DataFrame与Series的运算
我们经常需要对一个DataFrame 和一个Series 进行计算,行列对齐方式与之前类似。也就是说,DataFrame 和Series 的运算规则,与NumPy 中二维数组与一维数组的运算规则是一样的。
A = rng.randint(10, size=(3, 4))
A
#
array([[3, 8, 2, 4],
[2, 6, 4, 8],
[6, 1, 3, 8]])
A - A[0]
#
array([[ 0, 0, 0, 0],
[-1, -2, 2, 4],
[ 3, -7, 1, 4]])
根据NumPy 的广播规则,让二维数组减自身的一行数据会按行计算。
在Pandas里面默认也是按行运算的:
df = pd.DataFrame(A, columns=list('QRST'))
df - df.iloc[0]
#
Q R S T
0 0 0 0 0
1 -1 -2 2 4
2 3 -7 1 4
如果想按照列计算,那么就需要通过axis
参数设置:
df.subtract(df['R'], axis=0)
#
Q R S T
0 -5 0 -6 -4
1 -4 0 -2 2
2 5 0 2 7
会发现DataFrame / Series 的运算与前面介绍的运算一样,结果的索引都会自动对齐。这些行列索引的保留与对齐方法说明Pandas 在运算时会一直保存这些数据内容,从而避免在处理数据类型有差异和/ 或维度不一致的NumPy 数组时可能遇到的问题。