根据Python Pandas中的其他列对列进行分组

时间:2018-02-20 09:08:44

标签: python pandas

假设我有以下pd.DataFrame

Name | Color
------------------------------
John | Blue
Greg | Red
John | Yellow
Greg | Red
Greg | Blue

我想得到一个表格,列出每个名字的不同颜色 - 多少及其价值。意思是这样的:

Name | Distinct | Values
--------------------------------------
John |   2      | Blue, Yellow
Greg |   2      | Red, Blue

任何想法怎么做?

3 个答案:

答案 0 :(得分:5)

使用groupby + agg,传递自定义list聚合函数:

f = [
        ('Distinct', 'nunique'), 
        ('Values', lambda x: ', '.join(x.unique()))
]

df.groupby('Name').Color.agg(f).reset_index()

   Name  Distinct        Values
0  Greg         2     Red, Blue
1  John         2  Blue, Yellow

<强>计时

首先,设置 -

df = pd.DataFrame(
      np.random.randint(0, 1000, (10000, 2)).astype(str), columns=['Name', 'Color']
)

接下来,时间安排。似乎pd.Series.unique不合理地慢(慢4倍)。为了提高性能,我将使用np.unique代替:

# in this answer

%%timeit
f = [
        ('Distinct', 'nunique'), 
        ('Values', lambda x: ', '.join(np.unique(x.values).tolist()))
]

df.groupby('Name').Color.agg(f).reset_index()

122 ms ± 1.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

使用lambda x: ', '.join(x.unique())会导致4倍速放缓。使用set可以更快地对这些数据进行一些削减,但这实际上取决于。

# @jpp

%%timeit
v = df.groupby('Name')['Color'].apply(set).reset_index()
v['Distinct'] = v['Color'].map(len)
v['Color'] = v['Color'].map(', '.join)

219 ms ± 1.83 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# @jezrael

%%timeit
(df.groupby('Name')['Color'].agg(['nunique', lambda x: ', '.join(set(x))])
        .rename(columns={'nunique':'Distinct', '<lambda>':'Values'})
        .reset_index())

118 ms ± 4.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

与数据的性能差异很大 ,您可能希望在决定使用什么之前,先在自己的数据上计算所有解决方案。

答案 1 :(得分:4)

groupbyagg,最后rename列和reset_index一起使用:

df = (df.groupby('Name')['Color'].agg(['nunique', lambda x: ', '.join(set(x))])
        .rename(columns={'nunique':'Distinct', '<lambda>':'Values'})
        .reset_index())
print (df)
   Name  Distinct        Values
0  Greg         2     Blue, Red
1  John         2  Blue, Yellow

答案 2 :(得分:3)

避免效率低lambda的一种方法:

df = df.groupby('Name')['Color'].apply(set).reset_index()

df['Distinct'] = df['Color'].map(len)
df['Color'] = df['Color'].map(', '.join)

#    Name         Color  Distinct
# 0  Greg     Red, Blue         2
# 1  John  Yellow, Blue         2

顺便说一句,我注意到了方法链和/或单线计算的趋势。如果性能不是问题,我建议使用您发现更有用/可读的内容。就个人而言,我更喜欢将计算分为3个部分。

效果基准

import pandas as pd
import numpy as np
from random import choice
from string import ascii_uppercase

df = pd.DataFrame({'Name': np.random.randint(0, 100, 10000),
                   'Color': [''.join(choice(ascii_uppercase) for _ in range(2)) for k in range(10000)]})

def jpp(df):
    df = df.groupby('Name')['Color'].apply(set).reset_index()

    df['Distinct'] = df['Color'].map(len)
    df['Color'] = df['Color'].map(', '.join)
    return df

def jez(df):
    return df.groupby('Name')['Color'].agg(['nunique', lambda x: ', '.join(set(x))])\
            .rename(columns={'nunique':'Distinct', '<lambda>':'Values'})\
            .reset_index()

def cs(df):
    f = [
            ('Distinct', 'nunique'), 
            ('Values', lambda x: ', '.join(x.unique()))
    ]

    return df.groupby('Name').Color.agg(f).reset_index()

%timeit jpp(df)  # 100 loops, best of 3: 15.7 ms per loop
%timeit jez(df)  # 10 loops, best of 3: 22.9 ms per loop
%timeit cs(df)   # 10 loops, best of 3: 27.1 ms per loop