在500k记录中查找类似对象

时间:2012-08-08 14:48:42

标签: python mysql algorithm data-mining bigdata

编辑解决方案 这是解决方案,感谢@mprivat:

from mysql_wrapper2 import Mysql
import Levenshtein
import time

db = Mysql()
fields = [...]

 records = db.query('SELECT *, CONCAT(%s) as `string`, SOUNDEX(CONCAT(%s)) as `soundsLike` FROM records ORDER BY `soundsLike`' % (','.join(fields), ','.join(fields)))

print len(records)

matches = []


start = time.clock()
for k,v in enumerate(records):
        if k+1 == len(records):
                break

        if Levenshtein.ratio(records[k]['string'],records[k+1]['string']) >= .98:
                matches.append((records[k],records[k+1]))

print time.clock() - start
print len(matches)

结束解决方案

我有一个用于Web应用程序的MySQL数据库,可能(或可能不)在其中有重复记录。这些记录代表人(即姓名,地址等)。

目前,我们有一个python脚本可以提取所有记录,将它们与带状疱疹进行比较,然后呈现具有潜在重复项的管理用户。这个脚本与< 1000条记录。现在我们正在测试真实的数据(89k记录),它变得不可持续。我以16GB的内存使用率停止了脚本,我不知道它是否已经完成了! [虽然我重视我们的记忆,速度更重要,直到大约30GB的内存 - 如果处理500k记录]

该方法使用pygraph来记录数据,并使用shingles查找重复项。它仍然停留在图形中 - 所以我假设这意味着它甚至没有看完记录!

我们希望能够比较"类似的"匹配,因此字段的差异(例如' St。'和' Street')不会成为唯一性的基础。我们可能不会处理简单的同义词,因此只能使用同义词替换。所以我想我们正在寻找模糊匹配。

我尝试了一些简单的解决方案,但它们的速度还不够快。

虽然在这种情况下哈希很快建立(当前带有带状疱疹的例子没有......它已经建成了大约8个小时而且还没有完成),它在比较时的速度是可怕的(但是& #39; s算法效率低下,而不是解释器速度)

我也一直在研究尽可能多的选择。我已经坚持使用Nearest Neighbor Search一段时间了,并且点击了DNlog(n)搜索的承诺。

最近邻搜索有两个问题,您可能非常清楚如何克服,但我无法:

  1. 并非所有键都是数字
  2. 我们当前系统的约束是我们只需要一组[K-Nearest Neighbor]中的两个匹配,但前提是它们非常清晰匹配(固定半径最近邻)
  3. 我也很喜欢某种集群解决方案。我还没找到一个工作的python库,它有不同数量的集群。我发现并且无法实现的每个解决方案都需要提前了解群集的数量。

    1. 如果他们真的相似,我们应该只有匹配
    2. 我们不知道会有多少,如果有的话。
    3. 对于#2,一旦我们得到答案,我们当然可以过滤,但哪个更好?但只有在#1能够被克服的情况下才能实现。

      由于对我们进行比较的数据的限制,我还没有能够实现NN或群集解决方案,并且不得不转向简单的旧比较。在这样做时,为了删除比较域的数量,我连接了所有记录'值,并对字符串执行Levenshtein与所有其他字符串的比较。这个解决方案也不够快。它在大约20分钟内没有完成一条记录(我在那时停止了计时)。

      我想出的一个简单的例子:

      from mysql_wrapper2 import Mysql
      import Levenshtein
      
      db = Mysql()
      # Get 89k records from the database
      records = db.query(""""
          SELECT *
          FROM records 
      """)
      
      # Build a dictionary where the keys are our records, and their IDs are our data
      # Only passing ID, we only use about 3GB of memory, instead of 
      #  > 16GB with the actual dictionaries
      
      records_hash = {}
      for i in records:
          key = []
          for k in i.values():
              key.append(str(i))
              records_hash['-'.join(key)] = i['id']
      
      # Compare every key to every key with the Levenshtein ratio
      
      for i in records_hash.keys():
          print 'once...'
          for j in records_hash.keys():
              if Levenshtein.ratio(i,j) >= .75:
                  pass
      

      再一次,这是一个简单的例子,如果实际实施,将确保没有两个记录被检查两次等等。

      有什么解决方案吗?有没有办法让NN或Clustering能够适应我的问题呢?有没有其他模式我不考虑可以帮助?我是SOL吗?

3 个答案:

答案 0 :(得分:4)

我实际上没有尝试过,你可能会发现它不起作用,但无论如何我会分享,因为如果我遇到同样的问题,我会尝试这样做。

我会在表格中添加一个新列。然后我会查看所有记录并执行此操作:

  1. 将名字,姓氏,地址等连接成一个字符串,我会为其计算类似Soundex(或根据您的需要略微修改),例如,如果有任何数字,请删除数字,删除非字母数字字符,添加对长字符串的支持等...)

  2. 然后我会计算soundex代码并将其存储在我的新专栏中

  3. 然后,假设soundex完成它的工作,你可以查询记录,按soundex排序,然后应用你已经拥有的比较逻辑。我们的想法是仅比较排序结果集中彼此相邻的记录。

    与soundex一样,我的意思是与正常的soundex不同,它是一个字母和三个数字。但这对于长串不适用。可能会使用三个以上的数字。

    无论如何,只是想给你另一条探索的途径......

答案 1 :(得分:0)

据我所知,最快的方式是只在MySQL中完成这一切。

我必须在我的一张大桌子上找到所有副本,我做过类似的事情:

DELETE FROM t1 
where t1.id in (SELECT clonet1.id from t1 origine, t1 clone 
    WHERE clone.id != origine.id AND clone.id > origine.id AND ...

然后用你的比较作者替换......

如果在表格中添加更正索引,它应该足够快。

当我这样做时,我有数百万个字段需要检查,但我只是寻找直接的“=”。

尽管如此,我从来没有见过一个简单的比循环搜索慢的地方......

就像mprivat所说的那样,更多的是给你一种搜索实际解决方案的方法......我希望它能帮助你。

答案 2 :(得分:0)

我意识到你说这可能不实用,但我认为你最好以某种方式规范你的数据,以便St. vs street不是一个问题。你选择哪一个当然取决于你,但两者都让事情变得太复杂了。

然后你可以使用布隆过滤器,假设你有很多独特的和相对较少的重复。布隆过滤器是概率集数据结构,可以添加元素并测试成员资格。在测试成员资格时,它可以给出两个答案之一:“这个元素肯定不在集合中”或“这个元素几乎肯定在集合中”。这样,您就可以将大量的独特元素丢弃并减少输入,从而可以在合理的内存量中使用传统算法。该过程的后一阶段将事情恢复到准确性 - 布隆过滤器只是减少了精确算法需要考虑的输入数量。

我在http://stromberg.dnsalias.org/~strombrg/drs-bloom-filter/处有一个针对Python的bloom过滤器实现。当然还有其他人。