【问题标题】:Pandas groupby: How to get a union of stringsPandas groupby:如何获得字符串的联合
【发布时间】:2013-07-24 08:24:28
【问题描述】:

我有一个这样的数据框:

   A         B       C
0  1  0.749065    This
1  2  0.301084      is
2  3  0.463468       a
3  4  0.643961  random
4  1  0.866521  string
5  2  0.120737       !

打电话

In [10]: print df.groupby("A")["B"].sum()

将返回

A
1    1.615586
2    0.421821
3    0.463468
4    0.643961

现在我想对“C”列执行“相同”操作。因为该列包含字符串,所以 sum() 不起作用(尽管您可能认为它会连接字符串)。我真正想看到的是每个组的字符串列表或集合,即

A
1    {This, string}
2    {is, !}
3    {a}
4    {random}

我一直在想办法做到这一点。

Series.unique() (http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html) 不起作用,尽管

df.groupby("A")["B"]

是一个

pandas.core.groupby.SeriesGroupBy object

所以我希望任何系列方法都可以工作。有什么想法吗?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    按照@Erfan 的好回答,大多数时候在分析聚合值时,您需要这些现有字符值的唯一可能组合:

    unique_chars = lambda x: ', '.join(x.unique())
    (df
     .groupby(['A'])
     .agg({'C': unique_chars}))
    

    【讨论】:

    • 为了确认我的理解,在 lambda 中使用的.unique() 确定包含所有唯一值的系列是否在指定的按值组内?如果是这样,pandas 是否将每个唯一值(按值分组)临时存储在 unique_chars 变量之外的某个位置,以确定哪些值实际上是唯一的,然后最终将值分配给变量?也许值得一个新的 SO 问题。
    • @deesolie unique_chars 是向量 x 的函数。 Lambda 是在 python 中创建单行函数的简称。然后将该函数与每个列向量一起用作输入。
    • 我了解 lambda 函数。假设我们有df.groupby(['A'])['B'].transform(lambda x: ','.join(x.unique())),我很好奇 pandas 如何按系列临时存储组中的每个值,以检查进程值是否已经在连接的字符串中。同样,这可能是一个更大的问题,因此应该作为一个新的 SO 问题提出。
    • @deesolie 没有熊猫不会检查值是否已经在连接的字符串中。你把顺序弄错了。这是一个矢量化操作。首先,unique 返回唯一字符的向量。其次,.join 将向量的所有元素连接在一起。
    • 感谢 Paul,以这种方式查看操作顺序更容易理解这条线的运作方式。
    【解决方案2】:
    In [4]: df = read_csv(StringIO(data),sep='\s+')
    
    In [5]: df
    Out[5]: 
       A         B       C
    0  1  0.749065    This
    1  2  0.301084      is
    2  3  0.463468       a
    3  4  0.643961  random
    4  1  0.866521  string
    5  2  0.120737       !
    
    In [6]: df.dtypes
    Out[6]: 
    A      int64
    B    float64
    C     object
    dtype: object
    

    当您应用自己的函数时,不会自动排除非数字列。不过,这比将.sum() 应用于groupby

    In [8]: df.groupby('A').apply(lambda x: x.sum())
    Out[8]: 
       A         B           C
    A                         
    1  2  1.615586  Thisstring
    2  4  0.421821         is!
    3  3  0.463468           a
    4  4  0.643961      random
    

    sum 默认连接

    In [9]: df.groupby('A')['C'].apply(lambda x: x.sum())
    Out[9]: 
    A
    1    Thisstring
    2           is!
    3             a
    4        random
    dtype: object
    

    你可以做你想做的事

    In [11]: df.groupby('A')['C'].apply(lambda x: "{%s}" % ', '.join(x))
    Out[11]: 
    A
    1    {This, string}
    2           {is, !}
    3               {a}
    4          {random}
    dtype: object
    

    在整个框架上执行此操作,一次一组。关键是返回一个Series

    def f(x):
         return Series(dict(A = x['A'].sum(), 
                            B = x['B'].sum(), 
                            C = "{%s}" % ', '.join(x['C'])))
    
    In [14]: df.groupby('A').apply(f)
    Out[14]: 
       A         B               C
    A                             
    1  2  1.615586  {This, string}
    2  4  0.421821         {is, !}
    3  3  0.463468             {a}
    4  4  0.643961        {random}
    

    【讨论】:

    • 看来这些操作现在已经矢量化,不再需要applylambdas。我来到这里想知道为什么 pandas 实际上 concats 而不是在求和字符串时返回错误。
    • 如果您尝试连接字符串并在其间添加一个字符,下面@voithos 推荐的 .agg 解决方案比此处推荐的 .apply 快得多。在我的测试中,我的速度提高了 5-10 倍。
    【解决方案3】:

    pandas >= 0.25.0 命名聚合

    自 pandas 0.25.0 版以来,我们已经命名了聚合,我们可以在其中分组、聚合,同时为我们的列分配新名称。这样我们就不会得到 MultiIndex 列,并且考虑到它们包含的数据,列名更有意义:


    聚合并获取字符串列表

    grp = df.groupby('A').agg(B_sum=('B','sum'),
                              C=('C', list)).reset_index()
    
    print(grp)
       A     B_sum               C
    0  1  1.615586  [This, string]
    1  2  0.421821         [is, !]
    2  3  0.463468             [a]
    3  4  0.643961        [random]
    

    聚合和连接字符串

    grp = df.groupby('A').agg(B_sum=('B','sum'),
                              C=('C', ', '.join)).reset_index()
    
    print(grp)
       A     B_sum             C
    0  1  1.615586  This, string
    1  2  0.421821         is, !
    2  3  0.463468             a
    3  4  0.643961        random
    

    【讨论】:

      【解决方案4】:

      你可以试试这个:

      df.groupby('A').agg({'B':'sum','C':'-'.join})
      

      【讨论】:

      • 来自评论:您能否为您的答案添加更多解释?
      • Groupby 应用于“A”列,并且使用 agg 函数,我可以在不同的列上使用不同的函数,例如对“C”列中的元素求和,在插入“C”列时连接“C”列中的元素- '在单词之间
      【解决方案5】:

      如果您想覆盖数据框中的 B 列,这应该可以:

          df = df.groupby('A',as_index=False).agg(lambda x:'\n'.join(x))
      

      【讨论】:

        【解决方案6】:

        一个简单的解决方案是:

        >>> df.groupby(['A','B']).c.unique().reset_index()
        

        【讨论】:

        • 如果有人有兴趣将列表的内容加入字符串df.groupby(['A','B']).c.unique().apply(lambda x: ';'.join(x)).reset_index()
        【解决方案7】:

        您可以使用aggregate(或agg)函数来连接这些值。 (未经测试的代码)

        df.groupby('A')['B'].agg(lambda col: ''.join(col))
        

        【讨论】:

        • 确实有效。惊人的。正如@voithos 提到的“未经测试”,我不是很乐观。位我将他的版本作为 agg 字典中的条目进行了测试,它按预期工作: .agg({'tp': 'sum', 'BaseWgt': 'max','TP_short':lambda col: ', '.join (col)}) 成就了我的一天
        • 如果您尝试将字符串与某种类型的分隔符连接在一起,我发现这个 .agg 建议比 .apply 快得多。对于 600k+ 文本字符串的数据集,我得到相同结果的速度快了 5-10 倍。
        【解决方案8】:

        您可以使用apply 方法将任意函数应用于分组数据。所以如果你想要一套,请申请set。如果你想要一个列表,请申请list

        >>> d
           A       B
        0  1    This
        1  2      is
        2  3       a
        3  4  random
        4  1  string
        5  2       !
        >>> d.groupby('A')['B'].apply(list)
        A
        1    [This, string]
        2           [is, !]
        3               [a]
        4          [random]
        dtype: object
        

        如果你想要别的东西,只需要写一个函数来做你想做的事,然后apply那个。

        【讨论】:

        • 工作正常,但缺少 A 列。
        • @VineeshTP:列 A 被用作分组列,因此它在索引中,如您在示例中所见。您可以使用.reset_index() 将其作为列恢复。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-31
        • 2014-09-08
        • 1970-01-01
        • 2023-03-31
        • 2020-05-19
        • 2018-03-07
        相关资源
        最近更新 更多