【问题标题】:Merging with pandas : left_on with a date and right_on with the oldest time range having this date与 pandas 合并:left_on 与日期和 right_on 与具有此日期的最旧时间范围
【发布时间】:2021-09-21 18:43:15
【问题描述】:

让我们来看看这两个示例数据框:

df1 = pd.DataFrame({'Id':['A','A','B','C'], 'Date':["2020-03-01","2021-04-21","2020-12-10","2017-01-01"]})

  Id        Date
0  A  2020-03-01
1  A  2021-04-21
2  B  2020-12-10
3  C  2017-01-01

df2=pd.DataFrame({'Id':['A','A','B'], 'Start':["2020-01-01","2020-02-21","2019-12-10"],
                 'End':["2021-01-01","2021-02-21","2021-12-10"], "Value":[1,2,3]})

  Id       Start         End  Value
0  A  2020-01-01  2021-01-01      1
1  A  2020-02-21  2021-02-21      2
2  B  2019-12-10  2021-12-10      3

我想向 df1 添加一个值列。相应的值可以在 df2 中找到,具有相同的 Id 并且日期(在 df1 中)是否在开始和结束之间(在 df2 中)。如果有多种可能性,我想采用最旧开始日期的值。

我目前使用 for 循环来执行此操作,但是对于我真正的大数据框来说它非常慢,而且我有直觉认为我们可以通过左连接来做到这一点,但我不知道怎么做。请给个主意好吗?

预期输出:

  Id        Date  Valeur
0  A  2020-03-01     1.0
1  A  2021-04-21     NaN
2  B  2020-12-10     3.0
3  C  2017-01-01     NaN

【问题讨论】:

  • @ifly6 - 仅当“Id”为数字时才有效
  • IDby 变量,而不是 on 变量。没有任何要求它是数字的。 Date 列需要是连续变量,但支持日期。
  • @ifly6:我真的很想看到一个使用 OP 数据的工作示例
  • @mozway 我再次查看了这个问题,我最初的判断是错误的:OP 正在寻找最旧的匹配(在日期范围内的匹配)而不是最新的匹配。无论如何,它也不会是一个单行过程:as-of 合并需要在匹配日期超过结束日期的地方估算 NA。

标签: python pandas dataframe merge


【解决方案1】:

使用.merge() + .between() + drop_duplicates()

# Sort if not already in `Id`, `Start` order
#df2 = df2.sort_values(by=['Id', 'Start'])

df3 = df1.merge(df2, on='Id')
df3_filtered = df3.loc[df3['Date'].between(df3['Start'], df3['End'])]

df4 = df3_filtered.drop_duplicates(['Id', 'Date'], keep='first')

df_out = df1.merge(df4[['Id', 'Date', 'Value']], how='left')

结果:

print(df_out)

  Id        Date  Value
0  A  2020-03-01    1.0
1  A  2021-04-21    NaN
2  B  2020-12-10    3.0
3  C  2017-01-01    NaN

【讨论】:

    【解决方案2】:

    我最近回复了similar (although a bit different) question

    让我们make sure that dates are datetime 并按开始对 df2 进行排序:

    df1['Date'] = pd.to_datetime(df1['Date'])
    df2['Start'] = pd.to_datetime(df2['Start'])
    df2['End'] = pd.to_datetime(df2['End'])
    df2.sort_values(by='Start', inplace=True)
    

    将 df2 的索引设为 IntervalIndex:

    df2.index = pd.IntervalIndex.from_arrays(df2['Start'], df2['End'],closed='both')
    

    制作自定义函数并应用于行:

    def get_date(s):
        try:
            d = df2.loc[s['Date']]
            return d[d['Id'] == s['Id']].iloc[0]['Value']
        except KeyError:
            pass
    
    df1['Value'] = df1.apply(get_date, axis=1)
    

    输出:

      Id       Date  Value
    0  A 2020-03-01    1.0
    1  A 2021-04-21    NaN
    2  B 2020-12-10    3.0
    3  C 2017-01-01    NaN
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 2013-01-18
      • 1970-01-01
      • 2010-10-22
      • 2013-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多