BdN
BdN

Reputation: 11

Speed optimization for loop

I'm trying to predict the outcome of sports games and therefore want to transform my dataframe in such a way that I can train a model. Currently I am using a for loop to loop through all played games, pick the two players of the game and check how they performed the x games before the actual game took place. After this I want to take the mean of the statistics of previous games of these players and concatenate these together. In the end I add the true outcome of the actual game so I can train a model on the true outcome.

Now I got some speed performance issues, my current code takes about 9 minutes to complete for 20000 games (with ~200 variables). I already managed to go from 20 to 9 minutes.

I started with adding each game to a dataframe, later I changed this to adding each seperate dataframe to a list and make one big dataframe of this list in the end. I also included if statements which make sure that the loop continues if a player did not play at least x games.

I expect the outcome to be much faster than 9 minutes. I think it can be much faster.

Hope you guys can help me!

import pandas as pd
import numpy as np
import random
import string

letters = list(string.ascii_lowercase)
datelist = pd.date_range(start='1/1/2017', end='1/1/2019')

data = pd.DataFrame({'Date':np.random.choice(datelist,5000),
                     'League': np.random.choice(['LeagueA','LeagueB'], 5000),
                     'Home_player':np.random.choice(letters, 5000),
                     'Away_player':np.random.choice(letters, 5000),
                     'Home_strikes':np.random.randint(1,20,5000),
                     'Home_kicks':np.random.randint(1,20,5000),
                     'Away_strikes':np.random.randint(1,20,5000),
                     'Away_kicks':np.random.randint(1,20,5000),
                     'Winner':np.random.randint(0,2,5000)})

leagues = list(data['League'].unique())
home_columns = [col for col in data if col.startswith('Home')]
away_columns = [col for col in data if col.startswith('Away')]

# Determine to how many last x games to take statistics
total_games = 5 
final_df = []

# Make subframe of league
for league in leagues:
    league_data = data[data.League == league]
    league_data = league_data.sort_values(by='Date').reset_index(drop=True)
    # Pick the last game
    league_data = league_data.head(500)
    for i in range(0,len(league_data)):
        if i < 1:
            league_copy = league_data.sort_values(by='Date').reset_index(drop=True)
        else:
            league_copy = league_data[:-i].reset_index(drop=True)

        # Loop back from the last game
        last_game = league_copy.iloc[-1:].reset_index(drop=True)         

        # Take home and away player
        Home_player = last_game.loc[0,"Home_player"] # Pick home team
        Away_player = last_game.loc[0,'Away_player'] # pick away team

        # # Remove last row so current game is not picked
        df = league_copy[:-1] 

        # Now check the statistics of the games befóre this game was played
        Home = df[df.Home_player == Home_player].tail(total_games) # Pick data from home team           

        # If the player did not play at least x number of games, then continue
        if len(Home) < total_games:
            continue
        else:
            Home = Home[home_columns].reset_index(drop=True) # Pick all columnnames that start with "Home"


        # Do the same for the away team
        Away = df[df.Away_player == Away_player].tail(total_games) # Pick data from home team           

        if len(Away) < total_games:
            continue
        else:
            Away = Away[away_columns].reset_index(drop=True) # Pick all columnnames that start with "Home"


        # Now concat home and away player data
        Home_away = pd.concat([Home, Away], axis=1)
        Home_away.drop(['Away_player','Home_player'],inplace=True,axis=1)

        # Take the mean of all columns
        Home_away = pd.DataFrame(Home_away.mean().to_dict(),index=[0])

        # Now again add home team and away team to dataframe
        Home_away["Home_player"] = Home_player
        Home_away["Away_player"] = Away_player

        winner = last_game.loc[0,"Winner"]
        date = last_game.loc[0,"Date"]
        Home_away['Winner'] = winner
        Home_away['Date'] = date

        final_df.append(Home_away)
final_df = pd.concat(final_df, axis=0)
final_df = final_df[['Date','Home_player','Away_player','Home_kicks','Away_kicks','Home_strikes','Away_strikes','Winner']]

Upvotes: 1

Views: 74

Answers (2)

Michael Gardner
Michael Gardner

Reputation: 1813

This doesn't answer your question but you can leverage the package line_profiler to find the slow parts of your code.

