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源创计划”,欢迎正在阅读的你也加入,一起分享。

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