1. 程式人生 > >第五篇 pandas⼊⻔

第五篇 pandas⼊⻔

pandas含有使資料清洗和分析⼯作變得更快更簡單的資料結構和操作⼯具。
pandas經常和其它⼯具⼀同使⽤,如數值計算⼯具NumPy和SciPy,分析庫
statsmodels和scikit-learn,和資料視覺化庫matplotlib。pandas
是基於NumPy陣列構建的,特別是基於陣列的函式和不使⽤for迴圈的資料處理。

pandas與NumPy的不同:pandas採⽤了⼤量的NumPy編碼⻛格,不同是pandas是專⻔為處理表格和混雜資料設計的。⽽NumPy更適合處理統⼀的數值陣列資料。

從2010年pandas開源以來,pandas逐漸成⻓為⼀個⾮常⼤的庫,應⽤於許多真實案例。開發者社群已經有了800個獨⽴的貢獻者。

pandas導⼊約定:import pandas as pd
Series和DataFrame⽤的次數多,可將其引⼊本地名稱空間中會更⽅便:
from pandas import Series, DataFram

一、pandas的資料結構介紹
兩個主要資料結構:SeriesDataFrame。雖不能解決所有問題,但它們為⼤多數應⽤提供了⼀種可靠的、易於使⽤的基礎。

1、Series
Series是⼀種類似於⼀維陣列的物件,它由⼀組資料(各種NumPy資料型別)以及⼀組與之相關的資料標籤(即索引)組成。僅由⼀組資料即可產⽣最簡單的Series:
obj = pd.Series([4, 7, -5, 3])
obj       # 輸出如下:包含元素索引、元素,以及資料型別
0    4
1    7
2    -5
3    3
dtype: int64
Series的字串表現形式為:索引在左邊,值在右邊。未指定索引,於是會⾃動建立⼀個0到N-1(N為資料的⻓度)的整數型索引。可通過Series 的values和index

屬性獲取其陣列表示形式和索引物件:
obj.values      # 輸出:array([ 4, 7, -5, 3], dtype=int64)
obj.index       # 輸出:RangeIndex(start=0, stop=4, step=1),(like range(4))

通常,希望所建立的Series帶有⼀個可以對各個資料點進⾏標記的索引:
obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])  # 指定行索引
obj2   # 輸出如下:
d    4
b    7
a    -5
c    3
dtype: int64
obj2.index # 輸出:Index(['d', 'b', 'a', 'c'], dtype='object')
與普通NumPy陣列相⽐,你可以通過索引的⽅式選取Series中的單個或⼀組值:
obj2['a']      # 輸出:-5
obj2['d'] = 6     # 設定索引'd'的值
obj2[['c', 'a', 'd']]    # 輸出:(注意引數是列表)
c    3
a    -5
d    6
dtype: int64
['c', 'a', 'd']是索引列表,即使它包含的是字串⽽不是整數。

使⽤NumPy函式或類似NumPy的運算(如根據布林型陣列進⾏過濾、標量乘法、應⽤數學函式等)都會保留索引值的連結:
obj2[obj2 > 0]     # 輸出:(布林運算)
d    6
b    7
c    3
dtype: int64
obj2 * 2        # 輸出:(標量乘法)
d    12
b    14
a    -10
c    6
dtype: int64
np.exp(obj2)     # 輸出:(運用於數學函式)
d    403.428793
b    1096.633158
a    0.006738
c    20.085537
dtype: float64

還可以將Series看成是⼀個定⻓的有序字典,因為它是索引值到資料值的⼀個對映。它可以⽤在許多原本需要字典引數的函式中:
'b' in obj2    # 輸出:True
'e' in obj2    # 輸出:False

