【翻譯】《利用Python進行數據分析·第2版》第5章(上)pandas入門

作者:SeanCheney Python愛好者社區專欄作者

簡書專欄:jianshu.com/u/130f76596

公眾號:Python愛好者社區

前文傳送門:

【翻譯】《利用Python進行數據分析·第2版》第1章 準備工作

【翻譯】《利用Python進行數據分析·第2版》第2章(上)Python語法基礎,IPython和Jupyter

【翻譯】《利用Python進行數據分析·第2版》第2章(中)Python語法基礎,IPython和Jupyter

【翻譯】《利用Python進行數據分析·第2版》第2章(下)Python語法基礎,IPython和Jupyter

【翻譯】《利用Python進行數據分析·第2版》第3章(上)Python的數據結構、函數和文件

【翻譯】《利用Python進行數據分析·第2版》第3章(中)Python的數據結構、函數和文件

【翻譯】《利用Python進行數據分析·第2版》第3章(下)Python的數據結構、函數和文件

【翻譯】《利用Python進行數據分析·第2版》第4章(上)NumPy基礎:數組和矢量計算

【翻譯】《利用Python進行數據分析·第2版》第4章(中)NumPy基礎:數組和矢量計算

【翻譯】《利用Python進行數據分析·第2版》第4章(下)NumPy基礎:數組和矢量計算

pandas是本書後續內容的首選庫。它含有使數據清洗和分析工作變得更快更簡單的數據結構和操作工具。pandas經常和其它工具一同使用,如數值計算工具NumPy和SciPy,分析庫statsmodels和scikit-learn,和數據可視化庫matplotlib。pandas是基於NumPy數組構建的,特別是基於數組的函數和不使用for循環的數據處理。

雖然pandas採用了大量的NumPy編碼風格,但二者最大的不同是pandas是專門為處理表格和混雜數據設計的。而NumPy更適合處理統一的數值數組數據。

自從2010年pandas開源以來,pandas逐漸成長為一個非常大的庫,應用於許多真實案例。開發者社區已經有了800個獨立的貢獻者,他們在解決日常數據問題的同時為這個項目提供貢獻。

在本書後續部分中,我將使用下面這樣的pandas引入約定:

In [1]: import pandas as pd

因此,只要你在代碼中看到pd.,就得想到這是pandas。因為Series和DataFrame用的次數非常多,所以將其引入本地命名空間中會更方便:

In [2]: from pandas import Series, DataFrame

5.1 pandas的數據結構介紹

要使用pandas,你首先就得熟悉它的兩個主要數據結構:Series和DataFrame。雖然它們並不能解決所有問題,但它們為大多數應用提供了一種可靠的、易於使用的基礎。

Series

Series是一種類似於一維數組的對象,它由一組數據(各種NumPy數據類型)以及一組與之相關的數據標籤(即索引)組成。僅由一組數據即可產生最簡單的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的字元串表現形式為:索引在左邊,值在右邊。由於我們沒有為數據指定索引,於是會自動創建一個0到N-1(N為數據的長度)的整數型索引。你可以通過Series 的values和index屬性獲取其數組表示形式和索引對象:

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)

通常,我們希望所創建的Series帶有一個可以對各個數據點進行標記的索引:

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數組相比,你可以通過索引的方式選取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]是索引列表,即使它包含的是字元串而不是整數。

使用NumPy函數或類似NumPy的運算(如根據布爾型數組進行過濾、標量乘法、應用數學函數等)都會保留索引值的鏈接:

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看成是一個定長的有序字典,因為它是索引值到數據值的一個映射。它可以用在許多原本需要字典參數的函數中:

In [24]: b in obj2

Out[24]: True

In [25]: e in obj2

Out[25]: False

如果數據被存放在一個Python字典中,也可以直接通過這個字典來創建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

如果只傳入一個字典,則結果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個值會被找出來並放到相應的位置上,但由於"California"所對應的sdata值找不到,所以其結果就為NaN(即「非數字」(not a number),在pandas中,它用於表示缺失或NA值)。因為『Utah』不在states中,它被從結果中除去。

我將使用缺失(missing)或NA表示缺失數據。pandas的isnull和notnull函數可用於檢測缺失數據:

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章詳細講解如何處理缺失數據。

對於許多應用而言,Series最重要的一個功能是,它會根據運算的索引標籤自動對齊數據:

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

數據對齊功能將在後面詳細講解。如果你使用過資料庫,你可以認為是類似join的操作。

