所以我从数据库中提取了许多元组列表,格式为:
ts
然后我有一个元组列表,格式为:
dataA = [('A', 'B', 'C', D, E, F), ('A', 'B', 'C', D, E, F), ...]
我需要通过匹配'A'='A','B'='B','C'='C'以及其中D> Y> E来匹配从dataA到dataB的元组。 ,我将使用值“ X”和“ F”。
'A','B','C','X'是最多16个字符的字符串。 D,E,F,Y是整数。
我尝试了许多方法来执行此操作(字符串比较,嵌套的for循环,设置交集),但是它们都很慢。做到这一点的最佳方法是什么?非常感谢
答案 0 :(得分:3)
这里的关键是选择正确的数据结构。 tl; dr是一组间隔树的字典是正确的数据结构,但这对您可能没有什么意义,并且绝对没有解释如何到达那里。
在开始之前……您可以将工作推送到关系数据库吗?毕竟,这些值首先是从数据库中提取的,而优化这些类型的查找正是RDBMS的全部目的。使用正确的索引,SELECT f FROM mytable WHERE a=?, b=?, c=?, ? BETWEEN e and d
应该以对数时间运行。另外,您不需要获取两个表的所有行,而只需获取匹配的行即可。
首先,您只希望其中的前三个值完全匹配的元组。因此,您需要一个以前三个成员为键,并具有匹配值的字典:
dictA = {}
for a, b, c, *d in dataA:
dictA.setdefault((a, b, c), set()).add(tuple(d))
这已经使每个比较的范围缩小到仅对具有正确A, B, C
的元组进行线性搜索,而不是对所有元组进行线性搜索。那可能已经足够了。
如果没有,如何减少线性搜索?
如果您有一个按A, B, C
值排序的排序列表(或二叉搜索树或其他内容),而不是D
的一组元组怎么办?然后,您可以找到第一个D > Y
对数而非线性时间。但是,不幸的是,您仍然必须从头到尾扫描列表的其余部分,因为它们都有D > Y
,并且在测试它们之前,您不知道哪个也有Y > E
所有。因此,您只将总时间减少了50%,而不是对数。
如果您有两个排序列表,一个列表按D
排序,另一个列表按E
排序怎么办?乍一看,这似乎很有希望,但是如何将它们结合起来?
您实际需要做的是将元组分解为不重叠的间隔,每个间隔包含一个或多个元组。例如,如果元组A具有D=4, E=1
,而元组B具有D=6, B=3
,则间隔(1, 3)
与元组{A}
,间隔(3, 4)
与元组{ {1}},间隔{A, B}
与(4, 6)
。
然后,您可以将那些不相交的间隔存储在二进制搜索树(或其他对数数据结构)中。由于它们是不相交的,因此您可以按字典顺序按{B}
排序,也可以按(begin, end, value)
排序,以您使用的tree API中较容易的一种为准。
因此,现在,搜索是在begin
上进行哈希查找,以找到由(a, b, c)
排序的不相交(d, e)
间隔的树,然后使用d
在该树上进行对数搜索,然后检查该间隔是否为y
,答案是否为该间隔内相应的d > y > e
元组。
Python显然没有内置用于间隔脱节的代码,但是构建自己并不难。
实际上,PyPI上有一些库可以包装整个间隔树结构。但这并不像dict,即使您不知道哈希意味着什么,也很难使用它。一旦牢牢掌握了抽象,使用间隔树就很容易了,但是在此之前很难弄清楚。例如,您需要考虑一下开放范围(正在测试{f}
,而不是通常的Python半开放D < Y < E
)应该如何处理边缘。因此,值得首先手动构建它。
因此,将它们放在一起,就可以像下面这样构建数据结构:
D < Y <= E
然后像这样使用它:
dictA = {}
for a, b, c, d, e, f in dataA:
dictA.setdefault((a, b, c), set()).add((d, e, f))
for key, values in dictA.items():
tree = IntervalTree()
for d, e, f in values:
tree.add(low=d, high=e, value=f)
dictA[key] = tree
这可能不是可运行的代码,尤其是因为我怀疑任何间隔树库都完全具有此API,但这几乎就是您的代码的结构方式。