在O(N)时间复杂度或更高的情况下,从熊猫数据帧中查找最接近的元组对(在容差范围内)

Finding the closest pair (within a tolerance) of tuple from a pandas data frame in O(N) time complexity or better

提问人:Stan 提问时间:9/30/2023 最后编辑:halferStan 更新时间:10/1/2023 访问量:69

问:

我面临的问题可能在循环中可以实现,但是,我需要找出一个具有最大 O(N) 时间复杂度的 pythonic 解决方案:

所以问题是这样的:

我有一个数据帧(我称之为查找)说如下:(它可能是一个非常大的数据帧,有 100M + 行)(我的 A 和 B 是要搜索的列,M、N 是我将要搜索的值,最后 col V 是我将提取的值)。所以让我明确一点。为了我们的论点,我只显示 5 行:

 lookup_df 

    A     B     M      N        V

    a1    b1    2.6   12.7     200.6
    u1    v1    4.5   19       145
    a2    b2    3.2   15.9     100
    a3    b3    5.5   21.5     45
    a7    b7    6.8   41.8     90
    a10   b10   70.0  120.5    123

所以现在的问题是我有另一个数据框,我称之为输入。因此,它可以具有确切的 A 和 B 列值,也可以具有一些新的 A 和 B 值。因此,输入可能看起来像这样(比如说)。我在这里还显示 5 行来说明这一点:

  input_df 

     A     B     M      N        

     u1   v1     4.5    19
     u1   v1     3.0    16.2
     a3   b3     5.5    21.5 
     a3   x1     7.0    41.5
     x7   y2    69.8   120.1

所以现在要解决的问题是,每当我在 A、B、M、N 之间的查找和输入数据帧中的值完全匹配时,我的 V 列就会按预期从查找中获取值。但是,例如,在输入数据帧的第二行中,A和B匹配,但M和N值不同。所以最接近的(0.3 说是我的阈值)是查找表的第 3 行,对应于 a2 和 b2 的 V,所以我选择了 V。输入中的 a3 和 b3 再次与查找 M 列和 N 列完全匹配,因此我可以拾取该 V。对于输入中的最后两行,此示例查找数据帧中没有行,因此我再次使用查找中最接近的值,对于 a3,x1 恰好是 a7,b7 的 V 中的值,对于 x7,y2 与 a10、b10 的值匹配。

因此,我的输入的输出 df 应如下所示:

 output_df 

     A     B     M      N        V

     u1   v1     4.5    19       145
     u1   v1     3.0    16.2     100
     a3   b3     5.5    21.5      45
     a3   x1     7.0    41.5      90
     x7   y2    69.8   120.1      123

所以我的查找数据框是巨大的(100M +),具有所有可能的组合。我的输入数据框可能没有那么大,可能是 10000 行,但是,假设我使用 0.3 的阈值来查找最近的一对,我怎样才能有一个有效的搜索代码来扫描查找以获得我想要的输出。

Python Pandas 索引 分组 查找

评论

0赞 AKX 9/30/2023
我很确定你不能到这里来。O(N)
0赞 Stan 9/30/2023
谢谢你@AKX。即使 mot O(N) 也能在 Python 中有效地使用任何算法?
0赞 AKX 9/30/2023
如果你有两个“坐标”(M、N),你可能正在寻找某种空间查找结构:en.wikipedia.org/wiki/Spatial_database#Spatial_index
0赞 Timeless 10/1/2023
@Stan,请查看 Quang Hoang 的答案 stackoverflow.com/a/73896664/16120011
0赞 Andrej Kesely 10/1/2023
@Stan 公差仅适用于列 ?0.3M

答:

1赞 Andrej Kesely 10/1/2023 #1

您可以将合并拆分为几个步骤:

1.尝试找到确切的合并

exact_matches = pd.merge(input_df, lookup_df, on=["A", "B", "M", "N"])
print(exact_matches)

指纹:

    A   B    M     N      V
0  u1  v1  4.5  19.0  145.0
1  a3  b3  5.5  21.5   45.0

2. 从中排除完全匹配项input_df

# exclude exact matches from input_df
input_df = input_df.loc[~input_df.index.isin(exact_matches.index)]

3. 合并方式M

# merge M
lookup_df = lookup_df.sort_values(by="M")
input_df = input_df.sort_values(by="M")

M_V = pd.merge_asof(
    input_df, lookup_df[["M", "V"]], on="M", direction="nearest", tolerance=0.3
)[["A", "B", "M", "N", "V"]]

4. 合并方式N

# merge N
lookup_df = lookup_df.sort_values(by="N")
input_df = input_df.sort_values(by="N")

N_V = pd.merge_asof(
    input_df, lookup_df[["N", "V"]], on="N", direction="nearest", tolerance=0.3
)[["V"]]

vals = pd.concat([M_V, N_V[["V"]].add_suffix("_2")], axis=1)
print(vals)

这打印:

    A   B     M      N      V   V_2
0  a3  b3   5.5   21.5   45.0  45.0
1  a3  x1   7.0   41.5   90.0  90.0
2  x7  y2  69.8  120.1  123.0   NaN

4. 将精确匹配和公差匹配组合在一起

但在这里你必须指定逻辑(如果 / 的值不同怎么办?现在我简单地将它们连接起来:VV_2

print(pd.concat([exact_matches, vals]))

指纹:

    A   B     M      N      V   V_2
0  u1  v1   4.5   19.0  145.0   NaN
1  a3  b3   5.5   21.5   45.0   NaN
0  a3  b3   5.5   21.5   45.0  45.0
1  a3  x1   7.0   41.5   90.0  90.0
2  x7  y2  69.8  120.1  123.0   NaN

评论

1赞 Stan 10/1/2023
谢谢你@Andrej Kesely。我将实施并随时向您发布。