我有一个名为df
的数据框,看起来与此类似(除了'mat_deliv'列的数量上升到mat_deliv_8之外,Client_ID
和之间有几百个客户端和许多其他列mat_deliv_1
-我在这里简化了。
Client_ID mat_deliv_1 mat_deliv_2 mat_deliv_3 mat_deliv_4
C1019876 xxx,yyy,zzz aaa,bbb,xxx xxx ddd
C1018765 yyy,zzz xxx xxx None
C1017654 yyy,xxx aaa,bbb ccc ddd
C1016543 aaa,bbb ccc None None
C1019876 yyy None None None
我想创建一个名为xxx_count
的新列,该列计算xxx
在mat_deliv_1
,mat_deliv_2
,mat_deliv_3
和{{1 }}。值应如下所示:
mat_deliv_4
我尝试了以下代码:
Client_ID mat_deliv_1 mat_deliv_2 mat_deliv_3 mat_deliv_4 xxx_count
C1019876 xxx,yyy,zzz aaa,xxx,bbb xxx ddd 3
C1018765 yyy,zzz xxx xxx None 2
C1017654 yyy,xxx aaa,bbb ccc ddd 1
C1016543 aaa,bbb ccc None None 0
C1015432 yyy None None None 0
但是它不产生计数,仅产生二进制变量,其中df = df.assign(xxx_count=df.loc[:, "mat_deliv_1":"mat_deliv_4"].\
apply(lambda col: col.str.count('xxx')).fillna(0).astype(int))
=不存在0
的情况,而xxx
=在以下至少一项中存在1
四个xxx
列。
注意:这是对以下问题的跟进问题:Creating a column based on the presence of part of a string in multiple other columns
答案 0 :(得分:3)
在计数之前尝试将它们水平合并吗?
df['counts'] = (df.loc[:, "mat_deliv_1":"mat_deliv_4"]
.fillna('')
.agg(','.join, 1)
.str.count('xxx'))
df
Client_ID mat_deliv_1 mat_deliv_2 mat_deliv_3 mat_deliv_4 counts
0 C1019876 xxx,yyy,zzz aaa,bbb,xxx xxx ddd 3
1 C1018765 yyy,zzz xxx xxx NaN 2
2 C1017654 yyy,xxx aaa,bbb ccc ddd 1
3 C1016543 aaa,bbb ccc NaN NaN 0
4 C1019876 yyy NaN NaN NaN 0
这将假设每个列最多出现一次“ xxx”,这将起作用。如果发生多次,它将对每次发生进行计数。
另一个选项涉及stack
:
df['counts'] = (
df.loc[:, "mat_deliv_1":"mat_deliv_4"].stack().str.count('xxx').sum(level=0))
df
Client_ID mat_deliv_1 mat_deliv_2 mat_deliv_3 mat_deliv_4 counts
0 C1019876 xxx,yyy,zzz aaa,bbb,xxx xxx ddd 3
1 C1018765 yyy,zzz xxx xxx NaN 2
2 C1017654 yyy,xxx aaa,bbb ccc ddd 1
3 C1016543 aaa,bbb ccc NaN NaN 0
4 C1019876 yyy NaN NaN NaN 0
使用str.contains
可以轻松地将其修改为仅对首次出现进行计数:
df['counts'] = (
df.loc[:, "mat_deliv_1":"mat_deliv_4"].stack().str.contains('xxx').sum(level=0))
如果“ xxx”有可能是子字符串,请先拆分然后计数:
df['counts'] = (df.loc[:, "mat_deliv_1":"mat_deliv_4"]
.stack()
.str.split(',', expand=True)
.eq('xxx')
.any(1) # change to `.sum(1)` to count all occurrences
.sum(level=0))
为提高性能,请使用列表理解:
df['counts'] = [
','.join(x).count('xxx')
for x in df.loc[:, "mat_deliv_1":"mat_deliv_4"].fillna('').values
]
df
Client_ID mat_deliv_1 mat_deliv_2 mat_deliv_3 mat_deliv_4 counts
0 C1019876 xxx,yyy,zzz aaa,bbb,xxx xxx ddd 3
1 C1018765 yyy,zzz xxx xxx NaN 2
2 C1017654 yyy,xxx aaa,bbb ccc ddd 1
3 C1016543 aaa,bbb ccc NaN NaN 0
4 C1019876 yyy NaN NaN NaN 0
为什么循环比使用str
方法或apply
更快?参见For loops with pandas - When should I care?。
答案 1 :(得分:2)
使用str.findall
df.iloc[:,1:].apply(lambda x : x.str.findall('xxx')).sum(1).str.len()
Out[433]:
0 3
1 2
2 1
3 0
4 0
dtype: int64
答案 2 :(得分:0)
您可以使用,
分割,然后在lambda
中使用lambda
。此解决方案的优点是,如果xxx
作为yyy
的子字符串存在,则不会看到错误的结果。
df['xxx_count'] = df.filter(like='mat_deliv').apply(lambda x: x.str.split(',')\
.apply(lambda x: 'xxx' in x)).sum(1)
print(df)
Client_ID mat_deliv_1 mat_deliv_2 mat_deliv_3 mat_deliv_4 xxx_count
0 C1019876 xxx,yyy,zzz aaa,bbb,xxx xxx ddd 3
1 C1018765 yyy,zzz xxx xxx None 2
2 C1017654 yyy,xxx aaa,bbb ccc ddd 1
3 C1016543 aaa,bbb ccc None None 0
4 C1019876 yyy None None None 0
或者更好地使用一个函数:
def sum_counts(series, value):
def finder(item, value):
return value in item
return series.str.split(',').apply(finder, value=value)
df['xxx_count'] = df.filter(like='mat_deliv').apply(sum_counts, value='xxx').sum(1)