2014-12-15 4 views
23

Для примера я следующие таблицы:Python панд - фильтр строк после 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:

Так что я хочу бросить строку с индексом 0 и сохранить строки с индексами 1 и 2

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

Так что я хочу бросить строку с индексом 4 и сохранить строку с индексом 3

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

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

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

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

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

+0

последнего бита совершенно неоднозначно: если вы удаляете строки в каждой группе, но * не * возвращаете dataframe, то что вы хотите вернуть? –

+0

Ваши данные при сравнении полной таблицы с группами не совпадают. пожалуйста, проясните это. –

+0

Жаль, что я был немного занят, когда писал этот вопрос. Теперь данные верны. Ну, я имею в виду просто удалить строки из групп и сохранить эти группы, как они есть - мне нужно применить несколько фильтров и после каждого применения требуется новая groupby. – jirinovo

ответ

29

Вам нужно всего лишь использовать 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 
+0

Спасибо, он отлично работает. Могу ли я просто спросить вас, что делает 'apply()' специально? И я немного путаюсь с 'g [g ['B']' – jirinovo

+2

@jirinovo 'groupby.apply (function)' запускает каждую отдельную группу через эту функцию и объединяет все результаты. 'g [...]' - fancy/boolean indexing, что означает, что он возвращает только строки, где это внутреннее условие истинно. В этом случае условием является 'g ['B'] == g ['B']. Max()', например, все строки, где значение в столбце B равно наибольшему значению B внутри этой группы , –

+0

Ничего себе я не знал о такой вещи, как булевское индексирование - это действительно здорово! Благодарю. – jirinovo

10

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', '>') 

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

%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

Мне пришлось вырезать кофе из-за того, как документация в пандах заставляла мое кровяное давление стрелять ... Могу я спросить, где вы узнали об этом? Кроме того, позвольте мне [ссылаться на страницу 'transform()' doc] (http://pandas.pydata.org/pandas-docs/stable/generated/pandas.core.groupby.GroupBy.transform.html#pandas. core.groupby.GroupBy.transform) – mccc

+2

Я люблю Панды, но документы, сообщения об ошибках и тестирование оставляют желать лучшего. Я не помню, где я впервые увидел «трансформировать», но я уверен, что он был здесь, на SO. Я часто нахожу новые способы решения проблем, рассматривая вопросы и ответы здесь. Если вы используете «ipython notebook», вы можете использовать табуляцию для сканирования через различные методы, прочитать docstrings (не очень хорошо, я знаю) и просто поэкспериментировать с ними (в этом случае создать группу по объекту и просмотреть ее методы) – JoeCondron

+0

@ mccc вам нужно посмотреть рукописные документы, а не автоматически созданные ссылки: http://pandas.pydata.org/pandas-docs/stable/groupby.html#transformation –

1

Вот другой пример: Фильтрация строк с максимальным значением после операции GroupBy с использованием 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 
Смежные вопросы