看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
本例是一个复杂的字符串匹配操作,在两份数据中,一个数据的每行要匹配另外一份数据中匹配的所有行,然后将匹配行所在值进行计算。类似这样的案例中,需要我们充分利用 pandas 的 Series 结构调用方法进行计算,从而减少大量的 for 循环,让代码逻辑更新清晰。
我们有以下两个原始数据,其中 df1 是我们的基础数据,df2 是需要对比计算的数据。
df1 = pd.DataFrame({'number': ['a#b', 'k#h', 'c#a'],
'count': [1, 3, 6] })
df1
'''
number count
0 a#b 1
1 k#h 3
2 c#a 6
'''
df2 = pd.DataFrame({'number': ['b#s#a', 'l#a#b', 'h#a#k'],
'count': [2, 6, 4] })
df2
'''
number count
0 b#s#a 2
1 l#a#b 6
2 h#a#k 4
'''
两份数据结构相同,现在需要以 df1 为基础,根据 df2 重新计算出 count 的值,得到以下结果:
# 最终结果
'''
number count
0 a#b 9
1 k#h 7
2 c#a 6
'''
count 计算逻辑是这样的。df1 number 中以井号为分隔的字母如果在 df2 对应列中井号分隔的字符串中全部存在,那此行为匹配行,然后把所有匹配行的 count 值连同 df1 自己的 count 值相加,得到 count 的新值。
例如,df1 中第一行 number 值为 'a#b'
, 它在 df2.number 列中的索引 0、1 都包含 a 和 b,则将这两行的值 2 和 6 以及 df1 自己的 1 相加,1+2+6=9,最终此行的 count 值为 9。
我们采用倒推的方法来梳理代码思路。
要计算 df1 中每行的新 count 值(以第一行的值 a#b
为例),除了现有的 count 值外,还需要 df2 中的包含行的计算值。
'a#b'.split('#')
# ['a', 'b']
需要 df2 中的包含行的计算值,就需要将包含 df1.number 此行的所有行筛选出来。
要筛选的话就要判断 df2.number 每行是否全包含 df1 中的值(以 df2 中的第一个值 b#s#a
为例):
# 接上代码
pd.Series('a#b'.split('#'))
'''
0 a
1 b
dtype: object
'''
# 利用 Series 进行判断
(
pd.Series('a#b'.split('#'))
.isin([*'b#s#a'])
)
'''
0 True
1 True
dtype: bool
'''
# 如果全为 True 说明均包含 df1 的值
(
pd.Series('a#b'.split('#'))
.isin([*'b#s#a'])
.all()
)
# True
这样就得到了当前 df1.number 对应 df2.number 的布尔序列,为 True 的即为匹配的,我们选择出来并对 count 列求和,就得到了此 df1.number 对应 df2 中的包含行的计算值。
最后连同自己的 count 值求和即可。
我们将上边的逻辑封装一个函数,用来产出每个 df1.number 对 df2 的计算值。
def func(val: str) -> int:
data = val.split('#')
ser = pd.Series(data)
included = df2.number.map(lambda x: ser.isin([*x]).all())
return df2[included]['count'].sum()
调用这个方法并计算:
df1.assign(count=df1.number.map(func)+df1['count'])
'''
number count
0 a#b 9
1 k#h 7
2 c#a 6
'''
以上代码要注意的 count 列不能用 df1.count
获取,因为 count 是 Series 的一个方法,因些我们要用 df1['count']
来操作。
这样我们就完成了这个需求。
另外,在是否包含部分我们可以利用集合的方法,判断 df1 中的是不是 df2 的子集,试验如下:
set('a#b') <= set('b#s#a')
# True
这里我们不需要对字符串做任何转换,因为两边字符串都有 #
号,集合也没有顺序而言,直接将字符串转为集合即可进行操作。
这样,代码就大大减少,逻辑也更加清晰:
def func(val: str) -> int:
included = df2.number.map(lambda x: set(val) <= set(x))
return df2[included]['count'].sum()
df1.assign(count=df1.number.map(func)+df1['count'])
可以得到同样的结果。
(完)
更新时间:2024-08-18 16:00:54 标签:pandas 字符串 包含 计算