Python 在多个字符串列中统计字符串的出现次数

Python 在多个字符串列中统计字符串的出现次数,python,string,pandas,dataframe,Python,String,Pandas,Dataframe,我有一个名为df的数据框,看起来与此类似(除了“mat_deliver”列的数量增加到mat_deliver_8之外,在Client_ID和mat_deliver_1之间有几百个客户端和许多其他列,我在这里对其进行了简化) 我想创建一个名为xxx\u count的新列,计算xxx出现在mat\u deliver\u 1、mat\u deliver\u 2、mat\u deliver\u 3和mat\u deliver\u 4中的次数。这些值应如下所示: Client_ID mat_deliv_

我有一个名为
df
的数据框,看起来与此类似(除了“mat_deliver”列的数量增加到mat_deliver_8之外,在
Client_ID
mat_deliver_1
之间有几百个客户端和许多其他列,我在这里对其进行了简化)

我想创建一个名为
xxx\u count
的新列,计算
xxx
出现在
mat\u deliver\u 1
mat\u deliver\u 2
mat\u deliver\u 3
mat\u deliver\u 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
=四列
mat\u deliver
中至少有一列存在
xxx


注意:这是一个关于这里所问问题的后续问题:

在数数之前试着将它们水平连接起来

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”在每列中仅出现一次,则此操作将有效。如果发生多次,则会计算每次发生的次数


另一个选项涉及堆栈:

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
更快?看

使用
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

您可以使用按
拆分,然后在
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)

“然后在lambda中使用lambda”。。。哭internally@coldspeed,哈哈,我也要用一个函数更新了。。。完成。感谢您的回复-但是,在我的df上运行之后,我收到一条错误消息,即.str无法在对象数据类型上运行,因此我选择下面的答案。这非常有效-我使用列表理解来获得最后的建议。非常感谢您的帮助。
count
需要注意的一点是,它将在一个较大的字符串中包含一个匹配的子字符串,例如,
xxx
xxxx
都将计数。如果可以的话,那就好了。如果没有,则需要测试相等性,例如,
[sum(1表示“,”中的单词。join(row)。split(“,”)表示df.loc[:,“mat_deliver_1:“mat_deliver_4”)。fillna(“”)。value]
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)