熊猫& # 39;计数(不同的)& # 39;等效

我使用熊猫作为数据库替代品,因为我有多个数据库(甲骨文SQL Server等),我无法使一个SQL等价命令序列。

我有一个表加载在一个DataFrame与一些列:

YEARMONTH, CLIENTCODE, SIZE, etc., etc.

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

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

结果就是

201301    5000
201302    13245

我如何在熊猫中做到这一点?

650793 次浏览

我相信这就是你想要的:

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

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

这是另一种简单得多的方法。假设你的数据帧名为daat,列名为YEARMONTH:

daat.YEARMONTH.value_counts()

使用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

我也使用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

使用新的Pandas版本,可以很容易地获得数据帧:

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

这里有一种方法可以使多个列上的计数不同。让我们看一些数据:

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

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

为了获得任意列(在你的例子中是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)

现在你也可以在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

创建一个数据透视表并使用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
    要获得每年不同客户端的数量和大小(即多个列的唯一值的数量),使用列列表:
    df.groupby('YEARMONTH')[['CLIENTCODE', 'SIZE']].nunique()
    
    实际上,上述代码的结果可以使用df上的SQL语法获得,使用pandasql(一个基于pandas的模块,允许您使用SQL语法查询pandas dataframe)。
    #! pip install pandasql
    from pandasql import sqldf
    sqldf("""
    SELECT COUNT(DISTINCT CLIENTCODE),
    COUNT(DISTINCT SIZE)
    FROM df
    GROUP BY YEARMONTH
    """)
    
  • 如果你想保持YEARMONTH作为一个列,即类似于下面的SQL查询
    SELECT YEARMONTH,
    COUNT(DISTINCT CLIENTCODE),
    COUNT(DISTINCT SIZE)
    FROM df
    GROUP BY YEARMONTH
    
    (将as_index设置为False):
    df.groupby('YEARMONTH', as_index=False)[['CLIENTCODE', 'SIZE']].nunique()
    
  • 如果你需要为聚合列设置自定义名称,即类似于下面的SQL查询:
    SELECT YEARMONTH,
    COUNT(DISTINCT CLIENTCODE) AS `No. clients`,
    COUNT(DISTINCT SIZE) AS `No. size`
    FROM df
    GROUP BY YEARMONTH
    
    在pandas中使用命名聚合:
    (
    df.groupby('YEARMONTH', as_index=False)
    .agg(**{'No. clients':('CLIENTCODE', 'nunique'),
    'No. size':('SIZE', 'nunique')})
    )