【问题标题】:Pandas 'count(distinct)' equivalent熊猫“计数(不同)”等效
【发布时间】:2023-03-21 20:26:01
【问题描述】:

我正在使用 Pandas 作为数据库替代品,因为我有多个数据库(OracleSQL Server 等),并且我无法将命令序列转换为 SQL 等效项。

我在 DataFrame 中加载了一个包含一些列的表:

YEARMONTH, CLIENTCODE, SIZE, etc., etc.

在 SQL 中,计算每年不同客户的数量是:

SELECT count(distinct CLIENTCODE) FROM table GROUP BY YEARMONTH;

结果是

201301    5000
201302    13245

如何在 Pandas 中做到这一点?

【问题讨论】:

  • 我已经完成了 table.groupby(['YEARMONTH'])['CLIENTCODE'].unique() 并带有由 YEARMONTH 索引的两个系列和所有唯一值。如何计算每个系列的值的数量?
  • 对于某些人来说,value_counts 可能是您正在寻找的答案:pandas.pydata.org/pandas-docs/stable/generated/…

标签: python pandas count group-by distinct


【解决方案1】:

我相信这就是你想要的:

table.groupby('YEARMONTH').CLIENTCODE.nunique()

例子:

In [2]: table
Out[2]: 
   CLIENTCODE  YEARMONTH
0           1     201301
1           1     201301
2           2     201301
3           1     201302
4           2     201302
5           2     201302
6           3     201302

In [3]: table.groupby('YEARMONTH').CLIENTCODE.nunique()
Out[3]: 
YEARMONTH
201301       2
201302       3

【讨论】:

  • 如果我有多个列想要一起唯一,比如 .drop_duplicates(subset=['col1','col2']) 怎么办?
  • 如何访问这个唯一计数。因为没有列名
  • 非常感谢,我在重采样的输出中使用了这种样式。 df_watch_record.resample('M').user.nunique() 统计每月观看电影的唯一用户数。
  • 并使用 table.groupby('YEARMONTH').CLIENTCODE.nunique().sort_values(ascending=False) 对它们进行排序
  • 是否可以将其应用于多个列?现在在示例中,只选择了一列。
【解决方案2】:

这是另一种方法,它更简单。假设您的数据框名称为daat,列名称为YEARMONTH

daat.YEARMONTH.value_counts()

【讨论】:

  • 我喜欢这个答案。如果我的列名有“。”,我该如何使用此方法在里面(例如'ck.Class')?谢谢
  • daat['ck.Class'].value_counts()
  • 这并没有解决所提出的问题。
  • 计算每组内的观察次数,而不是每组具有的特定列的唯一值。
  • 这是错误答案;它不反映问题中的DISTINCT 要求!此外,它不包括 NaN! 的计数
【解决方案3】:

有趣的是,len(unique()) 通常比 nunique() 快几倍 (3x-15x)。

【讨论】:

  • 你是这个意思? .CLIENTCODE.apply(lambda x: len(x.unique())),来自here
  • @user32185 您必须将其放入带有 lambda 的 apply 调用中。例如,df.groupby('YEARMONTH')['CLIENTCODE'].apply(lambda x: x.unique().shape[0])
  • 语法不完全清楚,我用len(df['column'].unique()) 不需要lambda函数
  • 我收到了来自Chen's 评论的TypeError: object of type 'method' has no len()3novak's 为我工作。
【解决方案4】:

我也在使用nunique,但如果您必须使用像'min', 'max', 'count' or 'mean' 等这样的聚合函数,这将非常有帮助。

df.groupby('YEARMONTH')['CLIENTCODE'].transform('nunique') #count(distinct)
df.groupby('YEARMONTH')['CLIENTCODE'].transform('min')     #min
df.groupby('YEARMONTH')['CLIENTCODE'].transform('max')     #max
df.groupby('YEARMONTH')['CLIENTCODE'].transform('mean')    #average
df.groupby('YEARMONTH')['CLIENTCODE'].transform('count')   #count

