You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数千万行网格配对表的高效多表内连接优化方案问询

高效处理大规模网格配对表的全局内连接问题

我需要对数千万行规模的多张网格配对表执行全局内连接,常规的pandas/polars标准连接在数据量提升后性能瓶颈明显,但这类表存在特殊关联特性,应该有更高效的优化空间。

网格配对表的定义

这类表表示两个网格索引的匹配关系,其中一列存储单个索引,另一列存储与之匹配的多个索引列表。例如:

AB
01, 2
14, 5
37

含义是:A[0]匹配B[1]、B[2];A[1]匹配B[4]、B[5];A[3]匹配B[7]。

当处理N个网格时,会生成C(N,2)张两两配对表,最终需要得到满足所有网格间两两匹配约束的全局索引组合。

示例与现有实现

以3个网格A、B、C为例,对应的3张配对表如下:

AB表

AB
01, 2
14, 5
37

AC表

AC
01, 2, 3
12

BC表

BC
11, 2
21, 2
51, 2, 3

最终需要的全局内连接结果是同时满足AB、AC、BC匹配约束的索引组合,有两种等价呈现形式:

压缩格式

ABC
01, 21, 2
152

展开格式

ABC
011
012
021
022
152

现有Pandas实现代码

from functools import reduce
import pandas as pd 

def create_table(indices: dict, names: list) -> pd.MultiIndex:
    table = pd.DataFrame.from_dict(indices, orient='index').stack().astype(int)
    index = table.reset_index().set_index(['level_0', 0]).index
    return index.set_names(names)
    
AB = create_table({
    0: [1, 2],
    1: [4, 5],
    3: [7],
}, ['A', 'B'])

AC = create_table({
    0: [1, 2, 3],
    1: [2],
}, ['A', 'C'])

BC = create_table({
    1: [1, 2],
    2: [1, 2],
    5: [1, 2, 3],
}, ['B', 'C'])

join   = lambda df1, df2: df1.join(df2, how='inner')
result = reduce(join, [AB, AC, BC]).reorder_levels(['A', 'B', 'C'])
print(result)

运行输出:

MultiIndex([(0, 1, 1),
            (0, 1, 2),
            (0, 2, 1),
            (0, 2, 2),
            (1, 5, 2)],
           names=['A', 'B', 'C'])

性能瓶颈

小规模数据下上述方案表现正常,但处理数百万行的多张表时,问题凸显:

  • 如果仅以单个网格为锚点(比如只做A-B、A-C连接,不校验B-C约束),我有一个几秒内完成、内存占用极低的方案;
  • 但添加全局两两匹配约束后,必须展开所有列表执行内连接,资源消耗直接提升一个数量级以上。

优化思路尝试

我考虑保留表格的压缩格式(列元素为列表),避免展开带来的内存爆炸,但需要实现压缩格式下的高效表转置操作,例如:

| A | B | | B | A |
| - | ---- | => | - | ---- |
| 0 | 1, 2 | | 1 | 0 |
| 1 | 1 | | 2 | 0, 1 |

贴近真实场景的Polars压力测试代码

from sklearn.neighbors import BallTree
from itertools import combinations
from functools import reduce
import numpy as np 
import polars as pl 
import time

def create_table_pl(indices, names: list):
    c1 = np.repeat(np.arange(len(indices), dtype='int64'), list(map(len, indices)) )
    c2 = np.concatenate(indices).astype(dtype='int64')
    print(f'{names}: {len(c2):,} rows')
    return pl.DataFrame(dict(zip(names, [c1,c2]))).set_sorted(names[0]).lazy()

def match(grids): 
    """ Use a BallTree to find close elements between grids """
    return BallTree(grids[1][:, None], p=np.inf).query_radius(grids[0][:, None], 5)

n = 500000
grids = np.arange(n), np.arange(0,n,2), np.arange(0,n,3), np.arange(0,n,5)
names = list('abcd')
pairs = list(map(create_table_pl, map(match, combinations(grids, 2)), combinations(names, 2)))

start = time.time()
join  = lambda df1, df2: df1.join(df2, on=set(df2.columns).intersection(set(df1.columns)))
table = reduce(join, pairs).select(names).sort(names)

print(table.collect())
print(f'{time.time()-start:.2f} seconds')

运行输出:

('a', 'b'): 2,749,985 rows
('a', 'c'): 1,833,325 rows
('a', 'd'): 1,099,994 rows
('b', 'c'): 916,662 rows
('b', 'd'): 549,997 rows
('c', 'd'): 366,665 rows
shape: (11_183_244, 4)
┌────────┬────────┬────────┬───────┐
│ a      ┆ b      ┆ c      ┆ d     │
│ ---    ┆ ---    ┆ ---    ┆ ---   │
│ i64    ┆ i64    ┆ i64    ┆ i64   │
╞════════╪════════╪════════╪═══════╡
│ 0      ┆ 0      ┆ 0      ┆ 0     │
│ 0      ┆ 0      ┆ 0      ┆ 1     │
│ 0      ┆ 0      ┆ 1      ┆ 0     │
│ 0      ┆ 0      ┆ 1      ┆ 1     │
│ …      ┆ …      ┆ …      ┆ …     │
│ 499999 ┆ 249998 ┆ 166665 ┆ 99999 │
│ 499999 ┆ 249998 ┆ 166666 ┆ 99999 │
│ 499999 ┆ 249999 ┆ 166665 ┆ 99999 │
│ 499999 ┆ 249999 ┆ 166666 ┆ 99999 │
└────────┴────────┴────────┴───────┘
1.66 seconds

图结构等价思路

整个问题可以等价转换为图结构问题:每个网格的索引是节点,配对表中的匹配关系是边,最终的全局匹配组合对应图中的全连接环。但目前不确定这条思路能否带来更高效的解决方案。


内容的提问来源于stack exchange,提问作者brandonsmithj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 13:51:58