【问题标题】:Groupby on pandas dataframe and concatenate strings with comma based on the frequency of values in a column在 pandas 数据帧上进行 Groupby,并根据列中值的频率用逗号连接字符串
【发布时间】:2018-10-19 22:21:09
【问题描述】:

这是对我的 DataFrame 结构的更新,我匆忙制定了结构,我正在检查单个用户并模拟了该结构。 @liliscent 的评论:“数据意外满足此条件”也是正确的,并且 value_counts 和 cum_sum() 解决了它。但是随后user_id也发生了变化,不同的用户如果有相同的文字,就可以有相同的meet_id。

更新的 DataFrames 结构:

   mytable = pd.DataFrame({'user_id': [ '3c', '3c', '3c', '3c','3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c', '3d',
                                 '3d', '3d', '3d', '3e', '3e', '3r', '3w', '3w', '3w', '3w'],
              'meet_id': [1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,5,6,1,2,1,1], 'text': ['abc', 'abc', 'abc', 'abc', 'abc', 'abc', 'abc',
        'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'npq', 'npq', 'npq', 'npq', 'tt', 'op', 'li', 'abc', 'xyz', 'abc', 'abc'], 'label': ['A', 'A', 'A', 'A', 'A','B', 'B', 'B', 'B', 'B',
    'C', 'C', 'A', 'G', 'H', 'H', 'H', 'A', 'A', 'B', 'E', 'G', 'B', 'B']})
   mytable =  mytable[['user_id', 'meet_id', 'text', 'label']] # ordering columns in the way I would like to be printed out.

   user_id  meet_id  text label
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     A
   3c        1      abc     B
   3c        1      abc     B
   3c        2      xyz     B
   3c        2      xyz     B
   3c        2      xyz     B
   3c        2      xyz     C
   3c        2      xyz     C
   3c        2      xyz     A
   3d        3      npq     G
   3d        3      npq     H
   3d        3      npq     H
   3d        3      npq     H
   3e        4      tt      A
   3e        5      op      A
   3r        6      li      B
   3w        1      abc     E
   3w        2      xyz     G 
   3w        1      abc     B
   3w        1      abc     B

我想在 [user_id & meet_id] 列上进行分组并连接标签列,以使该组的频率较高的标签保持不变,而第二频繁的标签将连接第一个标签,最后一个标签将连接所有标签。

更新的 DataFrame 输出是我正在寻找的

    mytable_pro = pd.DataFrame({'user_id': ['3c', '3c', '3c', '3c','3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c', '3c','3d',
                                 '3d', '3d', '3d', '3e', '3e', '3r', '3w', '3w', '3w', '3w'],
              'meet_id': [1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,5,6,1,2,1,1], 'text': ['abc', 'abc', 'abc', 'abc', 'abc', 'abc', 'abc',
        'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'xyz','npq', 'npq', 'npq', 'npq', 'tt', 'op', 'li', 'abc', 'xyz', 'abc', 'abc' ], 'label': ['A', 'A', 'A', 'A', 'A', 'B,A', 'B,A', 'B', 'B', 'B',
    'B, C', 'B, C', 'A,B,C', 'H,G', 'H', 'H', 'H', 'A', 'A', 'B', 'E,B', 'G', 'B', 'B']})
    mytable_pro = mytable_pro[['user_id', 'meet_id', 'text', 'label']] # ordering columns in the way I would like to be printed out.

这给出了:

    user_id  meet_id text  label
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc      A
   3c        1       abc     B,A
   3c        1       abc     B,A
   3c        2       xyz      B
   3c        2       xyz      B
   3c        2       xyz      B
   3c        2       xyz    B, C
   3c        2       xyz    B, C
   3c        2       xyz    A,B,C
   3d        3       npq     H,G
   3d        3       npq      H
   3d        3       npq      H
   3d        3       npq      H
   3e        4       tt       A
   3e        5       op       A
   3r        6       li       B
   3w        1       abc     E,B
   3w        2       xyz      G
   3w        1       abc      B
   3w        1       abc      B

@piRSquared 给出的答案:

    mytable.groupby('meet_id').label.value_counts().groupby('meet_id').apply(
lambda d: d.index.to_series().str[1].cumsum().str.join(', '))        

是我提出的错误问题的正确答案,非常感谢,非常抱歉。它解决了前面提到的排序问题,但如果不同的用户具有相同的 meet_id,它将无法工作。只是为了详尽无遗,如果一个组的标签频率相同,则哪个标签连接另一个标签并不重要。

它给出:

     user_id  meet_id  text       label
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc           A
   3c          1         abc        A, B
   3c          1         abc        A, B
   3c          2         xyz           B
   3c          2         xyz           B
   3c          2         xyz           B
   3c          2         xyz        B, C
   3c          2         xyz        B, C
   3c          2         xyz     B, C, A
   3d          3         npq        H, G
   3d          3         npq           H
   3d          3         npq           H
   3d          3         npq           H
   3e          4          tt           A
   3e          5          op           A
   3r          6          li           B
   3w          1         abc     A, B, E
   3w          2         xyz    B, C, A, G
   3w          1         abc        A, B
   3w          1         abc        A, B

