前置機器學習(四):一文掌握Pandas用法
阿新 • • 發佈:2020-12-13
> Pandas提供快速,靈活和富於表現力的**資料結構**,是強大的**資料分析**Python庫。
本文收錄於[機器學習前置教程系列](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=MzUxMjU4NjI4MQ==&scene=1&album_id=1627166768236412929&count=3#wechat_redirect)。
# 一、Series和DataFrame
Pandas建立在NumPy之上,更多**NumPy**相關的知識點可以參考我之前寫的文章[前置機器學習(三):30分鐘掌握常用NumPy用法](http://blog.caiyongji.com/2020/12/06/pre-ml-numpy-3.html)。
Pandas特別適合處理表格資料,如SQL表格、EXCEL表格。有序或無序的時間序列。具有行和列標籤的任意矩陣資料。
開啟Jupyter Notebook,匯入numpy和pandas開始我們的教程:
```
import numpy as np
import pandas as pd
```
## 1. pandas.Series
Series是帶有索引的一維ndarray陣列。索引值可不唯一,但必須是可雜湊的。
```
pd.Series([1, 3, 5, np.nan, 6, 8])
```
輸出:
```
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
```
我們可以看到預設索引值為0、1、2、3、4、5這樣的數字。新增`index`屬性,指定其為'c','a','i','yong','j','i'。
```
pd.Series([1, 3, 5, np.nan, 6, 8], index=['c','a','i','yong','j','i'])
```
輸出如下,我們可以看到index是可重複的。
```
c 1.0
a 3.0
i 5.0
yong NaN
j 6.0
i 8.0
dtype: float64
```
## 2. pandas.DataFrame
DataFrame是帶有行和列的表格結構。可以理解為多個Series物件的字典結構。
```
pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), index=['i','ii','iii'], columns=['A', 'B', 'C'])
```
輸出表格如下,其中`index`對應它的行,`columns`對應它的列。
| | A | B | C |
|:----|----:|----:|----:|
| i | 1 | 2 | 3 |
| ii | 4 | 5 | 6 |
| iii | 7 | 8 | 9 |
# 二、Pandas常見用法
## 1. 訪問資料
準備資料,隨機生成6行4列的二維陣列,行標籤為從20210101到20210106的日期,列標籤為A、B、C、D。
```
import numpy as np
import pandas as pd
np.random.seed(20201212)
df = pd.DataFrame(np.random.randn(6, 4), index=pd.date_range('20210101', periods=6), columns=list('ABCD'))
df
```
展示表格如下:
| | A | B | C | D |
|:--------------------|----------:|----------:|----------:|----------:|
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 |
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 |
| 2021-01-04 | -0.33032 | -1.40384 | -0.93809 | 1.48804 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 |
| 2021-01-06 | -0.816064 | 1.30197 | 0.656281 | -1.2718 |
### 1.1 head()和tail()
查看錶格前幾行:
```
df.head(2)
```
展示表格如下:
| | A | B | C | D |
|:--------------------|---------:|----------:|----------:|----------:|
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 |
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 |
查看錶格後幾行:
```
df.tail(3)
```
展示表格如下:
| | A | B | C | D |
|:--------------------|----------:|---------:|----------:|----------:|
| 2021-01-04 | -0.33032 | -1.40384 | -0.93809 | 1.48804 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 |
| 2021-01-06 | -0.816064 | 1.30197 | 0.656281 | -1.2718 |
### 1.2 describe()
`describe`方法用於生成DataFrame的描述統計資訊。可以很方便的檢視資料集的分佈情況。注意,這裡的統計分佈不包含``NaN``值。
```
df.describe()
```
展示如下:
| | A | B | C | D |
|:------|-----------:|-----------:|----------:|----------:|
| count | 6 | 6 | 6 | 6 |
| mean | 0.0825402 | 0.0497552 | -0.181309 | 0.22896 |
| std | 0.551412 | 1.07834 | 0.933155 | 1.13114 |
| min | -0.816064 | -1.40384 | -1.64592 | -1.2718 |
| 25% | -0.18 | -0.553043 | -0.737194 | -0.587269 |
| 50% | 0.298188 | -0.134555 | 0.106933 | 0.287363 |
| 75% | 0.342885 | 0.987901 | 0.556601 | 1.16805 |
| max | 0.696541 | 1.30197 | 0.656281 | 1.48804 |
我們首先回顧一下我們掌握的數學公式。
**平均數(mean)**:
$$\bar x = \frac{\sum_{i=1}^{n}{x_i}}{n}$$
**方差(variance)**:
$$s^2 = \frac{\sum_{i=1}^{n}{(x_i -\bar x)^2}}{n}$$
**標準差(std)**:
$$s = \sqrt{\frac{\sum_{i=1}^{n}{(x_i -\bar x)^2}}{n}}$$
我們解釋一下pandas的describe統計資訊各屬性的意義。我們僅以 `A` 列為例。
* `count`表示計數。A列有6個數據不為空。
* `mean`表示平均值。A列所有不為空的資料平均值為0.0825402。
* `std`表示標準差。A列的標準差為0.551412。
* `min`表示最小值。A列最小值為-0.816064。即,0%的資料比-0.816064小。
* `25%`表示四分之一分位數。A列的四分之一分位數為-0.18。即,25%的資料比-0.18小。
* `50%`表示二分之一分位數。A列的四分之一分位數為0.298188。即,50%的資料比0.298188小。
* `75%`表示四分之三分位數。A列的四分之三分位數為0.342885。即,75%的資料比0.342885小。
* `max`表示最大值。A列的最大值為0.696541。即,100%的資料比0.696541小。
### 1.3 T
`T`一般表示`Transpose`的縮寫,即轉置。行列轉換。
```
df.T
```
展示表格如下:
| | 2021-01-01 | 2021-01-02 | 2021-01-03 | 2021-01-04 | 2021-01-05 | 2021-01-06 |
|:---|-------------:|-------------:|-------------:|-------------:|-------------:|-------------:|
| A | 0.270961 | 0.696541 | 0.325415 | -0.33032 | 0.348708 | -0.816064 |
| B | -0.405463 | 0.136352 | -0.602236 | -1.40384 | 1.27175 | 1.30197 |
| C | 0.348373 | -1.64592 | -0.134508 | -0.93809 | 0.626011 | 0.656281 |
| D | 0.828572 | -0.69841 | 1.28121 | 1.48804 | -0.253845 | -1.2718 |
### 1.4 sort_values()
指定某一列進行排序,如下程式碼根據`C`列進行正序排序。
```
df.sort_values(by='C')
```
展示表格如下:
| | A | B | C | D |
|:-----------|----------:|----------:|----------:|----------:|
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 |
| 2021-01-04 | -0.33032 | -1.40384 | -0.93809 | 1.48804 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 |
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 |
| 2021-01-06 | -0.816064 | 1.30197 | 0.656281 | -1.2718 |
### 1.5 nlargest()
選擇某列最大的n行資料。如:`df.nlargest(2,'A')`表示,返回A列最大的2行資料。
```
df.nlargest(2,'A')
```
展示表格如下:
| | A | B | C | D |
|:-----------|---------:|---------:|----------:|----------:|
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 |
### 1.6 sample()
`sample`方法表示檢視隨機的樣例資料。
`df.sample(5)`表示返回隨機5行資料。
```
df.sample(5)
```
引數`frac`表示fraction,分數的意思。frac=0.01即返回1%的隨機資料作為樣例展示。
```
df.sample(frac=0.01)
```
## 2. 選擇資料
### 2.1 根據標籤選擇
我們輸入`df['A']`命令選取A列。
```
df['A']
```
輸出A列資料,同時也是一個Series物件:
```
2021-01-01 0.270961
2021-01-02 0.696541
2021-01-03 0.325415
2021-01-04 -0.330320
2021-01-05 0.348708
2021-01-06 -0.816064
Name: A, dtype: float64
```
`df[0:3]`該程式碼與``df.head(3)``同理。但`df[0:3]`是NumPy的陣列選擇方式,這說明了Pandas對於NumPy具有良好的支援。
```
df[0:3]
```
展示表格如下:
| | A | B | C | D |
|:-----------|---------:|----------:|----------:|----------:|
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 |
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 |
通過loc方法指定行列標籤。
```
df.loc['2021-01-01':'2021-01-02', ['A', 'B']]
```
展示表格如下:
| | A | B |
|:-----------|---------:|----------:|
| 2021-01-01 | 0.270961 | -0.405463 |
| 2021-01-02 | 0.696541 | 0.136352 |
### 2.2 根據位置選擇
`iloc` 與`loc`不同。`loc`指定具體的標籤,而`iloc`指定標籤的索引位置。`df.iloc[3:5, 0:3]`表示選取索引為3、4的行,索引為0、1、2的列。即,第4、5行,第1、2、3列。
注意,索引序號從0開始。冒號表示區間,左右兩側分別表示開始和結束。如`3:5`表示左開右閉區間`[3,5)`,即不包含5自身。
```
df.iloc[3:5, 0:3]
```
| | A | B | C |
|:-----------|----------:|---------:|----------:|
| 2021-01-04 | -0.33032 | -1.40384 | -0.93809 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 |
```
df.iloc[:, 1:3]
```
| | B | C |
|:-----------|----------:|----------:|
| 2021-01-01 | -0.405463 | 0.348373 |
| 2021-01-02 | 0.136352 | -1.64592 |
| 2021-01-03 | -0.602236 | -0.134508 |
| 2021-01-04 | -1.40384 | -0.93809 |
| 2021-01-05 | 1.27175 | 0.626011 |
| 2021-01-06 | 1.30197 | 0.656281 |
### 2.3 布林索引
DataFrame可根據條件進行篩選,當條件判斷`True`時,返回。當條件判斷為`False`時,過濾掉。
我們設定一個過濾器用來判斷A列是否大於0。
```
filter = df['A'] > 0
filter
```
輸出結果如下,可以看到`2021-01-04`和`2021-01-06`的行為False。
```
2021-01-01 True
2021-01-02 True
2021-01-03 True
2021-01-04 False
2021-01-05 True
2021-01-06 False
Name: A, dtype: bool
```
我們通過過濾器檢視資料集。
```
df[filter]
# df[df['A'] > 0]
```
查看錶格我們可以發現,`2021-01-04`和`2021-01-06`的行被過濾掉了。
| | A | B | C | D |
|:-----------|---------:|----------:|----------:|----------:|
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 |
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 |
## 3. 處理缺失值
準備資料。
```
df2 = df.copy()
df2.loc[:3, 'E'] = 1.0
f_series = {'2021-01-02': 1.0,'2021-01-03': 2.0,'2021-01-04': 3.0,'2021-01-05': 4.0,'2021-01-06': 5.0}
df2['F'] = pd.Series(f_series)
df2
```
展示表格如下:
| | A | B | C | D | F | E |
|:-----------|----------:|----------:|----------:|----------:|----:|----:|
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 | nan | 1 |
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 | 1 | 1 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 | 2 | 1 |
| 2021-01-04 | -0.33032 | -1.40384 | -0.93809 | 1.48804 | 3 | nan |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 | 4 | nan |
| 2021-01-06 | -0.816064 | 1.30197 | 0.656281 | -1.2718 | 5 | nan |
### 3.1 dropna()
使用dropna方法清空NaN值。注意:dropa方法返回新的DataFrame,並不會改變原有的DataFrame。
```
df2.dropna(how='any')
```
以上程式碼表示,當行資料有任意的數值為空時,刪除。
| | A | B | C | D | F | E |
|:-----------|---------:|----------:|----------:|---------:|----:|----:|
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 | 1 | 1 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 | 2 | 1 |
### 3.2 fillna()
使用filna命令填補NaN值。
```
df2.fillna(df2.mean())
```
以上程式碼表示,使用每一列的平均值來填補空缺。同樣地,fillna並不會更新原有的DataFrame,如需更新原有DataFrame使用程式碼`df2 = df2.fillna(df2.mean())`。
展示表格如下:
| | A | B | C | D | F | E |
|:-----------|----------:|----------:|----------:|----------:|----:|----:|
| 2021-01-01 | 0.270961 | -0.405463 | 0.348373 | 0.828572 | 3 | 1 |
| 2021-01-02 | 0.696541 | 0.136352 | -1.64592 | -0.69841 | 1 | 1 |
| 2021-01-03 | 0.325415 | -0.602236 | -0.134508 | 1.28121 | 2 | 1 |
| 2021-01-04 | -0.33032 | -1.40384 | -0.93809 | 1.48804 | 3 | 1 |
| 2021-01-05 | 0.348708 | 1.27175 | 0.626011 | -0.253845 | 4 | 1 |
| 2021-01-06 | -0.816064 | 1.30197 | 0.656281 | -1.2718 | 5 | 1 |
## 4. 操作方法
### 4.1 agg()
agg是Aggregate的縮寫,意為聚合。
常用聚合方法如下:
* mean(): Compute mean of groups
* sum(): Compute sum of group values
* size(): Compute group sizes
* count(): Compute count of group
* std(): Standard deviation of groups
* var(): Compute variance of groups
* sem(): Standard error of the mean of groups
* describe(): Generates descriptive statistics
* first(): Compute first of group values
* last(): Compute last of group values
* nth() : Take nth value, or a subset if n is a list
* min(): Compute min of group values
* max(): Compute max of group values
```
df.mean()
```
返回各列平均值
```
A 0.082540
B 0.049755
C -0.181309
D 0.228960
dtype: float64
```
可通過加引數axis檢視行平均值。
```
df.mean(axis=1)
```
輸出:
```
2021-01-01 0.260611
2021-01-02 -0.377860
2021-01-03 0.217470
2021-01-04 -0.296053
2021-01-05 0.498156
2021-01-06 -0.032404
dtype: float64
```
如果我們想檢視某一列的多項聚合統計怎麼辦?
這時我們可以呼叫**agg**方法:
```
df.agg(['std','mean'])['A']
```
返回結果顯示標準差std和均值mean:
```
std 0.551412
mean 0.082540
Name: A, dtype: float64
```
對於不同的列應用不同的聚合函式:
```
df.agg({'A':['max','mean'],'B':['mean','std','var']})
```
返回結果如下:
| | A | B |
|:-----|------------:|------------:|
| max | 0.696541 | nan |
| mean | 0.0825402 | 0.0497552 |
| std | nan | 1.07834 |
| var | nan | 1.16281 |
### 4.2 apply()
apply()是對方法的呼叫。
如`df.apply(np.sum)`表示每一列呼叫np.sum方法,返回每一列的數值和。
```
df.apply(np.sum)
```
輸出結果為:
```
A 0.495241
B 0.298531
C -1.087857
D 1.373762
dtype: float64
```
apply方法支援lambda表示式。
```
df.apply(lambda n: n*2)
```
| | A | B | C | D |
|:-----------|----------:|----------:|----------:|---------:|
| 2021-01-01 | 0.541923 | -0.810925 | 0.696747 | 1.65714 |
| 2021-01-02 | 1.39308 | 0.272704 | -3.29185 | -1.39682 |
| 2021-01-03 | 0.65083 | -1.20447 | -0.269016 | 2.56242 |
| 2021-01-04 | -0.66064 | -2.80768 | -1.87618 | 2.97607 |
| 2021-01-05 | 0.697417 | 2.5435 | 1.25202 | -0.50769 |
| 2021-01-06 | -1.63213 | 2.60393 | 1.31256 | -2.5436 |
### 4.3 value_counts()
value_counts方法檢視各行、列的數值重複統計。
我們重新生成一些整數資料,來保證有一定的資料重複。
```
np.random.seed(101)
df3 = pd.DataFrame(np.random.randint(0,9,size = (6,4)),columns=list('ABCD'))
df3
```
| | A | B | C | D |
|---:|----:|----:|----:|----:|
| 0 | 1 | 6 | 7 | 8 |
| 1 | 4 | 8 | 5 | 0 |
| 2 | 5 | 8 | 1 | 3 |
| 3 | 8 | 3 | 3 | 2 |
| 4 | 8 | 3 | 7 | 0 |
| 5 | 7 | 8 | 4 | 3 |
呼叫value_counts()方法。
```
df3['A'].value_counts()
```
檢視輸出我們可以看到 A列的數字8有兩個,其他數字的數量為1。
```
8 2
7 1
5 1
4 1
1 1
Name: A, dtype: int64
```
### 4.4 str
Pandas內建字串處理方法。
```
names = pd.Series(['andrew','bobo','claire','david','4'])
names.str.upper()
```
通過以上程式碼我們將Series中的字串全部設定為大寫。
```
0 ANDREW
1 BOBO
2 CLAIRE
3 DAVID
4 4
dtype: object
```
首字母大寫:
```
names.str.capitalize()
```
輸出為:
```
0 Andrew
1 Bobo
2 Claire
3 David
4 4
dtype: object
```
判斷是否為數字:
```
names.str.isdigit()
```
輸出為:
```
0 False
1 False
2 False
3 False
4 True
dtype: bool
```
字串分割:
```
tech_finance = ['GOOG,APPL,AMZN','JPM,BAC,GS']
tickers = pd.Series(tech_finance)
tickers.str.split(',').str[0:2]
```
以逗號分割字串,結果為:
```
0 [GOOG, APPL]
1 [JPM, BAC]
dtype: object
```
## 5. 合併
### 5.1 concat()
concat用來將資料集串聯起來。我們先準備資料。
```
data_one = {'Col1': ['A0', 'A1', 'A2', 'A3'],'Col2': ['B0', 'B1', 'B2', 'B3']}
data_two = {'Col1': ['C0', 'C1', 'C2', 'C3'], 'Col2': ['D0', 'D1', 'D2', 'D3']}
one = pd.DataFrame(data_one)
two = pd.DataFrame(data_two)
```
使用concat方法將兩個資料集串聯起來。
```
pt(pd.concat([one,two]))
```
得到表格:
| | Col1 | Col2 |
|---:|:-------|:-------|
| 0 | A0 | B0 |
| 1 | A1 | B1 |
| 2 | A2 | B2 |
| 3 | A3 | B3 |
| 0 | C0 | D0 |
| 1 | C1 | D1 |
| 2 | C2 | D2 |
| 3 | C3 | D3 |
### 5.2 merge()
merge相當於SQL操作中的join方法,用於將兩個資料集通過某種關係連線起來
```
registrations = pd.DataFrame({'reg_id':[1,2,3,4],'name':['Andrew','Bobo','Claire','David']})
logins = pd.DataFrame({'log_id':[1,2,3,4],'name':['Xavier','Andrew','Yolanda','Bobo']})
```
我們根據`name`來連線兩個張表,連線方式為`outer`。
```
pd.merge(left=registrations, right=logins, how='outer',on='name')
```
返回結果為:
| | reg_id | name | log_id |
|---:|---------:|:--------|---------:|
| 0 | 1 | Andrew | 2 |
| 1 | 2 | Bobo | 4 |
| 2 | 3 | Claire | nan |
| 3 | 4 | David | nan |
| 4 | nan | Xavier | 1 |
| 5 | nan | Yolanda | 3 |
我們注意,**how : {'left', 'right', 'outer', 'inner'}** 有4種連線方式。表示是否選取左右兩側表的nan值。如left表示保留左側表中所有資料,當遇到右側表資料為nan值時,不顯示右側的資料。
簡單來說,把left表和right表看作兩個集合。
* left表示取左表全部集合+兩表交集
* right表示取右表全部集合+兩表交集
* outer表示取兩表並集
* inner表示取兩表交集
## 6. 分組GroupBy
Pandas中的分組功能非常類似於SQL語句`SELECT Column1, Column2, mean(Column3), sum(Column4)FROM SomeTableGROUP BY Column1, Column2`。即使沒有接觸過SQL也沒有關係,分組就相當於把表格資料按照某一列進行拆分、統計、合併的過程。
準備資料。
```
np.random.seed(20201212)
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],
'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
'C': np.random.randn(8),
'D': np.random.randn(8)})
df
```
可以看到,我們的A列和B列有很多重複資料。這時我們可以根據foo/bar或者one/two進行分組。
| | A | B | C | D |
|---:|:----|:------|----------:|----------:|
| 0 | foo | one | 0.270961 | 0.325415 |
| 1 | bar | one | -0.405463 | -0.602236 |
| 2 | foo | two | 0.348373 | -0.134508 |
| 3 | bar | three | 0.828572 | 1.28121 |
| 4 | foo | two | 0.696541 | -0.33032 |
| 5 | bar | two | 0.136352 | -1.40384 |
| 6 | foo | one | -1.64592 | -0.93809 |
| 7 | foo | three | -0.69841 | 1.48804 |
### 6.1 單列分組
我們應用`groupby`方法將上方表格中的資料進行分組。
```
df.groupby('A')
```
執行上方程式碼可以看到,groupby方法返回的是一個型別為`DataFrameGroupBy`的物件。我們無法直接檢視,需要應用聚合函式。參考本文4.1節