【问题标题】:Pandas fuzzy merge by int coordinatesPandas 通过 int 坐标进行模糊合并
【发布时间】:2020-01-16 08:14:23
【问题描述】:

假设我们有 2 个坐标为 ['X','Y'] 的数据框:

df1:

 X            Y          House №
2531        2016           175
2219        2196           11
2901        3426           201
6901        4431           46
7891        1126           89

df2:

 X            Y      Delivery office №
2534        2019            O1
6911        4421            O2
2901        3426            O3
7894.5      1120            O4 

我的想法是合并它们并得到:

df3

 X            Y          House №    Delivery office №
2531        2016           175            01
2219        2196           11             NA
2901        3426           201            03
6901        4431           46             02
7891        1126           89             04

所以我们想通过阈值实现'模糊'合并(这个参数应该由用户给出)。您可以看到 11 号门牌没有得到任何送货办公室号码,因为它距离 df2 中所有显示的办公室太远了。

所以我需要 df2 中的所有行 'find' 它是最接近 df1 的行并将它的 'Cost' 值添加到它 您可以看到通常的内置 pd.merge 以及使用 levenshtein 距离等实现模糊逻辑与字符串值相关的自定义包在那里不起作用

【问题讨论】:

标签: python pandas left-join coordinates


【解决方案1】:

我认为以下应该可以解决问题,计算 欧几里得距离以及 left-outer join,不需要使用任何 模糊逻辑 虽然:

import pandas as pd
import numpy as np

df1 = pd.DataFrame([[2531,2016,175],[2219,2196,11],[2901,3426,201],[6901,4431,46],[7891,1126,89]], columns = ['X','Y','House'])
df2 = pd.DataFrame([[2534,2019,'O1'],[6911,4421,'O2'],[2901,3426,'03'],[7894.5,1120,'O4']], columns = ['X','Y','Delivery office'])
threshold = 20

df = df1.assign(key=1).merge(df2.assign(key=1), on="key").drop("key", axis=1)
df['dist'] = df.apply(lambda row: np.linalg.norm(row[['X_x', 'Y_x']].values - row[['X_y','Y_y']].values), axis=1)
df = df.sort_values(by=['dist'])
print(df.head())
#     X_x   Y_x  House     X_y   Y_y Delivery office        dist
#10  2901  3426    201  2901.0  3426              03    0.000000
#0   2531  2016    175  2534.0  2019              O1    4.242641
#19  7891  1126     89  7894.5  1120              O4    6.946222
#13  6901  4431     46  6911.0  4421              O2   14.142136
#4   2219  2196     11  2534.0  2019              O1  361.322571

df = pd.merge(df1, df[df.dist < threshold], left_on=['X','Y','House'], right_on=['X_x','Y_x','House'], how='left')[['X','Y','House','Delivery office']]
print(df.head())
#      X     Y  House Delivery office
#0  2531  2016    175              O1
#1  2219  2196     11             NaN
#2  2901  3426    201              03
#3  6901  4431     46              O2
#4  7891  1126     89              O4

编辑

发布另一个解决方案,这次不使用 join - 执行时间会更昂贵,但空间复杂度会更低。

res = df1.apply(lambda row: df2.apply(lambda row2: np.linalg.norm(row[['X', 'Y']].values - row2[['X', 'Y']].values), axis=1), axis=1)
df1['dist'] = res.min(axis=1)
df1['Delivery office'] = df2.iloc[res.idxmin(axis=1).values,:]['Delivery office'].values
df1.loc[df1.dist >= threshold, 'Delivery office'] = np.nan
print(df1.head())
#       X     Y  House        dist Delivery office
# 0  2531  2016    175    4.242641              O1
# 1  2219  2196     11  361.322571             NaN
# 2  2901  3426    201    0.000000              03
# 3  6901  4431     46   14.142136              O2
# 4  7891  1126     89    6.946222              O4
df1 = df1[['X','Y','House','Delivery office']]

【讨论】:

  • 好的,谢谢。无法在我的数据上测试您的解决方案,因为它崩溃(内存错误),“df”突然变大。
  • 发布了另一个空间复杂度更低、时间复杂度更高的解决方案,我认为你应该可以测试一下@Андрей Севостьянов
猜你喜欢
  • 1970-01-01
  • 2023-03-21
相关资源
最近更新 更多