【问题标题】:Matching Closest Date From Same Data Frame匹配来自同一数据框的最近日期
【发布时间】:2019-11-10 00:11:03
【问题描述】:

我正在处理有关大联盟棒球比赛上座率的数据。

我正在尝试在我的数据框中创建一个新列,该列返回指定对手球队比赛的最近日期(但不能晚于给定日期)。

例如,对于包含洛杉矶天使队比赛数据的行:

Game_Num      Date         Team        Win      Attendance      Net Wins
23            2010-04-05   LAA         1        43504           12

我想找到最近的洛杉矶道奇队 ('LAD') 比赛的前一个日期,并将其附加到一个新列中。


我的最终目标是创建另一个列,显示对手球队的净胜场在比赛中的影响,这样我就可以看到另一支球队是否有一个好的赛季,如果它影响门票销售。

这是我迄今为止尝试过的:

for index, row in bbattend.iterrows():
    if row['Team'] == 'LAA':
        basedate = row['Date']
        tempdf = bbattend.loc[(bbattend['Team'] == 'LAD') & (bbattend['Date'] < basedate)]
        tempdf['Datediff'] = abs(basedate-tempdf['Date']).days
        mindiff = tempdf['Datediff'].min()
        bbattend['CloseRivalDate'] = tempdf[tempdf['Date']==mindiff]['Date']
        bbattend['RivalNetWins'] = tempdf[tempdf['Date']==mindiff]['Net_Wins']
        bbattend['RivalWinPer'] = tempdf[tempdf['Date']==mindiff]['Win_Per'] 

这是我从中得到的错误:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-128-f2be88528772> in <module>
      3         basedate = row['Date']
      4         tempdf = bbattend.loc[(bbattend['Team'] == 'LAD') & (bbattend['Date'] < basedate)]
----> 5         tempdf['Datediff'] = abs(basedate-tempdf['Date']).days
      6         mindiff = tempdf['Datediff'].min()
      7         bbattend['CloseRivalDate'] = tempdf[tempdf['Date']==mindiff]['Date']

~/anaconda3/lib/python3.7/site-packages/pandas/core/generic.py in __getattr__(self, name)
   5065             if self._info_axis._can_hold_identifiers_and_holds_name(name):
   5066                 return self[name]
-> 5067             return object.__getattribute__(self, name)
   5068 
   5069     def __setattr__(self, name, value):

AttributeError: 'Series' object has no attribute 'days'





这是我的数据框代码,以防万一有帮助:

import requests
import pandas as pd
import numpy as np
from datetime import datetime
import re

Teams = ['LAA', 'LAD', 'NYY', 'NYM', 'CHC', 'CHW', 'OAK', 'SFG']
Years = []
for year in range(2010,2020):
    Years.append(str(year))

list_of_df = list()

for team in Teams:
    for year in Years:
        url = 'https://www.baseball-reference.com/teams/' + team + '/' + year +'-schedule-scores.shtml'
        dfname = team + '_' + year
        html = requests.get(url).content
        df_list = pd.read_html(html)
        df = df_list[-1]

        #Formatting data table
        df.rename(columns={"Gm#": "GM_Num", "Unnamed: 4": "Home", "Tm": "Team", "D/N": "Night"}, inplace = True)
        df['Home'] = df['Home'].apply(lambda x: 0 if x == '@' else 1)
        df['Game_Win'] = df['W/L'].astype(str).str[0]
        df['Game_Win'] = df['Game_Win'].apply(lambda x: 0 if x == 'L' else 1)
        df['Night'] = df['Night'].apply(lambda x: 1 if x == 'N' else 0)
        df['Streak'] = df['Streak'].apply(lambda x: -1*len(x) if '-' in x else len(x))
        df.drop('Unnamed: 2', axis=1, inplace = True)
        df.drop('Orig. Scheduled', axis=1, inplace = True)
        df.drop('Win', axis=1, inplace = True)
        df.drop('Loss', axis=1, inplace = True)
        df.drop('Save', axis=1, inplace = True)
        #Drop rows that do not have data
        df = df[df['GM_Num'].str.isdigit()]
        WL = df["W-L"].str.split("-", n = 1, expand = True)
        df["Wins"] = WL[0].astype(dtype=np.int64)
        df["Losses"] = WL[1].astype(dtype=np.int64)
        df['Net_Wins'] = df['Wins'] - df['Losses']
        df['Win_Per'] = df['Wins']/(df['Wins']+df['Losses'])
        DayDate = df['Date'].str.split(", ", n = 1, expand = True)
        df['DayOfWeek'] = DayDate[0]
        df['Date'] = DayDate[1] + ', ' + year
        df['Date'] = [re.sub("\s\(\d+\)", "", str(x)) for x in df['Date']]
        df['Date'] = pd.to_datetime(df['Date'], format='%b %d, %Y')
        list_of_df.append(df)

