Поскольку результат вы ищете, по существу объединение на ['columnA', 'columnB']
, вы можете получить желаемый DataFrame с помощью
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
вызывает функцию для каждой группы. Если есть много групп, это может быть медленным.
Например, если у вас есть 10000-рядная DataFrame с 1000 групп,
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
(ниже) составляет ~ 250x быстрее, чем using_apply
:
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
Это аккуратный, что 'как = 'left'' также заботится о повторяющихся записей, всегда стоит помнить! (Это в 10 раз быстрее, чем idxmin с вашими испытаниями.) –