上一篇我們瞭解了 NumPy 的基本功能,本篇引入一個新的 python 數據處理庫——pandas。
NumPy 更適合處理統一的數值數組數據。Pandas 是基於 NumPy 數組構建的,專門處理表格和混雜數據。接下來,讓我們來了解一下 pandas 的基本使用吧。
首先讓我們導入 pandas 庫:
import pandas as pd
import numpy as np # 後續也會使用到 numpy,在此先導入進來
3.1 pandas 的數據結構
pandas 有兩個基本的數據結構:Series 和 DataFrame。
3.1.1 Series
我們可以把 Series 理解為帶有索引的一維 np 數組。讓我們創建出來對比一下:
in: obj = pd.Series([4, 7, -5, 3]) # 可以將一個 list 轉換為 Series
obj
out: 0 4
1 7
2 -5
3 3
dtype: int64
in: np_obj = np.array([4, 7, -5, 3])
np_obj
out: array([ 4, 7, -5, 3])
可以看到,用 pd.Series() 創建的 Series 比 np.array() 創建的 ndarray 多了索引值——0, 1, 2, 3。
Series 的兩個屬性也可以在此印證這個事實:
in: obj.values # Series 的值:確實是個 np 數組
out: array([ 4, 7, -5, 3], dtype=int64)
in: obj.index # Series 的索引(index)
out: RangeIndex(start=0, stop=4, step=1)
如果不想要 pandas 自動生成的索引,也可以指定:
in: obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
obj2
out: d 4
b 7
a -5
c 3
dtype: int64
如何從 Series 中選取出一個值或一組值呢?
in: obj2['a']
out: -5
in: obj2[['a', 'b']] # 取出一組值時,注意方括號的使用
out: a -5
b 7
dtype: int64 # 取出一組值時,取出的也是一個 Series
Series 如何運算呢?它可以像 NumPy 那樣運算。
obj2[obj2 > 2] # 用布爾值索引選取
obj2 * 2 # 元素級乘法運算
np.exp(obj2)
Series 有索引也有值,就像字典的“鍵-值”一樣。Series 的一些用法也像字典一樣:
in: "a" in obj2 # 像是在檢查某個值是否屬於字典的鍵
out: True
剛才在創建 Series 時,我們是將一個 list 轉換為 Series。其實,也可以將一個 dict 轉換為 Series(此時,字典的鍵就是 Series 的索引):
in: sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)
obj3
out: Ohio 35000
Texas 71000
Oregon 16000
Utah 5000
dtype: int64
在已經有索引的情況下,如果再指定索引,會發生什麼呢?
in: states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = pd.Series(sdata, index=states)
obj4
out: California NaN # 原索引被新索引代替了,新索引中的 California 找不到對應的原索引,因此賦空值 NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64 # 新索引中沒有 Utah 了,此處可以看到也沒有出現 Utah 的數據
我們在上一個代碼塊中看到了缺失值的存在。我們在處理數據時常常碰到有缺失數據的情況。那麼如何檢測數據中是否存在缺失數據呢?
in: pd.isnull(obj4) # 和下面效果一樣
obj4.isnull() # 和上面效果一樣
out: California True
Ohio False
Oregon False
Texas False
dtype: bool
in: pd.notnull(obj4) # 也可以檢測非空值
out: California False
Ohio True
Oregon True
Texas True
dtype: bool
最後,再介紹一個 Series 的重要功能:根據運算的索引標籤自動對齊數據。
in: obj3 + obj4 # 兩個 Series 的索引存在不同,而加法運算自動將能對應上的索引值相加
out: California NaN # 而對應不上的值直接賦空值
Ohio 70000.0
Oregon 32000.0
Texas 142000.0
Utah NaN # 對應不上,賦空值,不論其是否在某一個 Series 中有值
dtype: float64
3.1.2 DataFrame
DataFrame 是一個表格型數據結構,既有行索引也有列索引。一列的值類型是相同的,但不同列可以有不同的值類型。
首先讓我們創建一個 DataFrame。
in: 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]} # 建立一個值為等長 list 的字典
frame = pd.DataFrame(data) # 將上述字典轉為 DataFrame
frame
out: 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 # 可以看到這個 DataFrame 使用了字典的鍵作為列索引,行索引是自動創建的
我們時常會處理很大數據量的 DataFrame,當想查看這個表格結構時,並不想將整個表格都輸出。我們可以用 head() 查看一個 DataFrame 的前幾行:
in: frame.head()
out: 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行
in: frame.head(3) # 可以設置輸出的行數
out: state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
那如何取出一列呢?
in: frame['state'] # 像取字典的值那樣就可以,我們取出了一個 Series
frame.state # 這樣也可以,效果和上一行是一樣的,因為 DataFrame 有一個 name 屬性
out: 0 Ohio
1 Ohio
2 Ohio
3 Nevada
4 Nevada
5 Nevada
Name: state, dtype: object
在創建 DataFrame 時,我們還可以指定列(字段)的排列順序,讓表格按我們想要的順序排列:
in: pd.DataFrame(data, columns=['year', 'state', 'pop'])
out: 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: frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
index=['one', 'two', 'three', 'four', 'five', 'six'])
frame2
out: year state pop debt # 可以看到 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: frame2['debt'] = 12 # 可以直接賦一個整數,這會被廣播到整列
out: year state pop debt
one 2000 Ohio 1.5 12
two 2001 Ohio 1.7 12
three 2002 Ohio 3.6 12
four 2001 Nevada 2.4 12
five 2002 Nevada 2.9 12
six 2003 Nevada 3.2 12
in: frame2['debt'] = np.arange(6.) # 也可以加入一個與表格長度等長的序列
out: 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
in: val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val # 如果將一個帶有能匹配上的索引的 Series 賦值給缺失值列的話
frame2
out: 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 # 可以看到匹配上索引的賦值了,沒匹配上的仍是缺失值
如果我們不想要 debt 這列了,如何刪除呢?
in: del frame2['debt']
frame2.columns # 來查看一下還有哪幾列
out: Index(['year', 'state', 'pop'], dtype='object')
3.2 pandas 基本功能
3.2.1 插入新數據:使用“重新索引”
有時我們希望往 Series 或 DataFrame 中插入新數據,這可以用重新索引的方式實現。對於 Series 而言:
in: obj = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj
out: 0 blue
2 purple
4 yellow
dtype: object
in: obj.reindex(range(6)) # 我們通過改變並覆蓋原來的索引,達到插入新行的目的
out: 0 blue
1 NaN
2 purple
3 NaN
4 yellow
5 NaN
dtype: object
in: obj.reindex(range(6),method='ffill') # 指定 method 參數,還可以實現諸如插值處理的操作
out: 0 blue
1 blue # 不再是空值了,而是按照前一個值來填充
2 purple
3 purple
4 yellow
5 yellow
dtype: object
DataFrame 有兩個索引,因此我們可以通過重新索引向表格中插入行和列。
in: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
index=['a', 'c', 'd'],
columns=['Ohio', 'Texas', 'California'])
frame
out: Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
in: frame2 = frame.reindex(['a', 'b', 'c', 'd']) # 默認重新索引行,用一個 list 表述新的索引序列。
frame2
out: 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
in: states = ['Texas', 'Utah', 'California']
frame2.reindex(columns=states) # 指明 columns 參數,即可重新索引列
out: Texas Utah California # 原數據中,沒匹配索引的列被刪除了
a 1.0 NaN 2.0
b NaN NaN NaN
c 4.0 NaN 5.0
d 7.0 NaN 8.0
3.2.2 丟棄指定軸上的項
我們可以指定索引,來丟棄指定軸上的項。
對 Series 來説,只有一個索引,可以這麼做:
in: obj = pd.Series(np.arange(5.))
obj.drop([1, 3])
out: 0 0.0
2 2.0
4 4.0
dtype: float64
對於 DataFrame 來説,索引有兩個,drop() 默認對行刪除。注意,drop() 的刪除是一個視圖,並不是直接在原數據上操作的。如果想直接刪除的話,需要使用 del。
in: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
data.drop('Ohio')
out: one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
in: data.drop(['one', 'two'], axis=1) # 指定 axis=1 可以對列進行刪除
out: three four
Ohio 2 3
Colorado 6 7
Utah 10 11
New York 14 15
在實際操作中,我通常設置橫軸的索引,而是用默認自動創建的序號索引(0, 1, 2, ...)。
這樣的話,drop() 後,index 就會有殘缺。
in: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
columns=['one', 'two', 'three', 'four'])
data = data.drop(index=(data.loc[(data['one']>5) & (data['one']<10)]).index)
data
out: one two three four
0 0 1 2 3
1 4 5 6 7
3 12 13 14 15 # 缺失了 index = 2 的行
這時,我們需要重新設置 index:
in: data = data.reset_index(drop=True) # 指定 drop=True 可以不保留原來的 index
out: one two three four
0 0 1 2 3
1 4 5 6 7
2 12 13 14 15
3.2.3 選取 Series 中的一些數據
可以像字典那樣,寫出鍵,就可以選取:
in: obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj
out: a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
in: obj['b']
obj[1] # 即使我們設置了 index 的名稱,仍然可以用默認的數字 index 進行索引,和上一行產生同樣的效果
out: 1.0
in: obj[0:2] # 可以利用切片選取連續的幾個值
out: a 0.0
b 1.0
dtype: float64
in: obj[[0, 2]] # 也可以選取不連續的幾個值
out: a 0.0
c 2.0
dtype: float64
in: obj[obj<2] # 布爾值也可以索引,像 ndarray 一樣
out: a 0.0
b 1.0
dtype: float64
這裏用切片選取時,有個有意思的特點:用普通的 python 切片得到的數據是不包含末端的,但用我們設置的 index 標籤來切片,就會包含末端數據。
in: obj['a':'c']
out: a 0.0
b 1.0
c 2.0
dtype: float64 # 可以對比上面的 obj[0:2]
3.2.4 選取 DataFrame 中的一些數據
可以直接用類似上面的方法選取:
in: data
out: one two three four
0 0 1 2 3
1 4 5 6 7
2 12 13 14 15
in: data[['one', 'three']] # 選取指定的兩列數據
out: one three
0 0 2
1 4 6
2 12 14
in: data[:2] # 利用切片按行選取
out: one two three four
0 0 1 2 3
1 4 5 6 7
常用的選取方法是用布爾值選取,以達到按條件選取的目的:
in: data['three'] > 5 # 這樣可以返回一個布爾值序列
out: 0 False
1 True
2 True
Name: three, dtype: bool
in: data[data['three'] > 5] # 用這樣的布爾值選取,我們就可以實現按條件選取(選出 three 列中值 > 5 的行)
out: one two three four
1 4 5 6 7
2 12 13 14 15
此外,另外一種常見的選取方式是通過軸標籤(loc)或整數索引(iloc)來選取:
in: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
data
out: 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: data.loc['Utah', ['one', 'four']] # 指定行和列
data.iloc[2,[0,3]] # 和上式達到相同效果
out: one 8
four 11
Name: Utah, dtype: int32
in: data.loc[:'Utah', 'two'] # loc 和 iloc 也可以用切片索引
out: Ohio 1
Colorado 5
Utah 9
Name: two, dtype: int32
in: data.iloc[:, :3][data.three > 5] # 還可以加條件,通過在最後加一個方括號包含的布爾值序列的方式
out: one two three
Colorado 4 5 6
Utah 8 9 10
New York 12 13 14
3.2.5 算數運算和數據對齊
pandas 的一個重要特性是,它支持對兩個索引不同的對象做運算,並進行數據對齊——運算時,索引匹配上的值會進行運算,索引不匹配的值會直接賦值為空。
in: 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 + s2
out: a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN # 即使 s2 中有值,但 s1 中不存在這個索引,運算後該索引的值仍為空
dtype: float64
DataFrame 有兩個索引,數據對齊會同時發生在行和列上。
in: 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 + df2
out: 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
in: df1.add(df2, fill_value=0) # 如果不想要空值,可以使用 add(),並指明 fill_value=0
out: b c d e
Colorado 6.0 7.0 8.0 NaN # 可以看到還是存在空值,這是因為當兩個表中都不存在值時不能填充
Ohio 3.0 1.0 6.0 5.0
Oregon 9.0 NaN 10.0 11.0
Texas 9.0 4.0 12.0 8.0
Utah 0.0 NaN 1.0 2.0
如果是 DataFrame 和 Series 進行運算,運算會被逐行廣播:
in: frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
frame
series
out: 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
b 0.0
d 1.0
e 2.0
in: frame - series # 表格從第一行開始減 series,一直向下廣播,減到最後一行
out: 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
3.2.6 函數應用
numpy 的 ufuncs(元素級數組方法)也可用於操作 pandas 對象。而 pandas 對象也內置了許多常用的統計方法,可以直接調用。
in: frame = pd.DataFrame(np.random.randn(4, 3),
columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
frame
out: b d e
Utah -0.661018 0.130761 0.508856
Ohio 0.174303 -1.288930 -0.384396
Texas -0.523870 -0.657847 -0.822806
Oregon 0.969613 0.894881 -0.623135
in: np.abs(frame)
frame.abs() # DataFrame 已經內置了 abs() 方法,該行和上一行會產生相同的效果
out: Utah 0.661018 0.130761 0.508856
Ohio 0.174303 1.288930 0.384396
Texas 0.523870 0.657847 0.822806
Oregon 0.969613 0.894881 0.623135
複雜的計算可以使用 lambda 函數,並用 apply() 將函數應用到由各列或行所形成的一維數組上。
in: frame.apply(lambda x: x.max() - x.min()) # 對每列進行函數運算
out: b 1.630631
d 2.183811
e 1.331663
dtype: float64
in: frame.apply(lambda x: x.max() - x.min(), axis=1) # 匹配列,對每行進行函數運算
out: Utah 1.169874
Ohio 1.463233
Texas 0.298937
Oregon 1.592748
dtype: float64
3.2.7 排序
對於 DataFrame 來説,排序可以按行索引排,也可以按列索引排。
in: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
index=['three', 'one'],
columns=['d', 'a', 'b', 'c'])
frame
out: d a b c
three 0 1 2 3
one 4 5 6 7
in: frame.sort_index() # 按行排
out: d a b c
one 4 5 6 7
three 0 1 2 3
in: frame.sort_index(axis=1, ascending=False) # 按列排,降序排
out: d c b a
three 0 3 2 1
one 4 7 6 5
此外,還可以按值排序:
in: frame.sort_values(by='a',ascending=False) # 也可以按值排列
out: d a b c
one 4 5 6 7
three 0 1 2 3
in: me.sort_values(by=['a','b']) # 按多個列的值來排
out: d a b c
three 0 1 2 3
one 4 5 6 7
5.3 描述性統計
先創建一個 DataFrame 用於舉例:
in: 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'])
df
out: one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
描述性統計的方向默認是以列為單位,統計行數據的:
in: df.sum()
out: one 9.25
two -5.80
dtype: float64
in: df.mean(axis=1) # 也可以按行統計
out: a 1.400 # 包括一個空值時,空值被跳過了
b 1.300
c NaN # 都是空值時,返回的也是空值
d -0.275
dtype: float64
可以直接使用 describe() 返回描述性統計結果:
in: df.describe()
out: 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
還可以計算相關係數:
in: df.corr() # 默認為 pearson 相關係數
out: one two
one 1.0 -1.0
two -1.0 1.0
還有涉及到唯一值、值計數以及成員資格的統計:
in: obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
obj
out: 0 c
1 a
2 d
3 a
4 a
5 b
6 b
7 c
8 c
dtype: object
in: obj.unique() # 返回一個唯一值數組
out: array(['c', 'a', 'd', 'b'], dtype=object)
in: obj.value_counts() # 計算一個值出現的頻率
out: a 3
c 3
b 2
d 1
dtype: int64
in: obj.isin(['b', 'c']) # 是否在 [] 中
out: 0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
注:轉載請註明出處。
本文屬於《利用 Python 進行數據分析》讀書筆記系列:
- 利用 Python 進行數據分析 —— 1 數據結構、函數和文件
- 利用 Python 進行數據分析 —— 2 NumPy 基礎