假设我获得了一组结构化数据。众所周知,这些数据存在问题,我需要以某种方式“评分”它们的一致性。例如,我有如下所示的数据:
fieldA | fieldB | fieldC
-------+--------+-------
foo | bar | baz
fooo | bar | baz
foo | bar | lorem
.. | .. | ..
lorem | ipsum | dolor
lorem | upsum | dolor
lorem | ipsum | baz
因此假设第一行被认为是正确的条目,因为与第二行和第三行中的记录相比,该组合中的数据相对较多。在第二行中,fieldA
的值应为foo
(由于拼写错误而不一致)。然后在第三行中,fieldC
的值应为baz
,因为数据集中的其他条目具有fieldA
(foo
)和fieldB
的相似值( bar
)建议。
此外,在数据集的其他部分,还有另一种相对更常见的组合(lorem
,ipsum
,dolor
)。因此,以下记录中的问题与前面提到的问题相同,只是值组合不同。
我最初将所有内容转储到SQL数据库,并使用GROUP BY
语句来检查字段值的一致性。因此,对于每个要检查一致性的字段,以及每条记录,都会有1个查询。
SELECT fieldA, count(fieldA)
FROM cache
WHERE fieldB = 'bar' and fieldC = 'baz'
GROUP BY fieldA
然后我可以通过将记录引用到下面的对象(上一个SQL查询的处理结果)来检查记录的fieldA
值是否与其余记录一致。
{'foo': {'consistency': 0.99, 'count': 99, 'total': 100}
'fooo': {'consistency': 0.01, 'count': 1, 'total': 100}}
然而它非常慢(数据集有大约220万条记录,我正在检查4个字段,所以要查询大约9百万条),并且需要半天才能完成。然后我将SQL存储替换为elasticsearch,并且处理时间缩短到大约5个小时,是否可以以某种方式更快?
也是出于好奇,我在这里重新发明了一个轮子吗?有现成的工具吗?目前它在Python3中实现,带有elasticsearch。
答案 0 :(得分:1)
我刚读了你的问题并发现它非常有趣。我使用ntlk(python Natural Language Toolkit)做了类似的事情。 无论如何,在这种情况下,我认为你不需要复杂的string comparison algorithms。
所以我尝试了使用python difflib的方法。标题听起来很有希望:difflib - 计算增量的助手¶
difflib.SequenceMatcher 类说:
这是一个灵活的类,用于比较任何类型的序列对,只要序列元素是可清除的。
顺便说一下,我认为如果你想节省时间,你可以在内存中容易地处理和处理2.000.000个3元组(相对较短的)字符串。 (参见下面的testruns和Mem Usage)
所以我写了一个demo App,产生2.000.000(你可以改变)3元组的随机略微改组的字符串。洗牌后的字符串是基于并与你的默认模式进行比较:['foofoo','bar','lorem']。然后使用difflib.SequenceMatcher比较它们。全部记忆。
以下是比较代码:
def compare(intuple, pattern_list):
"""
compare two strings with difflib
intuple: in this case a n-tuple of strings
pattern_list: a given pattern list.
n-tuple and list must be of the same lenght.
return a dict (Ordered) with the tuple and the score
"""
d = collections.OrderedDict()
d["tuple"] = intuple
#d["pattern"] = pattern_list
scorelist = []
for counter in range(0,len(pattern_list)):
score = difflib.SequenceMatcher(None,intuple[counter].lower(),pattern_list[counter].lower()).ratio()
scorelist.append(score)
d["score"] = scorelist
return d
以下是运行时和内存使用结果:
2000 3元组: - 比较时间:417 ms = 0,417秒 - Mem用法:594 KiB
200.000 3元组: - 比较时间:5360毫秒= 5,3秒 - Mem用法:58 MiB
2.000.000 3元组: - 比较时间:462241毫秒= 462秒 - Mem用法:580 MiB
因此它在时间和内存使用方面呈线性扩展。并且它(仅)需要462秒来进行2.000.000个3元组的字符串比较。
结果如下所示:( 200,000行的示例)
[ TIMIMG ]
build function took 53304.028034 ms
[ TIMIMG ]
compare_all function took 462241.254807 ms
[ INFO ]
num rows: 2000000
pattern: ['foofoo', 'bar', 'lorem']
[ SHOWING 10 random results ]
0: {"tuple": ["foofoo", "bar", "ewrem"], "score": [1.0, 1.0, 0.6]}
1: {"tuple": ["hoofoo", "kar", "lorem"], "score": [0.8333333333333334, 0.6666666666666666, 1.0]}
2: {"tuple": ["imofoo", "bar", "lorem"], "score": [0.6666666666666666, 1.0, 1.0]}
3: {"tuple": ["foofoo", "bar", "lorem"], "score": [1.0, 1.0, 1.0]}
....
正如您所看到的,您可以根据字符串与模式的相似性得到分数。 1.0意味着相等,得分越低,下面的一切都越差。
difflib被认为不是最快的算法,但我认为7分钟是半天或5小时的改进。
我希望这可以帮助你(而且不是完全错误的解释)但是昨天编程这个很有趣。我学到了很多东西。 ;) 例如,使用tracemalloc跟踪内存使用情况。从来没有这样做过。
我将代码删除到github (as a one file gist)。