一.汇总和计算描述统计
pandas对象拥有一组常用的数学和统计方法。它们大部分属于约简和汇总统计,用于从Series中提取单个值(如sum或mean)或从DataFrame的行或列中提取一个Series。跟对应的Numpy数组方法相比,它们都是基于没有缺失数据的假设而构建的。接下来看一个简单的DataFrame:
In [1]: import numpy as np
In [2]: from pandas import Series,DataFrame
In [3]: df=DataFrame([[1.4,np.nan],[8.4,-3.5],[np.nan,np.nan],[0.65,-2.3]],inde
...: x=['a','b','c','d'],columns=['one','two'])
In [4]: df
Out[4]:
one two
a 1.40 NaN
b 8.40 -3.5
c NaN NaN
d 0.65 -2.3
调用DataFrame的sum方法将会返回一个含有列小计的Series:
In [5]: df.sum()
Out[5]:
one 10.45
two -5.80
dtype: float64
传入axis=1将会按行进行求和运算:
In [6]: df.sum(axis=1)
Out[6]:
a 1.40
b 4.90
c 0.00
d -1.65
dtype: float64
NA值会自动被排除,除非整个切片(这里指的是行或列)都是NA.通过skipna选项可以禁用该功能:
In [7]: df.sum(axis=1,skipna=False)
Out[7]:
a NaN
b 4.90
c NaN
d -1.65
dtype: float64
In [8]: df.mean(axis=1,skipna=False) #求每行的平均值,显示NA值,如果某行有NaN值,则这行平均值为NaN
Out[8]:
a NaN
b 2.450
c NaN
d -0.825
dtype: float64
下表列出了这些约简方法的常用选项。
选项 说明
axis 约简的轴。DataFrame的行用0,列用1
skipna 排除缺失值,默认值为True
level 如果轴是层次化索引的(积MultiIndex),则根据level分组约简
有些方法(如idxmin何idxmax)返回的是间接统计(比如达到最小值或最大值的索引):
In [12]: df.idxmax()
Out[12]:
one b
two d
dtype: object
In [13]: df.idxmin()
Out[13]:
one d
two b
dtype: object
另一些方法则是累计型的:
In [14]: df.cumsum()
Out[14]:
one two
a 1.40 NaN
b 9.80 -3.5
c NaN NaN
d 10.45 -5.8
还有一种方法,它既不是约简型也不是累计型。describe就是一个例子,它用于一次性产生多个汇总统计:
In [16]: df.describe()
Out[16]:
one two
count 3.000000 2.000000
mean 3.483333 -2.900000
std 4.274440 0.848528
min 0.650000 -3.500000
25% 1.025000 -3.200000
50% 1.400000 -2.900000
75% 4.900000 -2.600000
max 8.400000 -2.300000
对于非数值型数据,describe会产生另一种汇总统计
In [17]: obj=Series(['a','a','b','c']*3)
In [18]: obj
Out[18]:
0 a
1 a
2 b
3 c
4 a
5 a
6 b
7 c
8 a
9 a
10 b
11 c
dtype: object
In [19]: obj.describe()
Out[19]:
count 12
unique 3
top a
freq 6
dtype: object
下表列出了所有与描述统计相关的方法。
方法 说明
count 非NA值的数量
describe 针对Series或各DataFrame列计算汇总统计
min、max 计算最小值和最大值
argmin、argmax 计算能够获取到最小值和最大值的索引位置(整数)
idxmin、idxmax 计算能够获取到最小值和最大值的索引值
quantile 计算样本的分位数(0到1)
sum 值的总和
mean 值的平均值
median 值的算术中位数(50%分位数)
mad 根据平均值计算平均绝对离差
var 样本值的标准差
skew 样本值的偏度(三阶矩)
kurt 样本值的峰值(四阶矩)
cumsum 样本值的累计和
cummin、cummax 样本值的累计最大值和累计最小值
cumprod 样本值的累计积
diff 计算一阶差分(对时间序列很有用)
pct_change 计算百分数变化
二.相关系数与协方差
有些汇总统计(如相关系数和协方差)是通过参数对计算出来的。我们来看几个DataFrame:
In [30]: df=DataFrame(np.arange(20).reshape(10,2))
In [31]: df
Out[31]:
0 1
0 0 1
1 2 3
2 4 5
3 6 7
4 8 9
5 10 11
6 12 13
7 14 15
8 16 17
9 18 19
#计算列的百分比变化,如果要计算行,设置axis=1
In [32]: df.pct_change()
Out[32]:
0 1
0 NaN NaN
1 inf 2.000000
2 1.000000 0.666667
3 0.500000 0.400000
4 0.333333 0.285714
5 0.250000 0.222222
6 0.200000 0.181818
7 0.166667 0.153846
8 0.142857 0.133333
9 0.125000 0.117647
In [34]: df.pct_change(axis=1)
Out[34]:
0 1
0 NaN inf
1 NaN 0.500000
2 NaN 0.250000
3 NaN 0.166667
4 NaN 0.125000
5 NaN 0.100000
6 NaN 0.083333
7 NaN 0.071429
8 NaN 0.062500
9 NaN 0.055556
#默认输出5行,可以设置n参数来设置输出的行数
In [33]: df.head()
Out[33]:
0 1
0 0 1
1 2 3
2 4 5
3 6 7
4 8 9
In [33]: df.head()
Out[33]:
0 1
0 0 1
1 2 3
2 4 5
3 6 7
4 8 9
In [36]: df.tail()
Out[36]:
0 1
5 10 11
6 12 13
7 14 15
8 16 17
9 18 19
计算DataFrame列与列的相关系数和协方差
In [38]: df=DataFrame(np.arange(9).reshape((3,3)),index=['a','b','c'],columns=[
...: 'one','two','three'])
In [39]: df
Out[39]:
one two three
a 0 1 2
b 3 4 5
c 6 7 8
#计算第一列和第二列的相关系数
In [40]: df.one.corr(df.two)
Out[40]: 1.0
#返回一个相关系数矩阵
In [41]: df.corr()
Out[41]:
one two three
one 1.0 1.0 1.0
two 1.0 1.0 1.0
three 1.0 1.0 1.0
#计算第一列和第二列的协方差
In [42]: df.one.cov(df.two)
Out[42]: 9.0
#返回一个协方差矩阵
In [43]: df.cov()
Out[43]:
one two three
one 9.0 9.0 9.0
two 9.0 9.0 9.0
three 9.0 9.0 9.0
计算DataFrame与列或者Series的相关系数
In [38]: df=DataFrame(np.arange(9).reshape((3,3)),index=['a','b','c'],columns=[
...: 'one','two','three'])
In [39]: df
Out[39]:
one two three
a 0 1 2
b 3 4 5
c 6 7 8
#计算df与第三列的相关系数
In [44]: df.corrwith(df.three)
Out[44]:
one 1.0
two 1.0
three 1.0
dtype: float64
#计算df与Series的相关系数,在定义Series时,索引一定要和df索引一样
In [45]: obj=Series([8,24,23],index=['a','b','c'])
In [46]: obj
Out[46]:
a 8
b 24
c 23
dtype: int64
In [47]: df.corrwith(obj)
Out[47]:
one 0.836784
two 0.836784
three 0.836784
dtype: float64
注意:在使用DataFrame或Series在计算相关系数或者协方差的时候,都会计算索引重叠的、非NA的、按照索引对齐原则,对于无法对齐的索引会使用NA值进行填充。在使用DataFrame与指定的行或列或Series计算协方差和相关系数的时候,默认都是与DataFrame的列进行计算,如果想要计算行,设置axis参数为1即可。
三.唯一值、值计数以及成员资格
还有一类方法可以从一维Series的值中抽取信息。以下面这个Series为例:
In [50]: obj=Series(['c','a','d','a','a','c','c','b','b'])
In [51]: obj
Out[51]:
0 c
1 a
2 d
3 a
4 a
5 c
6 c
7 b
8 b
dtype: object
第一个函数是unique,它可以得到Series中的唯一值数组:
In [52]: uniques=obj.unique()
In [53]: uniques
Out[53]: array(['c', 'a', 'd', 'b'], dtype=object)
返回的唯一值是未排序的,如果需要的话,可以对结果再次进行排序(uniques.sort())。value_counts用于计算一个Series各值出现的频率:
In [54]: uniques.sort()
In [58]: uniques
Out[58]: array(['a', 'b', 'c', 'd'], dtype=object)
In [60]: obj.value_counts()
Out[60]:
a 3
c 3
b 2
d 1
dtype: int64
为了便于查看,结果Series是按值频率降序排列的。value_counts还是一个顶级pandas方法,可用于任何数组或序列:
In [61]: pd.value_counts(obj.values,sort=False)
Out[61]:
d 1
c 3
b 2
a 3
dtype: int64
最后是isin,它用于判断矢量化集合的成员资格,可用于选取Series中或DataFrame列中数据的子集:
In [63]: mask=obj.isin(['b','c'])
In [64]: mask
Out[64]:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
In [65]: obj[mask]
Out[65]:
0 c
5 c
6 c
7 b
8 b
dtype: object
下表为唯一值,值计数,成员资格方法
方法 说明
isin 计算一个表示"Series各值是否包含于传入的值序列中"的布尔型数组
unique 计算Series中的唯一值数组,按发现的顺序返回
value_counts 返回一个Series,其索引为唯一值,其值为频率,按计数值降序排列
四.处理缺失数据
缺失数据(missing data)在大部分数据分析应用中都很常见,pandas的设计目标之一就是让缺失数据的处理任务尽量轻松。例如,pandas对象上的所有描述统计都排除了缺失数据,正如我们在本章稍早的地方所看到的那样。
pandas使用浮点值NaN(Not a Number)表示浮点和非浮点数组中的缺失数据。它只是一个便于被检测出来的标记而已:
In [66]: s=Series(['Calvin','Kobe',np.nan,'Michale'])
In [67]: s
Out[67]:
0 Calvin
1 Kobe
2 NaN
3 Michale
dtype: object
In [68]: s.isnull()
Out[68]:
0 False
1 False
2 True
3 False
dtype: bool
Python内置的None值也会被当做NA处理:
In [69]: s[0]=None
In [70]: s.isnull()
Out[70]:
0 True
1 False
2 True
3 False
dtype: bool
不敢说pandas的NA表现形式是最优的,但它确实很简单也很可靠。由于Numpy的数据类型体系中缺乏真正的NA数据类型或位模式,所以它是能想到的最佳解决方案(一套简单的API以及足够全面的性能特征)。随着Numpy的不断发展,这个问题今后可能会发生变化。
下表为NA处理方法
方法 说明
dropna 根据各标签的值中是否存在缺失数据对轴标签进行过滤,可通过阈值调节对缺失值的容忍度
fillna 用指定值或插值方法(如ffill或bfill)填充缺失数据
isnull 返回一个含有布尔值的对象,这些布尔值表示哪些值是缺失值NA,该对象的类型与源类型一样
notnull isnull的否定式
1.滤除缺失数据
过滤掉缺失数据的办法有很多种。纯手工操作永远都是一个办法,但dropna可能会更实用一些。对于一个Series,dropna返回一个仅含非空数据和索引值的Series:
In [71]: from numpy import nan as NA
In [72]: data=Series([1,NA,5.5,NA,24])
In [73]: data.dropna()
Out[73]:
0 1.0
2 5.5
4 24.0
dtype: float64
当然,也可以通过布尔型索引达到这个目的:
In [74]: data[data.notnull()]
Out[74]:
0 1.0
2 5.5
4 24.0
dtype: float64
而对于DataFrame对象,事情就有点复杂了,你可能希望丢弃全NA或含有NA的行或列。dropna默认丢弃任何含有缺失值的行:
In [75]: data=DataFrame([[1,3,8.5],[1.4,NA,NA],[NA,NA,NA],[NA,8.8,3]])
In [77]: cleaned=data.dropna()
In [78]: data
Out[78]:
0 1 2
0 1.0 3.0 8.5
1 1.4 NaN NaN
2 NaN NaN NaN
3 NaN 8.8 3.0
In [79]: cleaned
Out[79]:
0 1 2
0 1.0 3.0 8.5
传入how='all'将只丢弃全为NA的那些行:
In [80]: cleaned=data.dropna(how='all')
In [81]: cleaned
Out[81]:
0 1 2
0 1.0 3.0 8.5
1 1.4 NaN NaN
3 NaN 8.8 3.0
要用这种方式丢弃列,只需传入axis=1即可:
In [82]: data[4]=NA
In [83]: data
Out[83]:
0 1 2 4
0 1.0 3.0 8.5 NaN
1 1.4 NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN 8.8 3.0 NaN
In [84]: data.dropna(axis=1,how='all')
Out[84]:
0 1 2
0 1.0 3.0 8.5
1 1.4 NaN NaN
2 NaN NaN NaN
3 NaN 8.8 3.0
另一个滤除DataFrame行的问题涉及时间序列数据。假设你只想留下一部分观测数据,可以用thresh参数实现此目的:
In [85]: df=DataFrame(np.random.randn(7,3))
In [86]: df.iloc[:4,1]=NA;df.iloc[:2,2]=NA
In [87]: df
Out[87]:
0 1 2
0 -0.361517 NaN NaN
1 -0.527158 NaN NaN
2 0.126102 NaN -2.513174
3 -0.585159 NaN -2.297443
4 1.030365 -0.192859 0.251283
5 -1.505839 -0.057001 0.193328
6 -1.102120 1.768084 0.280802
In [88]: df.dropna(thresh=3)
Out[88]:
0 1 2
4 1.030365 -0.192859 0.251283
5 -1.505839 -0.057001 0.193328
6 -1.102120 1.768084 0.280802
2.填充缺失数据
我们可能不想滤除缺失数据(有可能会丢弃跟它有关的其他数据),而是希望通过其他方式填补那些“空洞”。对于大多数情况而言,fillna方法是最主要的函数。通过一个常数调用fillna就会将缺失值替换为那个常数值:
In [89]: df.fillna(0)
Out[89]:
0 1 2
0 -0.361517 0.000000 0.000000
1 -0.527158 0.000000 0.000000
2 0.126102 0.000000 -2.513174
3 -0.585159 0.000000 -2.297443
4 1.030365 -0.192859 0.251283
5 -1.505839 -0.057001 0.193328
6 -1.102120 1.768084 0.280802
若是通过一个字典调用fillna,就可以实现对不同的列填充不同的值:
In [91]: df.fillna({1:0.5,2:-1}) #第二列NaN值填充为0,第二列的NaN值填充为-1
Out[91]:
0 1 2
0 -0.361517 0.500000 -1.000000
1 -0.527158 0.500000 -1.000000
2 0.126102 0.500000 -2.513174
3 -0.585159 0.500000 -2.297443
4 1.030365 -0.192859 0.251283
5 -1.505839 -0.057001 0.193328
6 -1.102120 1.768084 0.280802
fillna默认会返回新对象,但也可以对现有对象进行就地修改:
#总是返回被填充对象的引用
In [92]: _=df.fillna(0,inplace=True)
In [93]: df
Out[93]:
0 1 2
0 -0.361517 0.000000 0.000000
1 -0.527158 0.000000 0.000000
2 0.126102 0.000000 -2.513174
3 -0.585159 0.000000 -2.297443
4 1.030365 -0.192859 0.251283
5 -1.505839 -0.057001 0.193328
6 -1.102120 1.768084 0.280802
对reindex有效的那些插值方法也可用与fillna:
In [94]: df=DataFrame(np.random.randn(6,3))
In [95]: df.iloc[2:,1]=NA;df.iloc[4:,2]=NA
In [96]: df
Out[96]:
0 1 2
0 -0.098323 -2.351729 1.333503
1 0.006153 0.344866 0.729309
2 1.830895 NaN -1.002160
3 -0.573513 NaN 0.436669
4 -1.499363 NaN NaN
5 -1.371960 NaN NaN
In [97]: df.fillna(method='ffill')
Out[97]:
0 1 2
0 -0.098323 -2.351729 1.333503
1 0.006153 0.344866 0.729309
2 1.830895 0.344866 -1.002160
3 -0.573513 0.344866 0.436669
4 -1.499363 0.344866 0.436669
5 -1.371960 0.344866 0.436669
In [98]: df.fillna(method='ffill',limit=2)
Out[98]:
0 1 2
0 -0.098323 -2.351729 1.333503
1 0.006153 0.344866 0.729309
2 1.830895 0.344866 -1.002160
3 -0.573513 0.344866 0.436669
4 -1.499363 NaN 0.436669
5 -1.371960 NaN 0.436669
只要稍微动动脑子,我们就可以利用fillna实现许多别的功能。比如说,你可以传入Series的平均值或中位数:
In [99]: data=Series([1,NA,5.6,NA,8])
In [100]: data.fillna(data.mean())
Out[100]:
0 1.000000
1 4.866667
2 5.600000
3 4.866667
4 8.000000
dtype: float64
下表是fillna函数的参数
参数 说明
value 用于填充缺失值的标量值或字典对象
method 插值方式。如果函数调用时未指定其他参数的话,默认为"ffill"
axis 待填充的轴,默认axis=0
inplace 修改调用者对象而不产生副本
limit (对于前向和后向)填充可以连续填充的最大数量