如果資料被存放在⼀個Python字典中,也可以直接通過這個字典來建立Series
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)
obj3        # 輸出:
Ohio    35000
Texas   71000
Oregon  16000
Utah    5000
dtype: int64
如果只傳⼊⼀個字典,則結果Series中的索引就是原字典的鍵(有序排列)。你可以傳⼊排好序的字典的鍵以改變順序:
states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = pd.Series(sdata, index=states)
obj4        # 輸出:
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的isnullnotnull函式可⽤於檢測缺失資料:
pd.isnull(obj4)      # 檢測obj4中哪些資料缺失
California    True
Ohio      False
Oregon       False
Texas       False
dtype: bool
pd.notnull(obj4)     # 檢測obj4中哪些資料不缺失
California    False
Ohio      True
Oregon       True
Texas     True
dtype: bool
Series也有類似的例項⽅法:
obj4.isnull()       # 檢測obj4中哪些資料缺失,obj4.notnull()方法檢測資料不缺失
California    True
Ohio      False
Oregon       False
Texas       False
dtype: bool

對於許多應⽤⽽⾔,Series最重要的⼀個功能是,它會根據運算的索引標籤⾃動對⻬資料
obj3      # 輸出:
Ohio      35000
Texas     71000
Oregon       16000
Utah      5000
dtype: int64
obj4      # 輸出:
California    NaN
Ohio      35000.0
Oregon      16000.0
Texas         71000.0
dtype: float64
obj3 + obj4     # 輸出:
California    NaN
Ohio      70000.0
Oregon       32000.0
Texas      142000.0
Utah      NaN
dtype: float64
該功能類似於資料庫的join的操作

Series物件本身及其索引都有⼀個name屬性,該屬性跟pandas其他的關鍵功能關係⾮常密切:
obj4.name = 'population'     # 設定Series物件本身的name屬性
obj4.index.name = 'state'     # 設定Series物件索引的name屬性
obj4      # 輸出如下:
state
California    NaN
Ohio      35000.0
Oregon      16000.0
Texas       71000.0
Name: population, dtype: float64

Series的索引可以通過賦值的⽅式就地修改:
obj     # obj的原始索引及資料
0    4
1    7
2    -5
3    3
dtype: int64
obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']   # 修改obj的索引
obj     # obj修改後的索引及資料,輸出如下所示:
Bob       4
Steve    7
Jeff      -5
Ryan    3
dtype: int64

2、DataFrame

DataFrame是⼀個表格型的資料結構,它含有⼀組有序的列,每列可以是不同的值型別(數值、字串、布林值等)。DataFrame既有⾏索引也有列索引,它可以被看做由Series組成的字典(共⽤同⼀個索引)。DataFrame中的資料是以⼀個或多個⼆維塊存放的(⽽不是列表、字典或別的⼀維資料結構)。

雖然DataFrame是以⼆維結構儲存資料的,仍可以將其表示為更⾼維度的資料(層次化索引的表格型結構,這是pandas中許多⾼級資料處理功能的關鍵要素)。

建立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⼀樣),且全部列會被有序排列:
frame      # 輸出如下:
      state          year    pop
0    Ohio         2000    1.5
1    Ohio         2001    1.7
2    Ohio      2002    3.6
3    Nevada    2001    2.4
4    Nevada    2002    2.9
5    Nevada    2003    3.2
使⽤Jupyter notebook,pandas DataFrame物件會以對瀏覽器友好的HTML表格的⽅式呈現。
對於特別⼤的DataFrame,head⽅法會選取前五⾏:
frame.head()     # 輸出如下:
    state     year    pop
0    Ohio    2000    1.5
1    Ohio    2001    1.7
2    Ohio    2002    3.6
3    Nevada    2001    2.4
4    Nevada    2002    2.9
如果指定了列序列,則DataFrame的列就會按照指定順序進⾏排列:
pd.DataFrame(data, columns=['year', 'state', 'pop'])
     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
如果傳⼊的列在資料中找不到,就會在結果中產⽣缺失值:
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
            index=['one', 'two', 'three', 'four','five', 'six'])
frame2      # 輸出如下:
      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
frame2.columns     # 輸出: Index(['year', 'state', 'pop', 'debt'], dtype='object')
通過類似字典標記的⽅式或屬性的⽅式,可以將DataFrame的列獲取為⼀個Series:
frame2['state']     # 通過字典的方式獲取列
one    Ohio
two    Ohio
three     Ohio
four    Nevada
five     Nevada
six      Nevada
Name: state, dtype: object

frame2.year     # 通過屬性的方式獲取
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屬性
frame2.loc['three']   # 輸出如下:
year    2002
state    Ohio
pop     3.6
debt    NaN
Name: three, dtype: object

