Pandas 進階|數據透視表與逆透視
數據透視表將每一列數據作爲輸入,輸出將數據不斷細分成多個維度累計信息的二維數據表。在實際數據處理過程中,數據透視表使用頻率相對較高,今天雲朵君就和大家一起學習 pandas 數據透視表與逆透視的使用方法。
本次使用的數據來源於 Kaggle,車輛被警察攔下並進行搜查記錄數據集,簡稱車輛數據。文末有下載方式,大家按需獲取。
數據基本情況
groupby 數據透視表
使用 pandas.DataFrame.groupby 函數,其原理如下圖所示。
使用車輛數據集統計不同性別司機的平均年齡,聚合後用二維切片可以輸出 DataFrame 數據框。
data.groupby('driver_gender'
)[['driver_age']].mean()
在聚合後一維切片會得到 pandas.Series.
data.groupby('driver_gender'
)['driver_age'].mean()
driver_gender
F 32.607399
M 34.537886
Name: driver_age, dtype: float64
可能還想進一步探索,同時觀察不同司機性別與司機種族的平均年齡。根據 GroupBy 的操作流程,我們也許能夠實現想要的結果:將司機種族 ('driver_race') 與司機性別 ('driver_gender') 分組,然後選擇司機年齡 ('driver_age') 列,應用均值 ('mean') 累計函數,再將各組結果組合,最後通過行索引轉列索引操作將最裏層的行索引轉換成列索引,形成二維數組。
data.groupby(['driver_gender',
'driver_race']
)[['driver_age']].aggregate('mean')
通過 unstack 重排數據表
如果原表只有一級索引,unstack 就將每一個列都分出來,然後全部縱向疊加在一起,每一個列名作爲新的一級索引,原本的索引作爲二級索引。如果原表有二級索引,那麼 unstack 就會將二級索引作爲新的列名,一級索引作爲新的索引。
data.groupby(['driver_gender',
'driver_race']
)[['driver_age']].aggregate('mean').unstack()
pivot_table
雖然這樣就可以更清晰地觀察出不同司機性別與司機種族的平均年齡,但代碼有點複雜。要理解這個長長的語句可不是那麼容易的事。
由於二維的 GroupBy 應用場景非常普遍,因此 Pandas 提供了一個快捷方式 pivot_table 來快速解決多維的累計分析任務。
pivot_table() 的參數
values 待聚合的列的名稱。默認聚合所有數值列
index 用於分組的列名或其他分組鍵,出現在結果透視表的行
columns 用於分組的列名或其他分組鍵,出現在結果透視表的列
aggfunc 聚合函數或函數列表,默認爲'mean'。可以使任何對 groupby 有效的函數
fill_value 用於替換結果表中的缺失值
dropna 默認爲 True
margins_name 默認爲'ALL',當參數 margins 爲 True 時,ALL 行和列的名字
同樣是上面的需求,同時觀察不同司機性別與司機種族的平均年齡 ,用 pivot_table 實現透視表。
data.pivot_table(values='driver_age',
index='driver_gender',
columns='driver_race')
多級數據透視表
與 GroupBy 類似,數據透視表中的分組也可以通過各種參數指定多個等級。
行索引和列索引都可以再設置爲多層,不過行索引和列索引在本質上是一樣的,大家需要根據實際情況合理佈局。
data.pivot_table(values='driver_age',
index=['driver_gender',
'stop_duration'],
columns='driver_race')
自定義聚合函數
aggfunc 參數用於設置累計函數類型,默認值是均值 (mean)。累計函數可以用一些常見的字符串 ('sum'、'mean'、'count'、'min'、'max' 等) 表示,也可以用標準的累計函數(np.sum()、min()、sum() 等)。
還可以通過字典爲不同的列指定不同的累計函數。
-
如果傳入參數爲 list,則每個聚合函數對每個列都進行一次聚合。
-
如果傳入參數爲 dict,則每個列僅對其指定的函數進行聚合, 此時 values 參數可以不傳。
data.pivot_table(index='driver_gender',
columns='driver_race',
aggfunc={'driver_age':'mean',
'violation':'count'})
這裏沒有使用參數 values。其實在我們通過字典爲 aggfunc 指定映射關係的時候,待透視的數值就已經確定了。
margin 的標籤可以通過 margins_name 參數進行自定義, 默認值是 "All"。
下面按行、按列進行彙總,指定彙總列名爲 “Total”
data.pivot_table(index="driver_gender",
columns="driver_race",
values="violation",
aggfunc= "count",
margins=True,
margins_)
pandas.crosstab
crosstab 是交叉表,是一種特殊的數據透視表默認是計算分組頻率的特殊透視表(默認的聚合函數是統計行列組合出現的次數)。如果指定了聚合函數則按聚合函數來統計,但是要指定 values 的值,指明需要聚合的數據。
pandas.crosstab 參數
index:指定了要分組的列,最終作爲行。
columns:指定了要分組的列,最終作爲列。
values:指定了要聚合的值(由行列共同影響),需要指定 aggfunc 參數。
rownames:指定了行名稱。
colnames:指定了列名稱。
aggfunc:指定聚合函數。必須指定 values 的值。
margins:布爾值,是否分類統計。默認 False。
margins_name:分類統計的名稱,默認是 "All"。
dropna:是否包含全部是 NaN 的列。默認是 True。
pd.crosstab(index=data.driver_gender,
columns=data.driver_race,
margins=True)
逆透視
如果說 df.pivot()
將長數據集轉換成寬數據集,df.melt()
則是將寬數據集變成長數據集 melt()
既是頂級類函數也是實例對象函數,作爲類函數出現時,需要指明 DataFrame
的名稱
pd.melt 參數
frame 被 melt 的數據集名稱在 pd.melt() 中使用
id_vars 不需要被轉換的列名,在轉換後作爲標識符列(不是索引列)
value_vars 需要被轉換的現有列,如果未指明,除 id_vars 之外的其他列都被轉換
var_name 自定義列名名稱,設置由'value_vars' 組成的新的 column name
value_name 自定義列名名稱,設置由'value_vars' 的數據組成的新的 column name
col_level 如果列是 MultiIndex,則使用此級別
df = data.loc[:,['driver_gender',
'driver_race',
'violation']]
df
df_pivot = data.pivot_table(index="driver_gender",
columns="driver_race",
values="violation",
aggfunc= "count")
df_pivot
爲方便下面轉換的理解,經過去除 columns 的 name 後得到:
下面開始進行轉換。保留 "driver_gender",對剩下列全部轉換,並給設置對列定義列名。
df_wide.melt(id_vars='driver_gender',
var_name = 'driver_race',
value_name='violation_count')
上述同樣的結果用 groupby 也可以做到,如下所示。
df.groupby(['driver_gender',
'driver_race']
)[['violation']].agg('count').reset_index()
這裏補充說明下去除 columns 的 name 及 index 的 name 的方法。
如下圖所示 "driver_race" 和 "driver_gender" 分別是 columns 的 name,index 的 name。
下面演示一個平時較爲頭疼的事情。即將兩個 name 刪掉。下面介紹一個常見的方法。
使用 pandas.DataFrame.rename_axis 去除 columns 列的名稱
# 第一步,重置索引
df_wide = df_pivot.reset_index()
# 重置name,設置爲None即可
df_wide.rename_axis([None], axis=1)
# columns有N個,就用N個None
另外一種情況如下圖所示,在 column 上還有一個 "driver_age",此時需要在第一步使用 pandas.DataFrame.droplevel 把 "driver_age" 刪除:df.columns = df.columns.droplevel(0)
然後在執行上面兩步。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ZYzRHquNraQNKwvPIF8a1Q