Resource: http://gouthamanbalaraman.com/blog/profiling-python-jupyter-notebooks.html

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     2         1         35.0     35.0      0.0      letters = list(string.ascii_lowercase)
     3         1      11052.0  11052.0      0.0      datelist = pd.date_range(start='1/1/2017', end='1/1/2019')
     4                                           
     5         1       3483.0   3483.0      0.0      data = pd.DataFrame({'Date':np.random.choice(datelist,5000),
     6         1       1464.0   1464.0      0.0                           'League': np.random.choice(['LeagueA','LeagueB'], 5000),
     7         1       2532.0   2532.0      0.0                           'Home_player':np.random.choice(letters, 5000),
     8         1       1019.0   1019.0      0.0                           'Away_player':np.random.choice(letters, 5000),
     9         1        693.0    693.0      0.0                           'Home_strikes':np.random.randint(1,20,5000),
    10         1        682.0    682.0      0.0                           'Home_kicks':np.random.randint(1,20,5000),
    11         1        682.0    682.0      0.0                           'Away_strikes':np.random.randint(1,20,5000),
    12         1        731.0    731.0      0.0                           'Away_kicks':np.random.randint(1,20,5000),
    13         1      40409.0  40409.0      0.0                           'Winner':np.random.randint(0,2,5000)})
    14                                           
    15         1       6560.0   6560.0      0.0      leagues = list(data['League'].unique())
    16         1        439.0    439.0      0.0      home_columns = [col for col in data if col.startswith('Home')]
    17         1        282.0    282.0      0.0      away_columns = [col for col in data if col.startswith('Away')]
    18                                           
    19                                               # Determine to how many last x games to take statistics
    20         1         11.0     11.0      0.0      total_games = 5 
    21         1         12.0     12.0      0.0      final_df = []
    22                                           
    23                                               # Make subframe of league
    24         3         38.0     12.7      0.0      for league in leagues:
    25                                           
    26         2      34381.0  17190.5      0.0          league_data = data[data.League == league]
    27         2      30815.0  15407.5      0.0          league_data = league_data.sort_values(by='Date').reset_index(drop=True)
    28                                                   # Pick the last game
    29         2       5045.0   2522.5      0.0          league_data = league_data.head(500)
    30      1002      14202.0     14.2      0.0          for i in range(0,len(league_data)):
    31      1000      11943.0     11.9      0.0              if i < 1:
    32         2      28407.0  14203.5      0.0                  league_copy = league_data.sort_values(by='Date').reset_index(drop=True)
    33                                                       else:
    34       998    5305364.0   5316.0      4.2                  league_copy = league_data[:-i].reset_index(drop=True)
    35                                           
    36                                                       # Loop back from the last game
    37      1000    4945240.0   4945.2      3.9              last_game = league_copy.iloc[-1:].reset_index(drop=True)         
    38                                           
    39                                                       # Take home and away player
    40      1000    1504055.0   1504.1      1.2              Home_player = last_game.loc[0,"Home_player"] # Pick home team
    41      1000     899081.0    899.1      0.7              Away_player = last_game.loc[0,'Away_player'] # pick away team
    42                                           
    43                                                       # # Remove last row so current game is not picked
    44      1000    2539351.0   2539.4      2.0              df = league_copy[:-1] 
    45                                           
    46                                                       # Now check the statistics of the games befóre this game was played
    47      1000   16428854.0  16428.9     13.0              Home = df[df.Home_player == Home_player].tail(total_games) # Pick data from home team           
    48                                           
    49                                                       # If the player did not play at least x number of games, then continue
    50      1000      49133.0     49.1      0.0              if len(Home) < total_games:
    51       260       2867.0     11.0      0.0                  continue
    52                                                       else:
    53       740   12968016.0  17524.3     10.2                  Home = Home[home_columns].reset_index(drop=True) # Pick all columnnames that start with "Home"
    54                                           
    55                                           
    56                                                       # Do the same for the away team
    57       740   12007650.0  16226.6      9.5              Away = df[df.Away_player == Away_player].tail(total_games) # Pick data from home team           
    58                                           
    59       740      33357.0     45.1      0.0              if len(Away) < total_games:
    60        64        825.0     12.9      0.0                  continue
    61                                                       else:
    62       676   11598741.0  17157.9      9.1                  Away = Away[away_columns].reset_index(drop=True) # Pick all columnnames that start with "Home"
    63                                           
    64                                           
    65                                                       # Now concat home and away player data
    66       676    5114022.0   7565.1      4.0              Home_away = pd.concat([Home, Away], axis=1)
    67       676    9702001.0  14352.1      7.6              Home_away.drop(['Away_player','Home_player'],inplace=True,axis=1)
    68                                           
    69                                                       # Take the mean of all columns
    70       676   12171184.0  18004.7      9.6              Home_away = pd.DataFrame(Home_away.mean().to_dict(),index=[0])
    71                                           
    72                                                       # Now again add home team and away team to dataframe
    73       676    5112558.0   7563.0      4.0              Home_away["Home_player"] = Home_player
    74       676    4880017.0   7219.0      3.8              Home_away["Away_player"] = Away_player
    75                                           
    76       676     791718.0   1171.2      0.6              winner = last_game.loc[0,"Winner"]
    77       676     696925.0   1031.0      0.5              date = last_game.loc[0,"Date"]
    78       676    5142111.0   7606.7      4.1              Home_away['Winner'] = winner
    79       676    9630466.0  14246.3      7.6              Home_away['Date'] = date
    80                                           
    81       676      16125.0     23.9      0.0              final_df.append(Home_away)
    82         1    5088063.0 5088063.0      4.0      final_df = pd.concat(final_df, axis=0)
    83         1      18424.0  18424.0      0.0      final_df = final_df[['Date','Home_player','Away_player','Home_kicks','Away_kicks','Home_strikes','Away_strikes','Winner']]

Upvotes: 1

Quang Hoang
Quang Hoang

Reputation: 150765

IIUC, you can obtain the last 5 game statistics, including the current one by:

# replace this with you statistic columns
stat_cols = data.columns[4:]
total_games = 5 

data.groupby(['League','Home_player', 'Away_player'])[stat_cols].rolling(total_games).mean()

If you want to exclude the current one:

last_stats = data.groupby(['League','Home_player', 'Away_player']).apply(lambda x: x[stat_cols].shift().rolling(total_games).mean())

This last_stats data frame should have the same index as the the original one, so you can do:

train_data = data.copy()

# backup the actual outcome
train_data['Actual'] = train_data['Winner']

# copy the average statistics
train_data[stat_cols] = last_stats

All together should not take more than 1 min.

Upvotes: 0

Related Questions