列可以通過賦值的⽅式進⾏修改。例如,可以給那個空的"debt"列賦上⼀個標量值或⼀組值:
frame2['debt'] = 16.5     # 賦值一個標量值
frame2        # 輸出如下:
      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
frame2['debt'] = np.arange(6.)   # 賦值一組值
frame2        # 輸出如下:
      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的索引,所有的空位都將被填上缺失值:例如:
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val      # 精確匹配索引
frame2       # 輸出如下:
      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':
frame2['eastern'] = frame2.state == 'Ohio'
frame2        # 輸出如下:
      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⽅法可以⽤來刪除這列:
del frame2['eastern']
frame2.columns     # 輸出:Index(['year', 'state', 'pop', 'debt'], dtype='object')
注意:通過索引⽅式返回的列只是相應資料的檢視⽽已,並不是副本。因此,對返回的Series所做的任何就地修改全都會反映到源DataFrame上。通過Series的copy⽅法即可指定複製列。

另⼀種常⻅的資料形式是巢狀字典
pop = {'Nevada': {2001: 2.4, 2002: 2.9}, 'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
如果巢狀字典傳給DataFrame,pandas就會被解釋為:外層字典的鍵作為列索引,內層鍵則作為⾏索引:
frame3 = pd.DataFrame(pop)   # 將字典pop轉換為DataFrame
frame3        # 輸出:
    Nevada   Ohio
2000    NaN    1.5
2001    2.4      1.7
2002    2.9      3.6
也可以使⽤類似NumPy陣列的⽅法,對DataFrame進⾏轉置(交換⾏和列):
frame3.T
      2000    2001    2002
Nevada    NaN    2.4    2.9
Ohio      1.5    1.7    3.6
內層字典的鍵會被合併、排序以形成最終的索引。如果明確指定了索引,則不會這樣:
pd.DataFrame(pop, index=[2001, 2002, 2003])   # 要報錯:AttributeError: 'list' object has no attribute 'astype'
frame55 = pd.DataFrame(pop)        # 先將pop資料轉換為DataFrame資料後可指定索引
pd.DataFrame(frame55, index=[2001, 2002, 2003])   # 這樣指定索引排序不會報錯
    Nevada   Ohio
2001    2.4    1.7
2002    2.9    3.6
2003    NaN    NaN

由Series組成的字典差不多也是⼀樣的⽤法:
pdata = {'Ohio': frame3['Ohio'][:-1], 'Nevada': frame3['Nevada'][:2]}   # 注意:依次是列標籤,行標籤
pd.DataFrame(pdata)   # 輸出如下:(這裡使用:pd.DataFrame(pdata, index=[2001,2000]),不會報錯)
    Ohio    Nevada
2000    1.5    NaN
2001    1.7    2.4

下面列出了DataFrame建構函式所能接受的各種資料(表5-1):

如果設定了DataFrame的index和columns的name屬性,則這些資訊也會被顯示出來:
frame3.index.name = 'year'; frame3.columns.name = 'state'
frame3    # 輸出如下:
state    Nevada   Ohio
year
2000    NaN    1.5
2001    2.4      1.7
2002    2.9      3.6

跟Series⼀樣,values屬性也會以⼆維ndarray的形式返回DataFrame中的資料:
frame3.values     # 輸出如下:
array([[nan, 1.5],
   [2.4, 1.7],
      [2.9, 3.6]])

如果DataFrame各列的資料型別不同,則值陣列的dtype就會選⽤能相容所有列的資料型別:
frame2.values     # 輸出如下:
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)

1、索引物件
pandas的索引物件負責管理軸標籤和其他元資料(⽐如軸名稱等)。構建Series或DataFrame時,所⽤到的任何陣列或其他序列的標籤都會被轉換成⼀個Index:
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index     # 將obj的索引賦值給另一個變數
index       # 輸出:Index(['a', 'b', 'c'], dtype='object')
index[1:]         # 輸出:Index(['b', 'c'], dtype='object')

