看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
在商品分类、图书章节等场景下,会有多级分类、无限分类等情况,本需求需要仅保留叶子分类,即其下再没有分类的内容。而本需求提供的分类的数字形式文本形式,层级之间用点分隔,这对数据的处理带来了更多的挑战,我们来看看 pandas 是如何应对这种挑战的。
原数据只有一列,我们将原数据读取到 DataFrame 里:
import pandas as pd
from io import StringIO
data = '''
level
1
1.1
2
2.1
2.2
3
3.1
3.1.1
3.1.1.1
3.1.1.2
3.1.1.10
3.1.1.101
3.2.1
'''
df = pd.read_csv(StringIO(data), sep='\t')
df
# ...
level 列可以认为是分叉树,每一个.
都是分叉节点,需要保留所有末端分叉,主干和枝干全部删除。例如:1.1 和 2.2 没有以他们为节点的分叉,都保留,3.1.1 有分叉 3.1.1.1 和 3.1.1.2 因此删除。
我们的整体思路是,判断当前字符串在整体字符串中是否有以它开头的,如果只有一个(必然有一个,因为自己就是自己开头的),则说明它是叶子分类,否则说明它不是,它还有子分类。
但有种情况我们不得不考虑,如 3.1.1.1 和 3.1.1.10,会认为前者是后者开头的,从字符判断上来看也确实这样,但实际上它们均属于叶子层级。
为了解决这个问题,我们想到了 hash() 的特性,同一串字符有相同的 hash 值,我们将点之间的数字内容进行哈希,并用 hash 值替换,这样 1 和 10 就是不同的哈希值,可用于是否包含操作。
先对文本用点分隔,分隔为列表后再替换为哈希值,得到一个新的 Series:
# 将点之间的数据哈希 hash,就会消除字面特性
df.level.map(lambda x: '.'.join([str(hash(i)) for i in x.split('.')]))
# 以上代码相当于:
def hashed(val: str):
val = val.split('.') # 拆为列表
val = map(hash, val) # 哈希
val = map(str, val) # 转为字符
return '.'.join(val)
ser = df.level.map(hashed)
ser
'''
0 6291699096238399000
1 6291699096238399000.6291699096238399000
2 830142435501154396
3 830142435501154396.6291699096238399000
4 830142435501154396.830142435501154396
5 7899171769756609705
6 7899171769756609705.6291699096238399000
7 7899171769756609705.6291699096238399000.629169...
8 7899171769756609705.6291699096238399000.629169...
9 7899171769756609705.6291699096238399000.629169...
10 7899171769756609705.6291699096238399000.629169...
11 7899171769756609705.6291699096238399000.629169...
12 7899171769756609705.830142435501154396.6291699...
Name: level, dtype: object
'''
判断在整体 Series 是否仅有一个包含,如果只有一个返回的布尔序列为 True,将些布尔列表应用在选择操作中,就选出了所有叶子层级的数据:
# 算法:如果它没有子层,则以它为开头的只有自己一个
df[ser.map(lambda x: ser.str.startswith(x).sum() == 1)]
'''
level
1 1.1
3 2.1
4 2.2
8 3.1.1.1
9 3.1.1.2
10 3.1.1.10
11 3.1.1.101
12 3.2.1
'''
这样我们就完成了这个需求。
(完)
更新时间:2024-08-18 15:56:30 标签:pandas python 过滤