import jaro
# Calculamos similaridad de Jaro
sim_j = round(jaro.jaro_metric('ARGENTINA', 'ARGENTENA'),4)
print(sim_j)0.9259
UNIDADES 2 y 3
La similaridad de Jaro entre dos cadenas se calcula de la siguiente manera:
\[sim_{j} = \frac{1}{3}(\frac{m}{|s_1|} + \frac{m}{|s_2|} + \frac{m - t}{m})\]
\(|s_i|\) es la longitud de la cadena \(s_i\).
\(m\) es el número de caracteres que coinciden.
\(t\) es la mitad del número de caracteres transpuestos, de los que coinciden.
Winkler propone una modificación a la fórmula anterior en la que se aumenta el rating de similaridad si las cadenas comparadas comienzan con el mismo prefijo:
\[sim_{j-w} = sim_{j} + lp~(1 - sim_{j})\]
\(sim_{j}\) es la similaridad de Jaro entre las cadenas \(s_1\) y \(s_2\).
\(l\) es la es la cantidad de caracteres que coinciden exactamente al comienzo de la cadena, hasta un máximo de 4 caracteres.
\(p\) es un factor de escalado que indica cuánto se ajusta por la coincidencia en el prefijo, que no debería exceder \(p\) = 0.25 (valor estándar: \(p\) = 0.1).
En Python, podemos utilizar la librería jaro para calcular estas métricas (comando de instalación: pip install jaro-winkler).
Como ejemplo, calculemos ambas medidas para las siguientes cadenas:
| A | R | G | E | N | T | I | N | A |
| A | R | G | E | N | T | E | N | A |
Cálculo de la similaridad de Jaro
Cálculo de la similaridad de Jaro-Winkler
Veamos otro ejemplo, ahora con estas dos cadenas:
Cadena 1: MARIELA F.
Cadena 2: MARLENA F.
Cálculo de la similaridad de Jaro
# Calculamos similaridad de Jaro
sim_j = round(jaro.jaro_metric('MARIELA F.', 'MARLENA F.'),4)
print(sim_j)0.8963
Cálculo de la similaridad de Jaro-Winkler
Las distancias de edición son métricas que cuentan la menor cantidad de operaciones de edición que son necesarias para convertir a una cadena en otra.
La más básica de ellas es la distancia de edición de Levenshtein y está definida como el menor número de operaciones de inserción, borrado o sustitución de un único caracter que son necesarias para convertir la cadena \(s_{1}\) en la cadena \(s_{2}\).
🤔¿Cuál sería la distancia de Levenshtein entre las cadenas LEWENSTEIN y LEVENSHTEIN?
Para calcular esta métrica en Python, podemos utilizar herramientas de la librería Levenshtein:
De manera análoga a las métricas de similaridad vistas anteriormente, se define una medida de similaridad basada en la distancia de Levenshtein de la siguiente forma:
\[sim_{levenshtein} = 1 - \frac{dist_{levenshtein}(s_1,s_2)}{max(|s_1|,|s_2|)}\]
Para calcular esta métrica en Python, podemos volver a utilizar herramientas de la librería Levenshtein:
El enlace difuso es una técnica utilizada para matchear datos que pueden no coincidir perfectamente debido a posibles inconsistencias, omisiones o errores.
En lugar de buscar coincidencias exactas, se buscan coincidencias aproximadas utilizando diversas herramientas, como las métricas de similaridad vistas anteriormente.
df1: datos personales de 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']})
print(df1) Nombre Edad Email
0 Juan Pérez 21 juan@gmail.com
1 María González 19 maria@gmail.com
2 Luisa Martínez 20 luisa@gmail.com
df2: calificaciones obtenidas en las evaluaciones del semestre
🤔 ¿Qué resultado esperaríamos obtener si realizáramos una operación merge() sobre ambos DataFrames, utilizando la columna Nombre como key?
Utilizaremos el módulo fuzz de la librería fuzzywuzzy para realizar un enlace difuso (fuzzy join) entre los DataFrames df1 y df2 utilizando el campo Nombre como criterio de unión.
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.
En el análisis de datos, muchas veces necesitamos cuantificar qué tan parecidos o distintos son los objetos entre sí.
Esto es útil en varias tareas:
Clustering: agrupar elementos en grupos homogéneos (clusters) en función de las similaridades entre ellos.
Detección de outliers: identificar elementos muy diferentes al resto.
Clasificación de vecinos más cercanos (KNN): clasificar elementos basándose en las características de otros elementos similares (vecinos).
Es la medida de distancia más popular. Si \(\mathbf{i} = (x_{1i}, x_{2i},...,x_{pi})\) y \(\mathbf{j} = (x_{1j}, x_{2j},...,x_{pj})\) representan dos observaciones (o elementos) descritas por \(p\) variables cuantitativas, la distancia euclideana entre ellas se define como:
\[d_E(\mathbf{i},\mathbf{j}) = \sqrt{(x_{1i} - x_{1j})^2 + (x_{2i} - x_{2j})^2 + ... + (x_{pi} - x_{pj})^2}\]
⚠️ IMPORTANTE: esta medida es sensible a las unidades de las variables. Por eso, cuando las escalas son diferentes, conviene normalizar o estandarizar los datos previamente (Unidad 5).
Supongamos que tenemos el siguiente DataFrame:
Haciendo uso de la operación describe(), observamos que los tres conjuntos de observaciones (x1, x2 y x3) presentan valores medios y dispersiones similares. Por lo tanto, no es necesario estandarizar las variables para calcular la distancia euclídea.
La librería SciPy ofrece herramientas para calcular y representar matrices de distancias. Utilizaremos dos funciones del módulo scipy.spatial.distance:
pdist(): pairwise distances, calcula todas las distancias por pares entre filas de un DataFrame o matriz.
squareform(): convierte el resultado de pdist (que viene como un vector) en una matriz cuadrada simétrica.
from scipy.spatial.distance import pdist, squareform
# Matriz de distancias euclídeas
dist_matrix = pd.DataFrame(squareform(pdist(df[['x1', 'x2', 'x3']], metric='euclidean')),
index=df['id'], columns=df['id'])
dist_matrix.round(3)| id | A | B | C | D | E |
|---|---|---|---|---|---|
| id | |||||
| A | 0.000 | 0.735 | 1.039 | 8.746 | 9.749 |
| B | 0.735 | 0.000 | 0.707 | 8.083 | 9.076 |
| C | 1.039 | 0.707 | 0.000 | 8.196 | 9.198 |
| D | 8.746 | 8.083 | 8.196 | 0.000 | 1.068 |
| E | 9.749 | 9.076 | 9.198 | 1.068 | 0.000 |
Representación visual de la matriz de distancias:
🤓 ¿Qué interpretación podemos hacer de las distancias determinadas?
También llamada City-Block o Taxicab, mide la distancia entre dos puntos considerando que sólo se puede avanzar en líneas rectas horizontales o verticales, como si uno se desplazara por las calles de una ciudad en forma de cuadrícula.
Para dos objetos \(\mathbf{i}\) y \(\mathbf{j}\) con \(p\) atributos se define como:
\[d_{MHT}(\mathbf{i},\mathbf{j}) = \lvert x_{1i} - x_{1j} \rvert + \lvert x_{2i} - x_{2j} \rvert + ... + \lvert x_{pi} - x_{pj} \rvert\]
# Matriz de distancias Manhattan
dist_matrix_manhattan = pd.DataFrame(squareform(pdist(df[['x1', 'x2', 'x3']], metric='cityblock')),
index=df['id'],
columns=df['id'])
dist_matrix_manhattan.round(3)| id | A | B | C | D | E |
|---|---|---|---|---|---|
| id | |||||
| A | 0.0 | 1.2 | 1.4 | 15.0 | 16.8 |
| B | 1.2 | 0.0 | 1.2 | 13.8 | 15.6 |
| C | 1.4 | 1.2 | 0.0 | 14.0 | 15.8 |
| D | 15.0 | 13.8 | 14.0 | 0.0 | 1.8 |
| E | 16.8 | 15.6 | 15.8 | 1.8 | 0.0 |
Fue introducida en 1936 por el estadístico hindú Prasanta Chandra Mahalanobis. Mide qué tan lejos está un punto respecto a cómo se distribuyen los datos, considerando:
La variabilidad/dispersión de los datos.
La estructura de correlación entre las variables.
Supongamos que contamos con datos de dos variables con una correlación lineal positiva, y consideremos tres puntos: \(P_1\), \(P_2\) (dentro de la nube) y \(P_3\) (fuera de la nube).
Desde el punto de vista de la distancia euclideana, \(P_1\) está equidistante de \(P_2\) y \(P_3\), como se muestra en la siguiente matriz:
| P1 | P2 | P3 | |
|---|---|---|---|
| P1 | 0.000 | 1.524 | 1.524 |
| P2 | 1.524 | 0.000 | 2.017 |
| P3 | 1.524 | 2.017 | 0.000 |
🤨 Sin embargo, observando la estructura de los datos, \(P_2\) debería considerarse “más cercano” a \(P_1\), porque “sigue la dirección de la nube”.
Aunque la distancia euclídea ve \(P_2\) y \(P_3\) equidistantes, Mahalanobis ajusta según la forma y correlación de los datos, mostrando que \(P_2\) realmente está más cerca de \(P_1\).
| P1 | P2 | P3 | |
|---|---|---|---|
| P1 | 0.000 | 1.178 | 6.083 |
| P2 | 1.178 | 0.000 | 6.625 |
| P3 | 6.083 | 6.625 | 0.000 |
💡 Ventajas de emplear la distancia de Mahalanobis:
Considera la correlación: a diferencia de la distancia euclídea, Mahalanobis incorpora la covarianza entre variables, ajustando la distancia según la forma y orientación de la nube de datos (ideal cuando los atributos están correlacionados).
Es independiente de la escala: no se ve afectada por las unidades o rangos de las variables, ya que “normaliza” internamente las diferencias entre ellas.
Permite la detección de valores atípicos: permite identificar observaciones alejadas del patrón general de los datos.
Vamos a usar el dataset Iris, que ya conocemos. Para simplificar visualización y cálculo, consideraremos sólo dos variables: sepal_length y petal_length.
Objetivo: dado un nuevo punto con medidas de largo de sépalo y pétalo, determinar a qué especie pertenece.
Queremos clasificar una nueva planta con: sepal_length = 7 y petal_length = 5, representada con una X en el scatterplot:
🤔 Intuitivamente y según sus valores de longitud de sépalo y pétalo, ¿en qué especie podríamos clasificar la nueva planta?
Aproximación para dar una respuesta: calculamos la distancia de Mahalanobis del nuevo punto a cada observación de cada especie y representamos gráficamente los valores obtenidos:
Podríamos calcular la media de las distancias por especie:
| species | dist_mahal_promedio | |
|---|---|---|
| 0 | setosa | 20.40 |
| 1 | versicolor | 2.29 |
| 2 | virginica | 3.31 |
🤔 En base a la información anterior, ¿en qué especie clasificaríamos al nuevo punto?
Otra estrategia podría ser calcular la distancia de Mahalanobis del nuevo punto al centroide de cada grupo (especie), es decir, el punto que representa la media del conjunto de data points del grupo.
| species | dist_mahal_cent | |
|---|---|---|
| 0 | setosa | 20.374 |
| 1 | versicolor | 2.062 |
| 2 | virginica | 3.168 |
Se utiliza ampliamente en análisis de datos textuales, donde cada documento se representa como un vector de frecuencias de términos (term-frequency vector). Estos vectores suelen ser muy largos y dispersos, con muchos valores iguales a cero.
En este tipo de datos, las distancias tradicionales (como Euclídea o Manhattan) no reflejan bien la similitud, ya que dos documentos pueden compartir muchos ceros sin tener realmente contenido similar.
La similaridad del coseno se enfoca en los atributos que ambos documentos comparten (palabras que aparecen en ambos), y en la frecuencia relativa de esas palabras.
Para dos vectores \(\mathbf{i}\) y \(\mathbf{j}\), define como:
\[sim_{COS}(\mathbf{i}, \mathbf{j}) = \frac{\mathbf{i}\cdot\mathbf{j}}{\lvert\lvert \mathbf{i} \rvert \rvert \lvert\lvert \mathbf{j} \rvert \rvert}\]
En la expresión anterior, \(\lvert\lvert \mathbf{w} \rvert \rvert\) representa la norma Euclídea del vector \(\mathbf{w}\) = \((w_1, w_2, ... , w_p)\), es decir: \(\sqrt{w_1^2 + w_2^2 + ... + w_p^2}\).
En lugar de medir una distancia, se calcula el coseno del ángulo entre los vectores:
Un valor de \(sim_{COS}\) = 0 indica que los dos vectores son ortogonales (forman un ángulo de 90°) y no hay coincidencia entre ellos.
Cuanto más cercano sea \(sim_{COS}\) a 1, menor será el ángulo y mayor el grado de similitud entre ambos vectores.
Ejemplo extraído de Data Mining: Concepts and Techniques (Han, Kamber & Pei, 2012). Se cuenta con 5 documentos con los siguientes vectores de frecuencias de términos:
Supongamos que \(\mathbf{i}\) y \(\mathbf{j}\) son los dos primeros vectores de frecuencia de términos en la tabla anterior. ¿Cuán similares son \(\mathbf{i}\) y \(\mathbf{j}\)?
from scipy.spatial.distance import cosine
i = [5,0,3,0,2,0,0,2,0,0]
j = [3,0,2,0,1,1,0,1,0,1]
# La función cosine() devuelve la distancia coseno,
# por eso le restamos a 1 para obtener la correspondiente medida de similaridad
cos_sim = 1 - cosine(i, j)
print(cos_sim.round(3))0.936
De esta manera, si estuviéramos usando la similaridad de coseno para comparar estos dos documentos, los mismos serían considerados bastante similares entre sí.