Есть ли способ сохранить подмножество каждой группы для объекта pandas groupby? - PullRequest
3 голосов
/ 31 января 2020

У меня есть кадр данных, который выглядит следующим образом:

   id        date  isActive
0   1  2019-01-01         0
1   1  2019-01-02         1
2   1  2019-01-03         1
3   1  2019-01-04         0
4   1  2019-01-05         0
5   2  2019-01-01         0
6   2  2019-01-02         1
7   2  2019-01-03         0
8   2  2019-01-04         1
9   2  2019-01-05         0

Я хочу отфильтровать все неактивные ( isActive = 0) строки для каждого идентификатора, кроме самых последних строк для этот идентификатор неактивен. После этого мой фрейм данных должен выглядеть следующим образом:

   id        date  isActive
0   1  2019-01-02         1
1   1  2019-01-03         1
2   1  2019-01-04         0
3   1  2019-01-05         0
4   2  2019-01-02         1
5   2  2019-01-04         1
6   2  2019-01-05         0

Я подумал, что мне следует попробовать сохранить все активные строки вместе со строками, связанными с последним набором последовательных isActive значения для каждого идентификатора. Для этого я попытался создать флаг, указывающий, когда изменяется переменная isActive , а затем попытался получить размер каждой из этих групп:

df['flag'] = df.groupby(['id', df['isActive'].eq(1).cumsum()])['isActive'].transform('size')

Затем я попытался использовать apply и tail чтобы сохранить каждую строку, которая удовлетворяла условиям, указанным выше, но я понял, что не могу просто получить доступ к значению flag последнего столбца:

df.groupby(['ID']).apply(lambda x: (x['Status'].eq(2)) | (x['Status'].tail(x['flag'])))
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Я думаю Возможно, я переосмысливаю эту проблему. Есть ли какой-нибудь лучший подход?

Ответы [ 2 ]

4 голосов
/ 31 января 2020

Проверьте, равен ли isActive 0, и возьмите накопленный продукт снизу в группе. Это даст вам True за последнюю полосу 0 с (если есть). Объедините это со всеми строками 1, и у вас будет общая маска.

# Assumes sorted by date within each id
m = (df['isActive'].eq(0)[::-1].groupby(df['id']).cumprod().sort_index()
     | df['isActive'].eq(1))

df[m]
   id        date  isActive
1   1  2019-01-02         1
2   1  2019-01-03         1
3   1  2019-01-04         0
4   1  2019-01-05         0
6   2  2019-01-02         1
8   2  2019-01-04         1
9   2  2019-01-05         0
0 голосов
/ 31 января 2020

Используйте pyjanitor, чтобы привязать преобразование обратно к информационному фрейму, сдвиньте функцию, чтобы получить значение из следующей строки, и отфильтруйте любое значение, равное -1, так как 0-1 будет отрицательным, но 0-0 или 1- 1 не будет.

import pandas as pd
import pyjanitor

(df.groupby_agg(by='id',
                agg=lambda x: x - x.shift(-1),
                agg_column_name='isActive',
                new_column_name='cumu'
                )
 .fillna(0)
 .query('cumu != -1')
 .reset_index(drop=True)
 .drop('cumu',axis=1)
)



    id  date    isActive
0   1   2019-01-02  1
1   1   2019-01-03  1
2   1   2019-01-04  0
3   1   2019-01-05  0
4   2   2019-01-02  1
5   2   2019-01-04  1
6   2   2019-01-05  0
...