【问题标题】:Adding a pandas column based on grouped counts of another column?根据另一列的分组计数添加熊猫列?
【发布时间】:2015-01-08 18:37:50
【问题描述】:

如果我有一个 pandas 数据框,其中包含:

    Visited   PersonId
0   GB        1
1   US        1
2   US        1
3   GB        1
4   DE        1
5   CN        2
6   US        2
7   GB        3
8   GB        4

添加一个包含每个 PersonId 访问的唯一国家/地区计数的新列的最直接方法是什么?

例如,对于上述情况,人 1 访问了 3 个不同的国家。对于上述数据,添加新列后的新数据框应如下所示:

    Visited   PersonId    CountryCount
0   GB        1           3
1   US        1           3
2   US        1           3
3   GB        1           3
4   DE        1           3
5   CN        2           2
6   US        2           2
7   GB        3           1
8   GB        4           1

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    这可行,但我觉得有更好的方法

    In [104]:
    
    df['CountryCount'] = df['PersonId'].map(df.groupby(['PersonId'])['Visited'].unique().apply(len))
    df
    Out[104]:
      Visited  PersonId  CountryCount
    0      GB         1             3
    1      US         1             3
    2      US         1             3
    3      GB         1             3
    4      DE         1             3
    5      CN         2             2
    6      US         2             2
    7      GB         3             1
    8      GB         4             1
    

    【讨论】:

    • 不错。我一直在寻找类似 SQL 窗口函数的东西,但还没有找到任何东西
    • .nunique 也可以:df['PersonId'].map(df.groupby(['PersonId'])['Visited'].nunique())
    • 是的,抱歉,在我阅读了您的好答案后,我忘记在发布前刷新页面了。
    【解决方案2】:

    当您想跨组“广播”时,通常使用transform

    >>> df["CountryCount"] = df.groupby("PersonId")["Visited"].transform(pd.Series.nunique)
    >>> df
      Visited  PersonId CountryCount
    0      GB         1            3
    1      US         1            3
    2      US         1            3
    3      GB         1            3
    4      DE         1            3
    5      CN         2            2
    6      US         2            2
    7      GB         3            1
    8      GB         4            1
    

    【讨论】:

    • 我认为 transform 在这里是一种正确的使用方式,如果我们使用unique().size而不是nunique(),这也是最快的
    【解决方案3】:

    不知道这是否可以更优雅,但这可行

    >>> g = df.groupby('PersonId')['Visited'].nunique().reset_index()
    >>> g.columns = ['PersonId', 'CountryCount']
    >>> pd.merge(df, g)
      Visited  PersonId  CountryCount
    0      GB         1             3
    1      US         1             3
    2      US         1             3
    3      GB         1             3
    4      DE         1             3
    5      CN         2             2
    6      US         2             2
    7      GB         3             1
    8      GB         4             1
    

    或者,正如@EdChum 在 cmets 中建议的那样,它可以缩短为

    df['CountryCount'] = df['PersonId'].map(df.groupby('PersonId')['Visited'].nunique())
    

    以防万一,我检查了所有答案的执行时间。虽然我认为这在 OP 案例中并不重要,但事实证明 @EdChum 方法在这里显然是赢家:

    In [7]: %timeit df["CountryCount"] = df.groupby("PersonId")["Visited"].transform(pd.Series.nunique)
    100 loops, best of 3: 2.32 ms per loop
    
    In [8]: %timeit df['CountryCount'] = df['PersonId'].map(df.groupby('PersonId')['Visited'].nunique())
    100 loops, best of 3: 2.52 ms per loop
    
    In [9]: %timeit df['CountryCount'] = df['PersonId'].map(df.groupby(['PersonId'])['Visited'].unique().apply(len))
    1000 loops, best of 3: 1.29 ms per loop
    

    经过额外的测试,我发现@DSM 和@EdChums 的组合运行得更快:)

    In [26]: %timeit df["CountryCount"] = df.groupby("PersonId")["Visited"].transform(lambda x: x.unique().size)
    1000 loops, best of 3: 952 µs per loop
    

    Here'sgithub 上的某种相关问题。

    【讨论】:

    • 您可以缩短为 df['CountryCount'] = df['PersonId'].map(df.groupby('PersonId')['Visited'].nunique()) 并避免合并
    • 嗯,是的。那么我们的答案将几乎相同:)
    • 我认为使用nunique 比调用apply(len) 更好;) +1
    • @RomanPekar 恕我直言,我认为您应该编辑以避免合并部分并放置 nunique(),这看起来很棒。
    • @EdChum 签出时间,nunique()apply(len) 差:)
    猜你喜欢
    • 2021-06-25
    • 2019-12-27
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 2022-11-18
    • 2018-07-24
    相关资源
    最近更新 更多