Pandas案例精進 | 實現列表分列與字典分列的三個實例


小小明:「凹凸數據」專欄作者,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([
...     [1234],
...     [5678],
...     [9101112]
... ]).set_index([01]).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的分列操作比較熟悉呢?

歡迎大家在下方評論區留言你的看法。

點擊藍字進入Pandas案例精進專欄,可以直接訂閱話題,第一時間看到新文章!



安利一本📕:《機器學習與深度學習算法基礎》市場上的機器學習/深度學習算法的入門書籍要麼過於理論化和數學化,要麼過於偏重實操,本書將兩者更合理的結合在一起。點擊下圖可看詳情/購買!👇

感謝北京大學出版社的支持!「凹凸數據」🎁 繼續 300積分免費兌換啦,閱讀原文直達

本文分享自微信公衆號 - 凹凸數據(alltodata)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章