Index物件是不可變的,因此⽤戶不能對其進⾏修改:
index[1] = 'd'   # 提示錯誤:TypeError: Index does not support mutable operations
不可變可以使Index物件在多個數據結構之間安全共享:
labels = pd.Index(np.arange(3))    # 建立一個索引
labels      # 輸出:Int64Index([0, 1, 2], dtype='int64')
obj2 = pd.Series([1.5, -2.5, 0], index=labels)   # 建立物件時指定索引
obj2      # 輸出可以看出索引是建立時指定的索引
0    1.5
1    -2.5
2    0.0
dtype: float64
obj2.index is labels   # 輸出:True(驗證是否是同一個索引)
雖然Index功能不經常使用,但一些操作會生成包含索引化的資料,理解它的工作原理很重要。

除了類似於陣列,Index的功能也類似⼀個固定⼤⼩的集合:
frame3    # 先看下面前的frame3的輸出:
state    Nevada   Ohio
year
2000    NaN    1.5
2001    2.4       1.7
2002    2.9       3.6
'Ohio' in frame3.columns   # 輸出:True(判斷是否在列索引)
2003 in frame3.index        # 輸出:False(判斷是否在行索引)

與python的集合不同,pandas的Index可以包含重複的標籤:
dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])   # 包含重複索引
dup_labels   # 輸出:Index(['foo', 'foo', 'bar', 'bar'], dtype='object')
選擇重複的標籤,會顯示所有的結果。
每個索引都有⼀些⽅法和屬性,它們可⽤於設定邏輯並回答有關該索引所包含的資料的常⻅問題。下面列出了Index的方法和屬性(表5-2)

 

二、基本功能
下面介紹操作Series和DataFrame中的資料的基本⼿段。


1、重新索引(reindex)
pandas物件的⼀個重要⽅法是reindex,其作⽤是建立⼀個新物件,它的資料符合新的索引。例:
obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b','a', 'c'])
obj   # 輸出:
d    4.5
b    7.2
a    -5.3
c    3.6
dtype: float64
⽤該Series的reindex將會根據新索引進⾏重排。如果某個索引值當前不存在,就引⼊缺失值:
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])   # 重建索引
obj2     # 輸出:
a    -5.3
b    7.2
c    3.6
d    4.5
e    NaN
dtype: float64

對於時間序列這樣的有序資料,重新索引時可能需要做⼀些插值處理。method選項即可達到此⽬的,例如,使⽤ffill可以實現前向值填充:
obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3      # 輸出:
0    blue
2    purple
4    yellow
dtype: object
obj3.reindex(range(6), method='ffill')   # 輸出如下
0    blue
1    blue
2    purple
3    purple
4    yellow
5    yellow
dtype: object
藉助DataFrame,reindex可以修改(⾏和列)索引。只傳遞⼀個序列時,會重新索引結果的⾏:
frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
          index=['a', 'c', 'd'],
          columns=['Ohio', 'Texas', 'California'])
frame    # 輸出:
  Ohio    Texas   California
a    0    1    2
c    3    4    5
d    6    7    8
frame2 = frame.reindex(['a', 'b', 'c', 'd'])   # 只傳遞一個序列,修改行索引
frame2      # 輸出
     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關鍵字重新索引

states = ['Texas', 'Utah', 'California']
frame.reindex(columns=states)    # 指定columns關鍵字,修改列索引
  Texas   Utah     California
a    1    NaN    2
c    4    NaN    5
d    7    NaN    8

下面列出了reindex函式的各引數及說明(表5-3)

2、丟棄指定軸上的項
丟棄某條軸上的⼀個或多個項很簡單,只要有⼀個索引陣列或列表即可。由於需要執⾏⼀些資料整理和集合邏輯,所以drop⽅法返回的是⼀個在指定軸上刪除了指定值的新物件:
obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])   # 假設有這個Series序列
obj      # 輸出如下
a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64
new_obj = obj.drop('c')   # 刪除傳入的值,並得到新的Index
new_obj
a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64
obj.drop(['d', 'c'])      # 刪除傳入的值,注意引數是列表
a    0.0
b    1.0
e    4.0
dtype: float64

對於DataFrame,可以刪除任意軸上的索引值。先新建⼀個DataFrame例⼦:
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
          index=['Ohio', 'Colorado', 'Utah', 'New York'],
          columns=['one', 'two', 'three', 'four'])
