小小明:「凹凸數據」專欄作者,Pandas數據處理高手,致力於幫助無數數據從業者解決數據處理難題。
上次我分享了一道基礎題N種解題思路,其中一種讀取數據的過程涉及到列表分列,詳見:《一道基礎題,多種解題思路,引出Pandas多個知識點》
這次我將分享三個實際案例,讓大家看看列表分列的一些實際應用。
首先,我們先導包並設置Pandas顯示參數:
import pandas as pd
pd.set_option("display.max_colwidth", 100)
正則提取並分列
需求:
讀取數據:
df = pd.read_excel("正則提取與分列.xlsm", usecols=[0])
df.head()
結果:
實現代碼:
result = df.copy()
result["tmp"] = result["補回原因"].str.findall("([\d.]+[到至][\d.]+)")
result = result.agg({"補回原因": lambda x: x, "tmp": pd.Series}).droplevel(0, axis=1)
result.head()
結果:
分步解析:
df["tmp"] = df["補回原因"].str.findall("([\d.]+[到至][\d.]+)")
df.head(5)
結果:
這步使用正則提取出每個日期字符串,[\d.]+
表示連續的數字或.用於匹配時間字符串,兩個時間之間的連接字符可能是到或至。
然後我使用agg函數直接對Datafream分列:
df.agg({"補回原因": lambda x: x, "tmp": pd.Series})
結果:
由於列索引多了一級,所以需要刪除:
df.agg({"補回原因": lambda x: x, "tmp": pd.Series}).droplevel(0, axis=1).head()
結果:
droplevel(0, axis=1)
用於刪除多級索引指定的級別,axis=0可以刪除行索引,axis=1則可以刪除列索引,第一參數表示刪除級別0。當然如果列索引存在名稱時還可以傳入名稱字符串,可參考官網文檔:
df = pd.DataFrame([
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12]
... ]).set_index([0, 1]).rename_axis(['a', 'b'])
>>> df.columns = pd.MultiIndex.from_tuples([
... ('c', 'e'), ('d', 'f')
... ], names=['level_1', 'level_2'])
>>> df
level_1 c d
level_2 e f
a b
1 2 3 4
5 6 7 8
9 10 11 12
>>> df.droplevel('a')
level_1 c d
level_2 e f
b
2 3 4
6 7 8
10 11 12
>>> df.droplevel('level2', axis=1)
level_1 c d
a b
1 2 3 4
5 6 7 8
9 10 11 12
分組聚合並分列
需求:
首先,讀取數據:
df = pd.read_excel("分組聚合並分列.xlsx")
df
結果:
實現代碼:
(
df.groupby("姓名")["得分"]
.apply(list)
.apply(pd.Series)
.fillna("")
.rename(columns=lambda x: f"得分{x+1}")
.reset_index()
.astype({"得分1":"int8"})
)
結果:
分佈解析:
首先將每個姓名的得分聚合成列表,並最終返回一個Series:
df.groupby("姓名")["得分"].apply(list)
結果:
姓名
孫四娘 [7, 28]
看見星光 [88, 28, 23]
看見月光 [69, 10, 87]
老祝 [51, 29]
馬青梅 [99]
Name: 得分, dtype: object
當然,這步的標準寫法應該是使用Series的內部方法:
df.groupby("姓名")["得分"].apply(lambda x:x.to_list())
使用Series內部方法的性能比python列表方法轉換快一些。
作爲一個Series就可以通過將每個列表元素轉換爲Series,從而最終返回一個分列的Datafream:
_.apply(pd.Series)
結果:
注意:
_
在ipython表示上一個輸出返回的結果,jupyter還額外支持_num
表示num編號單元格的輸出。
_.fillna("")
結果:
fillna表示填充缺失值,傳入""表示將缺失值填充爲空字符串。
下面重命名一下列名:
_.rename(columns=lambda x: f"得分{x+1}")
結果:
然後還原索引:
_.reset_index()
結果:
發現結果中有一列,不是整數,所以還原成整數(總分100分,8位足夠存儲):
_.astype({"得分1":"int8"})
結果:
解析json字符串並字典分列
需求:
首先讀取數據:
df = pd.read_excel("字典分列.xlsx")
df.head()
結果:
處理代碼:
result = df.features.apply(eval).apply(pd.Series)
result["counts"] = df.counts
result
結果:
儲存條件 | 品牌 | 推薦理由 | 品種 | 食用方式 | 是否進口 | 特色服務 | 是否有機 | counts | |
---|---|---|---|---|---|---|---|---|---|
0 | 常溫 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 33 |
1 | 冷藏 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 24 |
2 | 常溫 | 禾煜 | NaN | NaN | NaN | NaN | NaN | NaN | 22 |
3 | 常溫 | 妙潔 | NaN | NaN | NaN | NaN | NaN | NaN | 16 |
4 | 冷凍 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 14 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2083 | 常溫 | 樂事 | 夠薄夠脆 | NaN | NaN | NaN | NaN | NaN | 1 |
2084 | 冷藏 | NaN | 生態種植 | 黃瓜 | NaN | NaN | NaN | 有機 | 1 |
2085 | 冷藏 | NaN | 腥味較淡 | 鯽魚 | NaN | NaN | 免費宰殺 | NaN | 1 |
2086 | 冷藏 | NaN | 甜脆可口 | 佛手瓜 | NaN | NaN | NaN | NaN | 1 |
2087 | 冷藏 | 叮咚日日鮮 | 全程可追溯 | 豬小排 | NaN | NaN | NaN | NaN | 1 |
2088 rows × 9 columns
淺析:
df.features.apply(eval)
用於將features列的每個json字符串解析爲字典對象。
**.apply(pd.Series)
則可以將每個字典對象轉換成Series,則可以將該字典擴展到多列,並將原始的Series轉換爲Datafream。
而result["counts"] = df.counts
則將原始數據的counts列添加到結果列中。
總結
經過三個案例的練習,大家是否已經對Pandas的分列操作比較熟悉呢?
歡迎大家在下方評論區留言你的看法。
安利一本📕:《機器學習與深度學習算法基礎》市場上的機器學習/深度學習算法的入門書籍要麼過於理論化和數學化,要麼過於偏重實操,本書將兩者更合理的結合在一起。點擊下圖可看詳情/購買!👇
本文分享自微信公衆號 - 凹凸數據(alltodata)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。