Capítulo 4. Emparejamiento probabilístico
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
En el Capítulo 3, exploramos cómo utilizar técnicas de concordancia aproximada para medir el grado de similitud entre los valores de los atributos. Establecimos un umbral por encima del cual declaramos la equivalencia y luego combinamos estas características de coincidencia, con igual peso, para concluir que dos registros se referían a la misma entidad cuando ambos coincidían. Evaluamos nuestro rendimiento sólo con las coincidencias exactas.
En este capítulo, examinaremos cómo utilizar las técnicas basadas en la probabilidad para calcular la ponderación óptima de cada atributo equivalente al calcular la probabilidad de una coincidencia global de entidades. Este enfoque basado en la probabilidad nos permite declarar una coincidencia cuando los atributos estadísticamente más significativos son equivalentes (exactos o aproximados), pero los menos significativos no son suficientemente similares. También nos permite graduar nuestra confianza en la declaración de una coincidencia y aplicar umbrales de coincidencia adecuados. El modelo que se presentará en esta sección se conoce como modelo Fellegi-Sunter (FS).
También presentaremos un marco probabilístico de resolución de entidades, Splink, que utilizaremos para ayudarnos a calcular estas métricas y resolver nuestras entidades juntas.
Ejemplo de problema
Volvamos a nuestros resultados de coincidencia exacta del final del Capítulo 2. Abriendo el cuaderno Capítulo4.ipynb, volvemos a cargar los conjuntos de datos normalizados de los sitios web Wikipedia y TheyWorkForYou. Como en el Capítulo 3, empezamos por calculando el producto cartesiano, o cruzado, de los dos conjuntos de datos como:
cross = df_w.merge(df_t, how='cross', suffixes=('_w', '_t'))
Esto nos da una población total de 650 × 650 = 422.500 pares de registros: un par por cada combinación de nombres entre los conjuntos de datos de Wikipedia y TheyWorkForYou.
A lo largo de este capítulo, utilizaremos varias veces las coincidencias exactas entre los campos Firstname
, Lastname
y Constituency
de cada uno de estos pares de registros. Por tanto, es más eficiente calcular estas coincidencias una vez y almacenarlas como columnas de características adicionales:
cross['Fmatch']= (cross['Firstname_w']==cross['Firstname_t']) cross['Lmatch']= (cross['Lastname_w']==cross['Lastname_t']) cross['Cmatch']= (cross['Constituency_w']==cross['Constituency_t'])
También calculamos el número total de columnas coincidentes, que utilizaremos más adelante:
cross['Tmatch'] = sum([cross['Fmatch'],cross['Lmatch'],cross['Cmatch']])
Basándonos en nuestra exploración de los datos en el Capítulo 2, sabemos que dentro de nuestra población total de 422.500 combinaciones tenemos 637 pares de registros que tienen una coincidencia exacta en circunscripción y nombre o apellidos. Ésta es nuestra población match
:
match = cross[cross['Cmatch'] & (cross['Fmatch'] | cross['Lmatch'])]
El resto, nuestra población notmatch
, se extrae como la inversa:
notmatch = cross[(~cross['Cmatch']) | (~cross['Fmatch'] & ~cross['Lmatch'])]
Estas combinaciones se resumen en la Tabla 4-1.
Población coincidente/no coincidente | Coincidencia de circunscripción | Coincidencia de nombre | Coincidencia de apellidos |
---|---|---|---|
No coincide | No | No | No |
No coincide | No | No | Sí |
No coincide | No | Sí | No |
No coincide | No | Sí | Sí |
No coincide | Sí | No | No |
Partido | Sí | No | Sí |
Partido | Sí | Sí | No |
Partido | Sí | Sí | Sí |
A continuación examinaremos hasta qué punto la equivalencia entre nombre y apellidos, tanto individualmente como en conjunto, puede predecir si un registro individual pertenece a la población de match
o de notmatch
.
Probabilidad de coincidencia de un solo atributo
Empecemos considerando si la equivalencia del nombre de pila por sí sola es un buen indicador de que dos entidades dentro de un par de registros se refieren a la misma persona. Examinaremos las poblaciones match
y notmatch
y estableceremos, dentro de cada uno de esos subconjuntos, cuántos nombres de pila coinciden y cuántos no.
Convención de nomenclatura
Como trabajamos a través de varios subconjuntos de estas poblaciones, es útil adoptar una convención de nomenclatura estándar para que podamos ver de un vistazo cómo se seleccionó cada población de registros. A medida que seleccionamos registros, añadimos los criterios de selección al nombre de la población, de derecha a izquierda, por ejemplo, first_match
debe leerse como que primero seleccionamos los registros que forman parte de la población match
y, dentro de ese subconjunto de la población, seleccionamos además sólo las filas en las que los nombres de pila son equivalentes.
Probabilidad de coincidencia del nombre
Partiendo de la población match
podemos seleccionar aquellos registros en los que los nombres de pila sean equivalentes para obtener nuestra población first_match
:
first_match = match[match['Fmatch']] len(first_match) 632
Repitiendo esto para las otras tres combinaciones de coincidencia/no coincidencia, y equivalencia o no del nombre de pila, podemos elaborar un mapa de población, como se muestra en la Figura 4-1.
Por tanto, basándonos sólo en la equivalencia del nombre, tenemos:
Ahora podemos calcular algunos valores de probabilidad. En primer lugar, la probabilidad de que un par de registros cuyos nombres de pila son equivalentes sea realmente una coincidencia positiva verdadera puede calcularse como el número de pares dentro de la población match
cuyos nombres de pila coinciden dividido por el número de pares cuyos nombres de pila coinciden en las poblaciones match
y notmatch
:
De esto se deduce que, con sólo un 23%, la equivalencia del nombre de pila por sí sola no es un buen indicador de la coincidencia entre dos registros. Este valor es una probabilidad condicional, es decir, es la probabilidad de una coincidencia positiva verdadera condicionada a que el nombre coincida. Se puede escribir como
donde el carácter pipa (|) se lee como "dado que".
Apellido Probabilidad de coincidencia
Aplicando los mismos cálculos al apellido, podemos dibujar un segundo mapa de población, como se muestra en la Figura 4-2.
Al igual que para el nombre, la probabilidad de que un par de registros cuyos apellidos son equivalentes coincida realmente puede calcularse como el número de pares dentro de la población match
cuyos apellidos coinciden dividido por el número de pares cuyos apellidos coinciden en las poblaciones match
y notmatch
.
Para estos registros, la equivalencia del apellido es claramente un mejor predictor de una coincidencia verdadera que el nombre, lo que instintivamente tiene sentido.
De nuevo, esto puede escribirse como:
Probabilidad de coincidencia de atributos múltiples
Ahora, si tenemos en cuenta tanto la equivalencia del nombre como la del apellido, podemos subdividir aún más nuestro mapa de población. Partiendo de nuestro mapa de nombres de pila y subdividiendo cada categoría de nombres en equivalencia de apellidos y no, podemos ver nuestra población como se muestra en la Figura 4-3.
Ampliando nuestro cálculo a las coincidencias exactas tanto del nombre como del apellido, podemos calcular la probabilidad de una coincidencia positiva verdadera dada la equivalencia tanto del nombre como del apellido como:
Si el nombre coincide pero el apellido no, ¿cuál es la probabilidad de que coincida?
Si el nombre no coincide pero el apellido sí, ¿cuál es la probabilidad de que coincida?
Como esperábamos, si el nombre o el apellido no coinciden exactamente, la probabilidad de una coincidencia positiva verdadera es baja, pero una coincidencia de apellido nos da más confianza que una de nombre.
Si no coinciden ni el nombre ni el apellido, la probabilidad de que coincida es:
Esto no es sorprendente, ya que definimos las coincidencias positivas verdaderas como los registros con una coincidencia exacta en la circunscripción electoral y el nombre o los apellidos.
En conclusión, podemos utilizar estas probabilidades para decidir si es probable que tengamos una coincidencia positiva verdadera o no. En este ejemplo, daríamos más peso a una coincidencia de apellidos que a una de nombres. Esto supone una mejora respecto a nuestro método del Capítulo 3, en el que les dábamos la misma ponderación (y exigíamos que ambos fueran equivalentes) para declarar una coincidencia.
Pero espera, tenemos un problema. En el ejemplo anterior, empezamos con una población conocida de coincidencias que utilizamos para calcular las probabilidades de que la equivalencia entre nombre y apellidos equivalga a una coincidencia. Sin embargo, en la mayoría de las situaciones no disponemos de una población conocida de match
; ¡de lo contrario no necesitaríamos realizar el cotejo en primer lugar! ¿Cómo superamos esto? Para ello, tenemos que replantear un poco nuestro cálculo y luego emplear algunas técnicas de estimación inteligentes.
Modelos probabilísticos
En la sección anterior, aprendimos que algunos atributos son más informativos que otros; es decir, tienen más poder predictivo para ayudarnos a decidir si es probable que una coincidencia sea correcta. En esta sección, examinamos cómo calcular estas contribuciones y cómo combinarlas para evaluar la probabilidad global de una coincidencia.
Empezaremos con un poco de teoría estadística (utilizando la equivalencia del nombre de pila como ejemplo) antes de generalizar para modelar lo que podemos implementar a escala.
Teorema de Bayes
El teorema de Bayes, llamado así por Thomas Bayes, afirma que la probabilidad condicional de un suceso, basada en la ocurrencia de otro suceso, es igual a la probabilidad del segundo suceso dado el primero, multiplicada por la probabilidad del primero.
Considera la probabilidad de que dos registros elegidos al azar sean una coincidencia positiva verdadera, P(coincidencia), multiplicada por la probabilidad de que dentro de esas coincidencias coincidan los primeros nombres, P (primera|comparación):
Igualmente, podríamos calcular el mismo valor en el orden inverso, empezando por la probabilidad de que el primer nombre coincida, P(primero) multiplicada por la probabilidad de que los registros dentro de esta población sean una coincidencia positiva verdadera:
Igualando estas probabilidades, tenemos
Reordenando podemos calcular:
Podemos calcular P(primero) como la suma de las probabilidades de las poblaciones match
y notmatch
:
Sustituyendo en la ecuación anterior, tenemos:
Alternativamente, podemos reordenar esto como:
Si podemos estimar los valores de esta ecuación, podemos determinar la probabilidad de que si un nombre de pila es equivalente, entonces el par de registros realmente coincide.
Examinemos estos valores con un poco más de detalle, simplificando la notación a medida que avanzamos.
m Valor
La probabilidad condicional de que un atributo sea equivalente en el conjunto de la población match
se conoce como valor m. Utilizando nuestro ejemplo Firstname
, podemos denotarlo como:
En un conjunto de datos perfecto, todos los nombres de pila de la población match
serían exactamente equivalentes y el valor m sería 1. Por tanto, este valor puede considerarse una medida de la calidad de los datos, es decir, de la variabilidad que existe en la forma en que se ha capturado un atributo en los conjuntos de datos. Un valor más alto indica un atributo de mejor calidad.
u Valor
La probabilidad condicional de que un atributo sea equivalente en el conjunto de la población notmatch
se conoce como valor u. De nuevo, utilizando nuestro ejemplo de Firstname
, podemos denotarlo como:
Este valor refleja el grado de coincidencia de este atributo en los conjuntos de datos. Un valor más bajo indica un atributo menos común y más distintivo que, si se encontrara equivalente en un caso concreto, nos llevaría a cuestionar si pertenece a la población de notmatch
y es realmente una coincidencia. Por el contrario, un valor u más alto nos indica que este atributo concreto no es tan valioso para determinar las coincidencias globales.
Un buen ejemplo de valor u es el atributo mes de nacimiento, que, suponiendo que la población se distribuye por igual a lo largo del año, tendrá un valor u de .
Lambda () Valor
El valor lambda de, también llamado prior, es la probabilidad de que dos registros elegidos al azar coincidan.
A diferencia de los valores m y u, el es un valor a nivel de registro no asociado a ningún atributo concreto. Este valor es una medida de cuánta duplicación hay en el conjunto de datos en general y es el punto de partida para nuestros cálculos de probabilidad.
La inversa, la probabilidad de que dos registros elegidos al azar no coincidan, puede escribirse así:
Factor de Bayes
Sustituyendo estas notaciones compactas podemos obtener lo siguiente:
La relación también se conoce como factor de Bayes, en este caso del parámetro Firstname
. El factor Bayes, como combinación de los valores m y u, da una medida de la importancia que debemos atribuir al hecho de que los valores Firstname
fueran equivalentes.
Modelo Fellegi-Sunter
El modelo Fellegi-Sunter, llamado así por Ivan P. Fellegi y Alan B. Sunter,1 describe cómo podemos ampliar nuestro sencillo enfoque bayesiano, combinando la contribución de múltiples atributos, para calcular la probabilidad global de una coincidencia. Se basa en el supuesto simplificador de independencia condicional entre atributos, también conocido como naive Bayes.
Utilizando el modelo FS, podemos combinar los factores de Bayes asociados a cada atributo de nuestro registro simplemente multiplicándolos entre sí. Tomando nuestro ejemplo de Firstname
y ampliándolo para considerar cuando el Lastname
también es equivalente, tenemos:
Cuando un atributo no es equivalente, el factor de Bayes se calcula como el inverso, . Por tanto, cuando el Firstname
es equivalente pero el Lastname
no lo es, calculamos la probabilidad de una coincidencia global como:
Una vez que podamos calcular los valores m y u de cada atributo, y la del conjunto de datos, podemos calcular fácilmente las probabilidades de cada par de registros. Simplemente determinamos la equivalencia de cada atributo (exacta o aproximada según convenga), seleccionamos los factores de Bayes adecuados y los multiplicamos entre sí mediante la fórmula anterior para calcular la probabilidad global de ese par de registros.
Por tanto, para nuestro sencillo ejemplo, nuestros factores de Bayes se calculan como se muestra en la Tabla 4-2.
Firstname equivalencia |
Lastname equivalencia |
Firstname Factor de Bayes |
Lastname Factor de Bayes |
Factor de Bayes combinado |
---|---|---|---|---|
No | No | |||
No | Sí | |||
Sí | No | |||
Sí | Sí |
Peso del partido
Para hacer más intuitivos los cálculos globales de las coincidencias, a veces se utiliza el logaritmo de los factores de Bayes, de modo que puedan sumarse en lugar de multiplicarse. De este modo es fácil visualizar la contribución relativa de cada atributo a la puntuación global.
Para nuestro sencillo ejemplo de equivalencia entre nombre y apellidos, el peso logarítmico de coincidencia podría calcularse (utilizando base 2) como:
Podemos calcular la probabilidad a partir del peso del partido como:
Ahora que sabemos cómo combinar nuestras probabilidades de atributos individuales, o ponderaciones de coincidencia, consideremos cómo estimar nuestra y nuestros valores m y u para cada atributo cuando no tenemos una población match
conocida. Una técnica que podemos utilizar se llama algoritmo de maximización de expectativas (EM).
Algoritmo de maximización de expectativas
El algoritmo de maximización de expectativas utiliza un enfoque iterativo para aproximar la y los valores m y u. Veamos una forma simplificada de esto en acción aplicada a nuestro problema de ejemplo.
Iteración 1
Para la primera iteración hacemos la suposición inicial de que los pares de registros en los que la mayoría de las columnas de características son equivalentes son coincidentes:
it1_match = cross[cross['Tmatch']>=2] it1_notmatch = cross[cross['Tmatch']<2] len(it1_match) 637
Esto nos da una pseudopoblación match
, it1_match
, de 637 registros. Además de las 628 coincidencias perfectas que encontramos en el Capítulo 2, también tenemos 9 coincidencias en las que o bien Firstname
o bien Lastname
(pero no ambas) no coinciden, como vemos en la Figura 4-4:
it1_match[~it1_match['Fmatch'] | ~it1_match['Lmatch']] [['Constituency_w','Firstname_w','Firstname_t', 'Lastname_w','Lastname_t']]
Nuestra inicial inicial es por tanto
Por tanto, nuestra ponderación de coincidencia previa inicial es .
Por tanto, como punto de partida, es extremadamente improbable que dos registros coincidan. Ahora calculemos nuestros valores m y u para poder actualizar nuestra probabilidad por registro.
Como tenemos una pseudopoblación match
y notmatch
, es sencillo calcular nuestros valores m y u como la proporción de cada población con un atributo equivalente. Para Firstname
, Lastname
, y Constituency
utilizamos:
mfi1 = len(it1_match[it1_match['Fmatch']])/len(it1_match) mli1 = len(it1_match[it1_match['Lmatch']])/len(it1_match) mci1 = len(it1_match[it1_match['Cmatch']])/len(it1_match) ufi1 = len(it1_notmatch[it1_notmatch['Fmatch']])/len(it1_notmatch) uli1 = len(it1_notmatch[it1_notmatch['Lmatch']])/len(it1_notmatch) uci1 = len(it1_notmatch[it1_notmatch['Cmatch']])/len(it1_notmatch)
La Tabla 4-3 muestra estos valores y los valores de peso de coincidencia resultantes por atributo.
Atributo | m valor | u valor | Factor Bayes de coincidencia | Peso del partido | No coincide con el factor de Bayes | No coincide con el peso |
---|---|---|---|---|---|---|
Firstname |
0.9921 | 0.0049 | 203.97 | 7.67 | 0.0079 | -6.98 |
Lastname |
0.9937 | 0.0008 | 1201.19 | 10.23 | 0.0063 | -7.31 |
Constituency |
1.0 | 0.0 | 0 |
No hay pares de registros en los que la circunscripción sea equivalente en la población notmatch
, por lo que su valor u es 0 y, por tanto, su peso match
es matemáticamente infinito y su peso notmatch
es infinito negativo.
Ahora podemos utilizar estos valores en el modelo Fellegi-Sunter para calcular la probabilidad de coincidencia de cada par de registros de la población completa. Utilizamos una función de ayuda para calcular estas probabilidades basándonos en los valores de las características de coincidencia Constituency
, Firstname
y Lastname
:
def match_prb(Fmatch,Lmatch,Cmatch,mf1,ml1,mc1,uf1,ul1,uc1, lmbda): if (Fmatch==1): mf = mf1 uf = uf1 else: mf = (1-mf1) uf = (1-uf1) if (Lmatch==1): ml = ml1 ul = ul1 else: ml = (1-ml1) ul = (1-ul1) if (Cmatch==1): mc = mc1 uc = uc1 else: mc = (1-mc1) uc = (1-uc1) prob = (lmbda * ml * mf * mc) / (lmbda * ml * mf * mc + (1-lmbda) * ul * uf * uc) return(prob)
Aplicamos esta función a toda la población con:
cross['prob'] = cross.apply(lambda x: match_prb( x.Fmatch,x.Lmatch,x.Cmatch, mfi1,mli1,mci1, ufi1,uli1,uci1, lmbda), axis=1)
Una vez calculados estos valores, podemos iterar de nuevo, resegmentando nuestra población en poblaciones match
y notmatch
en función de las probabilidades de coincidencia calculadas.
Iteración 2
A título ilustrativo, utilizamos una probabilidad de coincidencia global superior a 0,99 para definir nuestra nueva población supuesta match
y asignamos cualquier registro con una probabilidad igual o inferior a nuestra población notmatch
:
it2_match = cross[cross['prob']>0.99] it2_notmatch = cross[cross['prob']<=0.99] len(it2_match) 633
La aplicación de este umbral de 0,99 nos da una población match
ligeramente reducida de 633 habitantes. Veamos por qué. Si seleccionamos los registros situados justo por debajo del umbral, podemos ver:
it2_notmatch[it2_notmatch['prob']>0.9] [['Constituency_w', 'Lastname_w','Lastname_t','prob']]
Como vemos en la Figura 4-5, si el Lastname
no es equivalente, la nueva probabilidad de coincidencia cae justo por debajo de nuestro umbral de 0,99. Utilizando estas nuevas poblaciones match
y notmatch
podemos revisar nuestra m y u e iterar de nuevo, recalculando las probabilidades de coincidencia de cada par de registros.
En este caso, nuestra no cambia materialmente:
Sólo cambian ligeramente los valores de Lastname
, como se muestra en la Tabla 4-4.
Atributo | m valor | u valor | Factor Bayes de coincidencia | Peso del partido | No coincide con el factor de Bayes | No coincide con el peso |
---|---|---|---|---|---|---|
Firstname |
0.9921 | 0.0049 | 203.97 | 7.67 | 0.0079 | -6.98 |
Lastname |
1.0 | 0.0008 | 1208.79 | 10.24 | 0 | |
Constituency |
1.0 | 0.0 | 0 |
Iteración 3
En este sencillo ejemplo, esta siguiente iteración no cambia la población de match
, que sigue siendo 633, porque el algoritmo EM ya ha convergido.
Esto nos da unos valores finales de los parámetros de
Esto nos parece instintivamente correcto. Sabemos que una coincidencia siempre tendrá una circunscripción equivalente y que coincidirá el nombre o el apellido, siendo ligeramente más probable que el apellido sea equivalente que el nombre (cinco de cada nueve frente a cuatro de cada nueve en la muestra anterior).
Del mismo modo, sabemos que la circunscripción nunca será la misma en un par de registros de notmatch
y es muy poco probable que el nombre o el apellido coincidan accidentalmente (siendo ligeramente más probable el nombre).
Podemos convertir estos valores estimados en probabilidades de coincidencia utilizando las ecuaciones de la sección anterior:
Como era de esperar, estas probabilidades coinciden con los valores que calculamos utilizando los mapas de probabilidad de la Figura 4-3 cuando conocíamos de antemano la población de match
y notmatch
.
En conclusión, ahora podemos estimar las probabilidades de coincidencia para todas las permutaciones diferentes de equivalencia de atributos sin tener que conocer de antemano la población match
. Este enfoque probabilístico es potente y escalable a grandes conjuntos de datos con múltiples atributos. Para ayudarnos a aplicar estas técnicas con mayor facilidad, en la siguiente sección presentamos una biblioteca de código abierto eficaz y fácil de usar, Splink.
Presentación de Splink
Splink es un paquete de Python para la resolución probabilística de entidades. Splink implementa el modelo Fellegi-Sunter e incluye diversos resultados interactivos para ayudar a los usuarios a comprender los modelos y diagnosticar los problemas de vinculación.
Splink admite varios backends para realizar los cálculos de coincidencia. Para empezar, utilizaremos DuckDB, un sistema de gestión de bases de datos SQL en proceso, que podemos ejecutar localmente en nuestro portátil.
Configurar Splink
Para importar Splink a nuestro bloc de notas, utilizamos:
import splink
Splink requiere una columna ID única en cada conjunto de datos, así que tenemos que crearlas copiando sus respectivos índices DataFrame:
df_w['unique_id'] = df_w.index df_t['unique_id'] = df_t.index
Splink también necesita que las mismas columnas estén presentes en ambos conjuntos de datos. Por lo tanto, tenemos que crear columnas en blanco cuando éstas sólo estén presentes en un conjunto de registros y, a continuación, eliminar las columnas innecesarias:
df_w['Flink'] = None df_t['Notes'] = None df_w = df_w[['Firstname','Lastname','Constituency','Flink','Notes', 'unique_id']] df_t = df_t[['Firstname','Lastname','Constituency','Flink','Notes', 'unique_id']]
Nuestro siguiente paso es configurar los ajustes de Splink:
from splink.duckdb.linker import DuckDBLinker from splink.duckdb import comparison_library as cl settings = { "link_type": "link_only", "comparisons": [ cl.exact_match("Firstname"), cl.exact_match("Lastname"), cl.exact_match("Constituency"), ], } linker = DuckDBLinker([df_w, df_t], settings)
Splink admite tanto la deduplicación de registros dentro de un único conjunto de datos como la vinculación entre uno o más conjuntos de datos independientes. Aquí establecemos link_type
en link_only
para indicar a Splink que sólo queremos coincidencias, no deduplicaciones, entre nuestros dos conjuntos de datos. También indicamos a Splink las comparaciones que deseamos utilizar, en este caso coincidencias exactas entre nuestros tres atributos. Por último, instanciamos el enlazador con estos ajustes y nuestros DataFrames fuente.
Para ayudarnos a comprender nuestros conjuntos de datos, Splink proporciona una visualización de la distribución de las columnas que se van a emparejar:
linker.profile_columns(['Firstname','Lastname','Constituency'])
Los gráficos que vemos en la Figura 4-6 nos muestran la población combinada de ambos conjuntos de datos.
Empezando por la distribución de los nombres de pila, podemos ver en la parte inferior derecha del gráfico que, dentro de la población de 352 nombres distintos, aproximadamente el 35% sólo aparecen dos veces, muy probablemente una vez en cada conjunto de datos. Luego, moviéndonos de derecha a izquierda, vemos un aumento gradual de la frecuencia hasta el nombre más popular, con 32 ocurrencias. Si observamos los 10 valores principales por recuento de valores, vemos que John es el nombre más popular, seguido de Andrew, David, etc. Esto nos dice que Firstname
es un atributo razonable para buscar coincidencias, pero si se utiliza solo, dará lugar a algunos falsos positivos.
En cuanto a los apellidos, el patrón es más marcado, con una población mayor de 574 nombres distintos, de los que casi el 80% aparecen sólo dos veces. Si nos fijamos en los 10 valores principales, los apellidos más comunes, Smith y Jones, aparecen 18 veces, casi la mitad que el nombre más popular. Como era de esperar, esto nos dice que Lastname
es un atributo más rico que Firstname
y, por tanto, su equivalencia es un mejor predictor de la coincidencia de entidades.
Como era de esperar, las circunscripciones están emparejadas de forma única en los dos conjuntos de datos, por lo que todos los valores aparecen exactamente dos veces.
A efectos de este sencillo ejemplo, vamos a pedir a Splink que calcule todos los parámetros del modelo utilizando el algoritmo de maximización de expectativas que hemos introducido antes. El parámetro inicial True
indica a Splink que compare todos los registros de ambos conjuntos de datos sin bloquearlos (lo veremos en el próximo capítulo). También le decimos a Splink que recalcule los valores de u en cada iteración estableciendo fix_u_probabilities
en False
. Si establecemos fix_probability_two_random_records_match
en False
, la (la probabilidad general de coincidencia entre los dos conjuntos de datos) se recalculará en cada iteración. Por último, le decimos a Splink que utilice el valor actualizado de actualizado al calcular las probabilidades de los pares de registros.:
em_session = linker.estimate_parameters_using_expectation_maximisation( 'True', fix_u_probabilities=False, fix_probability_two_random_records_match=False, populate_probability_two_random_records_match_from_trained_values =True)
Rendimiento de Splink
El modelo EM de converge tras tres iteraciones. Splink produce un gráfico interactivo que muestra la progresión iterativa de los valores relativos de los pesos de coincidencia:
em_session.match_weights_interactive_history_chart()
La Figura 4-7 muestra los pesos finales de coincidencia que Splink ha calculado tras la tercera iteración. En primer lugar, tenemos el peso de coincidencia previo (inicial), que es una medida de la probabilidad de que coincidan dos registros elegidos al azar. Si pasas el ratón por encima de las barras de peso de coincidencia, podrás ver el valor calculado del peso de coincidencia junto con los parámetros m y u subyacentes. Éstos se calculan del siguiente modo:
A efectos ilustrativos, Splink aproxima el peso de la coincidencia no exacta de Constituency
como infinito negativo y lo muestra en un color diferente. Esto se debe a que no hay casos en los que los atributos Firstname
y Lastname
coincidan pero el Constituency
no.
Podemos ver los valores discretos que Splink ha calculado utilizando:
linker.save_settings_to_json("Chapter4_Splink_Settings.json", overwrite=True)
{'link_type': 'link_only', 'comparisons': [{'output_column_name': 'Firstname', 'comparison_levels': [{'sql_condition': '"Firstname_l" IS NULL OR "Firstname_r" IS NULL', 'label_for_charts': 'Null', 'is_null_level': True}, {'sql_condition': '"Firstname_l" = "Firstname_r"', 'label_for_charts': 'Exact match', 'm_probability': 0.992118804074688, 'u_probability': 0.004864290128404288}, {'sql_condition': 'ELSE', 'label_for_charts': 'All other comparisons', 'm_probability': 0.007881195925311958, 'u_probability': 0.9951357098715956}], 'comparison_description': 'Exact match vs. anything else'}, {'output_column_name': 'Lastname', 'comparison_levels': [{'sql_condition': '"Lastname_l" IS NULL OR "Lastname_r" IS NULL', 'label_for_charts': 'Null', 'is_null_level': True}, {'sql_condition': '"Lastname_l" = "Lastname_r"', 'label_for_charts': 'Exact match', 'm_probability': 0.9937726043638647, 'u_probability': 0.00082730840955421}, {'sql_condition': 'ELSE', 'label_for_charts': 'All other comparisons', 'm_probability': 0.006227395636135347, 'u_probability': 0.9991726915904457}], 'comparison_description': 'Exact match vs. anything else'}, {'output_column_name': 'Constituency', 'comparison_levels': [{'sql_condition': '"Constituency_l" IS NULL OR "Constituency_r" IS NULL', 'label_for_charts': 'Null', 'is_null_level': True}, {'sql_condition': '"Constituency_l" = "Constituency_r"', 'label_for_charts': 'Exact match', 'm_probability': 0.9999999403661186, 'u_probability': 3.092071473132138e-05}, {'sql_condition': 'ELSE', 'label_for_charts': 'All other comparisons', 'm_probability': 5.963388147277392e-08, 'u_probability': 0.9999690792852688}], 'comparison_description': 'Exact match vs. anything else'}], 'retain_intermediate_calculation_columns': True, 'retain_matching_columns': True, 'sql_dialect': 'duckdb', 'linker_uid': 'adm20und', 'probability_two_random_records_match': 0.0015075875293170335}
Las probabilidades m y u coinciden con las que calculamos manualmente utilizando el algoritmo de maximización de expectativas anteriormente en el capítulo.
Por último, como antes, aplicamos un umbral de probabilidad de coincidencia y seleccionamos el par de registros por encima del umbral:
pres = linker.predict(threshold_match_probability = 0.99).as_pandas_dataframe() len(pres) 633
El análisis de estas predicciones muestra que las 633 son verdaderos positivos, quedando los 13 verdaderos negativos de las elecciones parciales y 4 falsos negativos. Podemos ver los 4 falsos negativos con:
m_outer = match.merge( pres, left_on=['Constituency_t'], right_on=['Constituency_l'], how='outer') m_outer[m_outer['Constituency_t']!=m_outer['Constituency_l']] [['Constituency_w','Lastname_w','Lastname_t']]
El resultado, mostrado en la Figura 4-8, muestra que la falta de coincidencia en Lastname
es la razón por la que estas entidades quedan por debajo del umbral de coincidencia.
En comparación con los resultados no ponderados del Capítulo 3, Splink declara una coincidencia para "Liz Truss" frente a "Elizabeth Truss", pero no empareja "Anne Marie Morris" con "Anne Morris", ni "Martin Docherty-Hughes" con "Martin Docherty". Esto se debe a que está más influenciado por una falta de coincidencia en Lastname
, que estadísticamente es un mejor predictor negativo, que por una falta de coincidencia en Firstname
.
Resumen
Para recapitular, tomamos dos conjuntos de registros y los combinamos en un conjunto de datos compuesto que contenía cada combinación de pares de registros. A continuación, calculamos las características de coincidencia exacta entre campos equivalentes y luego combinamos esas características, ponderadas según la frecuencia con que se daban tanto en la población coincidente como en la no coincidente, para determinar la probabilidad global de coincidencia.
Hemos visto cómo utilizar la teoría de la probabilidad para calcular los pesos de coincidencia utilizando el algoritmo iterativo de maximización de expectativas cuando no tenemos poblaciones conocidas de match
.
Por último, introdujimos el marco de resolución probabilística de entidades Splink, que simplificó enormemente los cálculos al combinar múltiples atributos y nos ayudó a visualizar y comprender los resultados de nuestras coincidencias.
Ahora que hemos trabajado con un ejemplo a pequeña escala, aplicaremos las técnicas de emparejamiento aproximado y probabilístico a mayor escala.
1 El documento original está disponible en Internet.
Get Resolución práctica de entidades now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.