【讨论】:

    【解决方案5】:

    列的不同以及其他列上的聚合

    要获取任何列的不同数量的值(在您的情况下为CLIENTCODE),我们可以使用nunique。我们可以将输入作为字典传递给agg 函数,以及其他列上的聚合:

    grp_df = df.groupby('YEARMONTH').agg({'CLIENTCODE': ['nunique'],
                                          'other_col_1': ['sum', 'count']})
    
    # to flatten the multi-level columns
    grp_df.columns = ["_".join(col).strip() for col in grp_df.columns.values]
    
    # if you wish to reset the index
    grp_df.reset_index(inplace=True)
    

    【讨论】:

    • 我认为这个答案是最好的,因为它更接近您在 SQL 中使用不同计数的方式。如果您使用 Pandas agg 的最新语法,您甚至可以跳过展平步骤。 grp_df = df.groupby('YEARMONTH').agg(CLIENTCODE_UNIQ_CNT = ('CLIENTCODE', 'nunique'), other_col_1_sum = ('other_col_1', 'sum'), other_col_1_cnt = ('other_col_1', 'count'))
    • 哦,太好了,我不知道这种新语法。感谢您的评论:)
    【解决方案6】:

    使用crosstab,这将返回比groupby更多的信息nunique

    pd.crosstab(df.YEARMONTH,df.CLIENTCODE)
    Out[196]:
    CLIENTCODE  1  2  3
    YEARMONTH
    201301      2  1  0
    201302      1  2  1
    

    稍加修改后,结果如下:

    pd.crosstab(df.YEARMONTH,df.CLIENTCODE).ne(0).sum(1)
    Out[197]:
    YEARMONTH
    201301    2
    201302    3
    dtype: int64
    

    【讨论】:

    • 如何将其导出为两列 YEARMONTHcount。我也可以按降序设置计数吗?
    【解决方案7】:

    这是一种在多列上计数不同的方法。让我们有一些数据:

    data = {'CLIENT_CODE':[1,1,2,1,2,2,3],
            'YEAR_MONTH':[201301,201301,201301,201302,201302,201302,201302],
            'PRODUCT_CODE': [100,150,220,400,50,80,100]
           }
    table = pd.DataFrame(data)
    table
    
    CLIENT_CODE YEAR_MONTH  PRODUCT_CODE
    0   1       201301      100
    1   1       201301      150
    2   2       201301      220
    3   1       201302      400
    4   2       201302      50
    5   2       201302      80
    6   3       201302      100
    

    现在,列出感兴趣的列并以稍微修改的语法使用 groupby:

    columns = ['YEAR_MONTH', 'PRODUCT_CODE']
    table[columns].groupby(table['CLIENT_CODE']).nunique()
    

    我们得到:

    YEAR_MONTH  PRODUCT_CODE CLIENT_CODE
    1           2            3
    2           2            3
    3           1            1
    

    【讨论】:

      【解决方案8】:

      有了新的 Pandas 版本,作为数据框很容易获取:

      unique_count = pd.groupby(['YEARMONTH'], as_index=False).agg(uniq_CLIENTCODE=('CLIENTCODE', pd.Series.count))
      

      【讨论】:

      • 版本号是多少?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
      【解决方案9】:

      现在您还可以在 Python 中使用 dplyr 语法来做到这一点:

      >>> from datar.all import f, tibble, group_by, summarise, n_distinct
      >>>
      >>> data = tibble(
      ...     CLIENT_CODE=[1,1,2,1,2,2,3],
      ...     YEAR_MONTH=[201301,201301,201301,201302,201302,201302,201302]
      ... )
      >>>
      >>> data >> group_by(f.YEAR_MONTH) >> summarise(n=n_distinct(f.CLIENT_CODE))
         YEAR_MONTH       n
            <int64> <int64>
      0      201301       2
      1      201302       3
      

      【讨论】:

      • 什么是“dplyr 语法”?你能add 一个(权威的)引用它(为了上下文)吗? (但没有“编辑:”、“更新:”或类似的 - 答案应该看起来好像是今天写的。)
      【解决方案10】:

      创建一个数据透视表并使用 nunique 系列函数:

      ID = [ 123, 123, 123, 456, 456, 456, 456, 789, 789]
      domain = ['vk.com', 'vk.com', 'twitter.com', 'vk.com', 'facebook.com',
                'vk.com', 'google.com', 'twitter.com', 'vk.com']
      df = pd.DataFrame({'id':ID, 'domain':domain})
      fp = pd.pivot_table(data=df, index='domain', aggfunc=pd.Series.nunique)
      print(fp)
      

      输出:

                     id
      domain
      facebook.com   1
      google.com     1
      twitter.com    2
      vk.com         3
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-20
      • 2020-07-16
      • 1970-01-01
      • 1970-01-01
      • 2019-08-19
      • 2017-12-20
      • 2016-06-04
      相关资源
      最近更新 更多