Подтвердить что ты не робот

Python pandas - фильтровать строки после groupby

Например, у меня есть следующая таблица:

index,A,B
0,0,0
1,0,8
2,0,8
3,1,0
4,1,5

После группировки A:

0:
index,A,B
0,0,0
1,0,8
2,0,8

1:
index,A,B
3,1,5
4,1,3

Мне нужно отбросить строки из каждой группы, где число в столбце B меньше максимального значения из всех строк из столбца группы B. Ну, у меня проблемы с переводом и формулировкой этой проблемы на английский, вот пример:

Максимальное значение из строк в столбце B в группе 0: 8

Итак, я хочу свернуть строку с индексом 0 и сохранить строки с индексами 1 и 2

Максимальное значение из строк в столбце B в группе 1: 5

Итак, я хочу сбрасывать строку с индексом 4 и сохранять строку с индексом 3

Я попытался использовать функцию фильтра pandas, но проблема в том, что он работает во всех строках в группе за один раз:

data = <example table>
grouped = data.groupby("A")
filtered = grouped.filter(lambda x: x["B"] == x["B"].max())

Так что мне в идеале нужен фильтр, который выполняет итерацию по всем строкам в группе.

Спасибо за помощь!

P.S. Есть ли способ удалить строки только в группах и не вернуть объект DataFrame?

4b9b3361

Ответ 1

Вам просто нужно использовать apply для объекта groupby. Я изменил ваши данные примера, чтобы сделать это более понятным:

import pandas
from io import StringIO

csv = StringIO("""index,A,B
0,1,0.0
1,1,3.0
2,1,6.0
3,2,0.0
4,2,5.0
5,2,7.0""")

df = pandas.read_csv(csv, index_col='index')
groups = df.groupby(by=['A'])
print(groups.apply(lambda g: g[g['B'] == g['B'].max()]))

Какие принты:

         A  B
A index      
1 2      1  6
2 4      2  7

Ответ 2

EDIT: я просто научился гораздо более аккуратным способом сделать это с помощью группы .transform по методу:

def get_max_rows(df):
    B_maxes = df.groupby('A').B.transform(max)
    return df[df.B == B_maxes] 

B_maxes - это серия, которая идентично индексируется как исходная df, содержащая максимальное значение B для каждой группы A. Вы можете передать множество функций методу преобразования. Я думаю, как только они выводят либо как скаляр, либо вектор той же длины. Вы даже можете передать некоторые строки как общие имена функций, например 'median'. Это немного отличается от метода Пола Х в том, что "А" не будет индексом в результате, но вы можете легко установить его после.

import numpy as np
import pandas as pd
df_lots_groups = pd.DataFrame(np.random.rand(30000, 3), columns = list('BCD')
df_lots_groups['A'] = np.random.choice(range(10000), 30000)

%timeit get_max_rows(df_lots_groups)
100 loops, best of 3: 2.86 ms per loop

%timeit df_lots_groups.groupby('A').apply(lambda df: df[ df.B == df.B.max()])
1 loops, best of 3: 5.83 s per loop

EDIT:

Здесь абстракция, которая позволяет вам выбирать строки из групп, используя любой действительный оператор сравнения и любой действительный метод groupby:

def get_group_rows(df, group_col, condition_col, func=max, comparison='=='):
    g = df.groupby(group_col)[condition_col]
    condition_limit = g.transform(func)
    df.query('condition_col {} @condition_limit'.format(comparison))

Так, например, если вы хотите, чтобы все строки выше медианного значения B в каждой группе A вы вызывали

get_group_rows(df, 'A', 'B', 'median', '>')

Несколько примеров:

%timeit get_group_rows(df_lots_small_groups, 'A', 'B', 'max', '==')
100 loops, best of 3: 2.84 ms per loop
%timeit get_group_rows(df_lots_small_groups, 'A', 'B', 'mean', '!=')
100 loops, best of 3: 2.97 ms per loop

Ответ 3

Вот еще один пример для: Фильтрация строк с максимальным значением после групповой работы с использованием idxmax() и .loc()

In [465]: import pandas as pd

In [466]:   df = pd.DataFrame({
               'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2'],
               'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4'], 
               'value' : [3,2,5,8,10,1]     
                })

In [467]: df
Out[467]: 
   mt   sp  value
0  S1  MM1      3
1  S1  MM1      2
2  S3  MM1      5
3  S3  MM2      8
4  S4  MM2     10
5  S4  MM2      1

### Here, idxmax() finds the indices of the rows with max value within groups,
### and .loc() filters the rows using those indices :
In [468]: df.loc[df.groupby(["mt"])["value"].idxmax()]                                                                                                                           
Out[468]: 
   mt   sp  value
0  S1  MM1      3
3  S3  MM2      8
4  S4  MM2     10