Series對象本身及其索引都有一個name屬性,該屬性跟pandas其他的關鍵功能關係非常密切:

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是一個表格型的數據結構,它含有一組有序的列,每列可以是不同的值類型(數值、字元串、布爾值等)。DataFrame既有行索引也有列索引,它可以被看做由Series組成的字典(共用同一個索引)。DataFrame中的數據是以一個或多個二維塊存放的(而不是列表、字典或別的一維數據結構)。有關DataFrame內部的技術細節遠遠超出了本書所討論的範圍。

筆記:雖然DataFrame是以二維結構保存數據的,但你仍然可以輕鬆地將其表示為更高維度的數據(層次化索引的表格型結構,這是pandas中許多高級數據處理功能的關鍵要素,我們會在第8章討論這個問題)。

建DataFrame的辦法有很多,最常用的一種是直接傳入一個由等長列表或NumPy數組組成的字典:

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)

結果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表格的方式呈現。

對於特別大的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的列就會按照指定順序進行排列:

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

如果傳入的列在數據中找不到,就會在結果中產生缺失值:

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)

通過類似字典標記的方式或屬性的方式,可以將DataFrame的列獲取為一個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補全。

frame2[column]適用於任何列的名,但是frame2.column只有在列名是一個合理的Python變數名時才適用。

注意,返回的Series擁有原DataFrame相同的索引,且其name屬性也已經被相應地設置好了。

行也可以通過位置或名稱的方式進行獲取,比如用loc屬性(稍後將對此進行詳細講解):

In [53]: frame2.loc[three]

Out[53]:

year 2002

state Ohio

pop 3.6

debt NaN

Name: three, dtype: object

列可以通過賦值的方式進行修改。例如,我們可以給那個空的"debt"列賦上一個標量值或一組值:

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

將列表或數組賦值給某個列時,其長度必須跟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

為不存在的列賦值會創建出一個新列。關鍵字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創建新的列。

del方法可以用來刪除這列:

In [63]: del frame2[eastern]

In [64]: frame2.columns

Out[64]: Index([year, state, pop, debt], dtype=object)

注意:通過索引方式返回的列只是相應數據的視圖而已,並不是副本。因此,對返回的Series所做的任何就地修改全都會反映到源DataFrame上。通過Series的copy方法即可指定複製列。

另一種常見的數據形式是嵌套字典:

In [65]: pop = {Nevada: {2001: 2.4, 2002: 2.9},

....: Ohio: {2000: 1.5, 2001: 1.7, 2002: 3.6}}

如果嵌套字典傳給DataFrame,pandas就會被解釋為:外層字典的鍵作為列,內層鍵則作為行索引:

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數組的方法,對DataFrame進行轉置(交換行和列):

In [68]: frame3.T

Out[68]:

2000 2001 2002

Nevada NaN 2.4 2.9

Ohio 1.5 1.7 3.6

內層字典的鍵會被合併、排序以形成最終的索引。如果明確指定了索引,則不會這樣:

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構造函數所能接受的各種數據。

如果設置了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中的數據:

In [74]: frame3.values

Out[74]:

array([[ nan, 1.5],

[ 2.4, 1.7],

[ 2.9, 3.6]])

如果DataFrame各列的數據類型不同,則值數組的dtype就會選用能兼容所有列的數據類型:

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的索引對象負責管理軸標籤和其他元數據(比如軸名稱等)。構建Series或DataFrame時,所用到的任何數組或其他序列的標籤都會被轉換成一個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對象是不可變的,因此用戶不能對其進行修改:

index[1] = d # TypeError

不可變可以使Index對象在多個數據結構之間安全共享:

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

注意:雖然用戶不需要經常使用Index的功能,但是因為一些操作會生成包含被索引化的數據,理解它們的工作原理是很重要的。

除了類似於數組,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可以包含重複的標籤:

In [89]: dup_labels = pd.Index([foo, foo, bar, bar])

In [90]: dup_labels

Out[90]: Index([foo, foo, bar, bar], dtype=object)

選擇重複的標籤,會顯示所有的結果

每個索引都有一些方法和屬性,它們可用於設置邏輯並回答有關該索引所包含的數據的常見問題。表5-2列出了這些函數。


推薦閱讀:

泰坦尼克號存活率實踐筆記-排名3626
5機器學習入門:Kaggle和泰坦尼克號預測
2018數據分析實踐計劃
詳解特徵工程與推薦系統及其實踐
《專利審查指南修改草案》將圖形用戶界面(GUI)納入外觀設計專利範圍內,這改變意味著什麼?

TAG:Python | 數據分析 | pandas包 |