2016-04-15 173 views
2

假设我得到了一组结构化数据。数据已知是有问题的,我需要以某种方式“一致”地评分它们。例如,我有数据,如下图所示:数据集内的评分一致性

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,因为数据集中的其他条目具有fieldAfoo)和fieldBbar)的相似值。

此外,在数据集的其他部分,还有另一种相对更常见的组合(lorem,ipsum,dolor)。因此,以下记录中的问题与前面提到的相同,只是数值组合不同。

我最初将所有内容都转储到SQL数据库,并使用GROUP BY的语句来检查字段值的一致性。因此,对于每个我想检查一致性以及每条记录的字段,都会有一个查询。

SELECT fieldA, count(fieldA) 
FROM  cache 
WHERE  fieldB = 'bar' and fieldC = 'baz' 
GROUP BY fieldA 

然后,我可以检查的记录fieldA值是参照记录以下(以前的SQL查询的处理结果)的对象,其余是一致的。

{'foo': {'consistency': 0.99, 'count': 99, 'total': 100} 
'fooo': {'consistency': 0.01, 'count': 1, 'total': 100}} 

不过它非常慢(数据集有220万左右的记录,而我检查4个领域,所以作出有关9mil查询),并会采取半天才能完成。然后我将SQL存储换成了elasticsearch,处理时间缩短到5个小时左右,能否以某种方式更快?

也只是出于好奇,我在这里重新发明了一个轮子?有没有现成的工具?目前它是用Python3和elasticsearch实现的。

回答

1

我刚刚阅读你的问题,发现它很有趣。我使用ntlk(python Natural Language Toolkit)做了类似的事情。 无论如何,在这种情况下,我认为你不需要复杂的string comparison algorithms

所以我尝试了一种使用python difflib的方法。标题听起来前途:difflib - 助手计算增量

difflib.SequenceMatcher类说:

这是比较对任何类型的序列的灵活类,只要序列元素是可散列的。

顺便说一句,我认为,如果你想节省时间,你可以容易地在内存中容纳和处理2.000.000三元组(相对较短)的字符串。 (请参见下面的testruns和Mem Usage)

所以我写了一个demo App,它产生了2.000.000(可以改变这种情况)随机轻微混洗字符串的三元组。混洗后的字符串是基于并与您的默认模式进行比较:['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毫秒= 0417秒 - Mem使用:594 KiB

200.000三元组: - 比较时间:5360 ms = 5.3秒 - 内存使用:58 MIB

2.000.000 3元组: - 比较时间:462241毫秒= 462秒 - 内存使用:580 MIB

所以它缩放在时间和内存使用线性。它(仅)需要462秒,用于比较2.000.000个三元组字符串。

结果如下:(例如对于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)

+0

我还没有时间看解决方案,我可以用它来“评分”多项条目吗?例如“foo吧”与“fooz酒吧” – Jeffrey04

+0

也应该有效。 difflib使用散列进行比较。所以任何可排序的工作。 – klaas

+0

哈哈,看起来不像我需要的工具。因为我没有为每个领域提供所有可能的(相对)正确的规范值和组合。 – Jeffrey04