data    # 輸出如下:
    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
標籤序列調⽤drop會從⾏標籤(axis 0)刪除值:
data.drop(['Colorado', 'Ohio'])    # 輸出如下:
    one   two   three   four
Utah    8    9    10    11
New York 12   13    14    15
通過傳遞axis=1或axis='columns'可以刪除列的值:
data.drop('two', axis=1)    # 輸出如下:
    one  three    four
Ohio    0    2      3
Colorado  4    6      7
Utah    8   10    11
New York 12    14    15
data.drop(['two', 'four'], axis='columns')   # 輸出如下:
    one     three
Ohio    0    2
Colorado  4    6
Utah    8    10
New York 12   14

許多函式,如drop,會修改Series或DataFrame的⼤⼩或形狀,可就地修改物件,不會返回新的物件:
obj.drop('c', inplace=True)   # ⼩⼼使⽤inplace,它會銷燬所有被刪除的資料。
obj    # 輸出如下:
a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64

3、索引、選取和過濾
Series索引(obj[...])的⼯作⽅式類似於NumPy陣列的索引,只不過Series的索引值不只是整數。例如:
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj   # 輸出如下:
a    0.0
b    1.0
c    2.0
d    3.0
dtype: float64
obj['b']      # 輸出:1.0
obj[1]       # 輸出:1.0
obj[2:4]    # 輸出如下:
c    2.0
d    3.0
dtype: float64
obj[['b', 'a', 'd']]   # 輸出如下:
b    1.0
a    0.0
d    3.0
dtype: float64
obj[[1, 3]]      # 輸出如下:(引數是列表)
b    1.0
d    3.0
dtype: float64
obj[obj < 2]    # 輸出如下:(按條件索引,布林索引)
a    0.0
b    1.0
dtype: float64
利⽤標籤的切⽚運算與普通的Python切⽚運算不同,其末端是包含的:
obj['b':'c']    # 輸出如下:
b   1.0
c    2.0
dtype: float64
⽤切⽚可以對Series的相應部分進⾏設定
obj['b':'c'] = 5
obj    # 輸出如下:
a    0.0
b    5.0
c    5.0
d    3.0
dtype: float64

⽤⼀個值或序列對DataFrame進⾏索引其實就是獲取⼀個或多個列
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
          index=['Ohio', 'Colorado', 'Utah', 'New York'],
          columns=['one', 'two', 'three', 'four'])
data    # 輸出如下:
    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
data['two']   # 輸出如下
Ohio    1
Colorado  5
Utah    9
New York 13
Name: two, dtype: int32
data[['three', 'one']]   # 輸出如下:
    three   one
Ohio      2    0
Colorado    6    4
Utah    10    8
New York 14    12
data[:2]    # 輸出如下:(選取前2行)
    one   two   three    four
Ohio    0    1    2    3
Colorado  4    5    6    7
data[data['three'] > 5]   # 輸出如下:(選取滿足條件的行)
    one   two    three       four
Colorado  4    5    6      7
Utah    8    9   10    11
New York 12    13   14    15
選取⾏的語法data[:2]⼗分⽅便。向[ ]傳遞單⼀的元素或列表,就可選擇列

另⼀種⽤法是通過布林型DataFrame進⾏索引:⽐如下⾯這個由標量⽐較運算得出的
data < 5    # 輸出如下:
        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
data[data < 5] = 0   # 輸出如下:(改變滿足條件的值)
data
    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⼆維陣列的語法很像。

4、⽤loc和iloc進⾏選取

DataFrame⾏標籤索引,引⼊特殊的標籤運算子loc和iloc。它們可以像⽤類似NumPy的標記,使⽤軸標籤(loc)或整數索引(iloc),從DataFrame選擇⾏和列的⼦集。先看⼀個初步示例,通過標籤選擇⼀⾏和多列:
data.loc['Colorado', ['two', 'three']]   # 選取'Colorado'行,和 ['two', 'three']列,輸出如下:
two      5
three    6
Name: Colorado, dtype: int32
然後⽤iloc和整數進⾏選取:
data.iloc[2, [3, 0, 1]]    # 輸出如下:
four    11
one    8
two    9
Name: Utah, dtype: int32
data.iloc[2]   # 輸出如下:(選取第三行資料)
one    8
two    9
three   10
four    11
Name: Utah, dtype: int32
data.iloc[[1, 2], [3, 0, 1]]   # 輸出如下:(選取多行和多列)
      four    one    two
