Меня интересует число 'm' раз в последних событиях 'n', при которых выполняется условие, сгруппированное по человеку или пользователю . В частности, меня интересует, привык ли игрок к игре в данном классе или «категории», исходя из того, сколько из их последних нескольких матчей (а не каких-либо матчей) было сыграно на уровне или выше указанный уровень.
Я покорно вырвал одну группу из набора игрушечных данных для работы и получил мой код ниже, чтобы он работал. Однако, когда я пытаюсь случайно использовать ту же цепочку методов для объекта SeriesGroupBy
, все выходит из строя.
Во-первых, самый простой пример. Столбец hc
(высокий класс) равен 1
, когда 2 из 3 предыдущих матчей игрока были в категории 3. В противном случае это 0. (Я создал это вручную и использовал 0 | 1, а не True | False.):
import pandas as pd
pd.__version__
# '0.23.4'
match = ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c',
'c', 'c', 'd', 'd', 'd', 'e', 'e', 'e', 'e']
category = [3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2]
player = ['bar', 'baz', 'chaz', 'baz', 'choo', 'chaz', 'chaz', 'foo',
'baz', 'choo', 'foo', 'char', 'baz', 'choo', 'foo', 'chaz', 'baz']
hc = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1]
games = pd.DataFrame({'match': match, 'category': category, 'player': player, 'hc': hc})
games
# match category player hc
# 0 a 3 bar 0
# 1 a 3 baz 0
# 2 a 3 chaz 0
# 3 b 2 baz 0
# 4 b 2 choo 0
# 5 b 2 chaz 0
# 6 c 3 chaz 0
# 7 c 3 foo 0
# 8 c 3 baz 0
# 9 c 3 choo 0
# 10 d 3 foo 0
# 11 d 3 char 0
# 12 d 3 baz 1
# 13 e 2 choo 0
# 14 e 2 foo 1
# 15 e 2 chaz 1
# 16 e 2 baz 1
Достаточно запутавшись в моих длительных предыдущих усилиях, я принял (очевидно наивную) стратегию выбить интересного игрока и заставить расчеты поработать над разделенной группой:
baz = games.groupby('player').get_group('baz')
baz
# match category player hc
# 1 a 3 baz 0
# 3 b 2 baz 0
# 8 c 3 baz 0
# 12 d 3 baz 1
# 16 e 2 baz 1
result = baz.category.gt(2).rolling(3).sum().shift().gt(1)
result
# 1 False
# 3 False
# 8 False
# 12 True
# 16 True
# Name: category, dtype: bool
Успех! Я хакер панда! Я вознесся и теперь могу наставлять других в Пандорическом Пути! Позвольте мне сначала провести быструю проверку, прежде чем я куплю свои одежды и получу назначенную койку в монастыре:
games.groupby('player').category.gt(2).rolling(3).sum().shift().gt(1)
Traceback (последний вызов был последним):
Файл "", строка 1, в
Файл "(скучный путь) /lib/python3.6/site-packages/pandas/core/groupby/groupby.py", строка 762, в getattr
вернуть self._make_wrapper (attr)
Файл "(скучный путь) /lib/python3.6/site-packages/pandas/core/groupby/groupby.py", строка 799, в _make_wrapper
поднять AttributeError (msg)
AttributeError: Невозможно получить доступ к вызываемому атрибуту 'gt' объектов 'SeriesGroupBy', попробуйте использовать метод 'apply'
Тьфу. Я сброд.
Что за приличный способ сделать это? Кроме того, что я сделал не так? На мета-уровне, почему моя стратегия работы с одной группой, а затем обобщения не работает? Я попытался apply()
. Выводит ерунду.
РЕДАКТИРОВАТЬ: Возможный ответ с apply()
:
games['actual_hc'] =
games.groupby('player').category.apply(lambda x: x.shift().gt(2).rolling(3).sum().fillna(0, downcast='infer').astype(int).gt(1))
games
# match category player hc actual_hc
# 0 a 3 bar 0 False
# 1 a 3 baz 0 False
# 2 a 3 chaz 0 False
# 3 b 2 baz 0 False
# 4 b 2 choo 0 False
# 5 b 2 chaz 0 False
# 6 c 3 chaz 0 False
# 7 c 3 foo 0 False
# 8 c 3 baz 0 False
# 9 c 3 choo 0 False
# 10 d 3 foo 0 False
# 11 d 3 char 0 False
# 12 d 3 baz 1 True
# 13 e 2 choo 0 False
# 14 e 2 foo 1 True
# 15 e 2 chaz 1 True
# 16 e 2 baz 1 True
На моем фактическом 250-рядном DataFrame это занимает около 12 секунд. Я бы все еще любил что-то быстрее, просто чтобы знать «правильный путь», если он есть.