【问题标题】:group by only those values that are LIKE '[list of 3 values]'仅按 LIKE '[list of 3 values]' 的值分组
【发布时间】:2019-05-18 23:28:53
【问题描述】:

我有熊猫数据框。我想从 col1 中选择值,它应该在 col2 中包含所有 3 个值。 col2_values_should start_with = [P1.adv, P2.cmp, P3.part ] 此外,由于您可以在 col2 中看到值的后半部分,因此是虚拟的。所以我需要使用与 SQL 的 LIKE 运算符类似的选项来检查 col2 值。

所以上表的答案是:

因为“A”具有所需的所有三个值,与 B 和 C 不同。

所以基本上我想检查 col1 中的哪个值在 col2 中具有所有 3 个必需值。并且这些 col2 值应该以上面提到的某些字母开头。

编辑:

样本数据

ID col1 col2

1 A P1.adv abcd

2 A P2.cmp mkmfwk

3 A P3.part lpwf

4 B P1.adv abcd

5 B P2.cmp mkmfwk

6 C P1.addv abcd

7 C P1.adv abcd

【问题讨论】:

  • 请提供文本格式的示例数据,否则很难提供帮助

标签: python-3.x pandas dataframe group-by


【解决方案1】:

这是一种方法:

l = ('P1.adv', 'P2.cmp', 'P3.part' )
m1 = df.groupby('col1').apply(lambda x: x.col2.str.startswith(l, na=False)).reset_index()
m2 = df[m1.col2].groupby('col1').col2.nunique() > 2
df[df.col1.isin(m2[m2].index.values)]

   ID  col1         col2
0   1    A   P1.adv abcd
1   2    A  P2.cmp kmasd
2   3    A   P3.part lpw

说明

您可以首先检查df 中的哪些行包含col2,该col2 使用str.startswith 以列表中的一项开头。

print(m1)

 col1  level_1  col2
0    A        0  True
1    A        1  True
2    A        2  True
3    B        3  True
4    B        4  True
5    C        5  True
6    C        6  True

在您可以通过col1 分组并使用GroupBy.nunique() 检查唯一项的数量是否大于2(意味着列表中的每个元素至少存在一次)之后:

print(m2)

col1
A     True
B    False
C    False
Name: col2, dtype: bool

最后使用 m2 对原始 df 使用布尔索引:

result = df[df.col1.isin(m2[m2].index.values)]
print(result)

    ID  col1        col2
0   1    A   P1.adv abcd
1   2    A  P2.cmp kmasd
2   3    A   P3.part lpw

【讨论】:

    【解决方案2】:

    extract 与正则表达式一起使用,^ 用于匹配字符串的开头,| 用于OR,然后groupbytransform 并比较sets,如果相同长度的组也进行比较喜欢list:

    L = ["P1.adv", "P2.cmp", "P3.part"]
    
    pat = '|'.join(r"^{}".format(x) for x in L)
    s = df['col2'].str.extract('('+ pat + ')', expand=False)
    
    df = df[s.groupby(df['col1']).transform(lambda x: set(x) == set(L) and len(x) == len(L))]
    print (df)
       ID col1           col2
    0   1    A    P1.adv abcd
    1   2    A  P2.cmp mkmfwk
    2   3    A   P3.part 1pwf
    

    另一种解决方案是按startswith 过滤,然后与size 聚合以进行计数,通过nunique 获取唯一值的数量并按列表长度进行比较,最后通过boolean indexingall 进行过滤以进行检查如果两个值都是Trues:

    df = df[df['col2'].str.startswith(tuple(L))].copy()
    m = df.groupby('col1')['col2'].agg(['size','nunique']) == len(L)
    df = df[df['col1'].isin(m.index[np.all(m, axis=1)])]
    print (df)
       ID col1           col2
    0   1    A    P1.adv abcd
    1   2    A  P2.cmp mkmfwk
    2   3    A   P3.part 1pwf
    

    【讨论】:

    • 我认为这不是 OP 想要的
    • @jezrael - 这不是一个重复的问题。如果 col1 在 col2 中有某些值(不是 col2 中的每个值),我需要限制
    • 在您的第二个解决方案中,我遇到了内存错误。但是,在您的第一个解决方案中,如果没有缺少括号,您能告诉我吗?
    • @frozenshine - 对我来说它工作得很好,对你来说有一些语法错误吗?
    • @frozenshine - 添加了替代解决方案,希望没有内存错误。
    【解决方案3】:

    我建议使用正则表达式并计算 A 的出现次数等于 [pattern matches

    import pandas as pd
    df = pd.DataFrame({'a':[1,2,1,1,5,5],'b':["abc.more","abcde.kef","abc.mop","lop.e","lop.e","get.le"]})
    con = df.b.str.match('^(abc.m|lop.e)')
    df['c'] = con
    df['sum_c'] = df.groupby('a')['c'].transform('sum')
    df['count_a'] = df.groupby('a')['a'].transform('count')
    dff = df[df['count_a']==df['sum_c']]
    dff
    

    【讨论】:

      【解决方案4】:

      在数据框中创建一个临时列,检查 col2 中是否存在任何目标前缀。然后将它们按 col1 和 sum 分组。然后检查总和是否等于3

      ans = (df.assign(temp = df.col2.str.startswith(tuple(col2_values_should_start_with)))\
               .groupby('col1')[['temp']].sum() == 3).reset_index()
      

      最后,找到 col1 中包含 3 个起始字符串的值

      df[df['col1'].isin(list(ans[ans.temp == True].col1))]
      

      输出:

          ID   col1       col2
          1    A    P1.advabcd
          2    A  P2.cmpmkmfwk
          3    A   P3.partlpwf
      

      【讨论】:

        猜你喜欢
        • 2022-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-22
        • 1970-01-01
        • 2022-12-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多