博客 / 詳情

返回

利用 Python 進行數據分析 —— 3 pandas 入門

上一篇我們瞭解了 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 基礎
user avatar lintp 頭像 juqipeng 頭像 xiaopaipiper 頭像
3 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.