0
点赞
收藏
分享

微信扫一扫

Pandas实现列表分列与字典分列的三个实例


大家好,我是小小明,本人非常擅长解决各类复杂数据处理的逻辑,包括各类结构化与非结构化数据互转,字符串解析匹配等等。

至今已经帮助很多数据从业者解决工作中的实际问题,如果你在数据处理上遇到什么困难,欢迎与我交流。

上次我分享了一道基础题N种解题思路,其中一种读取数据的过程涉及到列表分列​

这次我将分享三个实际案例,让大家看看列表分列的一些实际应用。

首先,我们先导包并设置Pandas显示参数:

import pandas as pd
pd.set_option("display.max_colwidth", 100)

正则提取并分列

需求:

Pandas实现列表分列与字典分列的三个实例_字符串

读取数据:

df = pd.read_excel("正则提取与分列.xlsm", usecols=[0])
df.head()

结果:

补回原因

0

核实断网时间;20190128至20190217,20190223至20190311,20190407至20190622号无上网记录,协商补回3个月21天

1

核实断网时间;2019.06.20到2019.07.22无上网记录,断网1个月3天

2

核实断网时间从20190130到20190322,20190414到20190719无上网记录,断网5个月7天

3

核实断网时间:20190312到20190802号无上网记录,断网4个月20天

4

核实断网时间:2019.03.29到2019.06.29无上网记录,协商补回3个月

实现代码:

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()

结果:

补回原因

0

1

2

0

核实断网时间;20190128至20190217,20190223至20190311,20190407至20190622号无上网记录,协商补回3个月21天

20190128至20190217

20190223至20190311

20190407至20190622

1

核实断网时间;2019.06.20到2019.07.22无上网记录,断网1个月3天

2019.06.20到2019.07.22

NaN

NaN

2

核实断网时间从20190130到20190322,20190414到20190719无上网记录,断网5个月7天

20190130到20190322

20190414到20190719

NaN

3

核实断网时间:20190312到20190802号无上网记录,断网4个月20天

20190312到20190802

NaN

NaN

4

核实断网时间:2019.03.29到2019.06.29无上网记录,协商补回3个月

2019.03.29到2019.06.29

NaN

NaN

分步解析:

df["tmp"] = df["补回原因"].str.findall("([\d.]+[到至][\d.]+)")
df.head(5)

结果:

补回原因

tmp

0

核实断网时间;20190128至20190217,20190223至20190311,20190407至20190622号无上网记录,协商补回3个月21天

[20190128至20190217, 20190223至20190311, 20190407至20190622]

1

核实断网时间;2019.06.20到2019.07.22无上网记录,断网1个月3天

[2019.06.20到2019.07.22]

2

核实断网时间从20190130到20190322,20190414到20190719无上网记录,断网5个月7天

[20190130到20190322, 20190414到20190719]

3

核实断网时间:20190312到20190802号无上网记录,断网4个月20天

[20190312到20190802]

4

核实断网时间:2019.03.29到2019.06.29无上网记录,协商补回3个月

[2019.03.29到2019.06.29]

这步使用正则提取出每个日期字符串,​​[\d.]+​​表示连续的数字或.用于匹配时间字符串,两个时间之间的连接字符可能是到或至。

然后我使用agg函数直接对Datafream分列:

df.agg({"补回原因": lambda x: x, "tmp": pd.Series})

结果:

Pandas实现列表分列与字典分列的三个实例_正则_02

由于列索引多了一级,所以需要删除:

df.agg({"补回原因": lambda x: x, "tmp": pd.Series}).droplevel(0, axis=1).head()

结果:

补回原因

0

1

2

0

核实断网时间;20190128至20190217,20190223至20190311,20190407至20190622号无上网记录,协商补回3个月21天

20190128至20190217

20190223至20190311

20190407至20190622

1

核实断网时间;2019.06.20到2019.07.22无上网记录,断网1个月3天

2019.06.20到2019.07.22

NaN

NaN

2

核实断网时间从20190130到20190322,20190414到20190719无上网记录,断网5个月7天

20190130到20190322

20190414到20190719

NaN

3

核实断网时间:20190312到20190802号无上网记录,断网4个月20天

20190312到20190802

NaN

NaN

4

核实断网时间:2019.03.29到2019.06.29无上网记录,协商补回3个月

2019.03.29到2019.06.29

NaN

NaN

​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

分组聚合并分列

需求:

Pandas实现列表分列与字典分列的三个实例_读取数据_03

首先,读取数据:

df = pd.read_excel("分组聚合并分列.xlsx")

结果:

Pandas实现列表分列与字典分列的三个实例_字符串_04

实现代码:

(
df.groupby("姓名")["得分"]
.apply(list)
.apply(pd.Series)
.fillna("")
.rename(columns=lambda x: f"得分{x+1}")
.reset_index()
.astype({"得分1":"int8"})
)

结果:

Pandas实现列表分列与字典分列的三个实例_读取数据_05

分布解析:

首先将每个姓名的得分聚合成列表,并最终返回一个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)

结果:

Pandas实现列表分列与字典分列的三个实例_字符串_06

注意:​​_​​​在ipython表示上一个输出返回的结果,jupyter还额外支持​​_num​​表示num编号单元格的输出。

_.fillna("")

结果:

Pandas实现列表分列与字典分列的三个实例_正则_07

fillna表示填充缺失值,传入""表示将缺失值填充为空字符串。

下面重命名一下列名:

_.rename(columns=lambda x: f"得分{x+1}")

结果:

Pandas实现列表分列与字典分列的三个实例_字符串_08

然后还原索引:

_.reset_index()

结果:

Pandas实现列表分列与字典分列的三个实例_正则_09

发现结果中有一列,不是整数,所以还原成整数(总分100分,8位足够存储):

_.astype({"得分1":"int8"})

结果:

Pandas实现列表分列与字典分列的三个实例_读取数据_05

解析json字符串并字典分列

需求:

Pandas实现列表分列与字典分列的三个实例_正则_11

首先读取数据:

df = pd.read_excel("字典分列.xlsx")
df.head()

结果:

Pandas实现列表分列与字典分列的三个实例_正则_12

处理代码:

result = df.features.apply(eval(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(231, 243, 237); padding: 0px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px;">​**.apply(pd.Series)​​则可以将每个字典对象转换成Series,则可以将该字典扩展到多列,并将原始的Series转换为Datafream。

而​​result["counts"] = df.counts​​则将原始数据的counts列添加到结果列中。

总结

经过三个案例的练习,大家是否已经对Pandas的分列操作比较熟悉呢?

欢迎大家在下方评论区留言你的看法。


举报

相关推荐

0 条评论