2015-07-15 2 views
3

J'essaie d'utiliser le package Dedupe pour fusionner une petite donnée désordonnée à une table canonique. Comme la table canonique est très grande (122 millions de lignes), je ne peux pas tout charger en mémoire. L'approche actuelle que j'utilise basée sur this prend une journée entière sur les données de test: une table de 300k lignes de données désordonnées stockées dans un dict, et une table de 600k lignes de données canoniques stockées dans mysql. Si je fais tout cela en mémoire (lire la table canonique en dict) cela ne prend qu'une demi-heure.Comment lier efficacement des enregistrements à une grande table avec python Dedupe?

Y a-t-il un moyen de rendre cela plus efficace?

blocked_pairs = block_data(messy_data, canonical_db_cursor, gazetteer) 
clustered_dupes = gazetteer.matchBlocks(blocked_pairs, 0) 

def block_data(messy_data, c, gazetteer): 

    block_groups = itertools.groupby(gazetteer.blocker(messy_data.viewitems()), 
            lambda x: x[1]) 
    for (record_id, block_keys) in block_groups: 

     a = [(record_id, messy_data[record_id], set())] 

     c.execute("""SELECT * 
        FROM canonical_table 
        WHERE record_id IN 
         (SELECT DISTINCT record_id 
         FROM blocking_map 
         WHERE block_key IN %s)""", 
        (tuple(block_key for block_key, _ in block_keys),)) 

     b = [(row[self.key], row, set()) for row in c] 

     if b: 
      yield (a, b) 

Répondre

2

Accéléré considérablement en divisant la requête en deux requêtes. J'utilise mysql et toutes les colonnes utilisées dans l'exemple sont indexées ...

def block_data(messy_data, c, gazetteer): 

    block_groups = itertools.groupby(gazetteer.blocker(messy_data.viewitems()), 
           lambda x: x[1]) 
    for (record_id, block_keys) in block_groups: 

     a = [(record_id, messy_data[record_id], set())] 

     c.execute("""SELECT DISTINCT record_id 
        FROM blocking_map 
        WHERE block_key IN %s""", 
        (tuple(block_key for block_key, _ in block_keys),)) 

     values = tuple(row['record_id'] for row in c) 

     if values: 

      c.execute("""SELECT * 
         FROM canonical_table 
         WHERE record_id IN %s""", 
         (values,)) 

      b = [(row['record_id'], row, set()) 
       for row in c] 

      if b: 
       yield (a, b) 
0

Il serait probablement plus rapide encore si vous exprimez la requête en tant que membre:

SELECT canonical_table.* 
FROM canonical_table 
JOIN blocking_map 
ON (canonical_table.record_id = blocking_map.record_id) 
WHERE blocking_map IN %s 

Votre solution est En faisant une jointure en Python, la base de données ferait probablement mieux. La syntaxe "IN" dans votre tentative d'origine est rarement optimisée ainsi qu'une jointure correcte.