2017-02-22 28 views
2

Основываясь на this answer, а также учитывая, чтоПолучение соответствующего значения columnC в то время как группировка по колонку А и агрегирование по columnB

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

конечно, это возможно только для тех агрегирования функций (например, мин или макс), который «забрать» значение из группы, а не «совокупный» (например, сумма или среднее значение).

Любой ключ?

Заранее спасибо.

ответ

3

Поскольку результат вы ищете, по существу объединение на ['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 
+0

Это аккуратный, что 'как = 'left'' также заботится о повторяющихся записей, всегда стоит помнить! (Это в 10 раз быстрее, чем idxmin с вашими испытаниями.) –

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