【问题标题】:What is the best way to map each row in a DF to a row from another DF after comparing the first row with multiple rows from the second DF?在将第一行与第二个 DF 中的多行进行比较之后,将 DF 中的每一行映射到另一个 DF 中的行的最佳方法是什么?
【发布时间】:2021-09-02 03:06:14
【问题描述】:

我有两个 DataFrame,一个包含不同的日期和不同的 player_id。第二个包含每个玩家评分的历史记录,这意味着它包含不同的日期和不同的 player_id 和 player_rating ,每行都有唯一的 id。

现在,我想通过比较两个 DataFrame 中的日期列,向第一个 DataFrame 添加一个值为 player_rating 的列。

例如:

print(df_1)

   date  player_id
0  2006          1
1  2006          2
2  2012          3
3  2008          2
4  2010          1

print(df_2)

   date  player_id  player_rating
0  2005          1             50
1  2007          2             55
2  2005          3             70
3  2005          2             65
4  2009          1             60
5  2011          2             75
6  2011          3             85

在这里,我应该将 df_1 中的第一行与 df_2 中的第一和第五行(相同的 player_id)进行比较,然后从 df_2 中选择更新日期最新且小于 df_1 日期的行。所以,我应该选择第一行并将 player_rating 设置为 50。

最后,我应该得到:

print(df_1)
   date  player_id  player_rating
0  2006          1             50
1  2006          2             65
2  2012          3             85
3  2008          2             55
4  2010          1             60

我尝试了很多解决方案并得到了以下想法,但是相对于一个巨大的数据集(大约一百万行 df_1 和 +100k 行 df_2)运行需要很长时间:

def get_rating(date, player_id):
    return df_2[(df_2['player_id']==player_id) & (df_2['date'] < date)].sort_values(by=['date'], ascending=False).player_rating.iloc[0]
df_1['player_rating'] = [get_rating(x,y) for x, y in zip(df_1['date'], df_1['player_id'])]

如果有更好的方法来节省一些时间? 提前谢谢你。

【问题讨论】:

    标签: pandas dataframe


    【解决方案1】:

    您可能需要测试效率:

    (df1.merge(df2, on='player_id', how = 'left', suffixes=(None, '_y'))
        .sort_values(['date', 'date_y'])
        .query('date > date_y')
        .groupby(['date', 'player_id'])
        .tail(1)
        .drop(columns='date_y')
        .sort_index()
      )
     
         date  player_id  player_rating
    0   2006          1             50
    3   2006          2             65
    6   2012          3             85
    7   2008          2             55
    11  2010          1             60
    

    【讨论】:

    • 非常好的解决方案。对于更大的数据框,查询后的排序可能会更快。
    • 使用合并和尾部是非常简单的解决方案。正如 rapani 所说,在查询后使用排序很快。对于 df_1 的 +25k 行,使用第一种解决方案耗时 2 秒,查询后排序耗时 1.8 秒。谢谢你们。
    【解决方案2】:

    在这里您可以尝试合并player_id 上的两个数据框,然后使用您关于日期的逻辑。

    数据

    import pandas as pd
    df1 = pd.DataFrame(
        {'date': {0: 2006, 1: 2006, 2: 2012, 3: 2008, 4: 2010},
         'player_id': {0: 1, 1: 2, 2: 3, 3: 2, 4: 1}})
    
    df2 =  pd.DataFrame(
        {'date': {0: 2005, 1: 2007, 2: 2005, 3: 2005, 4: 2009, 5: 2011, 6: 2011},
         'player_id': {0: 1, 1: 2, 2: 3, 3: 2, 4: 1, 5: 2, 6: 3},
         'player_rating': {0: 50, 1: 55, 2: 70, 3: 65, 4: 60, 5: 75, 6: 85}})
    

    合并

    df = pd.merge(df1, df2, on=["player_id"])
    

    数据的样子

        date_x  player_id  date_y  player_rating
    0     2006          1    2005             50
    1     2006          1    2009             60
    2     2010          1    2005             50
    3     2010          1    2009             60
    4     2006          2    2007             55
    5     2006          2    2005             65
    6     2006          2    2011             75
    7     2008          2    2007             55
    8     2008          2    2005             65
    9     2008          2    2011             75
    10    2012          3    2005             70
    11    2012          3    2011             85
    

    过滤器

    现在你要求date_x大于date_y

    df = df[df["date_x"].gt(df["date_y"])].reset_index(drop=True)
    

    你有

    
       date_x  player_id  date_y  player_rating
    0    2006          1    2005             50
    1    2010          1    2005             50
    2    2010          1    2009             60
    3    2006          2    2005             65
    4    2008          2    2007             55
    5    2008          2    2005             65
    6    2012          3    2005             70
    7    2012          3    2011             85
    

    使用 groupby 查找每年的最新日期

    这里我们查找组内 date_y 较大的索引。

    grp = df.groupby(["date_x", "player_id"])["date_y"].idxmax()
    print(grp)
    
    date_x  player_id
    2006    1            0
            2            3
    2008    2            4
    2010    1            2
    2012    3            7
    Name: date_y, dtype: int64
    

    所以我们用这些索引过滤

    df = df[df.index.isin(grp.values)].reset_index(drop=True)
    

    看起来像

       date_x  player_id  date_y  player_rating
    0    2006          1    2005             50
    1    2010          1    2009             60
    2    2006          2    2005             65
    3    2008          2    2007             55
    4    2012          3    2011             85
    

    我们删除列并重命名第一个。

    df = df.drop(columns=["date_y"])\
        .rename(columns={"date_x":"date"})
    

    返回

    
       date  player_id  player_rating
    0  2006          1             50
    1  2010          1             60
    2  2006          2             65
    3  2008          2             55
    4  2012          3             85
    

    【讨论】:

      猜你喜欢
      • 2019-11-27
      • 2021-08-24
      • 2020-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      相关资源
      最近更新 更多