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