bbattend = pd.concat(list_of_df)
bbattend 

我知道这绝对不是最有效的方法,但它得到了我想要的结果。

【问题讨论】:

  • 你有没有碰巧看到这个thread1thread2
  • 是的,我在我的代码中添加了一些基于这些线程的东西,但是我遇到了我仍然无法解决的错误

标签: python dataframe datetime


【解决方案1】:

您看到的特定错误可以通过替换该行来修复

tempdf['Datediff'] = abs(basedate-tempdf['Date']).days

通过

tempdf['Datediff'] = abs(basedate-tempdf['Date']).dt.days

但是,您的代码仍然会产生不正确的结果。

在我看来,做你想做的事情的正确方法是在你的桌子上执行一个自我加入,你合并TeamOpp,然后计算团队的比赛日期和每个比赛日期之间的日期差。 Opp 在本赛季的比赛,过滤出在球队比赛之前发生的 Opp 比赛,最后只保留与球队比赛最接近的 Opp 比赛。

下面是执行此操作的代码:

# Create column Year to help on self-join
bbattend["Year"] = bbattend.Date.dt.year

# Create merged table
merged = bbattend.merge(
    bbattend[["Date", "Year", "Team", "Net_Wins", "Win_Per"]],
    how="inner",
    left_on=["Year", "Opp"],
    right_on=["Year", "Team"],
    suffixes=('', '_opp')
)
merged["date_diff"] = (merged.Date - merged.Date_opp).dt.days

# Keep only closest game from Opp
def get_closest_date(g):
    row_to_keep = g.date_diff.idxmin()
    return g.loc[row_to_keep, ["Date_opp", "Team_opp", "Net_Wins_opp", "Win_Per_opp", "date_diff"]]

merged.groupby(bbattend.columns.to_list()).apply(get_closest_date).reset_index()

生成的表格将在上一场比赛之后添加带有 Opp 团队的 Net_winsWin_per 的列。

注意事项:

  • 由于使用了内连接,该表将只提供 Team 和 Opp 都在原始表的 Team 列中的游戏。
  • 生成的表有成对的对称行,其中TeamOpp 切换。如果你想摆脱这些,只需过滤Home == 1

最后,这真的是在推动我在 pandas 中所做的事情的极限。如果您的表变大,自联接将需要二次方更多的内存。我建议您将此表加载到关系数据库中(sqlite 将是最简单的使用,因为它带有 Python),并使用 SQL 进行此计算。

【讨论】:

  • 非常感谢。我稍微修改了代码,因为我想匹配同一个市场竞争对手的信息(将洛杉矶天使队与洛杉矶道奇队匹配),所以我创建了一个名为 Same_Mkt_Team 的新列来指定竞争对手,然后将 left_on=["Year", "Opp"] 替换为 left_on=["Year", "Same_Mkt_Team"] ,但是,当我查看生成的数据框时, date_diff 列似乎真的关闭了,并且匹配的日期不是很接近。当我根本没有修改你的代码时,同样的事情发生了。
  • 另外,当我在创建 'date_diff' 列后添加merged = merged[merged['date_diff'] &gt; 0] 时,最终数据似乎错误,因为按日期排序时,有重复的游戏
  • 重复游戏是我在答案中提到的对称方面还是其他什么?
【解决方案2】:

这是我最终使用的最终代码: 它基于@foglerit 的回答

#Create game_id which will be used to delete duplicates later
bbattend['game_id'] = bbattend['Team'] + bbattend['Date'].astype(str)
#Create year variable for matching
bbattend['Year'] = bbattend.Date.dt.year

# Create merged table
# Will match all dates of games of team with dates within same year of teams from same-market team 
merged = bbattend.merge(
    bbattend[["Date", "Year", "Team", "Net_Wins", "Win_Per"]],
    how="inner",
    left_on=["Year", "Same_Mkt_Team"],
    right_on=["Year", "Team"],
    suffixes=('', '_Same_Mkt_Team')
)

merged["date_diff"] = (merged.Date - merged.Date_Same_Mkt_Team).dt.days
#Only keep the dates of same-market team that occurred before the date of home team's game
merged = merged[merged['date_diff'] > 0]

#Sort by date_diff so closest dates appear first
merged.sort_values(by='date_diff', inplace = True)

#Only keep first game_id which will include the data of the same-market team for the closest date before the game
merged.drop_duplicates(subset =['game_id'], keep = 'first', inplace = True)

merged

我添加了 date_diff 必须为正的条件,因为我想要在比赛前发生的同一市场球队的比赛日期。

然后我按 date_diff 对数据帧进行排序,并删除了 game_id 的重复项,这样最终的数据帧就只有最小的 date_diff。

【讨论】:

    猜你喜欢
    • 2020-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-26
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多