Colorado      7    0    5
Utah      11    8    9

這兩個索引函式也適⽤於⼀個標籤或多個標籤的切⽚:
data.loc[:'Utah', 'two']     # 輸出如下:
Ohio    0
Colorado  5
Utah    9
Name: two, dtype: int32
data.iloc[:, :3][data.three > 5]   # 輸出如下:(選取滿足多種條件的資料)
      one    two    three
Colorado      0    5    6
Utah         8    9    10
New York    12    13    14
在pandas中,有多個⽅法可以選取和重新組合資料。當然還有更多的方法進行層級化索引。

DataFrame的索引選項如下所示(表5-4):

5、整數索引
pandas整數索引與Python內建的列表和元組的索引語法不同
例如,你可能不認為下⾯的程式碼會出錯:
ser = pd.Series(np.arange(3.))
ser   # 輸出如下:
0   0.0
1   1.0
2   2.0
dtype: float64
ser[-1]   # 索引會報錯,KeyError: -1,因為軸索引含有整數
ser有包含0,1,2的索引,但是引⼊⽤戶想要的東⻄(基於標籤或位置的索引)很難:

另外,對於⾮整數索引,不會產⽣歧義:
ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
ser2[-1]    # 輸出:2.0
為了進⾏統⼀,如果軸索引含有整數,資料選取總會使⽤標籤。為了更準確,請使⽤loc(標籤)或iloc(整數)
ser[:1]   # 整數索引,注意看與下面2個索引的區別,輸出如下:
0    0.0
dtype: float64
ser.loc[:1]    # 標籤索引,輸出如下:
0    0.0
1    1.0
dtype: float64
ser.iloc[:1]    # 整數索引,輸出如下:
0    0.0
dtype: float64

6、算術運算和資料對⻬
pandas最重要的⼀個功能是,它可以對不同索引的物件進⾏算術運算在將物件相加時,如果存在不同的索引對,則結果的索引就是該索引對的並集。對於有資料庫經驗的話,這就像在索引標籤上進⾏⾃動外連線。看⼀個簡單的例⼦:
s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
        index=['a', 'c', 'e', 'f', 'g'])
s1    # 輸出如下:
a    7.3
c    -2.5
d    3.4
e    1.5
dtype: float64
s2    # 輸出如下:
a    -2.1
c    3.6
e    -1.5
f    4.0
g    3.1
dtype: float64
s1 + s2    # 輸出如下:
a    5.2
c    1.1
d    NaN
e    0.0
f    NaN
g    NaN
dtype: float64
⾃動的資料對⻬操作在不重疊的索引處引⼊了NA值。缺失值會在算術運算過程中傳播。

對於DataFrame,對⻬操作會同時發⽣在⾏和列上:
df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'), index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
df1    # 輸出如下:
      b     c       d
Ohio      0.0    1.0    2.0
Texas    3.0    4.0    5.0
Colorado    6.0    7.0    8.0
df2    # 輸出如下:
         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的並集:
df1 + df2    # 輸出如下:
        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物件中,在結果中以預設值呈現。⾏也是同樣。

如果DataFrame物件相加,沒有共⽤的列或⾏標籤,結果都會是空
df1 = pd.DataFrame({'A': [1, 2]})
df2 = pd.DataFrame({'B': [3, 4]})
df1    # 輸出如下:
      A
0    1
1    2
df2    # 輸出如下:
      B
0    3
1    4
df1 - df2    # 輸出如下:(沒有共⽤的列或⾏標籤,結果都會是空)
    A     B
0   NaN   NaN
1   NaN   NaN

7、在算術⽅法中填充值
在對不同索引的物件進⾏算術運算時,你可能希望當⼀個物件中某個軸標籤在另⼀個物件中找不到時填充⼀個特殊值(⽐如0):
df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))
df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)), columns=list('abcde'))
df2.loc[1, 'b'] = np.nan
df1    # 輸出如下:
    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
df2    # 輸出如下:
      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
