5.3汇总和计算描述性统计
pandas对象拥有一组常用的数学和统计方法。他们大部分都属于约简和汇总统计,用于从Series中提取单个值(如mean或sum)或从DataFrame的行或列中提取一个Series。跟对应的NumPy数组方法相比,他们都是基于没有缺失数据的假设而构建的。
下面是一个简单的DataFrame:
In [90]: df=DataFrame([[1.4,np.nan],[7.1,-4.5],[np.nan,np.nan],[0.75,-1.3]],index=['a','b','c','d'],columns=['one','two'])
In [91]: df
Out[91]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
调用DataFrame的sum方法将会返回一个含有列的小计的Series:
In [92]: df.sum()
Out[92]:
one 9.25
two -5.80
dtype: float64
传入axis=1 将会按行进行求和计算:
In [93]: df.sum(axis=1)
Out[93]:
a 1.40
b 2.60
c 0.00
d -0.55
dtype: float64
NA值将会被自动剔除,除非整个切片都是NA。通过skipna选项可以禁用该功能:
In [94]: df.mean(axis=1,skipna=False)
Out[94]:
a NaN
b 1.300
c NaN
d -0.275
dtype: float64
表5-9给出了约简方法常用选项
有些方法(如idxmin和idxmax)返回的是间接统计(比如达到最大值或最小值的索引):
In [95]: df.idxmax()
Out[95]:
one b
two d
dtype: object
一些方法则是累计型的:
In [96]: df.cumsum()
Out[96]:
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
还有一些方法,既不是约简型也不是累计型。
In [97]: df.describe()
Out[97]:
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
对于非数值型的数据,describe会产生另一种汇总统计:
In [98]: obj=Series(['a','a','b','c']*4)
In [99]: obj.describe()
Out[99]:
count 16
unique 3
top a
freq 8
dtype: object
表5-10列出了所有与描述统计相关的方法。
5.3.1相关系数和协方差
有些汇总统计(如先关系数和协方差)是通过参数计算出来的。
5.3.2唯一值、值计数以及成员资格
从一维Series的值中抽取信息。以下这个Series为例:
In [107]: obj=Series(['c','a','d','a','a','b','b','c','c'])
第一个函数是unique,可以得到Sseries中的唯一数组:
In [108]: uniques=obj.unique()
In [109]: uniques
Out[109]: array(['c', 'a', 'd', 'b'], dtype=object)
返回值的唯一未排序的,如果需要的话,可以对结果再次进行排序(unique.sort())
value_counts 用于计算一个Series中各值出现的频率:
In [110]: obj.value_counts()
Out[110]:
c 3
a 3
b 2
d 1
dtype: int64
为了方便查看,结果Series是按值频率降序排列的。
value_counts 还是一个顶级的pandas方法,可用于任何数组的或序列:
In [111]: pd.value_counts(obj.values,sort=False)
Out[111]:
a 3
c 3
b 2
d 1
dtype: int64
最后isin,它用于判断矢量化最集合的成员资格,可用于选取Series中或DataFrame列中的数据的子集:
In [112]: mask=obj.isin(['b','c'])
In [113]: mask
Out[113]:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
In [114]: obj[mask]
Out[114]:
0 c
5 b
6 b
7 c
8 c
dtype: object
有时,你可能需要得到DataFrame中多个相关的一张柱状图。
In [115]: data=DataFrame({'Qu1':[1,3,4,3,4],'Qu2':[2,3,1,2,3],'Qu3':[1,5,2,4,4]})
In [116]: data
Out[116]:
Qu1 Qu2 Qu3
0 1 2 1
1 3 3 5
2 4 1 2
3 3 2 4
4 4 3 4
In [117]: result=data.apply(pd.value_counts).fillna(0)
In [118]: result
Out[118]:
Qu1 Qu2 Qu3
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
5.4处理缺失数据
处理缺失数据(missing data)在大部分的数据分析应用中很常见。pandas的设计目标之一就是让缺失数据的处理任务尽量轻松。
pandas使用浮点数NaN(Not a Number)表示浮点和非浮点数组中的缺失数据。只是一个便于检测出来的标记而已:
In [119]: string_data=Series(['aardvark','arrichoke',np.nan,'avocado'])
In [120]: string_data
Out[120]:
0 aardvark
1 arrichoke
2 NaN
3 avocado
dtype: object
In [121]: string_data.isnull()
Out[121]:
0 False
1 False
2 True
3 False
dtype: bool
python内置的None值也会被当做NA处理:
In [123]: string_data.isnull()
Out[123]:
0 True
1 False
2 True
3 False
dtype: bool
5.4.1滤除缺失数据
对于一个Series,dropna返回一个仅含有非空数据的和索引值的Series:
In [124]: from numpy import nan as NA
In [125]: data=Series([1,NA,3.5,NA,7])
In [126]: data.dropna()
Out[126]:
0 1.0
2 3.5
4 7.0
dtype: float64
还可以通过布尔型索引达到这个目的:
In [127]: data[data.notnull()]
Out[127]:
0 1.0
2 3.5
4 7.0
dtype: float64
对于DataFrame对象,如果希望丢弃全NA或NA的行货列,dropna默认丢弃任何含有缺失值的行:
In [129]: data=DataFrame([[1.,6.5,3.],[1.,NA,NA],[NA,NA,NA],[NA,6.5,3.]])
In [130]: cleaned=data.dropna()
In [131]: data
Out[131]:
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3.0
In [132]: cleaned
Out[132]:
0 1 2
0 1.0 6.5 3.0
传入how=‘all’ 将只丢弃全为NA的那些行:
In [133]: data.dropna(how='all')
Out[133]:
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
3 NaN 6.5 3.0
要用这种方式丢弃列,只需传入axis=1即可:
In [134]: data[4]=NA
In [135]: data
Out[135]:
0 1 2 4
0 1.0 6.5 3.0 NaN
1 1.0 NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN 6.5 3.0 NaN
In [136]: data.dropna(axis=1,how='all')
Out[136]:
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3.0
另一个滤出DataFrame行的问题涉及时间序列数据。
假设你只想留下一部分观测数据,可以用thresh参数实现此目的:
In [137]: df=DataFrame(np.random.randn(7,3))
In [138]: df.ix[:4,1]=NA;df.ix[:2,2]=NA
In [139]: df
Out[139]:
0 1 2
0 0.452896 NaN NaN
1 1.071009 NaN NaN
2 -0.135804 NaN NaN
3 0.010116 NaN 0.064880
4 -1.038639 NaN -0.756553
5 0.738134 -1.505505 -0.052306
6 -1.712077 0.386785 0.436945
In [140]: df.dropna(thresh=3)
Out[140]:
0 1 2
5 0.738134 -1.505505 -0.052306
6 -1.712077 0.386785 0.436945
5.4.2填充缺失数据
如果不想滤除数据,而是希望填充数据。
通过一个常数来fillna就会将缺失值替换为那个常数值:
In [141]: df.fillna(0)
Out[141]:
0 1 2
0 0.452896 0.000000 0.000000
1 1.071009 0.000000 0.000000
2 -0.135804 0.000000 0.000000
3 0.010116 0.000000 0.064880
4 -1.038639 0.000000 -0.756553
5 0.738134 -1.505505 -0.052306
6 -1.712077 0.386785 0.436945
通过一个字典调用fillna,就可以实现对不同的列填充不同的值:
In [142]: df.fillna({1:0.5,3:-1})
Out[142]:
0 1 2
0 0.452896 0.500000 NaN
1 1.071009 0.500000 NaN
2 -0.135804 0.500000 NaN
3 0.010116 0.500000 0.064880
4 -1.038639 0.500000 -0.756553
5 0.738134 -1.505505 -0.052306
6 -1.712077 0.386785 0.436945
fillna默认会返回新对象,但是也可以对现有的对象进行修改:
In [143]: _=df.fillna(0,inplace=True)
In [144]: df
Out[144]:
0 1 2
0 0.452896 0.000000 0.000000
1 1.071009 0.000000 0.000000
2 -0.135804 0.000000 0.000000
3 0.010116 0.000000 0.064880
4 -1.038639 0.000000 -0.756553
5 0.738134 -1.505505 -0.052306
6 -1.712077 0.386785 0.436945
对reindex有效的那些插值方法也可以用于fillna:
In [145]: df=DataFrame(np.random.randn(6,3))
In [146]: df.ix[2:,1]=NA;df.ix[4:,2]=NA
In [147]: df
Out[147]:
0 1 2
0 0.501394 -1.735750 0.197643
1 2.099104 1.441581 0.743717
2 -0.451567 NaN -0.150315
3 -0.032894 NaN 0.418310
4 1.285966 NaN NaN
5 -0.058611 NaN NaN
In [148]: df.fillna(method='ffill')
Out[148]:
0 1 2
0 0.501394 -1.735750 0.197643
1 2.099104 1.441581 0.743717
2 -0.451567 1.441581 -0.150315
3 -0.032894 1.441581 0.418310
4 1.285966 1.441581 0.418310
5 -0.058611 1.441581 0.418310
In [149]: df.fillna(method='ffill',limit=2)
Out[149]:
0 1 2
0 0.501394 -1.735750 0.197643
1 2.099104 1.441581 0.743717
2 -0.451567 1.441581 -0.150315
3 -0.032894 1.441581 0.418310
4 1.285966 NaN 0.418310
5 -0.058611 NaN 0.418310
只要稍微动脑子,就可以利用fillna的=实现许多的别的功能。
比如,传入Series的平均值或中位数:
In [150]: data=Series([1.,NA,3.5,NA,7])
In [151]: data.fillna(data.mean())
Out[151]:
0 1.000000
1 3.833333
2 3.500000
3 3.833333
4 7.000000
dtype: float64
5.5层次化索引
层次化索引,是你能在一个轴上拥有多个(两个以上)索引级别。
即能以低纬度形式处理高纬度数据。
我们来创建一个Series,并用一个由列表或数组组成的列表作为索引:
In [152]: data=Series(np.random.randn(10),index=[['a','a','a','b','b','b','c','c','d','d'],[1,2,3,1,2,3,1,2,2,3]])
In [153]: data
Out[153]:
a 1 0.024162
2 0.969526
3 -0.215712
b 1 1.136933
2 -0.158487
3 0.482377
c 1 -0.551888
2 -1.090750
d 2 -0.073204
3 -1.217613
dtype: float64
这就是带有MultiIndex 索引的Series 的格式输出格式。索引之间的“间隔”表示“直接使用上面的标签”:
In [154]: data.index
Out[154]:
MultiIndex(levels=[[u'a', u'b', u'c', u'd'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 1, 2]])
对于一个层次化索引的对象,选取数据子集的操作很简单:
data['b']
Out[155]:
1 1.136933
2 -0.158487
3 0.482377
dtype: float64
data['b':'c']
Out[156]:
b 1 1.136933
2 -0.158487
3 0.482377
c 1 -0.551888
2 -1.090750
dtype: float64
data.ix[['b','d']]
Out[158]:
b 1 1.136933
2 -0.158487
3 0.482377
d 2 -0.073204
3 -1.217613
dtype: float64
甚至还可以在“内层”中进行选取:
In [159]: data[:,2]
Out[159]:
a 0.969526
b -0.158487
c -1.090750
d -0.073204
dtype: float64
层次化索引的在数据重塑和基于分组的操作(如透视表生成)中扮演重要角色。比如说,这段数据可以通过unstack方法被重新安排到一个DataFrame中:
n [160]: data.unstack()
Out[160]:
1 2 3
a 0.024162 0.969526 -0.215712
b 1.136933 -0.158487 0.482377
c -0.551888 -1.090750 NaN
d NaN -0.073204 -1.21761
unstack的逆运算是stack:
In [161]: data.unstack().stack()
Out[161]:
a 1 0.024162
2 0.969526
3 -0.215712
b 1 1.136933
2 -0.158487
3 0.482377
c 1 -0.551888
2 -1.090750
d 2 -0.073204
3 -1.217613
dtype: float64
对于一个DataFrame,每条轴都可以分层索引:
In [162]: frame=DataFrame(np.arange(12).reshape((4,3)),index=[['a','a','b','b'],[1,2,1,2]],columns=[['Ohio','Ohio','Colorado'],['Green','Red','Green']])
In [163]: frame
Out[163]:
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
各层都可以有名字(可以是字符串,也可以是python对象)。如果指定了名称,他们就会显示在控制台输出中:
In [164]: frame.index.names=['key1','key2']
In [165]: frame.columns.names=['state','color']
In [166]: frame
Out[166]:
state Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
有了分部的列索引,因此可以选取列分组:
In [167]: frame['Ohio']
Out[167]:
color Green Red
key1 key2
a 1 0 1
2 3 4
b 1 6 7
2 9 10
可以单独的创建MultiIndex 然后复用,上面那个DataFrame中的(分级)列可以这样创建:
In [168]: MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],names=['state','color'])
5.5.1重排分级顺序
如果需要调整某条轴上各级别的顺序,或根据指定界级别上的值对数据进行排序。swaplevel接受两个级别编号或名称,并返回一个互换级别的新对象(但数据不会发生变化):
In [169]: frame.swaplevel('key1','key2')
Out[169]:
state Ohio Colorado
color Green Red Green
key2 key1
1 a 0 1 2
2 a 3 4 5
1 b 6 7 8
2 b 9 10 11
而sortleval则根据单个级别中的值对数据进行排序(稳定的)。交流级别时,常常也会用到sortlevel,这样最终的结果就是有序的了:
In [170]: frame.sortlevel(1)
Out[170]:
state Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
b 1 6 7 8
a 2 3 4 5
b 2 9 10 11
In [172]: frame.swaplevel(0,1).sortlevel(0)
Out[172]:
state Ohio Colorado
color Green Red Green
key2 key1
1 a 0 1 2
b 6 7 8
2 a 3 4 5
b 9 10 11
5.5.2根据级别汇总统计
许多对DataFrame 和Series的描述和汇总统计都有一个leve选项,用于指定在某条轴上对求和的级别,再也上面的那个DataFrame为例子,我们根据行或列上的级别进行求和:
In [173]: frame.sum(level='color',axis=1)
Out[173]:
color Green Red
key1 key2
a 1 2 1
2 8 4
b 1 14 7
2 20 10
In [174]: frame.sum(level='key2')
Out[174]:
state Ohio Colorado
color Green Red Green
key2
1 6 8 10
2 12 14 16
5.5.3使用DataFrame的列
将DataFrame的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame要的列。
In [14]: frame=DataFrame({'a':range(7),'b':range(7,0,-1),
'c':['one','one','one','two','two','two','two'],'d':[0,1,2,0,1,2,3]})
In [15]: frame
Out[15]:
a b c d
0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
3 3 4 two 0
4 4 3 two 1
5 5 2 two 2
6 6 1 two 3
DataFrame的set_index函数会将一个或多个列转换为行索引,并创建一个新的DataFrame:
In [17]: frame2
Out[17]:
a b
c d
one 0 0 7
1 1 6
2 2 5
two 0 3 4
1 4 3
2 5 2
3 6 1
默认情况下,那些列会从DataFrame中移除,但是也可以将其保留:
In [18]: frame.set_index(['c','d'],drop=False)
Out[18]:
a b c d
c d
one 0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
two 0 3 4 two 0
1 4 3 two 1
2 5 2 two 2
3 6 1 two 3
reset_index的功能跟set_index刚好相反,层次化索引的级别会被转移到列里面:
In [20]: frame2.reset_index()
Out[20]:
c d a b
0 one 0 0 7
1 one 1 1 6
2 one 2 2 5
3 two 0 3 4
4 two 1 4 3
5 two 2 5 2
6 two 3 6 1
5.6其他有关pandas的话题
5.6.1整数索引
操作由整数索引的pandas对象常常会让新手抓狂,因为他们跟内置的python数据结构(如列表和元组)在索引语义上有些不同。
例如,你可能认为下面的代码不会报错。
In [24]: ser=Series(np.arange(3.))
In [25]: ser[-1]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
in ()
----> 1 ser[-1]
pandas\src\hashtable_class_helper.pxi in pandas.hashtable.Int64HashTable.get_item (pandas\hashtable.c:85
08)()
KeyError: -1L
虽然pandas会“求助于”整数索引,但是没有哪种方法能够既不引入bug,又能解决问题的。
我们有一个含有0,1,2的索引,但是很难推断出用户想要什么:
In [26]: ser
Out[26]:
0 0.0
1 1.0
2 2.0
dtype: float64
相反,对于一个非整数索引,就没有这样的歧义:
In [30]: ser2=Series(np.arange(3.),index=['a','b','c'])
In [31]: ser2[-1]
Out[31]: 2.0
为了保持良好的一致性,如果你的轴索引含有索引器,那么根据整数进行数据选取的操作蒋总是面向标签的。这也包括用ix进行切片:
In [32]: ser.ix[:1]
Out[32]:
0 0.0
1 1.0
dtype: float64
如果需要可靠的,不考虑索引类型的,基于位置的索引,可以使用Series 的 iget_value 方法和DataFrame的irow和icol方法:
In [33]: ser3=Series(range(3),index=[-5,1,3])
In [35]: ser3.iget_value(2)
Out[35]: 2
In [37]: frame=DataFrame(np.arange(6).reshape(3,2),index=[2,0,1])
In [38]: frame.irow(0)
Out[38]:
0 0
1 1
Name: 2, dtype: int32