3w 的标签已关闭,因为选择了 meet_id 的标签而忽略了 user_id 的差异。我的错!

现在,由于还必须考虑 user_id,我尝试了以下方法:

    s = mytable.groupby(['user_id', 'meet_id']).label.value_counts().groupby(['user_id, 'meet_id']).apply(
lambda d: d.index.to_series().str[1].cumsum().str.join(', '))        

这会抛出:

    AttributeError: Can only use .str accessor with string values, which use np.object_ dtype in pandas

啊!另一个小更新,实际上我的标签列中有单词。

    dummy_boo = pd.DataFrame({'user_id': ['3g', '3g', '3g'], 'meet_id': [9,9,9], 'text': ['baby', 'baby', 'baby'], 'label':['hello', 'hello', 'why']}

输出:

    user_id  meet_id  text  label
      3g        9     baby  hello
      3g        9     baby  hello
      3g        9     baby   why

应用上述代码会导致每个字符用逗号分隔。

 user_id  meet_id  text   label
  3g        9      baby  h, e, l, l, o
  3g        9      baby  h, e, l, l, o
  3g        9      baby  h, e, l, l, o, w, h, y

我需要:

    user_id  meet_id  text   label
  3g        9      baby    hello
  3g        9      baby    hello
  3g        9      baby    hello, why

标签的数据类型是对象。我们应该改用 astype 吗?非常感谢大家帮助我。

【问题讨论】:

    标签: python pandas dataframe pandas-groupby


    【解决方案1】:

    value_countscumsum

    value_counts 按降序排列

    cols = ['meet_id', 'user_id']
    s = mytable.groupby(cols).label.value_counts().groupby(cols).apply(
        lambda d: d.index.to_series().str[-1].cumsum().str.join(', ')
    )
    
    mytable.assign(label=[s.get((a, b, c)) for a, b, c in mytable[cols + ['label']].values])
    
       user_id  meet_id text    label
    0       3c        1  abc        A
    1       3c        1  abc        A
    2       3c        1  abc        A
    3       3c        1  abc        A
    4       3c        1  abc        A
    5       3c        1  abc     A, B
    6       3c        1  abc     A, B
    7       3c        2  xyz        B
    8       3c        2  xyz        B
    9       3c        2  xyz        B
    10      3c        2  xyz     B, C
    11      3c        2  xyz     B, C
    12      3c        2  xyz  B, C, A
    13      3d        3  npq     H, G
    14      3d        3  npq        H
    15      3d        3  npq        H
    16      3d        3  npq        H
    17      3e        4   tt        A
    18      3e        5   op        A
    19      3r        6   li        B
    20      3w        1  abc     B, E
    21      3w        2  xyz        G
    22      3w        1  abc        B
    23      3w        1  abc        B
    

    也包括sorted

    cols = ['meet_id', 'user_id']
    s = mytable.groupby(cols).label.value_counts().groupby(cols).apply(
        lambda d: d.index.to_series().str[-1].cumsum().apply(sorted).str.join(', ')
    )
    
    mytable.assign(label=[s.get((a, b, c)) for a, b, c in mytable[cols + ['label']].values])
    
       user_id  meet_id text    label
    0       3c        1  abc        A
    1       3c        1  abc        A
    2       3c        1  abc        A
    3       3c        1  abc        A
    4       3c        1  abc        A
    5       3c        1  abc     A, B
    6       3c        1  abc     A, B
    7       3c        2  xyz        B
    8       3c        2  xyz        B
    9       3c        2  xyz        B
    10      3c        2  xyz     B, C
    11      3c        2  xyz     B, C
    12      3c        2  xyz  A, B, C
    13      3d        3  npq     G, H
    14      3d        3  npq        H
    15      3d        3  npq        H
    16      3d        3  npq        H
    17      3e        4   tt        A
    18      3e        5   op        A
    19      3r        6   li        B
    20      3w        1  abc     B, E
    21      3w        2  xyz        G
    22      3w        1  abc        B
    23      3w        1  abc        B
    

    并针对单词而不是单个字符进行调整

    cols = ['meet_id', 'user_id']
    s = mytable.groupby(cols).label.value_counts().groupby(cols).apply(
        lambda d: d.index.to_series().str[-1].add('|').cumsum().apply(
            lambda e: ', '.join(sorted(e.strip('|').split('|')))
        )
    )
    
    mytable.assign(label=[s.get((a, b, c)) for a, b, c in mytable[cols + ['label']].values])
    

    旧答案

    带有transform 和自定义累积唯一函数

    from collections import Counter
    
    def cum_unique(x):
        return pd.Series(list(map(
            Counter, x
        ))).cumsum().str.join(', ')
    
    mytable.assign(label=mytable.groupby('meet_id').label.transform(cum_unique))
    
       user_id  meet_id text    label
    0       3c        1  abc        A
    1       3c        1  abc        A
    2       3c        1  abc        A
    3       3c        1  abc        A
    4       3c        1  abc        A
    5       3c        1  abc     A, B
    6       3c        1  abc     A, B
    7       3c        2  xyz        B
    8       3c        2  xyz        B
    9       3c        2  xyz        B
    10      3c        2  xyz     B, C
    11      3c        2  xyz     B, C
    12      3c        2  xyz  B, C, A
    

    缩短版

    mytable.assign(label=mytable.groupby('meet_id').label.transform(
        lambda x: pd.Series(list(map(Counter, x))).cumsum().str.join(', ')
    ))
    

    每条评论

    liliscent

    我们可以先按meet_id和组大小排序

    sizes = mytable.groupby(['meet_id', 'label']).label.transform('size')
    
    m1 = mytable.assign(sizes=sizes).sort_values(
        ['meet_id', 'sizes'], ascending=[True, False]).drop('sizes', 1)
    m1
    
    m1.assign(label=m1.groupby('meet_id').label.transform(
        lambda x: pd.Series(list(map(Counter, x))).cumsum().str.join(', ')
    )).reindex(mytable.index)
    

    【讨论】:

    • 您好,先生,您能帮帮我吗,我真的找不到问题在这里mytable.groupby(['user_id', 'meet_id', 'text'])['label'].apply(lambda x : x.rolling(len(x),min_periods=1).apply(lambda x : ','.join(list(x))))
    • OP 希望“该组的更高频率保持不变”,而不是按原始顺序。 OP 的数据意外地满足了这个条件。奇怪的是,这里的所有答案似乎都有缺陷……
    【解决方案2】:

    您可以尝试以下方法:

    mytable['label'] = (mytable.groupby('meet_id')
                        .label.transform(lambda x: list(x.cumsum()))
                        .apply(set))
    
    >>> mytable
       user_id  meet_id text      label
    0       3c        1  abc        {A}
    1       3c        1  abc        {A}
    2       3c        1  abc        {A}
    3       3c        1  abc        {A}
    4       3c        1  abc        {A}
    5       3c        1  abc     {A, B}
    6       3c        1  abc     {A, B}
    7       3c        2  xyz        {B}
    8       3c        2  xyz        {B}
    9       3c        2  xyz        {B}
    10      3c        2  xyz     {C, B}
    11      3c        2  xyz     {C, B}
    12      3c        2  xyz  {C, B, A}
    

    如果您想摆脱 set 数据类型并将其作为字符串(如您所需的输出),您可以应用 ', '.join(sorted(set(x)))) 而不是简单的 set(感谢 @Wen 和 @ScottBoston ):

    mytable['label'] = (mytable.groupby('meet_id')
                        .label.transform(lambda x: list(x.cumsum()))
                        .apply(lambda x: ', '.join(sorted(set(x)))))
    >>> mytable
       user_id  meet_id text    label
    0       3c        1  abc        A
    1       3c        1  abc        A
    2       3c        1  abc        A
    3       3c        1  abc        A
    4       3c        1  abc        A
    5       3c        1  abc     A, B
    6       3c        1  abc     A, B
    7       3c        2  xyz        B
    8       3c        2  xyz        B
    9       3c        2  xyz        B
    10      3c        2  xyz     B, C
    11      3c        2  xyz     B, C
    12      3c        2  xyz  A, B, C
    

    【讨论】:

    • 添加加入可能吗?
    • @Wen,是的,你是对的,这样它与预期的输出相匹配,虽然我实际上更愿意将它作为一个集合来我认为......谢谢,作为编辑添加:)
    • (mytable.groupby('meet_id') .label.transform(lambda x: list(x.cumsum())) .apply(lambda x: sorted(set(x)))) 获取 OP 的排序列表。我喜欢这个解决方案。 +1
    • 我没有在 lambda x 中使用 list 得到了相同的结果:
    【解决方案3】:

    编辑:好的更简单的解决方案:

    mytable['label'] = mytable.groupby(['user_id','meet_id','text'])['label']\
           .apply(lambda x: x.cumsum()).apply(lambda x: sorted(set(x)))
    

    我的丑陋尝试:

    mytable['label'] = mytable.groupby(['user_id','meet_id','text'])['label']\
          .apply(lambda x: x.cumsum().str.extractall('(.)')\
                            .groupby(level=0)[0].apply(lambda x: sorted(set(x))))
    

    输出:

       user_id  meet_id text      label
    0       3c        1  abc        [A]
    1       3c        1  abc        [A]
    2       3c        1  abc        [A]
    3       3c        1  abc        [A]
    4       3c        1  abc        [A]
    5       3c        1  abc     [A, B]
    6       3c        1  abc     [A, B]
    7       3c        2  xyz        [B]
    8       3c        2  xyz        [B]
    9       3c        2  xyz        [B]
    10      3c        2  xyz     [B, C]
    11      3c        2  xyz     [B, C]
    12      3c        2  xyz  [A, B, C]
    

    【讨论】:

      猜你喜欢
      • 2020-05-19
      • 2015-09-09
      • 2022-06-21
      • 1970-01-01
      • 2020-07-28
      • 2015-11-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多