以this answer为基础,并给予
>>> df
columnA columnB columnC
0 cat1 3 400
1 cat1 2 20
2 cat1 5 3029
3 cat2 1 492
4 cat2 4 30
5 cat3 2 203
6 cat3 6 402
7 cat3 4 391
>>> df.groupby(['columnA']).agg({'columnA':'size','columnB':'min'}).rename(columns={'columnA':'size'})
size min
columnA
cat1 3 2
cat2 2 1
cat3 3 2
我想获得一个DataFrame,它还包含columnC的值,对应于(在同一行)显示的columnB最小值,即:
size min columnC
columnA
cat1 3 2 20
cat2 2 1 492
cat3 3 2 203
当然,这只适用于那些从组中选择一个值而不是“聚合”(如总和或平均值)的聚合函数(如min或max)。
有任何线索吗?
提前致谢。
答案 0 :(得分:3)
由于您要查找的结果基本上是['columnA', 'columnB']
上的联接,因此您可以使用
result = pd.merge(result, df, on=['columnA', 'columnB'], how='left')
如果我们使用正确的列名设置result
:
import pandas as pd
df = pd.DataFrame(
{'columnA': ['cat1', 'cat1', 'cat1', 'cat2', 'cat2', 'cat3', 'cat3', 'cat3'],
'columnB': [3, 2, 5, 1, 4, 2, 6, 4],
'columnC': [400, 20, 3029, 492, 30, 203, 402, 391]})
result = df.groupby('columnA').agg({'columnA':'size', 'columnB':'min'})
result = result.rename(columns={'columnA':'size'})
result = result.reset_index()
result = pd.merge(result, df, on=['columnA', 'columnB'], how='left')
result = result.set_index('columnA')
result = result.rename(columns={'columnB':'min'})
print(result)
产量
min size columnC
columnA
cat1 2 3 20
cat2 1 2 492
cat3 2 3 203
您可能希望使用pd.merge
代替groupby/apply
的原因是因为groupby/apply
为每个组调用了一个函数。如果有很多组,这可能会很慢。
例如,如果您有一个包含1000个组的10000行DataFrame,
import numpy as np
import pandas as pd
N = 10000
df = pd.DataFrame(
{'columnA': np.random.choice(['cat{}'.format(i) for i in range(N//10)],
size=N),
'columnB': np.random.randint(10, size=N),
'columnC': np.random.randint(100, size=N)})
然后using_merge
(下方)比using_apply
快〜250倍:
def using_merge(df):
result = df.groupby('columnA').agg({'columnA':'size', 'columnB':'min'})
result = result.rename(columns={'columnA':'size'})
result = result.reset_index()
result = pd.merge(result, df, on=['columnA', 'columnB'], how='left')
result = result.set_index('columnA')
result = result.rename(columns={'columnB':'min'})
return result
def using_apply(df):
return (df.groupby("columnA")
.apply(lambda g: (g[g.columnB == g.columnB.min()]
.assign(size = g.columnA.size)
.rename(columns={'columnB': 'min'})
.drop('columnA', 1)))
.reset_index(level=1, drop=True))
In [80]: %timeit using_merge(df)
100 loops, best of 3: 7.99 ms per loop
In [81]: %timeit using_apply(df)
1 loop, best of 3: 2.06 s per loop
In [82]: 2060/7.99
Out[82]: 257.8222778473091
答案 1 :(得分:2)
您可以使用idxmin
来提取这些行的行索引:
In [11]: g = df.groupby(['columnA'])
In [12]: res = g.agg({'columnA': 'size', 'columnB': 'min'})
In [13]: g['columnB'].idxmin()
Out[13]:
columnA
cat1 1
cat2 3
cat3 5
Name: columnB, dtype: int64
In [14]: df["columnC"].iloc[g['columnB'].idxmin()]
Out[14]:
1 20
3 492
5 203
Name: columnC, dtype: int64
您可以将其作为列添加到res
:
In [15]: res["columnC"] = df["columnC"].iloc[g['columnB'].idxmin()].values
In [16]: res
Out[16]:
columnA columnB columnC
columnA
cat1 3 2 20
cat2 2 1 492
cat3 3 2 203