Pandas 也是建構於 Numpy 之上,主要設計的定位是用來資料處理及分析。 與 Numpy 陣列不同的地方在,Pandas 表格式的資料容器 DataFrame
可以存放及操作異質資料型態,資料欄位可以有標籤,易於處理 missing data、類別資料、與時間序列資料,而且針對常用的資料儲存格式提供了相當廣泛的輸出入的支援。
一下教材內容節錄自 Pandas 官方文件。
pandas
套件¶import numpy as np
import pandas as pd
Series
與 DataFrame
基本認識¶Pandas 主要的資料結構是 Series
與 DataFrame
。
Series
- 一維,有標籤,同質性(homogeneously-typed)的資料結構。DataFrame
- 二維,有欄位標籤及列記錄標籤,欄位異質性(heterogeneously-typed )的資料結構。標籤就像 Dict 容器的 Key,可以是字串、數值、時間等可以當成 Key 的資料型態。 Series 可以想成是每一個元素位置都帶有標籤的 row向量 或 column向量,由 DataFrame 取出某個 row 或某個 column 就是一個 Series,取出的 Series 就帶有原本的 row標籤 或 column標籤。
在建立 DataFrame 或 Series 時,若沒有指定標籤,預設會使用位置順序的數值序號。
# Creating a Series by passing a list of values, letting pandas create a default integer index
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
# Creating a DataFrame by passing a NumPy array, with a datetime index and labeled columns
dates = pd.date_range('20190401', periods=6)
print(dates)
DatetimeIndex(['2019-04-01', '2019-04-02', '2019-04-03', '2019-04-04', '2019-04-05', '2019-04-06'], dtype='datetime64[ns]', freq='D')
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))
df
A | B | C | D | |
---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 |
# Creating a DataFrame by passing a dict of objects that can be converted to series-like.
# 注意: 單一值會自動 broadcast
df2 = pd.DataFrame({'A': 1.,
'B': pd.Timestamp('20130102'),
'C': pd.Series(1, index=list(range(4)), dtype='float32'),
'D': np.array([3] * 4, dtype='int32'),
'E': pd.Categorical(["test", "train", "test", "train"]),
'F': 'foo'})
df2
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
0 | 1.0 | 2013-01-02 | 1.0 | 3 | test | foo |
1 | 1.0 | 2013-01-02 | 1.0 | 3 | train | foo |
2 | 1.0 | 2013-01-02 | 1.0 | 3 | test | foo |
3 | 1.0 | 2013-01-02 | 1.0 | 3 | train | foo |
# 檢視前幾筆
df.head(3)
A | B | C | D | |
---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 |
# 檢視後幾筆
df.tail(3)
A | B | C | D | |
---|---|---|---|---|
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 |
# 檢視記錄標籤
df.index
DatetimeIndex(['2019-04-01', '2019-04-02', '2019-04-03', '2019-04-04', '2019-04-05', '2019-04-06'], dtype='datetime64[ns]', freq='D')
# 檢視記錄欄位標籤
df.columns
Index(['A', 'B', 'C', 'D'], dtype='object')
# 檢視資料類型
df.dtypes
A float64 B float64 C float64 D float64 dtype: object
# 資料都是數值的話,轉成 numpy 陣列非常快
# Pandas 版本 < 0.24 要使用 DataFrame.values
df.to_numpy()
array([[ 0.4615414 , -1.21661998, 0.70400939, 0.27153872], [ 0.98339876, -0.70162361, 0.89459705, 0.09695603], [-0.29496219, -1.07716089, -0.83439422, -1.16282439], [-0.96200379, 0.15779416, -2.0703014 , 0.22941439], [-0.17872924, -0.63933357, -0.7246541 , 0.37790513], [ 1.13209463, -0.98746243, 0.53697748, 1.95374221]])
# 檢視資料類型
df2.dtypes
A float64 B datetime64[ns] C float32 D int32 E category F object dtype: object
# 異質欄位資料也可以轉成 numpy 陣列的話,但成本較高,通常在 numpy 也不會比較容易處理異質資料
df2.to_numpy()
array([[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'], [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo'], [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'], [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo']], dtype=object)
# 基本統計描述(column-wise):平均值、標準差、最小值、第一四分位數、中位數、第二四分位數、最大值
df.describe()
A | B | C | D | |
---|---|---|---|---|
count | 6.000000 | 6.000000 | 6.000000 | 6.000000 |
mean | 0.190223 | -0.744068 | -0.248961 | 0.294455 |
std | 0.811051 | 0.493887 | 1.159299 | 0.992736 |
min | -0.962004 | -1.216620 | -2.070301 | -1.162824 |
25% | -0.265904 | -1.054736 | -0.806959 | 0.130071 |
50% | 0.141406 | -0.844543 | -0.093838 | 0.250477 |
75% | 0.852934 | -0.654906 | 0.662251 | 0.351314 |
max | 1.132095 | 0.157794 | 0.894597 | 1.953742 |
# Transpose
df.T
2019-04-01 00:00:00 | 2019-04-02 00:00:00 | 2019-04-03 00:00:00 | 2019-04-04 00:00:00 | 2019-04-05 00:00:00 | 2019-04-06 00:00:00 | |
---|---|---|---|---|---|---|
A | 0.461541 | 0.983399 | -0.294962 | -0.962004 | -0.178729 | 1.132095 |
B | -1.216620 | -0.701624 | -1.077161 | 0.157794 | -0.639334 | -0.987462 |
C | 0.704009 | 0.894597 | -0.834394 | -2.070301 | -0.724654 | 0.536977 |
D | 0.271539 | 0.096956 | -1.162824 | 0.229414 | 0.377905 | 1.953742 |
# 根據某欄位排序
df.sort_values(by='B', ascending=False)
A | B | C | D | |
---|---|---|---|---|
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 |
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 |
注意: Pandas 可以直接用中括號 [ ] 選取 DataFrame 的資料,由於括號中的標籤可以是欄位標籤也可以是列序號,在容易混淆誤用時,請儘可能使用下一節中提到的 loc[]
或 iloc[]
語法。
DataFrame 可以直接使用中括號 [ ] 指定欄位標籤存取欄位資料,主要語法為:
DataFrame[欄位標籤], 或
DataFrame[欄位標籤清單]
若只需要存取單一欄位,而且 columns 標籤是字串形式,也可以使用以下語法(注意不帶字串的引號):
DataFrame.欄位標籤
Series 直接使用中括號 [ ] 的語法為:
Series[標籤], 或
Series[標籤清單]
若只需要存取序列的單一位置,而且 index 標籤是字串形式,也可以使用以下語法(注意不帶字串的引號):
Series.index標籤
# 選取單一欄位,返回一個 Series
df['A']
2019-04-01 0.461541 2019-04-02 0.983399 2019-04-03 -0.294962 2019-04-04 -0.962004 2019-04-05 -0.178729 2019-04-06 1.132095 Freq: D, Name: A, dtype: float64
# df['A'] 語法等同於 df.A
df.A
2019-04-01 0.461541 2019-04-02 0.983399 2019-04-03 -0.294962 2019-04-04 -0.962004 2019-04-05 -0.178729 2019-04-06 1.132095 Freq: D, Name: A, dtype: float64
# 資料型別轉換
df.A.astype(int)
2019-04-01 0 2019-04-02 0 2019-04-03 0 2019-04-04 0 2019-04-05 0 2019-04-06 1 Freq: D, Name: A, dtype: int32
loc
及 iloc
操作¶建議使用針對 Pandas 資料結構最佳化過的 loc[]
、和 iloc[]
語法,序號(indexing)及片段(slicing)的語法類似 Numpy 陣列。
loc 存取方式 |
Series | DataFrame |
---|---|---|
標籤 | s.loc[標籤] |
df.loc[標籤, 標籤] |
標籤清單 | s.loc[標籤清單] |
df.loc[標籤清單, 標籤清單] |
標籤片段 | s.loc[標籤片段] |
df.loc[標籤片段, 標籤片段] |
Boolean 遮罩陣列 | s.loc[遮罩陣列] |
df.loc[遮罩陣列, 遮罩陣列] |
注意片段語法 loc[start:stop:step]
,返回結果為封閉區間的 [start, stop],包含結束的 stop 項。
iloc
主要以位置順序的數值序號為存取方式。
iloc 存取方式 |
Series | DataFrame |
---|---|---|
序號 | s.iloc[序號] |
df.iloc[序號, 序號] |
序號清單 | s.iloc[序號清單] |
df.loc[序號清單, 序號清單] |
序號片段 | s.iloc[序號片段] |
df.loc[序號片段, 序號片段] |
注意片段語法 iloc[start:stop:step]
,返回結果為半開放區間的 [start, stop),不包含結束的 stop 項。
# 使用 loc,選取特定列標籤及欄位標籤位置
df.loc['2019-04-02', 'A']
0.9833987590643353
# 使用 loc,選取特定列標籤,欄位標籤省略則預設為全選
df.loc['2019-04-02']
A 0.983399 B -0.701624 C 0.894597 D 0.096956 Name: 2019-04-02 00:00:00, dtype: float64
# 使用 loc 選取特定列後,再指定欄位標籤
df.loc['2019-04-02'].D
0.09695602819994861
# 所有列(slicing 語法)及部分欄位選取,注意標籤可重複
df.loc[:, ['A', 'C', 'A']]
A | C | A | |
---|---|---|---|
2019-04-01 | 0.461541 | 0.704009 | 0.461541 |
2019-04-02 | 0.983399 | 0.894597 | 0.983399 |
2019-04-03 | -0.294962 | -0.834394 | -0.294962 |
2019-04-04 | -0.962004 | -2.070301 | -0.962004 |
2019-04-05 | -0.178729 | -0.724654 | -0.178729 |
2019-04-06 | 1.132095 | 0.536977 | 1.132095 |
# 部份列片段,及部分欄位片段選取,注意片段有包含 stop 項
df.loc['20190402':'20190404', 'A':'C']
A | B | C | |
---|---|---|---|
2019-04-02 | 0.983399 | -0.701624 | 0.894597 |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 |
# 使用 iloc,序號選取特定列及欄位
df.iloc[3, 1]
0.15779415836220198
# 使用 iloc,選取特定列序號,欄位序號省略則預設為全選
df.iloc[3]
A -0.962004 B 0.157794 C -2.070301 D 0.229414 Name: 2019-04-04 00:00:00, dtype: float64
# 使用 iloc 選取特定列後,再指定欄位標籤
df.iloc[3].C
-2.0703014049380495
# 類似 numpy 的序號與片段語法,注意片段沒有包含 stop 項
df.iloc[3:5, 0:2]
A | B | |
---|---|---|
2019-04-04 | -0.962004 | 0.157794 |
2019-04-05 | -0.178729 | -0.639334 |
bool
陣列。比較運算操作 | 說明 |
---|---|
X < Y | 小於 |
X <= Y | 小於或等於 |
X > Y | 大於 |
X >= Y | 大於或等於 |
X == Y | 等於 |
X != Y | 不等於 |
(條件1) & (條件2) | 真值邏輯 AND |
(條件1) | (條件2) | 真值邏輯 OR |
~(條件1) | 真值邏輯 NOT |
# 類似 Numpy,比較結果為 Boolean 遮罩
df.A > 0
2019-04-01 True 2019-04-02 True 2019-04-03 False 2019-04-04 False 2019-04-05 False 2019-04-06 True Freq: D, Name: A, dtype: bool
# Boolean 陣列選取,使用單一欄位的條件
df.loc[df.A > 0]
A | B | C | D | |
---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 |
# 兩個條件的邏輯 OR 比較,要用 | 符號
(df.A > 0) | (df.B > 0)
2019-04-01 True 2019-04-02 True 2019-04-03 False 2019-04-04 True 2019-04-05 False 2019-04-06 True Freq: D, dtype: bool
df.loc[(df.A > 0) | (df.B > 0)]
A | B | C | D | |
---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 |
DataFrame 可以直接用括號 [ ] 指定新標籤來新增欄位,也可以用 loc
語法指定新的標籤來新增列或欄位數據。 另外也可以使用物件方法及 concat 函式:
DataFrame.append()
- 新增列數據在最後,返回新物件。DataFrame.assign()
- 新增新欄位,返回新物件。Series.append()
- 串接另一個 Series,返回新物件。Pandas.concat()
- 串接 Pandas 的 Series 或 DataFrame 物件。刪除使用 drop 物件方法:
DataFrame.drop()
- 刪除指定的行或列標籤,可就地變更。Series.drop()
- 刪除指定標籤,可就地變更。# 追加一個欄位資料
df['E'] = ['one', 'one', 'two', 'three', 'four', 'three']
df
A | B | C | D | E | |
---|---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | one |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | one |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | two |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | three |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | four |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | three |
# 使用 loc 新增列數據
df.loc[pd.to_datetime('2019-04-07'),:] = pd.Series({'A':0.1, 'B':0.2, 'C':0.3, 'D':0.4, 'E':'four'})
df
A | B | C | D | E | |
---|---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | one |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | one |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | two |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | three |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | four |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | three |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | four |
# 使用 loc 給新欄位標籤新增欄位數據
df.loc[:, 'F'] = [True, False, True, True, False, True, False]
df
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | one | True |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | one | False |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | two | True |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | three | True |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | four | False |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | three | True |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | four | False |
# 使用 Series.isin 返回 Boolean 遮罩
df.E.isin(['two', 'four'])
2019-04-01 False 2019-04-02 False 2019-04-03 True 2019-04-04 False 2019-04-05 True 2019-04-06 False 2019-04-07 True Freq: D, Name: E, dtype: bool
# 使用 Series.isin 返回的遮罩選取
df.loc[df.E.isin(['two', 'four'])]
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | two | True |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | four | False |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | four | False |
# 串接,預設 axis 0
pd.concat([df.iloc[[1,3]], df.loc[df.E.isin(['two', 'four'])]])
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | one | False |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | three | True |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | two | True |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | four | False |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | four | False |
# 用 append 追加數據列
s3 = df.iloc[3]
s3.name = pd.to_datetime('2019-04-07')
df.append(s3)
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | one | True |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | one | False |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | two | True |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | three | True |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | four | False |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | three | True |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | four | False |
2019-04-07 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | three | True |
# 刪除欄位,可就地刪除
df.drop(columns=['E','F'], inplace=False)
A | B | C | D | |
---|---|---|---|---|
2019-04-01 | 0.461541 | -1.216620 | 0.704009 | 0.271539 |
2019-04-02 | 0.983399 | -0.701624 | 0.894597 | 0.096956 |
2019-04-03 | -0.294962 | -1.077161 | -0.834394 | -1.162824 |
2019-04-04 | -0.962004 | 0.157794 | -2.070301 | 0.229414 |
2019-04-05 | -0.178729 | -0.639334 | -0.724654 | 0.377905 |
2019-04-06 | 1.132095 | -0.987462 | 0.536977 | 1.953742 |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 |
時常取得的數據資料只有簡短的文字說明,就算有通常也很難完整交代數據的細節。 除了用 DataFrame.decribe()
整體檢視外,個別欄位或列 Series 也有敘述統計指標的方法可以用,另外也還有許多常用的方法可以用來深入檢視:
nunique()
、unique()
、value_counts()
、duplicated()
、drop_duplicates()
、equals()
。idxmax()
、idxmin()
。apply()
、map()
、transform()
。# 檢視某欄位的中位數
df.D.median()
0.2715387197756772
# 各欄位有多少不重複的值
df.nunique(axis=0)
A 7 B 7 C 7 D 7 E 4 F 2 dtype: int64
# 不重複的值是那些(類別)
df.E.unique()
#df.E.drop_duplicates()
array(['one', 'two', 'three', 'four'], dtype=object)
# 各類別出現次數
df.E.value_counts()
one 2 three 2 four 2 two 1 Name: E, dtype: int64
# 某種數據比例條件下,最大/最小比值是 E 欄位的那一種類別
df.loc[(df.A / df.D).idxmax(), 'E']
'one'
# 根據數值意義取門檻值,轉成三種類別
cut3levels = lambda row: 'high' if row.A > 1 else ('middle' if row.A > 0 else 'low')
df.apply(cut3levels, axis='columns')
2019-04-01 middle 2019-04-02 middle 2019-04-03 low 2019-04-04 low 2019-04-05 low 2019-04-06 high 2019-04-07 middle Freq: D, dtype: object
# 計算符合特定條件的數據出現次數
negcount = lambda x: x < 0
n_negA = df.A.map(negcount).sum()
n_negB = df.B.map(negcount).sum()
pd.Series([n_negA, n_negB], index=['# Negatives in A', '# Negatives in B'])
# Negatives in A 3 # Negatives in B 5 dtype: int64
# 根據欄位 E 分群,群中各有多少
df.groupby('E').size()
E four 2 one 2 three 2 two 1 dtype: int64
# 根據欄位 E 分群,計算每群中的 F 欄位有多少 True
df.groupby('E').F.sum()
E four 0.0 one 1.0 three 2.0 two 1.0 Name: F, dtype: float64
# 根據 F 欄分群,輸出分群結果
for name, group in df.groupby('F'):
print('Group', name, ':')
print(group, '\n')
Group False : A B C D E F 2019-04-02 0.983399 -0.701624 0.894597 0.096956 one False 2019-04-05 -0.178729 -0.639334 -0.724654 0.377905 four False 2019-04-07 0.100000 0.200000 0.300000 0.400000 four False Group True : A B C D E F 2019-04-01 0.461541 -1.216620 0.704009 0.271539 one True 2019-04-03 -0.294962 -1.077161 -0.834394 -1.162824 two True 2019-04-04 -0.962004 0.157794 -2.070301 0.229414 three True 2019-04-06 1.132095 -0.987462 0.536977 1.953742 three True
# 根據 F 欄分群,各群中最大的 C 欄值是多少?
df.groupby('F').C.max()
F False 0.894597 True 0.704009 Name: C, dtype: float64
# 根據 F 欄分群,各群中數值欄位的平均值是多少?
df.groupby('F').mean()
A | B | C | D | |
---|---|---|---|---|
F | ||||
False | 0.301557 | -0.380319 | 0.156648 | 0.291620 |
True | 0.084168 | -0.780862 | -0.415927 | 0.322968 |
# 根據 A 欄四捨五入的整數分群並排序,其他各欄位的平均值
df.groupby(df.A.round())['B', 'C', 'D'].mean().sort_index(ascending=True)
B | C | D | |
---|---|---|---|
A | |||
-1.0 | 0.157794 | -2.070301 | 0.229414 |
0.0 | -0.683279 | -0.138760 | -0.028345 |
1.0 | -0.844543 | 0.715787 | 1.025349 |
# 根據 A 欄正負分群,其他各欄位平均值
cut2levels = lambda row: 'A+' if row.A > 0 else 'A-'
df.groupby(df.apply(cut2levels, axis='columns'))['B', 'C', 'D'].mean()
B | C | D | |
---|---|---|---|
A+ | -0.676427 | 0.608896 | 0.680559 |
A- | -0.519567 | -1.209783 | -0.185168 |
Pandas 的 aggregate()
(別名: agg()
)用來套用函式作整體的向量式運算,與 Numpy 函式不同的是,Pandas 的 aggregate
永遠只會針對欄位方向或列方向套用。 aggregate()
函式接受的參數為:
內建可以直接使用字串名稱的函式: sum
、mean
、min
、max
、size
、count
、std
、var
、sem
(standard error of the mean)、describe
。
# 根據 A 欄正負分群,B 欄的最大及最小值
df.groupby(df.apply(cut2levels, axis='columns')).B.agg(['min','max'])
min | max | |
---|---|---|
A+ | -1.216620 | 0.200000 |
A- | -1.077161 | 0.157794 |
# 根據 F 欄分群,各欄位的最大及最小值
df.groupby('F').agg({'A':['min','max'], 'B':['min','max'], 'C':['min','max'], 'D':['min','max']})
A | B | C | D | |||||
---|---|---|---|---|---|---|---|---|
min | max | min | max | min | max | min | max | |
F | ||||||||
False | -0.178729 | 0.983399 | -0.701624 | 0.200000 | -0.724654 | 0.894597 | 0.096956 | 0.400000 |
True | -0.962004 | 1.132095 | -1.216620 | 0.157794 | -2.070301 | 0.704009 | -1.162824 | 1.953742 |
# 根據 E 欄分群,C 欄位的最大及最小值,並以最小值排序
df.groupby('E').C.agg(['min','max']).sort_values(by=['min'], ascending=False)
min | max | |
---|---|---|
E | ||
one | 0.704009 | 0.894597 |
four | -0.724654 | 0.300000 |
two | -0.834394 | -0.834394 |
three | -2.070301 | 0.536977 |
漏失數據(missing data,又稱 NA)在 Pandas 中主要使用 numpy.nan
(Not a Number)形態表示,但也不排除使用 Python 的 None
來指定。 要注意的是,兩個 numpy.nan
互相比較永遠不會相等,但是 None
會相等,所以偵測漏失數據要使用 Pandas 提供的 isna()
函式。
# NaN 永遠不會等於 NaN
print('(NaN == NaN) is', np.nan == np.nan)
# None 等於 None
print('(None == None) is', None == None)
(NaN == NaN) is False (None == None) is True
# 刪除欄位,可就地刪除
df.drop(columns=['E'], inplace=True)
# 刻意製造含 NaN 的數據,不常用這樣的選取方式
dfmiss = df[df > 0]
dfmiss
A | B | C | D | F | |
---|---|---|---|---|---|
2019-04-01 | 0.461541 | NaN | 0.704009 | 0.271539 | 1.0 |
2019-04-02 | 0.983399 | NaN | 0.894597 | 0.096956 | NaN |
2019-04-03 | NaN | NaN | NaN | NaN | 1.0 |
2019-04-04 | NaN | 0.157794 | NaN | 0.229414 | 1.0 |
2019-04-05 | NaN | NaN | NaN | 0.377905 | NaN |
2019-04-06 | 1.132095 | NaN | 0.536977 | 1.953742 | 1.0 |
2019-04-07 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | NaN |
# 偵測 NA 返回 Boolean 遮罩,注意:不能使用 dfmiss == np.nan 這樣的比較
dfmiss.isna()
A | B | C | D | F | |
---|---|---|---|---|---|
2019-04-01 | False | True | False | False | False |
2019-04-02 | False | True | False | False | True |
2019-04-03 | True | True | True | True | False |
2019-04-04 | True | False | True | False | False |
2019-04-05 | True | True | True | False | True |
2019-04-06 | False | True | False | False | False |
2019-04-07 | False | False | False | False | True |
# 計算各欄位分別有多少漏失數據
dfmiss.isna().sum()
A 3 B 5 C 3 D 1 F 3 dtype: int64
# 針對 datetime 類型資料,Pandas 內部另外提供了 `NaT` 的資料類型來代表漏失的時間數據。
dfmiss['T'] = pd.Timestamp('20190417')
print(dfmiss)
A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 NaN 2019-04-17 2019-04-03 NaN NaN NaN NaN 1.0 2019-04-17 2019-04-04 NaN 0.157794 NaN 0.229414 1.0 2019-04-17 2019-04-05 NaN NaN NaN 0.377905 NaN 2019-04-17 2019-04-06 1.132095 NaN 0.536977 1.953742 1.0 2019-04-17 2019-04-07 0.100000 0.200000 0.300000 0.400000 NaN 2019-04-17
dfmiss.iloc[[1, 3, 4, 5], 5] = None
print(dfmiss)
A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 NaN NaT 2019-04-03 NaN NaN NaN NaN 1.0 2019-04-17 2019-04-04 NaN 0.157794 NaN 0.229414 1.0 NaT 2019-04-05 NaN NaN NaN 0.377905 NaN NaT 2019-04-06 1.132095 NaN 0.536977 1.953742 1.0 NaT 2019-04-07 0.100000 0.200000 0.300000 0.400000 NaN 2019-04-17
# 敘述統計函式,sum 把 NA 當 0,mean, cunsum 掠過
print('column A sum =', dfmiss['A'].sum())
print('column A mean =', dfmiss['A'].mean())
print('column A cumsum =\n', dfmiss['A'].cumsum())
column A sum = 2.677034795611951 column A mean = 0.6692586989029877 column A cumsum = 2019-04-01 0.461541 2019-04-02 1.444940 2019-04-03 NaN 2019-04-04 NaN 2019-04-05 NaN 2019-04-06 2.577035 2019-04-07 2.677035 Freq: D, Name: A, dtype: float64
# 把有漏失任何欄位值的記錄丟掉,也可指定全部欄位沒有值才丟
dfmiss.dropna()
A | B | C | D | F | T |
---|
# 丟掉很可惜,填補值來用
print('【填補前】:\n{}\n'.format(dfmiss))
print('【填補 0】:\n{}\n'.format(dfmiss.fillna(0)))
print('【後向填補】:\n{}\n'.format(dfmiss.fillna(method='bfill')))
print('【前向填補】:\n{}\n'.format(dfmiss.fillna(method='ffill')))
【填補前】: A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 NaN NaT 2019-04-03 NaN NaN NaN NaN 1.0 2019-04-17 2019-04-04 NaN 0.157794 NaN 0.229414 1.0 NaT 2019-04-05 NaN NaN NaN 0.377905 NaN NaT 2019-04-06 1.132095 NaN 0.536977 1.953742 1.0 NaT 2019-04-07 0.100000 0.200000 0.300000 0.400000 NaN 2019-04-17 【填補 0】: A B C D F T 2019-04-01 0.461541 0.000000 0.704009 0.271539 1.0 2019-04-17 00:00:00 2019-04-02 0.983399 0.000000 0.894597 0.096956 0.0 0 2019-04-03 0.000000 0.000000 0.000000 0.000000 1.0 2019-04-17 00:00:00 2019-04-04 0.000000 0.157794 0.000000 0.229414 1.0 0 2019-04-05 0.000000 0.000000 0.000000 0.377905 0.0 0 2019-04-06 1.132095 0.000000 0.536977 1.953742 1.0 0 2019-04-07 0.100000 0.200000 0.300000 0.400000 0.0 2019-04-17 00:00:00 【後向填補】: A B C D F T 2019-04-01 0.461541 0.157794 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 0.157794 0.894597 0.096956 1.0 2019-04-17 2019-04-03 1.132095 0.157794 0.536977 0.229414 1.0 2019-04-17 2019-04-04 1.132095 0.157794 0.536977 0.229414 1.0 2019-04-17 2019-04-05 1.132095 0.200000 0.536977 0.377905 1.0 2019-04-17 2019-04-06 1.132095 0.200000 0.536977 1.953742 1.0 2019-04-17 2019-04-07 0.100000 0.200000 0.300000 0.400000 NaN 2019-04-17 【前向填補】: A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 1.0 2019-04-17 2019-04-03 0.983399 NaN 0.894597 0.096956 1.0 2019-04-17 2019-04-04 0.983399 0.157794 0.894597 0.229414 1.0 2019-04-17 2019-04-05 0.983399 0.157794 0.894597 0.377905 1.0 2019-04-17 2019-04-06 1.132095 0.157794 0.536977 1.953742 1.0 2019-04-17 2019-04-07 0.100000 0.200000 0.300000 0.400000 1.0 2019-04-17
# 使用內插值填補,部份方法來自 scipy.interpolate 模組
print('【填補前】:\n{}\n'.format(dfmiss))
print('【linear 內插填補】:\n{}\n'.format(dfmiss.interpolate(method='linear')))
print('【pchip 內插填補】:\n{}\n'.format(dfmiss.interpolate(method='pchip')))
print('【krogh 內插填補】:\n{}\n'.format(dfmiss.interpolate(method='krogh')))
【填補前】: A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 NaN NaT 2019-04-03 NaN NaN NaN NaN 1.0 2019-04-17 2019-04-04 NaN 0.157794 NaN 0.229414 1.0 NaT 2019-04-05 NaN NaN NaN 0.377905 NaN NaT 2019-04-06 1.132095 NaN 0.536977 1.953742 1.0 NaT 2019-04-07 0.100000 0.200000 0.300000 0.400000 NaN 2019-04-17 【linear 內插填補】: A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 1.0 NaT 2019-04-03 1.020573 NaN 0.805192 0.163185 1.0 2019-04-17 2019-04-04 1.057747 0.157794 0.715787 0.229414 1.0 NaT 2019-04-05 1.094921 0.171863 0.626382 0.377905 1.0 NaT 2019-04-06 1.132095 0.185931 0.536977 1.953742 1.0 NaT 2019-04-07 0.100000 0.200000 0.300000 0.400000 1.0 2019-04-17 【pchip 內插填補】: A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 1.0 NaT 2019-04-03 1.053862 NaN 0.865482 0.139266 1.0 2019-04-17 2019-04-04 1.099728 0.157794 0.787156 0.229414 1.0 NaT 2019-04-05 1.124604 0.171863 0.673145 0.377905 1.0 NaT 2019-04-06 1.132095 0.185931 0.536977 1.953742 1.0 NaT 2019-04-07 0.100000 0.200000 0.300000 0.400000 1.0 2019-04-17 【krogh 內插填補】: A B C D F T 2019-04-01 0.461541 NaN 0.704009 0.271539 1.0 2019-04-17 2019-04-02 0.983399 NaN 0.894597 0.096956 1.0 NaT 2019-04-03 1.428300 NaN 0.946704 0.703491 1.0 2019-04-17 2019-04-04 1.679327 0.157794 0.886813 0.229414 1.0 NaT 2019-04-05 1.619565 0.171863 0.741410 0.377905 1.0 NaT 2019-04-06 1.132095 0.185931 0.536977 1.953742 1.0 NaT 2019-04-07 0.100000 0.200000 0.300000 0.400000 1.0 2019-04-17
Pandas 在 NumPy 的 datetime64
和 timedelta64
資料形態的基礎上,建構了相當多針對時間序列作處理的功能。 主要以四種資料類型來處理不同的時間概念:
Timestamp
- (Date times時間概念) 支援時區資訊的特定日期時間,類似 Python 標準函式庫裡的 datetime.datetime
。Timedelta
- (Time deltas時間概念) 絕對的連續性時間間距。類似 Python 標準函式庫裡的 datetime.timedelta
。Period
- (Time spans時間概念) 週期性時間間隔的時間點。DateOffset
- (Date offsets時間概念) 用於不同曆法的相對時間間距。相對於時間概念的操作,Pandas 提供的資料形態:
時間概念 | 純量類別 | 陣列類別 | Pandas 資料類別 | 主要建立方法 |
---|---|---|---|---|
Date times | Timestamp |
DatetimeIndex |
datetime64[ns] |
to_datetime() 或 date_range() |
Time deltas | Timedelta |
TimedeltaIndex |
timedelta64[ns] |
to_timedelta() 或 timedelta_range() |
Time spans | Period |
PeriodIndex |
period[freq] |
Period() 或 period_range() |
Date offsets | DateOffset |
None | None | DateOffset() |
# Date time 資料型態的建立可以接受各種不同的格式
import datetime
pd.to_datetime(['4/1/2019', np.datetime64('2019-04-01'), datetime.datetime(2018, 4, 1)])
DatetimeIndex(['2019-04-01', '2019-04-01', '2018-04-01'], dtype='datetime64[ns]', freq=None)
date_range(start, end, periods, freq, ...)
參數:
start - 開始時間
end - 結束時間
periods - 共產生幾個時間點
freq - 指定時距週期頻率的格式字串
date_range
的 freq
參數可接受指定格式字串:
D
- calendar day frequencyB
- business day frequencyW
- weekly frequencyM
- month end frequencyQ
- quarter end frequencyY
- year end frequencyH
- hourly frequencymin
- minutely frequencyS
- secondly frequencyms
- millisecondsus
- microsecondsN
- nanoseconds# 產生固定時間週期的序列
pd.date_range('2019-04-01', periods=6, freq='H')
DatetimeIndex(['2019-04-01 00:00:00', '2019-04-01 01:00:00', '2019-04-01 02:00:00', '2019-04-01 03:00:00', '2019-04-01 04:00:00', '2019-04-01 05:00:00'], dtype='datetime64[ns]', freq='H')
# Timestamp 物件的建立
aFriday = pd.Timestamp('2019-05-03')
print(aFriday.date(), 'is', aFriday.day_name())
2019-05-03 is Friday
# 增加一天(絕對的連續性時間間距)
aSaturday = aFriday + pd.Timedelta('1 day')
print(aFriday.date(),'加一天 =', aSaturday.date(), 'is', aSaturday.day_name())
2019-05-03 加一天 = 2019-05-04 is Saturday
# 增加一個工作天(Business Day,相對的時間間距概念)
aMonday = aFriday + pd.offsets.BDay()
print(aFriday.date(),'加一個工作天 =', aMonday.date(), 'is', aMonday.day_name())
2019-05-03 加一個工作天 = 2019-05-06 is Monday
# 時間序列的 DataFrame 及 Series 資料,可以使用 Timestamp 當作 index
pd.Series(np.random.randn(3), index=[pd.Timestamp('2019-05-01'), pd.Timestamp('2019-05-02'), pd.Timestamp('2019-05-03')])
2019-05-01 -0.101792 2019-05-02 -0.571034 2019-05-03 1.078528 dtype: float64
# 時間序列的 DataFrame 及 Series 資料,也可以使用 Period 當作 index
pd.Series(np.random.randn(3), index=[pd.Period('2019-04'), pd.Period('2019-05'), pd.Period('2019-06')])
2019-04 -0.609403 2019-05 1.268617 2019-06 0.913145 Freq: M, dtype: float64
# 如果日期時間被分割成不同的欄位,也可以用 to_datetime() 作合併轉換,但欄位標籤要是可以辨識的名字,如: year, month, day, ...
dfYMDH = pd.DataFrame({'year': [2015, 2016], 'month': [2, 3], 'day': [4, 5], 'hour': [2, 3]})
print('時間欄位被切割的 DataFrame:\n', dfYMDH)
pd.to_datetime(dfYMDH)
時間欄位被切割的 DataFrame: year month day hour 0 2015 2 4 2 1 2016 3 5 3
0 2015-02-04 02:00:00 1 2016-03-05 03:00:00 dtype: datetime64[ns]
# Series 的 index 是時間,可以用中括號及時間字串選取資料
df.A['2019-04-02':'2019-04-06']
2019-04-02 0.983399 2019-04-03 -0.294962 2019-04-04 -0.962004 2019-04-05 -0.178729 2019-04-06 1.132095 Freq: D, Name: A, dtype: float64
# 可以用部分字串選取大範圍時間
df.B['2019-04']
2019-04-01 -1.216620 2019-04-02 -0.701624 2019-04-03 -1.077161 2019-04-04 0.157794 2019-04-05 -0.639334 2019-04-06 -0.987462 2019-04-07 0.200000 Freq: D, Name: B, dtype: float64
# 使用 shift() 將所有資料列向前(正的 Period)或向後(負的 Period)移動,
# 注意: 試試 df.shift(1),沒有指定 freq 參數的話,資料與時間不會對齊
df.shift(1, freq='D')
A | B | C | D | F | |
---|---|---|---|---|---|
2019-04-02 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | True |
2019-04-03 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | False |
2019-04-04 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | True |
2019-04-05 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | True |
2019-04-06 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | False |
2019-04-07 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | True |
2019-04-08 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | False |
# 使用 tshift() 將所有資料列向前(正的 Period)或向後(負的 Period)移動,
# 結果與 shift(1, freq='D') 一樣
df.tshift(1)
A | B | C | D | F | |
---|---|---|---|---|---|
2019-04-02 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | True |
2019-04-03 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | False |
2019-04-04 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | True |
2019-04-05 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | True |
2019-04-06 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | False |
2019-04-07 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | True |
2019-04-08 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | False |
# Downsampling 成兩天一群
df.resample('2D').indices
defaultdict(list, {Timestamp('2019-04-01 00:00:00', freq='2D'): [0, 1], Timestamp('2019-04-03 00:00:00', freq='2D'): [2, 3], Timestamp('2019-04-05 00:00:00', freq='2D'): [4, 5], Timestamp('2019-04-07 00:00:00', freq='2D'): [6]})
# Upsampling 成每6小時一群,原本沒有的資料如同漏失資料一樣要再進行填補
df.resample('6H').interpolate().fillna(method='ffill')
A | B | C | D | F | |
---|---|---|---|---|---|
2019-04-01 00:00:00 | 0.461541 | -1.216620 | 0.704009 | 0.271539 | True |
2019-04-01 06:00:00 | 0.592006 | -1.087871 | 0.751656 | 0.227893 | True |
2019-04-01 12:00:00 | 0.722470 | -0.959122 | 0.799303 | 0.184247 | True |
2019-04-01 18:00:00 | 0.852934 | -0.830373 | 0.846950 | 0.140602 | True |
2019-04-02 00:00:00 | 0.983399 | -0.701624 | 0.894597 | 0.096956 | False |
2019-04-02 06:00:00 | 0.663809 | -0.795508 | 0.462349 | -0.217989 | False |
2019-04-02 12:00:00 | 0.344218 | -0.889392 | 0.030101 | -0.532934 | False |
2019-04-02 18:00:00 | 0.024628 | -0.983277 | -0.402146 | -0.847879 | False |
2019-04-03 00:00:00 | -0.294962 | -1.077161 | -0.834394 | -1.162824 | True |
2019-04-03 06:00:00 | -0.461723 | -0.768422 | -1.143371 | -0.814765 | True |
2019-04-03 12:00:00 | -0.628483 | -0.459683 | -1.452348 | -0.466705 | True |
2019-04-03 18:00:00 | -0.795243 | -0.150945 | -1.761325 | -0.118645 | True |
2019-04-04 00:00:00 | -0.962004 | 0.157794 | -2.070301 | 0.229414 | True |
2019-04-04 06:00:00 | -0.766185 | -0.041488 | -1.733890 | 0.266537 | True |
2019-04-04 12:00:00 | -0.570367 | -0.240770 | -1.397478 | 0.303660 | True |
2019-04-04 18:00:00 | -0.374548 | -0.440052 | -1.061066 | 0.340782 | True |
2019-04-05 00:00:00 | -0.178729 | -0.639334 | -0.724654 | 0.377905 | False |
2019-04-05 06:00:00 | 0.148977 | -0.726366 | -0.409246 | 0.771864 | False |
2019-04-05 12:00:00 | 0.476683 | -0.813398 | -0.093838 | 1.165824 | False |
2019-04-05 18:00:00 | 0.804389 | -0.900430 | 0.221570 | 1.559783 | False |
2019-04-06 00:00:00 | 1.132095 | -0.987462 | 0.536977 | 1.953742 | True |
2019-04-06 06:00:00 | 0.874071 | -0.690597 | 0.477733 | 1.565307 | True |
2019-04-06 12:00:00 | 0.616047 | -0.393731 | 0.418489 | 1.176871 | True |
2019-04-06 18:00:00 | 0.358024 | -0.096866 | 0.359244 | 0.788436 | True |
2019-04-07 00:00:00 | 0.100000 | 0.200000 | 0.300000 | 0.400000 | False |