首页 > 解决方案 > 映射大 DF

问题描述

我有两个数据框需要映射并合并为一个。第一个数据框包含 NBA 球员。标题是“日期”、“球员”、“团队”、“职位”、“薪水”、“职位 ID”、“分钟”、“FPTS”、“USG”。然后我有第二个数据框,它是第一个数据框,但按日期和团队分组。此 df 的标题是“日期”、“团队”、“分钟”、“FGA”、“FTA”、“至”。我正在尝试计算第一个数据帧中每个玩家的 USG 率。为此,我需要知道给定日期每场比赛中每支球队的总分钟数、投篮次数、罚球次数和失误率。然后,我将同一球员的统计数据除以球队的总统计数据。我有一个可行的解决方案,但它'

这是代码:

import pandas as pd

player_df = pd.read_csv('Sample Data') # replace with sample data file

no_dups = player_df.drop_duplicates()
no_dups.loc[:, 'USG'] = pd.Series(dtype=float)
no_dups = no_dups[no_dups.Minutes != 0]

grouped_teams = no_dups.groupby(['Date', 'Team']).agg({'Minutes':['sum'], 'FGA': ['sum'], 'FTA': ['sum'], 'TO': ['sum'] })
grouped_teams.columns = ['Minutes', 'FGA', 'FTA', 'TO']
grouped_teams = grouped_teams.reset_index()

for index, row in no_dups.iterrows():
    for i, r in grouped_teams.iterrows():
        if no_dups.at[index, 'Team'] == grouped_teams.at[i, 'Team'] and no_dups.at[index, 'Date'] == grouped_teams.at[i, 'Date']:
            no_dups.at[index, 'USG'] = (100*((no_dups.at[index, 'FGA'] + 0.44 * no_dups.at[index, 'FTA'] + no_dups.at[index, 'TO'])*(grouped_teams.at[i, 'Minutes']/5))) / (no_dups.at[index, 'Minutes']*(grouped_teams.at[i, 'FGA']+0.44*grouped_teams.at[i, 'FTA']+grouped_teams.at[i, 'TO']))
    
final_df = no_dups[['Date', 'Player', 'Team', 'Position', 'Salary',  'Minutes', 'FPTS', 'USG']]

print(final_df)

我删除了所有没有参加比赛的球员,并且有重复的球员,因为同一名球员可以在一个晚上参加多场比赛,所以我删除了这些球员。然后我创建一个名为 df 的 df grouped_teams,它是 df 中按日期和团队名称分组的每个团队。iterrows然后,我以相同的方式使用和第二个 df迭代第一个 df 。我需要找到每个球员的球队和具体日期,并将他的统计数据除以计算出的总数,以获得使用率。列是no_dups.at[index, 'USG']。我的 df 中有 73k 行,因此对每一行进行迭代需要很长时间。

样本数据

标签: pythonpandasdataframe

解决方案


这需要很长时间,因为您正在逐行迭代。我似乎找不到这篇文章,但我记得在比较迭代数据帧的方法时在某处读过,itertuples 比 iterrows 快约 10 倍,而 zip 快约 100 倍。我有时想从 iterrows 切换到 itertuples 时遇到的问题是,您丢失了作为索引的列名,因此您需要特别确定使用 itertuples 的列的顺序(尽管现在我正在考虑它,我认为有一种方法可以动态地跟踪它)。

但是你可以做到这一点的最快方法是对所有行进行计算,而不是单独进行每一行。

我会做的是,在您的第二个数据框中,您计算​​了团队总数。因此,请进行左连接/合并['Date','Team']以匹配数据帧上的总数no_dups。然后,您可以通过使用整行列来计算它,而不是一次计算 1 行。我还稍微更改了列的名称,就好像你合并并且有相同名称的列一样,它会添加一个后缀_x_y. 有办法解决这个问题,但想通了就直接改名字。我也稍微改变了你命名列的方式,而不是硬编码(这意味着列必须按顺序排列),它可以以更健壮的方式处理名称。

你还有另一个问题。日期列具有不同的格式(即'1/1/18''2018-01-01',因此在您的 groupby 中,它们不会聚合在一起。所以我们需要先处理这个问题。它似乎只与布鲁克林篮网队有关,但在您的完整数据中可能更多放。

代码:

import pandas as pd

player_df = pd.read_csv('Sample Data.csv') # replace with sample data file

# Get the date column to be the same
player_df['Date'] = pd.to_datetime(player_df['Date'])

no_dups = player_df.drop_duplicates()
no_dups = no_dups[no_dups.Minutes != 0]

grouped_teams = no_dups.groupby(['Date', 'Team']).agg({'Minutes':['sum'], 'FGA': ['sum'], 'FTA': ['sum'], 'TO': ['sum'] })
grouped_teams.columns = ['tot_' + col[0] for col in grouped_teams.columns]
grouped_teams = grouped_teams.reset_index()


# Merge grouped_teams to no_dups on Team and Date
no_dups = no_dups.merge(grouped_teams, how='left', on=['Team','Date'])

# Do the calculations
no_dups['USG'] = (100*((no_dups['FGA'] + 0.44 * no_dups['FTA'] + no_dups['TO'])*(no_dups['tot_Minutes']/5))) / (no_dups['Minutes']*(no_dups['tot_FGA']+0.44*no_dups['tot_FTA']+no_dups['tot_TO']))
    
final_df = no_dups[['Date', 'Player', 'Team', 'Position', 'Salary',  'Minutes', 'FPTS', 'USG']]

print(final_df)

时间:

我对每种方式都进行了计时(不包括读取 csv 的时间)。

在样本数据(4493 行)上,迭代次数约为3 minutes 46.66 seconds.
我的代码大约快了0.0568 seconds将近 4000 倍。


推荐阅读