我有两个数据框,我想根据日期合并它们。我希望 df2 的任何行与 df1 的行匹配,条件是 df2 条目上的日期位于 df1 条目日期的 5 年之前的窗口内。
详细来说,假设 df1 看起来像这样:
公司ID | 确认日期 |
---|---|
12345 | 1994年11月30日 |
12345 | 1995年3月31日 |
67890 | 1994年12月31日 |
67890 | 1995年3月31日 |
df2 看起来像这样:
公司ID | 专利 ID | 专利申请日期 |
---|---|---|
12345 | 14526368 | 1989年12月15日 |
12345 | 75896328 | 1994年5月6日 |
12345 | 42589651 | 1995年2月20日 |
67890 | 54216987 | 1990年2月20日 |
67890 | 65874139 | 1993年7月12日 |
67890 | 65874139 | 1994年5月17日 |
67890 | 58792543 | 1995年2月13日 |
所需的输出如下所示:
公司ID | 数据日期 | 专利 ID | 专利申请日期 |
---|---|---|---|
12345 | 1994年11月30日 | 14526368 | 1989年12月15日 |
12345 | 1994年11月30日 | 75896328 | 1994年5月6日 |
12345 | 1995年3月31日 | 75896328 | 1994年5月6日 |
12345 | 1995年3月31日 | 42589651 | 1995年2月20日 |
67890 | 1994年12月31日 | 54216987 | 1990年2月20日 |
67890 | 1994年12月31日 | 65874139 | 1993年7月12日 |
67890 | 1995年3月31日 | 65874139 | 1993年7月12日 |
67890 | 1995年3月31日 | 65874139 | 1994年5月17日 |
67890 | 1995年3月31日 | 58792543 | 1995年2月13日 |
如您所见,df2 中的一行可以与 df1 中的多行匹配,只要其日期位于 df1 日期的 5 年之前的窗口内即可。
到目前为止,我已经想出了以下代码:
df_merge = pd.merge_asof(df2, df1 , left_on='patent_filing_date' , right_on='datadate',
direction='forward', tolerance=pd.Timedelta(1824, unit="d"), by='firm_id')
问题是,上面的代码只匹配从 df2 到 df1 的每一行一次,并且在必要时不会重复这些行。也就是说,看起来我得到了以下内容:
公司ID | 数据日期 | 专利 ID | 专利申请日期 |
---|---|---|---|
12345 | 1994年11月30日 | 14526368 | 1989年12月15日 |
12345 | 1994年11月30日 | 75896328 | 1994年5月6日 |
67890 | 1994年12月31日 | 54216987 | 1990年2月20日 |
67890 | 1994年12月31日 | 65874139 | 1993年7月12日 |
67890 | 1995年3月31日 | 65874139 | 1994年5月17日 |
67890 | 1995年3月31日 | 58792543 | 1995年2月13日 |
然而,我希望 df2 中的每个条目与 df1 中所有可能的条目匹配,无论它们是否与 df1 的另一行匹配。
感谢任何有关 merge as of 或替代功能的帮助。 谢谢
无法使用
merge_asof
并返回多个匹配项。 您可以对 df1
中的每一行应用一个函数来过滤专利数据 (df2
):
def merge_firm_patents(
firm_id: int,
firm_date: dt.date,
patent_data: pd.DataFrame,
date_tolerance: pd.Timedelta
) -> pd.DataFrame:
# filter patent df for patent filing date within date range and for firm_id
active_patents = patent_data[
(patent_data["patent_filing_date"].le(firm_date))
& (patent_data["patent_filing_date"].ge(firm_date - date_tolerance))
& patent_data["firm_id"].eq(firm_id)
]
# assign the datadate column and keep only last row for each patent ID
# note this assumes patent_data sorted by date already
return (
active_patents
.assign(datadate=firm_date)
.drop_duplicates("patent_id", keep="last")
)
# apply function per row, and concatenate dataframe outputs
df_merged = pd.concat(
df1.apply(
lambda x: merge_firm_patents(
x.firm_id, x.firm_date, df2, pd.Timedelta(1824, unit="d")
),
axis=1
)
.values
)