假设我有以下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
任何想法怎么做?
答案 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)
将groupby
与agg
,最后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