將它們相加時,沒有重疊的位置就會產⽣NA值:
df1 + df2    # 輸出如下:
    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引數
df1.add(df2, fill_value=0)   # 輸出如下:(引數fill_value指定缺失的話用替代)
      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

下面列出了Series和DataFrame的算術⽅法。它們每個都有⼀個副本,以字⺟r開頭,
它會翻轉引數。因此這兩個語句是等價的:
1 / df1    # 輸出如下:
        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
df1.rdiv(1)    # 輸出如下:
        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的算術⽅法)(表5-5):

與此類似,在對Series或DataFrame重新索引時,也可以指定⼀個填充值
df1.reindex(columns=df2.columns, fill_value=0)     # 輸出如下:
    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

8、DataFrame和Series之間的運算

DataFrame和Series之間算術運算也是有明確規定的。先來看⼀個例⼦,計算⼀個⼆維陣列與其某⾏之間的差:
arr = np.arange(12.).reshape((3, 4))
arr    # 輸出如下:
array([[ 0., 1., 2., 3.],
  [ 4., 5., 6., 7.],
  [ 8., 9., 10., 11.]])
arr[0]      # 輸出:array([0., 1., 2., 3.])
arr - arr[0]    # 輸出如下:
array([[0., 0., 0., 0.],
  [4., 4., 4., 4.],
  [8., 8., 8., 8.]])
當從arr減去arr[0],每⼀⾏都會執⾏這個操作。這就叫做⼴播(broadcasting)。DataFrame和Series之間的運算差不多也是如此:
frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
frame   # 輸出如下:
      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
series    # 輸出如下:
b    0.0
d    1.0
e    2.0
Name: Utah, dtype: float64
預設情況下,DataFrame和Series之間的算術運算會將Series的索引匹配到DataFrame的列,然後沿著⾏⼀直向下⼴播:
frame - series   # 輸出如下:
       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的索引中找不到,則參與運算的兩個物件就會被重新索引以形成並集:
series2 = pd.Series(range(3), index=['b', 'e', 'f'])
frame + series2   # 輸出如下:
      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

如果你希望匹配⾏且在列上⼴播,則必須使⽤算術運算⽅法。例如:
frame    # 輸出如下:
         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
series3    # 輸出如下:
Utah    1.0
Ohio    4.0
Texas   7.0
Oregon  10.0
Name: d, dtype: float64
frame.sub(series3, axis='index')    # 輸出如下:
         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)並進⾏⼴播。

9、函式應⽤和對映
NumPy的ufuncs(元素級陣列⽅法)也可⽤於操作pandas物件:
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
frame    # 輸出如下:
          b        d          e
Utah    -0.238758   -1.228157   -0.433499
Ohio    0.281959    0.229944    1.908042
Texas   -1.180401   -0.234310   -0.264257
Oregon  0.635323   -1.248893    1.319811
np.abs(frame)   # 輸出如下:
        b        d       e
Utah    0.238758    1.228157    0.433499
Ohio    0.281959    0.229944    1.908042
Texas     1.180401    0.234310    0.264257
Oregon     0.635323    1.248893    1.319811

另⼀個常⻅的操作是,將函式應⽤到由各列或⾏所形成的⼀維陣列上。DataFrame的apply⽅法即可實現此功能:
f = lambda x: x.max() - x.min()
frame.apply(f)   # 輸出如下:
b    1.815723
d    1.478837
e    2.341541
dtype: float64
這⾥的函式f,計算了⼀個Series的最⼤和雖⼩的差,在frane的每列都執⾏了⼀次。結果是⼀個Series,使⽤frame的列作為索引。

如果傳遞axis='columns'到apply,這個函式會在每⾏執⾏:
frame.apply(f, axis='columns')   # 輸出如下:
Utah    0.989399
Ohio    1.678098
Texas   0.946090
Oregon   2.568704
dtype: float64
許多常⻅的陣列統計功能都被實現成DataFrame的⽅法(如sum和mean),因此⽆需使⽤apply⽅法。傳遞到apply的函式不是必須返回⼀個標量,還可以返回由多個值組成的Series:
def f(x):
  return pd.Series([x.min(), x.max()], index=['min', 'max'])
