【问题标题】:Pandas - function to conditionally update certain columns on the next rowPandas - 有条件地更新下一行的某些列的功能
【发布时间】:2019-04-02 16:05:35
【问题描述】:

我有一个 csv 文件,其中包含来自不同足球比赛的大量结果。 数据类似于下面的示例。 result 列可以包含 3 个可能的值:

  • H -> 主队获胜(主队+3分)
  • A -> 客队获胜(客场+3分)
  • D -> 平局(两队都获得+1分)
   HomeTeam    AwayTeam Result
0   FC_Fake  ABC_United      H
1  Team_123   FC_Berlin      A
2   FC_FAKE    TEAM_123      D

我想更新文件,这样每一行都包含每支球队的总积分as they are at the start of the match(因此尚未更新该行本身的比赛结果)

我已使用以下代码更新数据框,使其包含每个团队的 points_[TEAM_NAME] 虚拟列。

# Teams is a python list I extracted earlier
for team in teams:
    df['points_' + team] = 0

目标是转换数据框,使上面的例子变成下面的例子。

(同样,分数应该代表比赛开始时的情况。所以即使FC_FAKE在第一行赢得比赛,Points_FC_FAKE 列也是 0)

HomeTeam | AwayTeam | Result  Points_FC_FAKE | Points_TEAM_123 | Points_FC_Berlin |  etc
-------------------------------------------------------------------------------
 FC_Fake  ABC_United    H         0                  0             0
 Team_123 FC_Berlin     A         3                  0             0
 FC_FAKE  Team_123      D         3                  0             3

我创建了以下 python 函数,如果它遍历数据框中的所有行,则应该解析结果并将正确数量的积分奖励给正确的团队。

def point_updater(x):
    if x['Result'] == 'H':        
        home = x['HomeTeam']
        x.shift(-1)['points_' + home] += 3
        return x

    elif x['Result'] == 'A':        
        away = x['AwayTeam']
        x.shift(-1)['points_' + away] += 3
        return x

    elif x['Result'] == 'D':        
        home = x['AwayTeam']
        away = x['AwayTeam']
        x.shift(-1)['points_' + home] += 1
        x.shift(-1)['points_' + away] += 1
        return x

问题是当我将此函数应用于数据框时,点不会改变(全部保持 0)

df = df.apply(point_counter, axis=1)
df['points_FC_Fake'].value_counts()
----
0    2691

有谁知道我做错了什么?

【问题讨论】:

  • 所以每个团队都有一个新栏目?例如FC_BerlinABC_United 也会得到一个点列?
  • 正确! (我可能应该说得更清楚,我会更新问题)

标签: python pandas


【解决方案1】:

在某些例外情况下,我们可以为此使用iterrows。另外,我在开始计算之前进行了一些清理,使您的代码更具防错性和通用性:

# Convert to uppercase letters 
df['HomeTeam'] = df['HomeTeam'].str.upper()
df['AwayTeam'] = df['AwayTeam'].str.upper()

# get a list off all the teams in competition
lst_teams = list(set(list(df.HomeTeam.unique()) + list(df.AwayTeam.unique())))

# Create columns for each team
for team in lst_teams:
    df[team] = 0

# Iterate over each row and assign correct points
for idx, r in df.iterrows():
    if r['Result'] == 'H':
        df.loc[[idx], [r['HomeTeam']]] = 3
    if r['Result'] == 'A':
        df.loc[[idx], [r['AwayTeam']]] = 3
    if r['Result'] == 'D':
        df.loc[[idx], [r['AwayTeam']]] = 1
        df.loc[[idx], [r['HomeTeam']]] = 1

# Shift the rows one down, since points are only available at start of match
df.iloc[:, 3:] = df.iloc[:, 3:].cumsum().shift(1).fillna(0).astype(int)

输出

print(df)
   HomeTeam    AwayTeam Result  ABC_UNITED  TEAM_123  FC_FAKE  FC_BERLIN
0   FC_FAKE  ABC_UNITED      H           0         0        0          0
1  TEAM_123   FC_BERLIN      A           0         0        3          0
2   FC_FAKE    TEAM_123      D           0         0        3          3

【讨论】:

  • 做到了!感谢您的帮助!
【解决方案2】:

可能有一种更简洁的方式来执行这些操作,但现在应该就足够了。您可以使用df.replace()Result 键映射到它们的关联值,然后使用pd.concat()pd.DataFrame.pivot() 来实现您想要的结果:

import pandas as pd

df = pd.DataFrame({'HomeTeam': ['FC_Fake','Team_123','FC_Fake'], 'AwayTeam': ['ABC_United','FC_Berlin','Team_123'], 'Result': ['H','A','D']})

remap = df.replace({'H': 3, 'A': 3, 'D': 1})

new = pd.concat([remap.pivot(columns='HomeTeam', values='Result'), remap.pivot(columns='AwayTeam', values='Result')], axis=1).shift(1).fillna(0).astype(int).cumsum()

final = pd.concat([df, new], axis=1)

产量:

   HomeTeam    AwayTeam Result  FC_Fake  Team_123  ABC_United  FC_Berlin  \
0   FC_Fake  ABC_United      H        0         0           0          0   
1  Team_123   FC_Berlin      A        3         0           3          0   
2   FC_Fake    Team_123      D        3         3           3          3   

   Team_123  
0         0  
1         0  
2         0 

【讨论】:

    【解决方案3】:

    把你的功能改成这样:

    def point_updater(x):
        if x['Result'] == 'H':    
            home = x['HomeTeam']
            x['points_' + home] += 3
            return x
    
        elif x['Result'] == 'A':        
            away = x['AwayTeam']
            x['points_' + away] += 3
            return x
    
        elif x['Result'] == 'D':        
            home = x['HomeTeam']
            away = x['AwayTeam']
            x['points_' + home] += 1
            x['points_' + away] += 1
            return x
    

    然后在代码末尾添加:

    df = df.apply(point_updater,axis=1)
    for team in teams:
        df["points_" + team]= df["points_" + team].cumsum()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多