Fuzzy Joins#
En primer lugar, vamos a generar unos datos “de juguete” para usar como ejemplo
import pandas as pd
from fuzzywuzzy import fuzz
# Generamos datos "de juguete" para el ejemplo
## Info personal estudiantes
df1 = pd.DataFrame({'Nombre': ['Juan Pérez', 'María González', 'Luisa Martínez'],
'Edad': [21, 19, 20],
'Email': ['juan@gmail.com', 'maria@gmail.com', 'luisa@gmail.com']})
## Info calificaciones estudiantes
df2 = pd.DataFrame({'Nombre': ['Juan Perez', 'María Gonzales', 'Luisa Martínz'],
'Nota_Parcial': [8.5, 7.5, 9],
'Nota_Final': [9, 8, 9.5]})
A continuación, generamos un loop para el fuzzy join considerando que la unión entre ambos DataFrames será realizada por el campo Nombre
¿Qué parámetros podemos modificar del código que sigue?
for index, row in df2.iterrows():
name = row['Nombre']
max_score = 0
max_name = ""
## Acá iteramos para encontrar el mejor puntaje de similitud con el df1
for index2, row2 in df1.iterrows():
score = fuzz.token_sort_ratio(name, row2['Nombre'])
if score > max_score:
max_score = score
max_name = row2['Nombre']
if max_score > 90:
# Reemplazamos el nombre actual por el nombre de mejor puntaje
df2.at[index, 'Nombre'] = max_name
Sobre fuzz.token_sort_ratio()
:
en este método utilizado en el fuzzy matching de strings, las cadenas se tokenizan y se preprocesan convirtiendo las mayúsculas a minúsculas y eliminando los signos de puntuación. Posteriormente, se ordenan alfabéticamente y se unen. Una vez realizado todo lo anterior, se calcula la similaridad de Levenshtein entre las dos cadenas resultantes.
El último paso sería unir ambos DataFrames a través de merge()
Una alternativa:#
Podemos hacer la misma operación con un código más compacto utilizando process.extractOne()
de la librería fuzzywuzzy
, la cual permite encontrar la mejor coincidencia entre un término de búsqueda y una lista de posibles candidatos. Esta función realiza comparaciones de similitud de cadenas usando una métrica definida (como token_sort_ratio
, que compara cadenas ignorando el orden de las palabras, como se describió anteriormente), y devuelve la mejor coincidencia junto con un score de similaridad.
La sintaxis básica es la siguiente:
process.extractOne(query, choices, scorer=fuzz.token_sort_ratio, score_cutoff=0)
donde:
query
es la cadena que se quiere buscarchoices
es la lista de opciones en la que se quiere buscarscorer
(opcional) es la función de comparación que determina la similitud. El valor predeterminado esfuzz.WRatio
, pero se puede especificar otra, comofuzz.token_sort_ratio
score_cutoff
(opcional) es el umbral mínimo de score. Si no hay coincidencias por encima de este valor, la función devuelveNone
.
Utilizando un loop, el código nos quedaría así:
from fuzzywuzzy import process
# Iteramos sobre los nombres del df2
for index, row in df2.iterrows():
name = row['Nombre']
# Encontramos la mejor coincidencia en df1 utilizando process.extractOne()
best_match = process.extractOne(name, df1['Nombre'], scorer = fuzz.token_sort_ratio, score_cutoff = 70)
if best_match:
# Reemplazamos el nombre por el de mejor puntaje (primer elemento de la lista)
df2.at[index, 'Nombre'] = best_match[0]