frame.apply(f)    # 輸出如下:
        b         d         e
min   -1.180401   -1.248893   -0.433499
max      0.635323    0.229944    1.908042

元素級的Python函式也是可以⽤的。假如你想得到frame中各個浮點值的格式化字串,使⽤applymap即可:
format = lambda x: '%.2f' % x
frame.applymap(format)   # 輸出如下:
      b      d     e
Utah    -0.24    -1.23   -0.43
Ohio    0.28    0.23      1.91
Texas   -1.18    -0.23    -0.26
Oregon  0.64    -1.25    1.32
之所以叫做applymap,是因為Series有⼀個⽤於應⽤元素級函式的map⽅法
frame['e'].map(format)   # 輸出如下:
Utah    -0.43
Ohio    1.91
Texas   -0.26
Oregon   1.32
Name: e, dtype: object

10、排序和排名
根據條件對資料集排序(sorting)也是⼀種重要的內建運算。要對⾏或列索引進⾏排序(按字典順序),可使⽤sort_index⽅法,它將返回⼀個已排序的新物件:
obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
obj.sort_index()    # 輸出如下:
a    1
b    2
c    3
d    0
dtype: int64

對於DataFrame,則可以根據任意⼀個軸上的索引進⾏排序:
frame = pd.DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'], columns=['d', 'a', 'b', 'c'])
frame.sort_index()    # 輸出如下:(行索引排序)
       d    a    b    c
one      4    5    6    7
three    0    1   2    3
frame.sort_index(axis=1)   # 輸出如下:(指定引數axis=1按列標籤排序)
       a    b    c    d
three    1    2    3    0
one      5    6    7    4
資料預設是按升序排序的,但也可以降序排序:
frame.sort_index(axis=1, ascending=False)   # 輸出如下:(列標籤降序排序)
       d    c    b    a
three    0    3    2   1
one      4    7   6    5

若要按值對Series進⾏排序,可使⽤其sort_values⽅法:
obj = pd.Series([4, 7, -3, 2])
obj.sort_values()     # 輸出如下:
2    -3
3    2
0    4
1    7
dtype: int64
在排序時,任何缺失值預設都會被放到Series的末尾:
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
obj.sort_values()   # 輸出如下:
4    -3.0
5    2.0
0    4.0
2    7.0
1    NaN
3    NaN
dtype: float64

當排序⼀個DataFrame時,你可能希望根據⼀個或多個列中的值進⾏排序。將⼀個或多個列的名字傳遞給sort_values的by選項即可達到該⽬的:
frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
frame.sort_values(by='b')   # 輸出如下:(對b列排序)
      b    a
2   -3    0
3    2    1
0    4    0
1    7    1
要根據多個列進⾏排序,傳⼊名稱的列表即可:
frame.sort_values(by=['a', 'b'])   # 輸出如下:(引數是列表)
      b    a
2   -3    0
0    4    0
3    2    1
1    7    1

排名會從1開始⼀直到陣列中有效資料的數量。接下來介紹Series和DataFrame的rank⽅法
預設情況下,rank是通過“為各組分配⼀個平均排名”的⽅式破壞平級關係的:
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
obj.rank()    # 輸出如下:
0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64
也可以根據值在原資料中出現的順序給出排名:
obj.rank(method='first')   # 輸出如下:
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,它們被設成了6和7,因為資料中標籤0位於標籤2的前⾯。
也可以按降序進⾏排名:
obj.rank(ascending=False, method='max')   # 輸出如下:
0    2.0
1    7.0
2    2.0
3    4.0
4    5.0
5    6.0
6    4.0
dtype: float64

DataFrame可以在⾏或列上計算排名:
frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]})

frame    # 輸出如下:
    b    a       c
0    4.3    0   -2.0
1    7.0    1    5.0
2   -3.0    0    8.0
3    2.0    1   -2.5
frame.rank(axis='columns')   # 輸出如下:
    b      a     c
0    3.0    2.0    1.0
1    3.0    1.0    2.0
2    1.0    2.0    3.0
3    3.0    2.0    1.0

下面是所有⽤於破壞平級關係的method選項(排名時⽤於破壞平級關係的⽅法)(表5-6)

(未完待續)