Панды с скользящим счетчиком для категориальных переменных с использованием времени

У меня есть фрейм данных, который выглядит так:

Datetime   | Category | ID
--------------------------
2020-01-30 | A        | 1
2020-02-01 | B        | 1
2020-02-02 | A        | 1
2020-02-20 | A        | 1
2020-01-28 | B        | 2
2020-01-29 | C        | 2
2020-01-30 | C        | 2
2020-01-31 | D        | 2
2020-02-01 | D        | 2
2020-02-02 | D        | 2
2020-02-03 | C        | 2

Я хотел бы получить две самые частые категории для каждого идентификатора в течение 1-недельного окна строки (за исключением текущей строки). Возможно ли такое в пандах? Я пытался использовать .rolling и .value_counts, но, похоже, это не работает. Спасибо!

Ниже приведен фрейм данных, который я хотел бы получить:

Datetime   | Category | ID
--------------------------
2020-01-30 | NaN      | 1
2020-02-01 | [A, ""]  | 1
2020-02-02 | [A, B]   | 1
2020-02-20 | NaN      | 1
2020-01-28 | Nan      | 2
2020-01-29 | [B,""]   | 2
2020-01-30 | [B,C]    | 2
2020-01-31 | [B,C]    | 2
2020-02-01 | [C,D]    | 2
2020-02-02 | [C,D]    | 2
2020-02-03 | [C,D]    | 2

Спасибо!

Изменить. Ответ pd.get_dummies отличный, но поскольку мой набор данных огромен, он неэффективен. Был бы очень признателен, если бы у кого-нибудь есть эффективное решение для этого! Спасибо!


person yxc    schedule 27.05.2020    source источник


Ответы (1)


Вы можете использовать resample() вместо rolling(), потому что частота вашего временного индекса - ежедневно, и вам нужна еженедельная статистика, поэтому попробуйте что-нибудь вроде этого:

df.groupby('ID').resample('1w').apply(lambda s: s.value_counts().head(2))

Обратите внимание, что это работает только в версиях Pandas, где apply() разделяет данные на pd.Series, а не np.arrays. Также, если у вас есть больше столбцов в вашем фрейме данных, может потребоваться указать имя столбца в лямбда-функциях, то есть:

df.groupby('ID').resample('1w').apply(lambda s: s['Category'].value_counts().head(2))

А если вам нужно исключить первую строку в окне, используйте iloc[] нарезку:

df.groupby('ID').resample('1w').apply(lambda s: s['Category'].iloc[1:].value_counts().head(2))
person mac13k    schedule 27.05.2020
comment
большое спасибо! но поскольку некоторые из моих данных на самом деле моя частота несовместима (например, ID 1 может содержать строки данных с интервалом более 2 недель), я бы не смог использовать сдвиг в этом случае, верно? - person yxc; 27.05.2020
comment
Shift будет работать независимо от несоответствий в индексе - он просто переместит все строки на 1. Однако, если у вас есть такие большие пробелы, это может вызвать некоторые проблемы в результатах. - person mac13k; 27.05.2020
comment
Хорошо, может быть shift было плохой идеей. Я внес изменения. - person mac13k; 27.05.2020
comment
спасибо за разъяснения! попытался реализовать это, но я получаю сообщение об ошибке: AttributeError: объект DatetimeIndexResamplerGroupby не имеет атрибута value_counts, когда я пытаюсь сделать .apply - person yxc; 28.05.2020
comment
Какая версия Pandas у вас установлена? У меня 1.0.3. Вам необходимо перейти на версию, которая поддерживает серию внутри apply (). Я написал это в ответе. - person mac13k; 28.05.2020
comment
Yupp, версию проверил. У меня тоже 1.03. - person yxc; 28.05.2020
comment
Вы указываете столбец? Т.е. s['Category']... - person mac13k; 28.05.2020
comment
yupp сделал то же самое, что и ваш хм - person yxc; 28.05.2020
comment
Если вы получаете такую ​​ошибку, это означает, что объект, который вы вызываете методом value_counts(), не является pd.Series. Вы можете проверить это с помощью type: df.groupby('ID').resample('1w').apply(type) или df.groupby('ID').resample('1w').apply(lambda s: type(s['Category'])) - person mac13k; 28.05.2020