利用python進行數據分析之pandas入門(二)

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容