Фильтруйте Pandas DataFrame, используя GroupBy с количеством определенных значений - PullRequest
4 голосов
/ 02 июля 2019

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

Например, возвращать только те строки / группы df, где ['c2Группа ',' c3 '] имеет как минимум 2 строки со значением' c1 '1:

df = pd.DataFrame({'c1':[0,1,0,1,1,0], 'c2':[0,0,0,1,1,1], 'c3':[0,0,0,1,1,1]})

Результат должен возвращать только строки с индексами 3,4,5, поскольку только [c2, c3] = [1,1] группа имеет как минимум 2 строки со значением 'c1' 1.

df.groupby(['c2','c3']).filter(lambda x: x['c1'].count() >= 2)

не возвращает требуемый результат.Мне нужно, чтобы счет применялся конкретно к счету 1 с, а не только к любому значению 'c1'.

Следующее работает, но я не уверен, как сделать его более питоническим:

s = df.groupby(['c2','c3']).apply(lambda x: x[x['c1']==1].count() >= 2).all(axis=1)
df = df.reset_index().set_index(['c2','c3']).loc[s[s].index].reset_index().set_index(['index'])    

Ответы [ 2 ]

3 голосов
/ 02 июля 2019

Использование groupby + transform для суммирования логической серии, которую мы используем для маскировки исходного DataFrame.

m = df['c1'].eq(1).groupby([df['c2'], df['c3']]).transform('sum').ge(2)

# Alterntively assign the column
#m = df.assign(to_sum = df.c1.eq(1)).groupby(['c2', 'c3']).to_sum.transform('sum').ge(2) 

df.loc[m]
#   c1  c2  c3
#3   1   1   1
#4   1   1   1
#5   0   1   1

С фильтром count - неправильная логика. Используйте == (или .eq()), чтобы проверить, где 'c1' равно конкретному значению. Суммируйте логическую серию и убедитесь, что для вашего фильтра имеется не менее 2 таких случаев на группу.

df.groupby(['c2','c3']).filter(lambda x: x['c1'].eq(1).sum() >= 2)
#   c1  c2  c3
#3   1   1   1
#4   1   1   1
#5   0   1   1

Хотя это не заметно для небольшого DataFrame, filter с lambda ужасно медленен по мере роста числа групп. transform быстро:

import numpy as np
np.random.seed(123)
df = pd.DataFrame({'c1':np.random.randint(1,100,1000), 'c2':np.random.randint(1,100,1000), 
                   'c3':np.random.choice([1,0], 1000)})

%%timeit
m = df['c1'].eq(1).groupby([df.c3, df.c3]).transform('sum').ge(2)
df.loc[m]
#5.21 ms ± 15.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.groupby(['c2','c3']).filter(lambda x: x['c1'].eq(1).sum() >= 2)
#124 ms ± 714 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
1 голос
/ 02 июля 2019

Может использовать groupby + merge

s=df.groupby(['c2','c3']).c1.sum().ge(2)
s[s].index.to_frame().reset_index(drop=True).merge(df,how='left')
   c2  c3  c1
0   1   1   1
1   1   1   1
2   1   1   0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...