Выкатывание m из n самых последних случаев состояния в пандах - PullRequest
0 голосов
/ 24 января 2019

Меня интересует число '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 секунд. Я бы все еще любил что-то быстрее, просто чтобы знать «правильный путь», если он есть.

Ответы [ 2 ]

0 голосов
/ 04 июля 2019

Я недавно вернулся к этому вопросу и нашел ответ, transform(). По какой-то причине ответ с apply() от pj.dewitte нечувствителен к аргументу min_periods rolling(), независимо от того, насколько сильно я нажимал клавиши, когда набирал min_periods=2.

Полный ответ:

games['actual_hc'] = \
    games.groupby('player').category.transform(
        lambda g:
        g.gt(2).rolling(3, min_periods=2).sum().shift().gt(1)).astype(int)
#    match  category player  hc  actual_hc
# 0      a         3    bar   0          0
# 1      a         3    baz   0          0
# 2      a         3   chaz   0          0
# 3      b         2    baz   0          0
# 4      b         2   choo   0          0
# 5      b         2   chaz   0          0
# 6      c         3   chaz   0          0
# 7      c         3    foo   0          0
# 8      c         3    baz   0          0
# 9      c         3   choo   0          0
# 10     d         3    foo   0          0
# 11     d         3   char   0          0
# 12     d         3    baz   1          1
# 13     e         2   choo   0          0
# 14     e         2    foo   1          1
# 15     e         2   chaz   1          1
# 16     e         2    baz   1          1

Обратите внимание, что, хотя у foo было всего два матча до ее финального матча, они оба были в третьем классе, и она правильно указана как игрок высокого класса в games.iloc[14].

0 голосов
/ 25 января 2019

Использование apply:

games.groupby('player').apply(
    lambda group: group.category.gt(2).rolling(3).sum().shift().gt(1)
)

Это выводит:

player    
bar     0     False
baz     1     False
        3     False
        8     False
        12     True
        16     True
char    11    False
chaz    2     False
        5     False
        6     False
        15     True
choo    4     False
        9     False
        13    False
foo     7     False
        10    False
        14    False
Name: category, dtype: bool

, что, кажется, то, что вы хотите.

Я не знаю, является ли это производительностьВ лучшем случае, но так как ошибка предполагает использование apply, я предполагаю, что разработчики панд думают, что это хорошая идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...