Pandas IndexError: index 0 is out of bounds问题排查求助
Elo评分算法代码IndexError错误排查
问题描述
运行基于Elo评分的Python代码时触发IndexError: index 0 is out of bounds for axis 0 with size 0,错误发生在获取球队上一场比赛Elo评分的逻辑中。
数据信息
game_info.info()
输出:
<class 'pandas.core.frame.DataFrame'> RangeIndex: 30362 entries, 0 to 30361 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 competitionId 30362 non-null Int64 1 gameId 30349 non-null Int64 2 gameDate 30362 non-null datetime64[ns] 3 gameTime 30362 non-null datetime64[ns] 4 awayId 30362 non-null int64 5 homeId 30362 non-null int64 6 awayScore 30362 non-null int64 7 homeScore 30362 non-null int64 8 homeWin 29889 non-null Int64 9 homePush 30362 non-null Int64 dtypes: Int64(4), datetime64[ns](2), int64(4) memory usage: 2.4 MB
game_info.head()结果:
| competitionId | gameId | gameDate | gameTime | awayId | homeId | awayScore | homeScore | homeWin | homePush | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2021 | 4584762 | 2022-01-20 | 2022-01-20 20:00:00 | 765 | 829 | 75 | 56 | 0 | 0 |
| 1 | 2021 | 4586420 | 2022-01-08 | 2022-01-08 15:00:00 | 823 | 677 | 68 | 66 | 0 | 0 |
| 2 | 2018 | 3642155 | 2019-01-24 | 2019-01-24 18:00:00 | 1374 | 670 | 70 | 88 | 1 | 0 |
| 3 | 2021 | 4611286 | 2022-01-10 | 2022-01-10 19:30:00 | 825 | 824 | 58 | 95 | 1 | 0 |
| 4 | 2022 | 4678182 | 2022-12-29 | 2022-12-29 19:00:00 | 1099 | 824 | 54 | 92 | 1 | 0 |
代码
Elo计算函数
# 获取主队预期胜率 def get_home_E(homeElo, awayElo): home_win_prob = 1./(1 + 10 ** ((awayElo - homeElo) / (400))) return home_win_prob # 获取K值乘数 def get_k(MOV, elo_diff): k_initial = 20 if MOV > 0: multiplier = (MOV + 3)**0.8 / (7.5 + 0.006 * (elo_diff)) else: multiplier = (-MOV + 3)**0.8 / (7.5 + 0.006 * (-elo_diff)) return k_initial * multiplier # 更新Elo评分 def update_elo(homeScore, awayScore, homeEloPrev, awayEloPrev): home_adv = 100 homeElo = homeEloPrev + home_adv home_E = get_home_E(homeElo, awayEloPrev) elo_diff = homeEloPrev - awayEloPrev MOV = homeScore - awayScore if (MOV > 0) : home_S = 1 elif (MOV < 0): home_S= 0 else: home_S = 0.5 k = get_k(MOV, elo_diff) home_elo_update = k * (home_S - home_E) updated_home_elo = homeEloPrev + home_elo_update updated_away_elo = awayEloPrev - home_elo_update return updated_home_elo, updated_away_elo # 获取球队上一场比赛后的Elo评分 def get_prev_elo(teamId, gameTime, competitionId, game_info, elo_per_game): prev_game = game_info[game_info['gameTime'] < gameTime][((game_info['homeId'] == teamId) | (game_info['awayId'] == teamId)) & (game_info['gameId'] != gameId)].tail(1).iloc[0] if teamId == prev_game['homeId'] : elo_rating = elo_per_game[elo_per_game['gameId'] == prev_game['gameId']]['homeEloAfter'].values[0] else: elo_rating = elo_per_game[elo_per_game['gameId'] == prev_game['gameId']]['awayEloAfter'].values[0] if prev_game['competitionId'] != competitionId : return (0.75 * elo_rating) + (0.25 * 1505) else: return elo_rating
主循环代码
# 存储每局比赛的Elo结果 elo_per_game = pd.DataFrame(columns=['competitionId', 'gameId', 'gameDate', 'homeId', 'awayId', 'homeEloPrev', 'awayEloPrev', 'homeEloAfter', 'awayEloAfter']) elo_per_team = pd.DataFrame(columns=['competitionId', 'gameId', 'gameDate', 'teamId', 'elo', 'home']) for index, row in game_info.iterrows(): # 提取当前比赛信息 gameId = row['gameId'] competitionId = row['competitionId'] gameDate = row['gameDate'] gameTime = row['gameTime'] homeId, awayId = int(row['homeId']), int(row['awayId']) homeScore, awayScore = row['homeScore'], row['awayScore'] # 获取主队上一场Elo(首次比赛用1500) if (homeId not in elo_per_game['homeId'].values and homeId not in elo_per_game['awayId'].values): homeEloPrev = 1500 else: homeEloPrev = get_prev_elo(homeId, gameTime, competitionId, game_info, elo_per_game) # 获取客队上一场Elo(首次比赛用1500) if (awayId not in elo_per_game['homeId'].values and awayId not in elo_per_game['awayId'].values): awayEloPrev = 1500 else: awayEloPrev = get_prev_elo(awayId, gameTime, competitionId, game_info, elo_per_game) # 更新当前比赛的Elo评分 homeEloAfter, awayEloAfter = update_elo(homeScore, awayScore, homeEloPrev, awayEloPrev) # 将结果写入数据框 updated_row = { 'gameId': gameId, 'competitionId': competitionId, 'gameDate': gameDate, 'homeId': homeId, 'awayId': awayId, 'homeEloPrev': homeEloPrev, 'awayEloPrev': awayEloPrev, 'homeEloAfter' : homeEloAfter, 'awayEloAfter': awayEloAfter } elo_per_game = elo_per_game.append(updated_row, ignore_index = True) homeTeam = {'gameId': gameId, 'competitionId': competitionId, 'gameDate': gameDate, 'teamId': homeId, 'elo': homeEloPrev, 'home': True} awayTeam = {'gameId': gameId, 'competitionId': competitionId, 'gameDate': gameDate, 'teamId': awayId, 'elo': awayEloPrev, 'home': False} elo_per_team = elo_per_team.append(homeTeam, ignore_index = True) elo_per_team = elo_per_team.append(awayTeam, ignore_index = True) # 每处理4000条记录打印日志 if index % 4000 == 0: print(index)
错误信息
-------------------------------------------------------------------------- IndexError Traceback (most recent call last) Cell In[713], line 17 15 homeEloPrev = 1500 16 else: ---> 17 homeEloPrev = get_prev_elo(homeId, gameTime, competitionId, game_info, elo_per_game) 19 # grab away teams previous ELO (1500 if first game) 20 if (awayId not in elo_per_game['homeId'].values and awayId not in elo_per_game['awayId'].values): Cell In[712], line 44, in get_prev_elo(teamId, gameTime, competitionId, game_info, elo_per_game) 42 elo_rating = elo_per_game[elo_per_game['gameId'] == prev_game['gameId']]['homeEloAfter'].values[0] 43 else: ---> 44 elo_rating = elo_per_game[elo_per_game['gameId'] == prev_game['gameId']]['awayEloAfter'].values[0] 46 if prev_game['competitionId'] != competitionId : 47 return (0.75 * elo_rating) + (0.25 * 1505) IndexError: index 0 is out of bounds for axis 0 with size 0
错误原因分析
- 游戏处理顺序错误:
game_info中的比赛未按gameTime升序排列(比如第0行比赛时间晚于第1行),主循环按原DataFrame索引顺序处理时,会先处理时间较晚的比赛,此时该球队之前的更早比赛还未被处理,elo_per_game中没有对应记录,导致查找失败。 get_prev_elo函数参数缺失:函数中使用了gameId变量,但未将其作为参数传入,会意外引用主循环中的当前gameId,可能导致筛选逻辑错误。- 链式索引与安全访问缺失:直接使用
.iloc[0]和.values[0]访问数据,未判断结果是否为空,一旦找不到对应记录就会触发索引错误。
修复方案
1. 先按比赛时间排序game_info
确保处理顺序从最早到最晚,这样处理当前比赛时,球队之前的比赛已经被处理并写入elo_per_game:
# 按gameTime升序排序,确保处理顺序正确 game_info = game_info.sort_values('gameTime').reset_index(drop=True)
2. 修复get_prev_elo函数参数与逻辑
加入current_gameId参数,优化筛选逻辑,并增加空值判断:
def get_prev_elo(teamId, gameTime, competitionId, current_gameId, game_info, elo_per_game): # 筛选该球队参与的、时间早于当前比赛的所有记录 team_games = game_info[ ((game_info['homeId'] == teamId) | (game_info['awayId'] == teamId)) & (game_info['gameTime'] < gameTime) & (game_info['gameId'] != current_gameId) ] # 如果没有找到之前的比赛,返回初始值1500 if team_games.empty: return 1500 # 取最近的一场比赛 prev_game = team_games.sort_values('gameTime').iloc[-1] # 查找该比赛的Elo记录 prev_game_elo = elo_per_game[elo_per_game['gameId'] == prev_game['gameId']] if prev_game_elo.empty: return 1500 # 获取对应球队的Elo评分 if teamId == prev_game['homeId'] : elo_rating = prev_game_elo['homeEloAfter'].iloc[0] else: elo_rating = prev_game_elo['awayEloAfter'].iloc[0] # 跨赛事Elo调整 if prev_game['competitionId'] != competitionId : return (0.75 * elo_rating) + (0.25 * 1505) else: return elo_rating
3. 修改主循环中调用get_prev_elo的代码
传入current_gameId参数:
# 获取主队上一场Elo if (homeId not in elo_per_game['homeId'].values and homeId not in elo_per_game['awayId'].values): homeEloPrev = 1500 else: homeEloPrev = get_prev_elo(homeId, gameTime, competitionId, gameId, game_info, elo_per_game) # 获取客队上一场Elo if (awayId not in elo_per_game['homeId'].values and awayId not in elo_per_game['awayId'].values): awayEloPrev = 1500 else: awayEloPrev = get_prev_elo(awayId, gameTime, competitionId, gameId, game_info, elo_per_game)
4. 优化数据查找效率(可选)
为elo_per_game的gameId列建立索引,加快查找速度:
elo_per_game = pd.DataFrame(columns=['competitionId', 'gameId', 'gameDate', 'homeId', 'awayId', 'homeEloPrev', 'awayEloPrev', 'homeEloAfter', 'awayEloAfter']) elo_per_game.set_index('gameId', inplace=True) # 写入时修改为.loc方式 elo_per_game.loc[gameId] = updated_row # 查找时直接用索引 prev_game_elo = elo_per_game.loc[prev_game['gameId']]
验证修复
完成上述修改后,重新运行代码,应该不会再触发IndexError。可以在get_prev_elo中加入打印语句,验证找到的prev_game和对应的Elo记录是否正确。
内容的提问来源于stack exchange,提问作者Tim R
相关产品推荐
相关产品推荐

