[FIXED] Gibt es eine Möglichkeit, den Abgleich von Adressen und das Konfidenzniveau pro Übereinstimmung zwischen zwei Datenrahmen für große Datensätze zu beschleunigen?

Ausgabe

Ich habe unten ein Skript, das die Genauigkeit einer Spalte mit Adressen in meinem Datenrahmen mit einer Spalte mit Adressen in einem anderen Datenrahmen überprüft, um zu sehen, ob sie übereinstimmen und wie gut sie übereinstimmen.

Ich verwende Rapid Fuzz. Ich habe gehört, dass es schneller ist als Fuzzywuzzy. Es dauert jedoch immer noch sehr lange, den Abgleich und die Berechnungen durchzuführen. Hier sind die CSV-Dateien. main_dataset.csv enthält etwa 3 Millionen Datensätze und reference_dataset.csv enthält etwa 10 Datensätze.

Unten ist die Zeit, die für jeden Datensatz benötigt wurde.

start time: Thu Oct  6 10:51:18 2022
end time: Thu Oct  6 10:51:23 2022
start time: Thu Oct  6 10:51:23 2022
end time: Thu Oct  6 10:51:28 2022
start time: Thu Oct  6 10:51:28 2022
end time: Thu Oct  6 10:51:32 2022
start time: Thu Oct  6 10:51:32 2022
end time: Thu Oct  6 10:51:36 2022
start time: Thu Oct  6 10:51:36 2022
end time: Thu Oct  6 10:51:41 2022
start time: Thu Oct  6 10:51:41 2022
end time: Thu Oct  6 10:51:45 2022
start time: Thu Oct  6 10:51:45 2022
end time: Thu Oct  6 10:51:50 2022
start time: Thu Oct  6 10:51:50 2022
end time: Thu Oct  6 10:51:54 2022
start time: Thu Oct  6 10:51:54 2022
end time: Thu Oct  6 10:51:59 2022

Mein Skript ist hier:

import pandas as pd
from rapidfuzz import process, fuzz
import time
from dask import dataframe as dd

ref_df = pd.read_csv('reference_dataset.csv')
df = dd.read_csv('main_dataset.csv', low_memory=False)

contacts_addresses = list(df.address)
ref_addresses = list(ref_df.ref_address.unique())

def scoringMatches(x, s):
    o = process.extract(x, s, score_cutoff = 60)
    if o != None:
        return o[1]

def match_addresses(add, contacts_addresses, min_score=0):
    response = process.extract(add, contacts_addresses, scorer=fuzz.token_sort_ratio)
    return response


def get_highest_score(scores):
    total_scores = []
    for val in scores:
        total_scores.append(val[1])
    max_value = max(total_scores)
    max_index = total_scores.index(max_value)
    return scores[max_index]


scores_list = []
names = []
for x in ref_addresses:
    # start = time.time()
    # print("start time:", time.ctime(start))
    scores = match_addresses(x, contacts_addresses, 75)
    match = get_highest_score(scores)
    name = (str(x), str(match[0]))
    names.append(name)
    score = int(match[1])
    scores_list.append(score)
    # end = time.time()
    # print("end time:", time.ctime(end))
name_dict = dict(names)

match_df = pd.DataFrame(name_dict.items(), columns=['ref_address', 'matched_address'])
scores_df = pd.DataFrame(scores_list)

merged_results_01 = pd.concat([match_df, scores_df], axis=1)

merged_results_02 = pd.merge(ref_df, merged_results_01, how='right', on='ref_address')
merged_results_02.to_csv('results.csv')

Lösung

Es wird empfohlen, process.cdistwhich vergleicht zwei Sequenzen und erhält eine Ähnlichkeitsmatrix anstelle von process.extract/ process.extractOnejetzt, da viele der neueren Leistungsverbesserungen bisher nur zu diesem Algorithmus hinzugefügt wurden.

Diese Verbesserungen sind nämlich:

  1. Unterstützung für Multithreading mit dem workersArgument
  2. Unterstützung für den parallelen Vergleich mehrerer kurzer Sequenzen (<= 64 Zeichen) mit SIMD auf x64.

Diese beiden Verbesserungen werden irgendwann zu process.extractund hinzugefügt process.extractOne, aber zu diesem Zeitpunkt (rapidfuzz==v2.11.1) existieren sie nur.

Ein paar relevante Punkte für zukünftige Verbesserungen an dieser Front sind:

Dies könnte z. B. folgendermaßen umgesetzt werden:

from itertools import islice

chunk_size = 100
ref_addr_iter = iter(ref_addresses)
while ref_addr_chunk := list(islice(ref_addr_iter, chunk_size)):
    scores = process.cdist(ref_addr_chunk, contacts_addresses, scorer=fuzz.token_sort_ratio, score_cutoff=75, workers=-1)
    max_scores_idx = scores.argmax(axis=1)
    for ref_addr_idx, score_idx in enumerate(max_scores_idx):
        names.append((ref_addr_chunk[ref_addr_idx], contacts_addresses[score_idx]))
        scores_list.append(scores[ref_addr_idx,score_idx])


Beantwortet von –
maxbachmann


Antwort geprüft von –
Senaida (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like