Есть ли способ более эффективно проходить по строкам в фрейме данных Pandas? - PullRequest
0 голосов
/ 07 декабря 2018

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

** РЕДАКТИРОВАТЬ: я изменю пример кода, чтобы лучше отражать фактические данные: Это заставило меня осознать наличие значений, отличных от «потерянный» или «выиграл» делает этонамного сложнее.

d = {'date': ['21.01.96', '22.02.96', '23.02.96', '24.02.96', '25.02.96',
          '26.02.96', '27.02.96', '28.02.96', '29.02.96', '30.02.96'], 
     'challenger': [5, 5, 10, 5, 4, 5, 8, 8, 10, 8],
     'opponent': [2, 4, 5, 4, 5, 10, 5, 2, 4, 10],
     'outcome': ['win', 'lost', 'declined', 'win', 'declined', 'win', 'declined', 'declined', 'lost', 'lost']
     }
df = pd.DataFrame(data=d)

Для каждого матча я хочу вычислить предыдущие выигрыши / проигрыши в новой переменной.В этом примере переменная prev_wins будет [0, 0, 0, 1, 0, 0, 0, 0, 0, 0].Мне удалось создать рабочий код для этого, который выглядит следующим образом:

data['prev_wins_spec_challenger'] = 0
data['prev_losses_spec_challenger'] = 0               

data['challenger'] = data['challenger'].astype(str)
data['opponent'] = data['opponent'].astype(str)

data['matchups'] = data['challenger'] + '-' + data['opponent']

# create list of matchups with unique pairings
matchups_temp = list(data['matchups'].unique())
matchups = []
for match in matchups_temp:
    if match[::-1] in matchups:
        pass
    else:
        matchups.append(match)

prev_wins = {}
for i in matchups:
    prev_wins[i] = 0

prev_losses = {}
for i in matchups:
    prev_losses[i] = 0

# go through data set for each matchup and calculate variables
for i in range(0, len(matchups)):
    match = matchups[i].split('-')
    challenger = match[0]
    opponent = match[1]
    for index, row in data.iterrows():
        if row['challenger'] == challenger and row['opponent'] == opponent:
            if row['outcome'] == 'won':
                data['prev_wins_spec_challenger'][index] = prev_wins[matchups[i]]
                prev_wins[matchups[i]] += 1
            elif row['outcome'] == 'lost':
                data['prev_losses_spec_challenger'][index] = prev_losses[matchups[i]]
                prev_losses[matchups[i]] += 1
        elif row['challenger'] == opponent and row['opponent'] == challenger:
            if row['outcome'] == 'won':
                data['prev_losses_spec_challenger'][index] = prev_losses[matchups[i]]
                prev_losses[matchups[i]] += 1
            elif row['outcome'] == 'lost':
                data['prev_wins_spec_challenger'][index] = prev_wins[matchups[i]]
                prev_wins[matchups[i]] += 1

Проблема в том, что это занимает невероятно много времени, потому что в общей сложности ~ 65.000 различных совпадений и фрейм данных имеют ~170 000 строк.На моем ноутбуке это может занять около 180 часов, что недопустимо.

Я уверен, что есть лучшее решение для этого, но даже после поиска в Интернете целый день я не смог его найти.Как я могу сделать этот код быстрее?

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

IIUC, groupby и cumsum

df['outcome'] = df.outcome.map({'win':1, 'loss':0})

Тогда

df.groupby('challenger').outcome.cumsum().sub(1).clip(lower=0)

Конечно, вы этого не сделаетенеобходимо перезаписать значения в outcome (вы можете создать новый столбец и работать с ним).Но обычно при работе с int с операции в пандах выполняются намного быстрее, чем при работе с string с.Таким образом, с точки зрения производительности предпочтительно иметь 0 и 1, представляющие выигрыши и проигрыши, чем фактические слова loss и win.

В последнем слое,именно тогда, когда вы представляете информацию, вы возвращаетесь к понятным для человека словам.Но для обработки обычно не нужны строки

0 голосов
/ 07 декабря 2018

IIUC, вы можете сделать что-то подобное, используя shift() для просмотра предыдущих результатов и получив кумулятивную сумму логического значения, где она равна win:

data['previous_wins'] = data.groupby('challenger').outcome.transform(lambda x: x.shift().eq('win').cumsum())

>>> data
   challenger      date  opponent outcome  previous_wins
0           5  21.01.96         6     win              0
1           4  22.02.96         3    loss              0
2           5  23.02.96         6     win              1

Если вы хотите подсчитать, сколько побед претендент выиграл у определенного противника, вы можете просто сгруппировать как соперника, так и противника:

data['previous_wins'] = data.groupby(['opponent','challenger']).outcome.transform(lambda x: x.shift().eq('win').cumsum())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...