稍微松弛地合并两个数据帧
Merging two dataframes with a bit of slack
假设我有以下数据框,它是通过记录某种事件生成的,按“clazz”列分类。
test_data = [
[1, "A", "x", "WH", "2021-10-01 04:58:27"],
[2, "A", "x", "OR", "2021-10-01 04:58:28"],
[3, "B", "x", "WH", "2021-10-01 04:58:27"],
[4, "B", "x", "OR", "2021-10-01 04:58:27"],
[5, "A", "y", "WH", "2021-10-01 04:58:27"],
[6, "A", "y", "OR", "2021-10-01 04:58:27"]
]
test_df = pd.DataFrame(test_data, columns=["id", "cat1", "cat2", "clazz", "date"])
有两个 类,WH 和 OR 如果 cat1 和 cat2 相等并且它们同时发生,它们在逻辑上属于一起。
通常我会像下面这样合并它们:
wh = test_data[test_data.clazz == "WH"]
oh = test_data[test_data.clazz == "OH"]
wh.merge(right=oh, how="left", on=["cat1", "cat2", "date"])
不幸的是,由于记录数据时有些不准确,有时日期会相差大约一秒。因此,id 为 1 和 2 的行将不会被匹配。
有没有一种简单高效的方法可以在合并时以某种方式更正此问题?
我在 post 此处 is it possible to do fuzzy match merge with python pandas? 找到了类似的方向。
在那里我尝试创建一个连接列,由 cat1、cat2 和日期的串联组成,并尝试更正如下值:
oh["fuzz"] = (oh.cat1 + oh.cat2 + oh.str.date)
wh["fuzz"] = (wh.cat1 + wh.cat2 + wh.str.date)
oh.fuzz = oh.fuzz.map(lambda x: difflib.get_close_matches(x, wh.fuzz, n=2)[0])
这可行,但不幸的是它非常慢。我将我的示例分解为非常短的时间段,其中 wh 只剩下大约 4000 个条目,并且对一行执行 get_close_matches 花费了大约 500 毫秒,这不适用于我大约 300 万的数据集。
谢谢
您可以使用 merge_asof
:
test_df['date'] = pd.to_datetime(test_df['date'])
test_df = test_df.sort_values('date')
wh = test_df[test_df.clazz == "WH"]
oh = test_df[test_df.clazz == "OR"]
df = pd.merge_asof(wh,
oh,
by=["cat1", "cat2"],
on='date',
tolerance = pd.Timedelta(1, unit='s'),
direction='nearest')
print (df)
id_x cat1 cat2 clazz_x date id_y clazz_y
0 1 A x WH 2021-10-01 04:58:27 2 OR
1 3 B x WH 2021-10-01 04:58:27 4 OR
2 5 A y WH 2021-10-01 04:58:27 6 OR
假设我有以下数据框,它是通过记录某种事件生成的,按“clazz”列分类。
test_data = [
[1, "A", "x", "WH", "2021-10-01 04:58:27"],
[2, "A", "x", "OR", "2021-10-01 04:58:28"],
[3, "B", "x", "WH", "2021-10-01 04:58:27"],
[4, "B", "x", "OR", "2021-10-01 04:58:27"],
[5, "A", "y", "WH", "2021-10-01 04:58:27"],
[6, "A", "y", "OR", "2021-10-01 04:58:27"]
]
test_df = pd.DataFrame(test_data, columns=["id", "cat1", "cat2", "clazz", "date"])
有两个 类,WH 和 OR 如果 cat1 和 cat2 相等并且它们同时发生,它们在逻辑上属于一起。
通常我会像下面这样合并它们:
wh = test_data[test_data.clazz == "WH"]
oh = test_data[test_data.clazz == "OH"]
wh.merge(right=oh, how="left", on=["cat1", "cat2", "date"])
不幸的是,由于记录数据时有些不准确,有时日期会相差大约一秒。因此,id 为 1 和 2 的行将不会被匹配。
有没有一种简单高效的方法可以在合并时以某种方式更正此问题?
我在 post 此处 is it possible to do fuzzy match merge with python pandas? 找到了类似的方向。 在那里我尝试创建一个连接列,由 cat1、cat2 和日期的串联组成,并尝试更正如下值:
oh["fuzz"] = (oh.cat1 + oh.cat2 + oh.str.date)
wh["fuzz"] = (wh.cat1 + wh.cat2 + wh.str.date)
oh.fuzz = oh.fuzz.map(lambda x: difflib.get_close_matches(x, wh.fuzz, n=2)[0])
这可行,但不幸的是它非常慢。我将我的示例分解为非常短的时间段,其中 wh 只剩下大约 4000 个条目,并且对一行执行 get_close_matches 花费了大约 500 毫秒,这不适用于我大约 300 万的数据集。
谢谢
您可以使用 merge_asof
:
test_df['date'] = pd.to_datetime(test_df['date'])
test_df = test_df.sort_values('date')
wh = test_df[test_df.clazz == "WH"]
oh = test_df[test_df.clazz == "OR"]
df = pd.merge_asof(wh,
oh,
by=["cat1", "cat2"],
on='date',
tolerance = pd.Timedelta(1, unit='s'),
direction='nearest')
print (df)
id_x cat1 cat2 clazz_x date id_y clazz_y
0 1 A x WH 2021-10-01 04:58:27 2 OR
1 3 B x WH 2021-10-01 04:58:27 4 OR
2 5 A y WH 2021-10-01 04:58:27 6 OR