資料來源:https://github.com/BrambleXu/pydata-notebook
pandas是本書后續(xù)內(nèi)容的首選庫况毅。它含有使數(shù)據(jù)清洗和分析工作變得更快更簡單的數(shù)據(jù)結(jié)構(gòu)和操作工具。pandas經(jīng)常和其它工具一同使用魁衙,如數(shù)值計算工具NumPy和SciPy伤提,分析庫statsmodels和scikit-learn巫俺,和數(shù)據(jù)可視化庫matplotlib。pandas是基于NumPy數(shù)組構(gòu)建的肿男,特別是基于數(shù)組的函數(shù)和不使用for循環(huán)的數(shù)據(jù)處理介汹。
雖然pandas采用了大量的NumPy編碼風(fēng)格,但二者最大的不同是pandas是專門為處理表格和混雜數(shù)據(jù)設(shè)計的舶沛。而NumPy更適合處理統(tǒng)一的數(shù)值數(shù)組數(shù)據(jù)嘹承。
自從2010年pandas開源以來,pandas逐漸成長為一個非常大的庫如庭,應(yīng)用于許多真實案例叹卷。開發(fā)者社區(qū)已經(jīng)有了800個獨(dú)立的貢獻(xiàn)者,他們在解決日常數(shù)據(jù)問題的同時為這個項目提供貢獻(xiàn)。
在本書后續(xù)部分中骤竹,我將使用下面這樣的pandas引入約定:
In [1]: import pandas as pd
因此帝牡,只要你在代碼中看到pd.蒙揣,就得想到這是pandas。因為Series和DataFrame用的次數(shù)非常多罩息,所以將其引入本地命名空間中會更方便:
In [2]: from pandas import Series, DataFrame
5.1 pandas的數(shù)據(jù)結(jié)構(gòu)介紹
要使用pandas扣汪,你首先就得熟悉它的兩個主要數(shù)據(jù)結(jié)構(gòu):Series和DataFrame崭别。雖然它們并不能解決所有問題茅主,但它們?yōu)榇蠖鄶?shù)應(yīng)用提供了一種可靠的诀姚、易于使用的基礎(chǔ)玷禽。
Series
Series是一種類似于一維數(shù)組的對象赫段,它由一組數(shù)據(jù)(各種NumPy數(shù)據(jù)類型)以及一組與之相關(guān)的數(shù)據(jù)標(biāo)簽(即索引)組成。僅由一組數(shù)據(jù)即可產(chǎn)生最簡單的Series:
In [11]: obj = pd.Series([4, 7, -5, 3])
In [12]: obj
Out[12]:
0 4
1 7
2 -5
3 3
dtype: int64
Series的字符串表現(xiàn)形式為:索引在左邊矢赁,值在右邊糯笙。由于我們沒有為數(shù)據(jù)指定索引,于是會自動創(chuàng)建一個0到N-1(N為數(shù)據(jù)的長度)的整數(shù)型索引撩银。你可以通過Series 的values和index屬性獲取其數(shù)組表示形式和索引對象:
In [13]: obj.values
Out[13]: array([ 4, 7, -5, 3])
In [14]: obj.index # like range(4)
Out[14]: RangeIndex(start=0, stop=4, step=1)
通常给涕,我們希望所創(chuàng)建的Series帶有一個可以對各個數(shù)據(jù)點(diǎn)進(jìn)行標(biāo)記的索引:
In [15]: obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
In [16]: obj2
Out[16]:
d 4
b 7
a -5
c 3
dtype: int64
In [17]: obj2.index
Out[17]: Index(['d', 'b', 'a', 'c'], dtype='object')
與普通NumPy數(shù)組相比,你可以通過索引的方式選取Series中的單個或一組值:
In [18]: obj2['a']
Out[18]: -5
In [19]: obj2['d'] = 6
In [20]: obj2[['c', 'a', 'd']]
Out[20]:
c 3
a -5
d 6
dtype: int64
['c', 'a', 'd']是索引列表额获,即使它包含的是字符串而不是整數(shù)够庙。
使用NumPy函數(shù)或類似NumPy的運(yùn)算(如根據(jù)布爾型數(shù)組進(jìn)行過濾、標(biāo)量乘法抄邀、應(yīng)用數(shù)學(xué)函數(shù)等)都會保留索引值的鏈接:
In [21]: obj2[obj2 > 0]
Out[21]:
d 6
b 7
c 3
dtype: int64
In [22]: obj2 * 2
Out[22]:
d 12
b 14
a -10
c 6
dtype: int64
In [23]: np.exp(obj2)
Out[23]:
d 403.428793
b 1096.633158
a 0.006738
c 20.085537
dtype: float64
還可以將Series看成是一個定長的有序字典耘眨,因為它是索引值到數(shù)據(jù)值的一個映射。它可以用在許多原本需要字典參數(shù)的函數(shù)中:
In [24]: 'b' in obj2
Out[24]: True
In [25]: 'e' in obj2
Out[25]: False
如果數(shù)據(jù)被存放在一個Python字典中境肾,也可以直接通過這個字典來創(chuàng)建Series:
In [26]: sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
In [27]: obj3 = pd.Series(sdata)
In [28]: obj3
Out[28]:
Ohio 35000
Oregon 16000
Texas 71000
Utah 5000
dtype: int64
如果只傳入一個字典剔难,則結(jié)果Series中的索引就是原字典的鍵(有序排列)胆屿。你可以傳入排好序的字典的鍵以改變順序:
In [29]: states = ['California', 'Ohio', 'Oregon', 'Texas']
In [30]: obj4 = pd.Series(sdata, index=states)
In [31]: obj4
Out[31]:
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
在這個例子中,sdata中跟states索引相匹配的那3個值會被找出來并放到相應(yīng)的位置上结闸,但由于"California"所對應(yīng)的sdata值找不到蔫耽,所以其結(jié)果就為NaN(即“非數(shù)字”(not a number)图甜,在pandas中钦讳,它用于表示缺失或NA值)缚去。因為‘Utah’不在states中,它被從結(jié)果中除去。
我將使用缺失(missing)或NA表示缺失數(shù)據(jù)滋尉。pandas的isnull和notnull函數(shù)可用于檢測缺失數(shù)據(jù):
In [32]: pd.isnull(obj4)
Out[32]:
California True
Ohio False
Oregon False
Texas False
dtype: bool
In [33]: pd.notnull(obj4)
Out[33]:
California False
Ohio True
Oregon True
Texas True
dtype: bool
Series也有類似的實例方法:
In [34]: obj4.isnull()
Out[34]:
California True
Ohio False
Oregon False
Texas False
dtype: bool
我將在第7章詳細(xì)講解如何處理缺失數(shù)據(jù)。
對于許多應(yīng)用而言虱而,Series最重要的一個功能是,它會根據(jù)運(yùn)算的索引標(biāo)簽自動對齊數(shù)據(jù):
In [35]: obj3
Out[35]:
Ohio 35000
Oregon 16000
Texas 71000
Utah 5000
dtype: int64
In [36]: obj4
Out[36]:
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
In [37]: obj3 + obj4
Out[37]:
California NaN
Ohio 70000.0
Oregon 32000.0
Texas 142000.0
Utah NaN
dtype: float64
數(shù)據(jù)對齊功能將在后面詳細(xì)講解。如果你使用過數(shù)據(jù)庫剔蹋,你可以認(rèn)為是類似join的操作律想。
Series對象本身及其索引都有一個name屬性,該屬性跟pandas其他的關(guān)鍵功能關(guān)系非常密切:
In [38]: obj4.name = 'population'
In [39]: obj4.index.name = 'state'
In [40]: obj4
Out[40]:
state
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
Name: population, dtype: float64
Series的索引可以通過賦值的方式就地修改:
In [41]: obj
Out[41]:
0 4
1 7
2 -5
3 3
dtype: int64
In [42]: obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
In [43]: obj
Out[43]:
Bob 4
Steve 7
Jeff -5
Ryan 3
dtype: int64
DataFrame
DataFrame是一個表格型的數(shù)據(jù)結(jié)構(gòu),它含有一組有序的列葵陵,每列可以是不同的值類型(數(shù)值伤柄、字符串秤朗、布爾值等)。DataFrame既有行索引也有列索引稽物,它可以被看做由Series組成的字典(共用同一個索引)怨酝。DataFrame中的數(shù)據(jù)是以一個或多個二維塊存放的(而不是列表、字典或別的一維數(shù)據(jù)結(jié)構(gòu))斤葱。有關(guān)DataFrame內(nèi)部的技術(shù)細(xì)節(jié)遠(yuǎn)遠(yuǎn)超出了本書所討論的范圍汤纸。
筆記:雖然DataFrame是以二維結(jié)構(gòu)保存數(shù)據(jù)的,但你仍然可以輕松地將其表示為更高維度的數(shù)據(jù)(層次化索引的表格型結(jié)構(gòu)囊蓝,這是pandas中許多高級數(shù)據(jù)處理功能的關(guān)鍵要素蝎宇,我們會在第8章討論這個問題)。
建DataFrame的辦法有很多报嵌,最常用的一種是直接傳入一個由等長列表或NumPy數(shù)組組成的字典:
data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)
結(jié)果DataFrame會自動加上索引(跟Series一樣)血筑,且全部列會被有序排列:
In [45]: frame
Out[45]:
pop state year
0 1.5 Ohio 2000
1 1.7 Ohio 2001
2 3.6 Ohio 2002
3 2.4 Nevada 2001
4 2.9 Nevada 2002
5 3.2 Nevada 2003
如果你使用的是Jupyter notebook择懂,pandas DataFrame對象會以對瀏覽器友好的HTML表格的方式呈現(xiàn)表伦。
對于特別大的DataFrame纲熏,head方法會選取前五行:
In [46]: frame.head()
Out[46]:
pop state year
0 1.5 Ohio 2000
1 1.7 Ohio 2001
2 3.6 Ohio 2002
3 2.4 Nevada 2001
4 2.9 Nevada 2002
如果指定了列序列局劲,則DataFrame的列就會按照指定順序進(jìn)行排列:
In [47]: pd.DataFrame(data, columns=['year', 'state', 'pop'])
Out[47]:
year state pop
0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9
5 2003 Nevada 3.2
如果傳入的列在數(shù)據(jù)中找不到车柠,就會在結(jié)果中產(chǎn)生缺失值:
In [48]: frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
....: index=['one', 'two', 'three', 'four',
....: 'five', 'six'])
In [49]: frame2
Out[49]:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 NaN
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 NaN
five 2002 Nevada 2.9 NaN
six 2003 Nevada 3.2 NaN
In [50]: frame2.columns
Out[50]: Index(['year', 'state', 'pop', 'debt'], dtype='object')
通過類似字典標(biāo)記的方式或?qū)傩缘姆绞教奘希梢詫ataFrame的列獲取為一個Series:
In [51]: frame2['state']
Out[51]:
one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
six Nevada
Name: state, dtype: object
In [52]: frame2.year
Out[52]:
one 2000
two 2001
three 2002
four 2001
five 2002
six 2003
Name: year, dtype: int64
筆記:IPython提供了類似屬性的訪問(即frame2.year)和tab補(bǔ)全。
frame2[column]適用于任何列的名竹祷,但是frame2.column只有在列名是一個合理的Python變量名時才適用谈跛。
注意,返回的Series擁有原DataFrame相同的索引阻桅,且其name屬性也已經(jīng)被相應(yīng)地設(shè)置好了宏侍。
行也可以通過位置或名稱的方式進(jìn)行獲取毯盈,比如用loc屬性(稍后將對此進(jìn)行詳細(xì)講解):
In [53]: frame2.loc['three']
Out[53]:
year 2002
state Ohio
pop 3.6
debt NaN
Name: three, dtype: object
列可以通過賦值的方式進(jìn)行修改。例如,我們可以給那個空的"debt"列賦上一個標(biāo)量值或一組值:
In [54]: frame2['debt'] = 16.5
In [55]: frame2
Out[55]:
year state pop debt
one 2000 Ohio 1.5 16.5
two 2001 Ohio 1.7 16.5
three 2002 Ohio 3.6 16.5
four 2001 Nevada 2.4 16.5
five 2002 Nevada 2.9 16.5
six 2003 Nevada 3.2 16.5
In [56]: frame2['debt'] = np.arange(6.)
In [57]: frame2
Out[57]:
year state pop debt
one 2000 Ohio 1.5 0.0
two 2001 Ohio 1.7 1.0
three 2002 Ohio 3.6 2.0
four 2001 Nevada 2.4 3.0
five 2002 Nevada 2.9 4.0
six 2003 Nevada 3.2 5.0
將列表或數(shù)組賦值給某個列時,其長度必須跟DataFrame的長度相匹配亡容。如果賦值的是一個Series我擂,就會精確匹配DataFrame的索引尺锚,所有的空位都將被填上缺失值:
In [58]: val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
In [59]: frame2['debt'] = val
In [60]: frame2
Out[60]:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 -1.2
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 -1.5
five 2002 Nevada 2.9 -1.7
six 2003 Nevada 3.2 NaN
為不存在的列賦值會創(chuàng)建出一個新列。關(guān)鍵字del用于刪除列车遂。
作為del的例子拯欧,我先添加一個新的布爾值的列曙寡,state是否為'Ohio':
In [61]: frame2['eastern'] = frame2.state == 'Ohio'
In [62]: frame2
Out[62]:
year state pop debt eastern
one 2000 Ohio 1.5 NaN True
two 2001 Ohio 1.7 -1.2 True
three 2002 Ohio 3.6 NaN True
four 2001 Nevada 2.4 -1.5 False
five 2002 Nevada 2.9 -1.7 False
six 2003 Nevada 3.2 NaN False
注意:不能用frame2.eastern創(chuàng)建新的列耙替。
del方法可以用來刪除這列:
In [63]: del frame2['eastern']
In [64]: frame2.columns
Out[64]: Index(['year', 'state', 'pop', 'debt'], dtype='object')
注意:通過索引方式返回的列只是相應(yīng)數(shù)據(jù)的視圖而已,并不是副本。因此匈仗,對返回的Series所做的任何就地修改全都會反映到源DataFrame上。通過Series的copy方法即可指定復(fù)制列逢慌。
另一種常見的數(shù)據(jù)形式是嵌套字典:
In [65]: pop = {'Nevada': {2001: 2.4, 2002: 2.9},
....: 'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
如果嵌套字典傳給DataFrame悠轩,pandas就會被解釋為:外層字典的鍵作為列,內(nèi)層鍵則作為行索引:
In [66]: frame3 = pd.DataFrame(pop)
In [67]: frame3
Out[67]:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
你也可以使用類似NumPy數(shù)組的方法攻泼,對DataFrame進(jìn)行轉(zhuǎn)置(交換行和列):
In [68]: frame3.T
Out[68]:
2000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6
內(nèi)層字典的鍵會被合并火架、排序以形成最終的索引。如果明確指定了索引忙菠,則不會這樣:
In [69]: pd.DataFrame(pop, index=[2001, 2002, 2003])
Out[69]:
Nevada Ohio
2001 2.4 1.7
2002 2.9 3.6
2003 NaN NaN
由Series組成的字典差不多也是一樣的用法:
In [70]: pdata = {'Ohio': frame3['Ohio'][:-1],
....: 'Nevada': frame3['Nevada'][:2]}
In [71]: pd.DataFrame(pdata)
Out[71]:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
表5-1列出了DataFrame構(gòu)造函數(shù)所能接受的各種數(shù)據(jù)何鸡。
如果設(shè)置了DataFrame的index和columns的name屬性,則這些信息也會被顯示出來:
In [72]: frame3.index.name = 'year'; frame3.columns.name = 'state'
In [73]: frame3
Out[73]:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
跟Series一樣牛欢,values屬性也會以二維ndarray的形式返回DataFrame中的數(shù)據(jù):
In [74]: frame3.values
Out[74]:
array([[ nan, 1.5],
[ 2.4, 1.7],
[ 2.9, 3.6]])
如果DataFrame各列的數(shù)據(jù)類型不同骡男,則值數(shù)組的dtype就會選用能兼容所有列的數(shù)據(jù)類型:
In [75]: frame2.values
Out[75]:
array([[2000, 'Ohio', 1.5, nan],
[2001, 'Ohio', 1.7, -1.2],
[2002, 'Ohio', 3.6, nan],
[2001, 'Nevada', 2.4, -1.5],
[2002, 'Nevada', 2.9, -1.7],
[2003, 'Nevada', 3.2, nan]], dtype=object)
索引對象
pandas的索引對象負(fù)責(zé)管理軸標(biāo)簽和其他元數(shù)據(jù)(比如軸名稱等)。構(gòu)建Series或DataFrame時傍睹,所用到的任何數(shù)組或其他序列的標(biāo)簽都會被轉(zhuǎn)換成一個Index:
In [76]: obj = pd.Series(range(3), index=['a', 'b', 'c'])
In [77]: index = obj.index
In [78]: index
Out[78]: Index(['a', 'b', 'c'], dtype='object')
In [79]: index[1:]
Out[79]: Index(['b', 'c'], dtype='object')
Index對象是不可變的隔盛,因此用戶不能對其進(jìn)行修改:
index[1] = 'd' # TypeError
不可變可以使Index對象在多個數(shù)據(jù)結(jié)構(gòu)之間安全共享:
In [80]: labels = pd.Index(np.arange(3))
In [81]: labels
Out[81]: Int64Index([0, 1, 2], dtype='int64')
In [82]: obj2 = pd.Series([1.5, -2.5, 0], index=labels)
In [83]: obj2
Out[83]:
0 1.5
1 -2.5
2 0.0
dtype: float64
In [84]: obj2.index is labels
Out[84]: True
注意:雖然用戶不需要經(jīng)常使用Index的功能,但是因為一些操作會生成包含被索引化的數(shù)據(jù)拾稳,理解它們的工作原理是很重要的吮炕。
除了類似于數(shù)組,Index的功能也類似一個固定大小的集合:
In [85]: frame3
Out[85]:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
In [86]: frame3.columns
Out[86]: Index(['Nevada', 'Ohio'], dtype='object', name='state')
In [87]: 'Ohio' in frame3.columns
Out[87]: True
In [88]: 2003 in frame3.index
Out[88]: False
與python的集合不同熊赖,pandas的Index可以包含重復(fù)的標(biāo)簽:
In [89]: dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
In [90]: dup_labels
Out[90]: Index(['foo', 'foo', 'bar', 'bar'], dtype='object')
選擇重復(fù)的標(biāo)簽来屠,會顯示所有的結(jié)果。
每個索引都有一些方法和屬性震鹉,它們可用于設(shè)置邏輯并回答有關(guān)該索引所包含的數(shù)據(jù)的常見問題。表5-2列出了這些函數(shù)捆姜。
5.2 基本功能
本節(jié)中传趾,我將介紹操作Series和DataFrame中的數(shù)據(jù)的基本手段。后續(xù)章節(jié)將更加深入地挖掘pandas在數(shù)據(jù)分析和處理方面的功能泥技。本書不是pandas庫的詳盡文檔浆兰,主要關(guān)注的是最重要的功能,那些不大常用的內(nèi)容(也就是那些更深奧的內(nèi)容)就交給你自己去摸索吧。
重新索引
pandas對象的一個重要方法是reindex簸呈,其作用是創(chuàng)建一個新對象榕订,它的數(shù)據(jù)符合新的索引⊥杀悖看下面的例子:
In [91]: obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
In [92]: obj
Out[92]:
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
用該Series的reindex將會根據(jù)新索引進(jìn)行重排劫恒。如果某個索引值當(dāng)前不存在,就引入缺失值:
In [93]: obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
In [94]: obj2
Out[94]:
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
對于時間序列這樣的有序數(shù)據(jù)轿腺,重新索引時可能需要做一些插值處理两嘴。method選項即可達(dá)到此目的,例如族壳,使用ffill可以實現(xiàn)前向值填充:
In [95]: obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
In [96]: obj3
Out[96]:
0 blue
2 purple
4 yellow
dtype: object
In [97]: obj3.reindex(range(6), method='ffill')
Out[97]:
0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
dtype: object
借助DataFrame憔辫,reindex可以修改(行)索引和列。只傳遞一個序列時仿荆,會重新索引結(jié)果的行:
In [98]: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
....: index=['a', 'c', 'd'],
....: columns=['Ohio', 'Texas', 'California'])
In [99]: frame
Out[99]:
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
In [100]: frame2 = frame.reindex(['a', 'b', 'c', 'd'])
In [101]: frame2
Out[101]:
Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0
列可以用columns關(guān)鍵字重新索引:
In [102]: states = ['Texas', 'Utah', 'California']
In [103]: frame.reindex(columns=states)
Out[103]:
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
表5-3列出了reindex函數(shù)的各參數(shù)及說明贰您。
丟棄指定軸上的項
丟棄某條軸上的一個或多個項很簡單,只要有一個索引數(shù)組或列表即可拢操。由于需要執(zhí)行一些數(shù)據(jù)整理和集合邏輯枉圃,所以drop方法返回的是一個在指定軸上刪除了指定值的新對象:
In [105]: obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
In [106]: obj
Out[106]:
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
In [107]: new_obj = obj.drop('c')
In [108]: new_obj
Out[108]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
In [109]: obj.drop(['d', 'c'])
Out[109]:
a 0.0
b 1.0
e 4.0
dtype: float64
對于DataFrame,可以刪除任意軸上的索引值庐冯。為了演示孽亲,先新建一個DataFrame例子:
In [110]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
.....: index=['Ohio', 'Colorado', 'Utah', 'New York'],
.....: columns=['one', 'two', 'three', 'four'])
In [111]: data
Out[111]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
用標(biāo)簽序列調(diào)用drop會從行標(biāo)簽(axis 0)刪除值:
In [112]: data.drop(['Colorado', 'Ohio'])
Out[112]:
one two three four
Utah 8 9 10 11
New York 12 13 14 15
通過傳遞axis=1或axis='columns'可以刪除列的值:
In [113]: data.drop('two', axis=1)
Out[113]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
In [114]: data.drop(['two', 'four'], axis='columns')
Out[114]:
one three
Ohio 0 2
Colorado 4 6
Utah 8 10
New York 12 14
許多函數(shù),如drop展父,會修改Series或DataFrame的大小或形狀返劲,可以就地修改對象,不會返回新的對象:
In [115]: obj.drop('c', inplace=True)
In [116]: obj
Out[116]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
小心使用inplace栖茉,它會銷毀所有被刪除的數(shù)據(jù)篮绿。
索引、選取和過濾
Series索引(obj[...])的工作方式類似于NumPy數(shù)組的索引吕漂,只不過Series的索引值不只是整數(shù)亲配。下面是幾個例子:
In [117]: obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
In [118]: obj
Out[118]:
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
In [119]: obj['b']
Out[119]: 1.0
In [120]: obj[1]
Out[120]: 1.0
In [121]: obj[2:4]
Out[121]:
c 2.0
d 3.0
dtype: float64
In [122]: obj[['b', 'a', 'd']]
Out[122]:
b 1.0
a 0.0
d 3.0
dtype: float64
In [123]: obj[[1, 3]]
Out[123]:
b 1.0
d 3.0
dtype: float64
In [124]: obj[obj < 2]
Out[124]:
a 0.0
b 1.0
dtype: float64
利用標(biāo)簽的切片運(yùn)算與普通的Python切片運(yùn)算不同,其末端是包含的:
In [125]: obj['b':'c']
Out[125]:
b 1.0
c 2.0
dtype: float64
用切片可以對Series的相應(yīng)部分進(jìn)行設(shè)置:
In [126]: obj['b':'c'] = 5
In [127]: obj
Out[127]:
a 0.0
b 5.0
c 5.0
d 3.0
dtype: float64
用一個值或序列對DataFrame進(jìn)行索引其實就是獲取一個或多個列:
In [128]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
.....: index=['Ohio', 'Colorado', 'Utah', 'New York'],
.....: columns=['one', 'two', 'three', 'four'])
In [129]: data
Out[129]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
In [130]: data['two']
Out[130]:
Ohio 1
Colorado 5
Utah 9
New York 13
Name: two, dtype: int64
In [131]: data[['three', 'one']]
Out[131]:
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
New York 14 12
這種索引方式有幾個特殊的情況惶凝。首先通過切片或布爾型數(shù)組選取數(shù)據(jù):
In [132]: data[:2]
Out[132]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
In [133]: data[data['three'] > 5]
Out[133]:
one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
選取行的語法data[:2]十分方便吼虎。向[ ]傳遞單一的元素或列表,就可選擇列苍鲜。
另一種用法是通過布爾型DataFrame(比如下面這個由標(biāo)量比較運(yùn)算得出的)進(jìn)行索引:
In [134]: data < 5
Out[134]:
one two three four
Ohio True True True True
Colorado True False False False
Utah False False False False
New York False False False False
In [135]: data[data < 5] = 0
In [136]: data
Out[136]:
one two three four
Ohio 0 0 0 0
Colorado 0 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
這使得DataFrame的語法與NumPy二維數(shù)組的語法很像思灰。
用loc和iloc進(jìn)行選取
對于DataFrame的行的標(biāo)簽索引,我引入了特殊的標(biāo)簽運(yùn)算符loc和iloc混滔。它們可以讓你用類似NumPy的標(biāo)記洒疚,使用軸標(biāo)簽(loc)或整數(shù)索引(iloc)歹颓,從DataFrame選擇行和列的子集。
作為一個初步示例油湖,讓我們通過標(biāo)簽選擇一行和多列:
In [137]: data.loc['Colorado', ['two', 'three']]
Out[137]:
two 5
three 6
Name: Colorado, dtype: int64
然后用iloc和整數(shù)進(jìn)行選任】浮:
In [138]: data.iloc[2, [3, 0, 1]]
Out[138]:
four 11
one 8
two 9
Name: Utah, dtype: int64
In [139]: data.iloc[2]
Out[139]:
one 8
two 9
three 10
four 11
Name: Utah, dtype: int64
In [140]: data.iloc[[1, 2], [3, 0, 1]]
Out[140]:
four one two
Colorado 7 0 5
Utah 11 8 9
這兩個索引函數(shù)也適用于一個標(biāo)簽或多個標(biāo)簽的切片:
In [141]: data.loc[:'Utah', 'two']
Out[141]:
Ohio 0
Colorado 5
Utah 9
Name: two, dtype: int64
In [142]: data.iloc[:, :3][data.three > 5]
Out[142]:
one two three
Colorado 0 5 6
Utah 8 9 10
New York 12 13 14
所以,在pandas中乏德,有多個方法可以選取和重新組合數(shù)據(jù)撤奸。對于DataFrame,表5-4進(jìn)行了總結(jié)鹅经。后面會看到寂呛,還有更多的方法進(jìn)行層級化索引。
筆記:在一開始設(shè)計pandas時瘾晃,我覺得用frame[:, col]選取列過于繁瑣(也容易出錯)贷痪,因為列的選擇是非常常見的操作。我做了些取舍蹦误,將花式索引的功能(標(biāo)簽和整數(shù))放到了ix運(yùn)算符中劫拢。在實踐中,這會導(dǎo)致許多邊緣情況强胰,數(shù)據(jù)的軸標(biāo)簽是整數(shù)舱沧,所以pandas團(tuán)隊決定創(chuàng)造loc和iloc運(yùn)算符分別處理嚴(yán)格基于標(biāo)簽和整數(shù)的索引。
ix運(yùn)算符仍然可用偶洋,但并不推薦熟吏。
整數(shù)索引
處理整數(shù)索引的pandas對象常常難住新手,因為它與Python內(nèi)置的列表和元組的索引語法不同玄窝。例如牵寺,你可能不認(rèn)為下面的代碼會出錯:
ser = pd.Series(np.arange(3.))
ser
ser[-1]
這里,pandas可以勉強(qiáng)進(jìn)行整數(shù)索引恩脂,但是會導(dǎo)致小bug帽氓。我們有包含0,1,2的索引,但是引入用戶想要的東西(基于標(biāo)簽或位置的索引)很難:
In [144]: ser
Out[144]:
0 0.0
1 1.0
2 2.0
dtype: float64
另外俩块,對于非整數(shù)索引黎休,不會產(chǎn)生歧義:
In [145]: ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
In [146]: ser2[-1]
Out[146]: 2.0
為了進(jìn)行統(tǒng)一,如果軸索引含有整數(shù)玉凯,數(shù)據(jù)選取總會使用標(biāo)簽势腮。為了更準(zhǔn)確,請使用loc(標(biāo)簽)或iloc(整數(shù)):
In [147]: ser[:1]
Out[147]:
0 0.0
dtype: float64
In [148]: ser.loc[:1]
Out[148]:
0 0.0
1 1.0
dtype: float64
In [149]: ser.iloc[:1]
Out[149]:
0 0.0
dtype: float64
算術(shù)運(yùn)算和數(shù)據(jù)對齊
pandas最重要的一個功能是壮啊,它可以對不同索引的對象進(jìn)行算術(shù)運(yùn)算嫉鲸。在將對象相加時,如果存在不同的索引對歹啼,則結(jié)果的索引就是該索引對的并集玄渗。對于有數(shù)據(jù)庫經(jīng)驗的用戶,這就像在索引標(biāo)簽上進(jìn)行自動外連接狸眼√偈鳎看一個簡單的例子:
In [150]: s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
In [151]: s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
.....: index=['a', 'c', 'e', 'f', 'g'])
In [152]: s1
Out[152]:
a 7.3
c -2.5
d 3.4
e 1.5
dtype: float64
In [153]: s2
Out[153]:
a -2.1
c 3.6
e -1.5
f 4.0
g 3.1
dtype: float64
將它們相加就會產(chǎn)生:
In [154]: s1 + s2
Out[154]:
a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64
自動的數(shù)據(jù)對齊操作在不重疊的索引處引入了NA值。缺失值會在算術(shù)運(yùn)算過程中傳播拓萌。
對于DataFrame岁钓,對齊操作會同時發(fā)生在行和列上:
In [155]: df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
.....: index=['Ohio', 'Texas', 'Colorado'])
In [156]: df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
.....: index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [157]: df1
Out[157]:
b c d
Ohio 0.0 1.0 2.0
Texas 3.0 4.0 5.0
Colorado 6.0 7.0 8.0
In [158]: df2
Out[158]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
把它們相加后將會返回一個新的DataFrame,其索引和列為原來那兩個DataFrame的并集:
In [159]: df1 + df2
Out[159]:
b c d e
Colorado NaN NaN NaN NaN
Ohio 3.0 NaN 6.0 NaN
Oregon NaN NaN NaN NaN
Texas 9.0 NaN 12.0 NaN
Utah NaN NaN NaN NaN
因為'c'和'e'列均不在兩個DataFrame對象中微王,在結(jié)果中以缺省值呈現(xiàn)屡限。行也是同樣。
如果DataFrame對象相加炕倘,沒有共用的列或行標(biāo)簽钧大,結(jié)果都會是空:
In [160]: df1 = pd.DataFrame({'A': [1, 2]})
In [161]: df2 = pd.DataFrame({'B': [3, 4]})
In [162]: df1
Out[162]:
A
0 1
1 2
In [163]: df2
Out[163]:
B
0 3
1 4
In [164]: df1 - df2
Out[164]:
A B
0 NaN NaN
1 NaN NaN
在算術(shù)方法中填充值
在對不同索引的對象進(jìn)行算術(shù)運(yùn)算時,你可能希望當(dāng)一個對象中某個軸標(biāo)簽在另一個對象中找不到時填充一個特殊值(比如0):
In [165]: df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
.....: columns=list('abcd'))
In [166]: df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
.....: columns=list('abcde'))
In [167]: df2.loc[1, 'b'] = np.nan
In [168]: df1
Out[168]:
a b c d
0 0.0 1.0 2.0 3.0
1 4.0 5.0 6.0 7.0
2 8.0 9.0 10.0 11.0
In [169]: df2
Out[169]:
a b c d e
0 0.0 1.0 2.0 3.0 4.0
1 5.0 NaN 7.0 8.0 9.0
2 10.0 11.0 12.0 13.0 14.0
3 15.0 16.0 17.0 18.0 19.0
將它們相加時罩旋,沒有重疊的位置就會產(chǎn)生NA值:
In [170]: df1 + df2
Out[170]:
a b c d e
0 0.0 2.0 4.0 6.0 NaN
1 9.0 NaN 13.0 15.0 NaN
2 18.0 20.0 22.0 24.0 NaN
3 NaN NaN NaN NaN NaN
使用df1的add方法啊央,傳入df2以及一個fill_value參數(shù):
In [171]: df1.add(df2, fill_value=0)
Out[171]:
a b c d e
0 0.0 2.0 4.0 6.0 4.0
1 9.0 5.0 13.0 15.0 9.0
2 18.0 20.0 22.0 24.0 14.0
3 15.0 16.0 17.0 18.0 19.0
表5-5列出了Series和DataFrame的算術(shù)方法。它們每個都有一個副本涨醋,以字母r開頭瓜饥,它會翻轉(zhuǎn)參數(shù)。因此這兩個語句是等價的:
In [172]: 1 / df1
Out[172]:
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250000 0.200000 0.166667 0.142857
2 0.125000 0.111111 0.100000 0.090909
In [173]: df1.rdiv(1)
Out[173]:
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250000 0.200000 0.166667 0.142857
2 0.125000 0.111111 0.100000 0.090909
與此類似浴骂,在對Series或DataFrame重新索引時乓土,也可以指定一個填充值:
In [174]: df1.reindex(columns=df2.columns, fill_value=0)
Out[174]:
a b c d e
0 0.0 1.0 2.0 3.0 0
1 4.0 5.0 6.0 7.0 0
2 8.0 9.0 10.0 11.0 0
DataFrame和Series之間的運(yùn)算
跟不同維度的NumPy數(shù)組一樣,DataFrame和Series之間算術(shù)運(yùn)算也是有明確規(guī)定的溯警。先來看一個具有啟發(fā)性的例子趣苏,計算一個二維數(shù)組與其某行之間的差:
In [175]: arr = np.arange(12.).reshape((3, 4))
In [176]: arr
Out[176]:
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
In [177]: arr[0]
Out[177]: array([ 0., 1., 2., 3.])
In [178]: arr - arr[0]
Out[178]:
array([[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.],
[ 8., 8., 8., 8.]])
當(dāng)我們從arr減去arr[0],每一行都會執(zhí)行這個操作愧膀。這就叫做廣播(broadcasting)拦键,附錄A將對此進(jìn)行詳細(xì)講解。DataFrame和Series之間的運(yùn)算差不多也是如此:
In [179]: frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
.....: columns=list('bde'),
.....: index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [180]: series = frame.iloc[0]
In [181]: frame
Out[181]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
In [182]: series
Out[182]:
b 0.0
d 1.0
e 2.0
Name: Utah, dtype: float64
默認(rèn)情況下檩淋,DataFrame和Series之間的算術(shù)運(yùn)算會將Series的索引匹配到DataFrame的列芬为,然后沿著行一直向下廣播:
In [183]: frame - series
Out[183]:
b d e
Utah 0.0 0.0 0.0
Ohio 3.0 3.0 3.0
Texas 6.0 6.0 6.0
Oregon 9.0 9.0 9.0
如果某個索引值在DataFrame的列或Series的索引中找不到,則參與運(yùn)算的兩個對象就會被重新索引以形成并集:
In [184]: series2 = pd.Series(range(3), index=['b', 'e', 'f'])
In [185]: frame + series2
Out[185]:
b d e f
Utah 0.0 NaN 3.0 NaN
Ohio 3.0 NaN 6.0 NaN
Texas 6.0 NaN 9.0 NaN
Oregon 9.0 NaN 12.0 NaN
如果你希望匹配行且在列上廣播蟀悦,則必須使用算術(shù)運(yùn)算方法媚朦。例如:
In [186]: series3 = frame['d']
In [187]: frame
Out[187]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
In [188]: series3
Out[188]:
Utah 1.0
Ohio 4.0
Texas 7.0
Oregon 10.0
Name: d, dtype: float64
In [189]: frame.sub(series3, axis='index')
Out[189]:
b d e
Utah -1.0 0.0 1.0
Ohio -1.0 0.0 1.0
Texas -1.0 0.0 1.0
Oregon -1.0 0.0 1.0
傳入的軸號就是希望匹配的軸。在本例中日戈,我們的目的是匹配DataFrame的行索引(axis='index' or axis=0)并進(jìn)行廣播询张。
函數(shù)應(yīng)用和映射
NumPy的ufuncs(元素級數(shù)組方法)也可用于操作pandas對象:
In [190]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
.....: index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In [191]: frame
Out[191]:
b d e
Utah -0.204708 0.478943 -0.519439
Ohio -0.555730 1.965781 1.393406
Texas 0.092908 0.281746 0.769023
Oregon 1.246435 1.007189 -1.296221
In [192]: np.abs(frame)
Out[192]:
b d e
Utah 0.204708 0.478943 0.519439
Ohio 0.555730 1.965781 1.393406
Texas 0.092908 0.281746 0.769023
Oregon 1.246435 1.007189 1.296221
另一個常見的操作是,將函數(shù)應(yīng)用到由各列或行所形成的一維數(shù)組上浙炼。DataFrame的apply方法即可實現(xiàn)此功能:
In [193]: f = lambda x: x.max() - x.min()
In [194]: frame.apply(f)
Out[194]:
b 1.802165
d 1.684034
e 2.689627
dtype: float64
這里的函數(shù)f份氧,計算了一個Series的最大值和最小值的差唯袄,在frame的每列都執(zhí)行了一次。結(jié)果是一個Series蜗帜,使用frame的列作為索引恋拷。
如果傳遞axis='columns'到apply,這個函數(shù)會在每行執(zhí)行:
In [195]: frame.apply(f, axis='columns')
Out[195]:
Utah 0.998382
Ohio 2.521511
Texas 0.676115
Oregon 2.542656
dtype: float64
許多最為常見的數(shù)組統(tǒng)計功能都被實現(xiàn)成DataFrame的方法(如sum和mean)厅缺,因此無需使用apply方法蔬顾。
傳遞到apply的函數(shù)不是必須返回一個標(biāo)量,還可以返回由多個值組成的Series:
In [196]: def f(x):
.....: return pd.Series([x.min(), x.max()], index=['min', 'max'])
In [197]: frame.apply(f)
Out[197]:
b d e
min -0.555730 0.281746 -1.296221
max 1.246435 1.965781 1.393406
元素級的Python函數(shù)也是可以用的湘捎。假如你想得到frame中各個浮點(diǎn)值的格式化字符串诀豁,使用applymap即可:
In [198]: format = lambda x: '%.2f' % x
In [199]: frame.applymap(format)
Out[199]:
b d e
Utah -0.20 0.48 -0.52
Ohio -0.56 1.97 1.39
Texas 0.09 0.28 0.77
Oregon 1.25 1.01 -1.30
之所以叫做applymap,是因為Series有一個用于應(yīng)用元素級函數(shù)的map方法:
In [200]: frame['e'].map(format)
Out[200]:
Utah -0.52
Ohio 1.39
Texas 0.77
Oregon -1.30
Name: e, dtype: object
排序和排名
根據(jù)條件對數(shù)據(jù)集排序(sorting)也是一種重要的內(nèi)置運(yùn)算窥妇。要對行或列索引進(jìn)行排序(按字典順序)舷胜,可使用sort_index方法,它將返回一個已排序的新對象:
In [201]: obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
In [202]: obj.sort_index()
Out[202]:
a 1
b 2
c 3
d 0
dtype: int64
對于DataFrame秩伞,則可以根據(jù)任意一個軸上的索引進(jìn)行排序:
In [203]: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
.....: index=['three', 'one'],
.....: columns=['d', 'a', 'b', 'c'])
In [204]: frame.sort_index()
Out[204]:
d a b c
one 4 5 6 7
three 0 1 2 3
In [205]: frame.sort_index(axis=1)
Out[205]:
a b c d
three 1 2 3 0
one 5 6 7 4
數(shù)據(jù)默認(rèn)是按升序排序的逞带,但也可以降序排序:
In [206]: frame.sort_index(axis=1, ascending=False)
Out[206]:
d c b a
three 0 3 2 1
one 4 7 6 5
若要按值對Series進(jìn)行排序,可使用其sort_values方法:
In [207]: obj = pd.Series([4, 7, -3, 2])
In [208]: obj.sort_values()
Out[208]:
2 -3
3 2
0 4
1 7
dtype: int64
在排序時纱新,任何缺失值默認(rèn)都會被放到Series的末尾:
In [209]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
In [210]: obj.sort_values()
Out[210]:
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
當(dāng)排序一個DataFrame時展氓,你可能希望根據(jù)一個或多個列中的值進(jìn)行排序。將一個或多個列的名字傳遞給sort_values的by選項即可達(dá)到該目的:
In [211]: frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
In [212]: frame
Out[212]:
a b
0 0 4
1 1 7
2 0 -3
3 1 2
In [213]: frame.sort_values(by='b')
Out[213]:
a b
2 0 -3
3 1 2
0 0 4
1 1 7
要根據(jù)多個列進(jìn)行排序脸爱,傳入名稱的列表即可:
In [214]: frame.sort_values(by=['a', 'b'])
Out[214]:
a b
2 0 -3
0 0 4
3 1 2
1 1 7
排名會從1開始一直到數(shù)組中有效數(shù)據(jù)的數(shù)量遇汞。接下來介紹Series和DataFrame的rank方法。默認(rèn)情況下簿废,rank是通過“為各組分配一個平均排名”的方式破壞平級關(guān)系的:
In [215]: obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
In [216]: obj.rank()
Out[216]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
也可以根據(jù)值在原數(shù)據(jù)中出現(xiàn)的順序給出排名:
In [217]: obj.rank(method='first')
Out[217]:
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
這里空入,條目0和2沒有使用平均排名6.5,它們被設(shè)成了6和7族檬,因為數(shù)據(jù)中標(biāo)簽0位于標(biāo)簽2的前面歪赢。
你也可以按降序進(jìn)行排名:
# Assign tie values the maximum rank in the group
In [218]: obj.rank(ascending=False, method='max')
Out[218]:
0 2.0
1 7.0
2 2.0
3 4.0
4 5.0
5 6.0
6 4.0
dtype: float64
表5-6列出了所有用于破壞平級關(guān)系的method選項。DataFrame可以在行或列上計算排名:
In [219]: frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
.....: 'c': [-2, 5, 8, -2.5]})
In [220]: frame
Out[220]:
a b c
0 0 4.3 -2.0
1 1 7.0 5.0
2 0 -3.0 8.0
3 1 2.0 -2.5
In [221]: frame.rank(axis='columns')
Out[221]:
a b c
0 2.0 3.0 1.0
1 1.0 3.0 2.0
2 2.0 1.0 3.0
3 2.0 3.0 1.0
帶有重復(fù)標(biāo)簽的軸索引
直到目前為止单料,我所介紹的所有范例都有著唯一的軸標(biāo)簽(索引值)埋凯。雖然許多pandas函數(shù)(如reindex)都要求標(biāo)簽唯一,但這并不是強(qiáng)制性的扫尖。我們來看看下面這個簡單的帶有重復(fù)索引值的Series:
In [222]: obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
In [223]: obj
Out[223]:
a 0
a 1
b 2
b 3
c 4
dtype: int64
索引的is_unique屬性可以告訴你它的值是否是唯一的:
In [224]: obj.index.is_unique
Out[224]: False
對于帶有重復(fù)值的索引白对,數(shù)據(jù)選取的行為將會有些不同。如果某個索引對應(yīng)多個值换怖,則返回一個Series甩恼;而對應(yīng)單個值的,則返回一個標(biāo)量值:
In [225]: obj['a']
Out[225]:
a 0
a 1
dtype: int64
In [226]: obj['c']
Out[226]: 4
這樣會使代碼變復(fù)雜,因為索引的輸出類型會根據(jù)標(biāo)簽是否有重復(fù)發(fā)生變化条摸。
對DataFrame的行進(jìn)行索引時也是如此:
In [227]: df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
In [228]: df
Out[228]:
0 1 2
a 0.274992 0.228913 1.352917
a 0.886429 -2.001637 -0.371843
b 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
In [229]: df.loc['b']
Out[229]:
0 1 2
b 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
5.3 匯總和計算描述統(tǒng)計
pandas對象擁有一組常用的數(shù)學(xué)和統(tǒng)計方法悦污。它們大部分都屬于約簡和匯總統(tǒng)計,用于從Series中提取單個值(如sum或mean)或從DataFrame的行或列中提取一個Series屈溉。跟對應(yīng)的NumPy數(shù)組方法相比塞关,它們都是基于沒有缺失數(shù)據(jù)的假設(shè)而構(gòu)建的抬探∽咏恚看一個簡單的DataFrame:
In [230]: df = pd.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 [231]: df
Out[231]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
調(diào)用DataFrame的sum方法將會返回一個含有列的和的Series:
In [232]: df.sum()
Out[232]:
one 9.25
two -5.80
dtype: float64
傳入axis='columns'或axis=1將會按行進(jìn)行求和運(yùn)算:
In [233]: df.sum(axis=1)
Out[233]:
a 1.40
b 2.60
c NaN
d -0.55
NA值會自動被排除,除非整個切片(這里指的是行或列)都是NA小压。通過skipna選項可以禁用該功能:
In [234]: df.mean(axis='columns', skipna=False)
Out[234]:
a NaN
b 1.300
c NaN
d -0.275
dtype: float64
表5-7列出了這些約簡方法的常用選項线梗。
有些方法(如idxmin和idxmax)返回的是間接統(tǒng)計(比如達(dá)到最小值或最大值的索引):
In [235]: df.idxmax()
Out[235]:
one b
two d
dtype: object
另一些方法則是累計型的:
In [236]: df.cumsum()
Out[236]:
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
還有一種方法,它既不是約簡型也不是累計型怠益。describe就是一個例子仪搔,它用于一次性產(chǎn)生多個匯總統(tǒng)計:
In [237]: df.describe()
Out[237]:
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
對于非數(shù)值型數(shù)據(jù),describe會產(chǎn)生另外一種匯總統(tǒng)計:
In [238]: obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
In [239]: obj.describe()
Out[239]:
count 16
unique 3
top a
freq 8
dtype: object
表5-8列出了所有與描述統(tǒng)計相關(guān)的方法蜻牢。
相關(guān)系數(shù)與協(xié)方差
有些匯總統(tǒng)計(如相關(guān)系數(shù)和協(xié)方差)是通過參數(shù)對計算出來的烤咧。我們來看幾個DataFrame,它們的數(shù)據(jù)來自Yahoo!Finance的股票價格和成交量抢呆,使用的是pandas-datareader包(可以用conda或pip安裝):
conda install pandas-datareader
我使用pandas_datareader模塊下載了一些股票數(shù)據(jù):
import pandas_datareader.data as web
all_data = {ticker: web.get_data_yahoo(ticker)
for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
price = pd.DataFrame({ticker: data['Adj Close']
for ticker, data in all_data.items()})
volume = pd.DataFrame({ticker: data['Volume']
for ticker, data in all_data.items()})
注意:此時Yahoo! Finance已經(jīng)不存在了煮嫌,因為2017年Yahoo!被Verizon收購了。參閱pandas-datareader文檔抱虐,可以學(xué)習(xí)最新的功能昌阿。
現(xiàn)在計算價格的百分?jǐn)?shù)變化,時間序列的操作會在第11章介紹:
In [242]: returns = price.pct_change()
In [243]: returns.tail()
Out[243]:
AAPL GOOG IBM MSFT
Date
2016-10-17 -0.000680 0.001837 0.002072 -0.003483
2016-10-18 -0.000681 0.019616 -0.026168 0.007690
2016-10-19 -0.002979 0.007846 0.003583 -0.002255
2016-10-20 -0.000512 -0.005652 0.001719 -0.004867
2016-10-21 -0.003930 0.003011 -0.012474 0.042096
Series的corr方法用于計算兩個Series中重疊的恳邀、非NA的懦冰、按索引對齊的值的相關(guān)系數(shù)。與此類似谣沸,cov用于計算協(xié)方差:
In [244]: returns['MSFT'].corr(returns['IBM'])
Out[244]: 0.49976361144151144
In [245]: returns['MSFT'].cov(returns['IBM'])
Out[245]: 8.8706554797035462e-05
因為MSTF是一個合理的Python屬性刷钢,我們還可以用更簡潔的語法選擇列:
In [246]: returns.MSFT.corr(returns.IBM)
Out[246]: 0.49976361144151144
另一方面,DataFrame的corr和cov方法將以DataFrame的形式分別返回完整的相關(guān)系數(shù)或協(xié)方差矩陣:
In [247]: returns.corr()
Out[247]:
AAPL GOOG IBM MSFT
AAPL 1.000000 0.407919 0.386817 0.389695
GOOG 0.407919 1.000000 0.405099 0.465919
IBM 0.386817 0.405099 1.000000 0.499764
MSFT 0.389695 0.465919 0.499764 1.000000
In [248]: returns.cov()
Out[248]:
AAPL GOOG IBM MSFT
AAPL 0.000277 0.000107 0.000078 0.000095
GOOG 0.000107 0.000251 0.000078 0.000108
IBM 0.000078 0.000078 0.000146 0.000089
MSFT 0.000095 0.000108 0.000089 0.000215
利用DataFrame的corrwith方法乳附,你可以計算其列或行跟另一個Series或DataFrame之間的相關(guān)系數(shù)内地。傳入一個Series將會返回一個相關(guān)系數(shù)值Series(針對各列進(jìn)行計算):
In [249]: returns.corrwith(returns.IBM)
Out[249]:
AAPL 0.386817
GOOG 0.405099
IBM 1.000000
MSFT 0.499764
dtype: float64
傳入一個DataFrame則會計算按列名配對的相關(guān)系數(shù)。這里许溅,我計算百分比變化與成交量的相關(guān)系數(shù):
In [250]: returns.corrwith(volume)
Out[250]:
AAPL -0.075565
GOOG -0.007067
IBM -0.204849
MSFT -0.092950
dtype: float64
傳入axis='columns'即可按行進(jìn)行計算瓤鼻。無論如何,在計算相關(guān)系數(shù)之前贤重,所有的數(shù)據(jù)項都會按標(biāo)簽對齊茬祷。
唯一值、值計數(shù)以及成員資格
還有一類方法可以從一維Series的值中抽取信息并蝗〖婪福看下面的例子:
In [251]: obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
第一個函數(shù)是unique秸妥,它可以得到Series中的唯一值數(shù)組:
In [252]: uniques = obj.unique()
In [253]: uniques
Out[253]: array(['c', 'a', 'd', 'b'], dtype=object)
返回的唯一值是未排序的,如果需要的話沃粗,可以對結(jié)果再次進(jìn)行排序(uniques.sort())粥惧。相似的,value_counts用于計算一個Series中各值出現(xiàn)的頻率:
In [254]: obj.value_counts()
Out[254]:
c 3
a 3
b 2
d 1
dtype: int64
為了便于查看最盅,結(jié)果Series是按值頻率降序排列的突雪。value_counts還是一個頂級pandas方法,可用于任何數(shù)組或序列:
In [255]: pd.value_counts(obj.values, sort=False)
Out[255]:
a 3
b 2
c 3
d 1
dtype: int64
isin用于判斷矢量化集合的成員資格涡贱,可用于過濾Series中或DataFrame列中數(shù)據(jù)的子集:
In [256]: obj
Out[256]:
0 c
1 a
2 d
3 a
4 a
5 b
6 b
7 c
8 c
dtype: object
In [257]: mask = obj.isin(['b', 'c'])
In [258]: mask
Out[258]:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
In [259]: obj[mask]
Out[259]:
0 c
5 b
6 b
7 c
8 c
dtype: object
與isin類似的是Index.get_indexer方法咏删,它可以給你一個索引數(shù)組,從可能包含重復(fù)值的數(shù)組到另一個不同值的數(shù)組:
In [260]: to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
In [261]: unique_vals = pd.Series(['c', 'b', 'a'])
In [262]: pd.Index(unique_vals).get_indexer(to_match)
Out[262]: array([0, 2, 1, 1, 0, 2])
表5-9給出了這幾個方法的一些參考信息问词。
有時激挪,你可能希望得到DataFrame中多個相關(guān)列的一張柱狀圖辰狡。例如:
In [263]: data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
.....: 'Qu2': [2, 3, 1, 2, 3],
.....: 'Qu3': [1, 5, 2, 4, 4]})
In [264]: data
Out[264]:
Qu1 Qu2 Qu3
0 1 2 1
1 3 3 5
2 4 1 2
3 3 2 4
4 4 3 4
將pandas.value_counts傳給該DataFrame的apply函數(shù),就會出現(xiàn):
In [265]: result = data.apply(pd.value_counts).fillna(0)
In [266]: result
Out[266]:
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
這里垄分,結(jié)果中的行標(biāo)簽是所有列的唯一值宛篇。后面的頻率值是每個列中這些值的相應(yīng)計數(shù)。
5.4 總結(jié)
在下一章锋喜,我們將討論用pandas讀刃┘骸(或加載)和寫入數(shù)據(jù)集的工具。
之后嘿般,我們將更深入地研究使用pandas進(jìn)行數(shù)據(jù)清洗段标、規(guī)整、分析和可視化工具炉奴。