Tutorial: Detección de relaciones en el conjunto de datos de Synthea con el vínculo semántico

En este tutorial se muestra cómo detectar relaciones en el conjunto de datos público de Synthea con el vínculo semántico.

Al trabajar con nuevos datos o sin un modelo de datos existente, puede resultar útil detectar relaciones automáticamente. Esta detección de relaciones puede ayudarle a:

  • Comprender el modelo en un nivel alto
  • Obtener más información durante el análisis de datos exploratorios
  • Validar datos actualizados o nuevos, datos entrantes
  • Limpiar datos

Incluso si las relaciones se conocen de antemano, una búsqueda de relaciones puede ayudar a comprender mejor el modelo de datos o la identificación de problemas de calidad de los datos.

En este tutorial, comenzará con un ejemplo de línea base simple en el que experimentará con solo tres tablas para que las conexiones entre ellas sean fáciles de seguir. A continuación, se mostrará un ejemplo más complejo con un conjunto de tablas más grande.

En este tutorial, aprenderá a:

  • Utilice los componentes de la biblioteca Python de vínculo semántico (SemPy), que admiten la integración con Power BI y le ayudarán a automatizar el análisis de datos. Estos componentes incluyen:
    • FabricDataFrame: una estructura similar a Pandas mejorada con información semántica adicional.
    • Funciones para extraer modelos semánticos de un área de trabajo de Fabric en el cuaderno.
    • Funciones que automatizan la detección y visualización de relaciones en los modelos semánticos.
  • Solución de problemas del proceso de detección de relaciones para modelos semánticos con varias tablas e interdependencias.

Requisitos previos

  • Seleccione Áreas de trabajo en el panel de navegación izquierdo para buscar y seleccionar el área de trabajo. Esta área de trabajo se convertirá en el área de trabajo actual.

Seguimiento en el cuaderno

El cuaderno relationships_detection_tutorial.ipynb acompaña a este tutorial.

Para abrir el cuaderno complementario para este tutorial, siga las instrucciones en Preparación del sistema para los tutoriales de ciencia de datos para importar el cuaderno en el área de trabajo.

Si prefiere copiar y pegar el código de esta página, puede crear un cuaderno nuevo.

Asegúrese de adjuntar una instancia de LakeHouse al cuaderno antes de empezar a ejecutar código.

Configuración del cuaderno

En esta sección, configurará un entorno de cuaderno con los módulos y los datos necesarios.

  1. Instale SemPy desde PyPI mediante la funcionalidad de instalación en línea %pip en el cuaderno:

    %pip install semantic-link
    
  2. Realice las importaciones necesarias de módulos SemPy que necesitará más adelante:

    import pandas as pd
    
    from sempy.samples import download_synthea
    from sempy.relationships import (
        find_relationships,
        list_relationship_violations,
        plot_relationship_metadata
    )
    
  3. Importe Pandas para aplicar una opción de configuración que ayude con el formato de salida:

    import pandas as pd
    pd.set_option('display.max_colwidth', None)
    
  4. Incorpore los datos de ejemplo. En este tutorial, usará el conjunto de datos de Synthea de registros médicos sintéticos (versión reducida, para simplificar):

    download_synthea(which='small')
    

Detección de relaciones en un pequeño subconjunto de tablas de Synthea

  1. Seleccione tres tablas de un conjunto mayor:

    • patients especifica la información de los pacientes.
    • encounters especifica los pacientes que tuvieron encuentros médicos (por ejemplo, una cita médica, un procedimiento).
    • providers especifica qué proveedores médicos asistieron a los pacientes.

    La tabla encounters resuelve una relación de varios a varios entre patients y providers, y se puede describir como una entidad asociativa:

    patients = pd.read_csv('synthea/csv/patients.csv')
    providers = pd.read_csv('synthea/csv/providers.csv')
    encounters = pd.read_csv('synthea/csv/encounters.csv')
    
  2. Busque relaciones entre las tablas mediante la función find_relationships de SemPy:

    suggested_relationships = find_relationships([patients, providers, encounters])
    suggested_relationships
    
  3. Visualice las relaciones de DataFrame como un grafo mediante la función plot_relationship_metadata de SemPy.

    plot_relationship_metadata(suggested_relationships)
    

    Captura de pantalla que muestra las relaciones entre las tablas del conjunto de datos.

    La función establece la jerarquía de relaciones desde el lado izquierdo hasta el lado derecho, que corresponde a las tablas de origen y de destino de la salida. En otras palabras, las tablas de origen independientes del lado izquierdo usan sus claves externas para apuntar a sus tablas de dependencias de destino en el lado derecho. Cada cuadro de entidad muestra columnas que participan en el lado de origen o de destino de una relación.

    De forma predeterminada, las relaciones se generan como "m:1" (no como "1:m") o "1:1". Las relaciones "1:1" se pueden generar de una o ambas maneras, dependiendo de si la proporción de valores asignados a todos los valores supera coverage_threshold en una o ambas direcciones. Más adelante en este tutorial, tratará el caso menos frecuente de relaciones "m:m".

Solución de problemas de detección de relaciones

En el ejemplo de base de referencia se muestra una detección correcta de relaciones en datos limpios de Synthea. En la práctica, los datos rara vez están limpios, lo que impide la detección correcta. Hay varias técnicas que pueden ser útiles cuando los datos no están limpios.

En esta sección de este tutorial se aborda la detección de relaciones cuando el modelo semántico contiene datos sucios.

  1. Comience manipulando los DataFrames originales para obtener datos "sucios" e imprima el tamaño de estos datos.

    # create a dirty 'patients' dataframe by dropping some rows using head() and duplicating some rows using concat()
    patients_dirty = pd.concat([patients.head(1000), patients.head(50)], axis=0)
    
    # create a dirty 'providers' dataframe by dropping some rows using head()
    providers_dirty = providers.head(5000)
    
    # the dirty dataframes have fewer records than the clean ones
    print(len(patients_dirty))
    print(len(providers_dirty))
    
    
  2. Para comparar, imprima los tamaños de las tablas originales:

    print(len(patients))
    print(len(providers))
    
  3. Busque relaciones entre las tablas mediante la función find_relationships de SemPy:

    find_relationships([patients_dirty, providers_dirty, encounters])
    

    La salida del código muestra que no se detectó ninguna relación debido a los errores que introdujo anteriormente para crear el modelo semántico "sucio".

Uso de la validación

La validación es la mejor herramienta para solucionar errores de detección de relaciones porque:

  • Informa claramente por qué una relación determinada no sigue las reglas de clave externa y, por lo tanto, no se puede detectar.
  • Se ejecuta rápidamente con grandes modelos semánticos debido a que solo se centra en las relaciones declaradas y no realiza una búsqueda.

La validación puede usar cualquier DataFrame con columnas similares a las generadas por find_relationships. En el código siguiente, el DataFrame suggested_relationships hace referencia a patients en lugar de a patients_dirty, pero puede establecer un alias en los objetos DataFrame con un diccionario:

dirty_tables = {
    "patients": patients_dirty,
    "providers" : providers_dirty,
    "encounters": encounters
}

errors = list_relationship_violations(dirty_tables, suggested_relationships)
errors

Criterios de búsqueda flexibles

En escenarios más confusos, puede intentar flexibilizar los criterios de búsqueda. Este método aumenta la posibilidad de falsos positivos.

  1. Establezca include_many_to_many=True y evalúe si ayuda:

    find_relationships(dirty_tables, include_many_to_many=True, coverage_threshold=1)
    

    Los resultados muestran que se detectó la relación de encounters a patients, pero hay dos problemas:

    • La relación indica una dirección de patients a encounters, que es la inversa de la relación esperada. Esto se debe a que toda la tabla patients resultó estar cubierta por encounters (Coverage From es 1,0) mientras que encounters solo está cubierta parcialmente por patients (Coverage To = 0,85), ya que faltan filas de pacientes.
    • Hay una coincidencia accidental en una columna GENDER de cardinalidad baja, que coincide por nombre y valor en ambas tablas, pero no es una relación "m:1" de interés. La cardinalidad baja se indica mediante las columnas Unique Count From y Unique Count To.
  2. Vuelva a ejecutar find_relationships para buscar solo las relaciones "m:1", pero con un valor de coverage_threshold=0.5 inferior:

    find_relationships(dirty_tables, include_many_to_many=False, coverage_threshold=0.5)
    

    El resultado muestra la dirección correcta de las relaciones de encounters a providers. Sin embargo, no se detecta la relación de encounters a patients, porque patients no es única, por lo que no puede estar en el lado "Uno" de la relación "m:1".

  3. Flexibilice tanto include_many_to_many=True como coverage_threshold=0.5:

    find_relationships(dirty_tables, include_many_to_many=True, coverage_threshold=0.5)
    

    Ahora ambas relaciones de interés son visibles, pero hay mucho más ruido:

    • Está presente una coincidencia de cardinalidad baja en GENDER.
    • Aparece una coincidencia de cardinalidad más alta de "m:m" en ORGANIZATION lo que hace que sea evidente que ORGANIZATION se trate probablemente de una columna desnormalizada en ambas tablas.

Coincidencia de nombres de columna

De forma predeterminada, SemPy considera como coincidencias solo los atributos que muestran similitud de nombres, aprovechando el hecho de que los diseñadores de bases de datos suelen nombrar de la misma manera las columnas relacionadas. Este comportamiento ayuda a evitar relaciones falsas, que se producen con más frecuencia con claves de enteros de cardinalidad baja. Por ejemplo, si hay categorías de producto 1,2,3,...,10 y un código de estado de pedido 1,2,3,...,10, se confundirán entre sí cuando solo se examinen las asignaciones de valores sin tener en cuenta los nombres de columna. Las relaciones falsas no deberían ser un problema con las claves de tipo GUID.

SemPy busca una similitud entre los nombres de columna y los nombres de tabla. La coincidencia es aproximada y no distingue mayúsculas de minúsculas. Omite las subcadenas de "decorador" más frecuentes, como "id", "code", "name", "key", "pk" o "fk". Como resultado, los casos de coincidencia más típicos son:

  • Un atributo denominado "column" en la entidad "foo" coincide con un atributo denominado "column" (también "COLUMN" o "Column") en la entidad "bar".
  • Un atributo denominado "column" en la entidad "foo" coincide con un atributo denominado "column_id" en "bar".
  • Un atributo denominado "bar" en la entidad "foo" coincide con un atributo denominado "code" en "bar".

Al hacer coincidir primero los nombres de las columnas, la detección se ejecuta más rápidamente.

  1. Hacer coincidir con los nombres de columna:

    • Para comprender qué columnas se seleccionan para su posterior evaluación, use la opción verbose=2 (verbose=1 enumera solo las entidades que se están procesando).
    • El parámetro name_similarity_threshold determina cómo se comparan las columnas. El umbral de 1 indica que solo se está interesado en la coincidencia del 100 %.
    find_relationships(dirty_tables, verbose=2, name_similarity_threshold=1.0);
    

    La ejecución con una similitud del 100 % no tiene en cuenta las pequeñas diferencias entre los nombres. En el ejemplo, las tablas tienen un formato plural con sufijo "s", lo que da como resultado una coincidencia exacta. Esto se controla bien con el valor predeterminado name_similarity_threshold=0.8.

  2. Vuelva a ejecutar con el valor predeterminado name_similarity_threshold=0.8:

    find_relationships(dirty_tables, verbose=2, name_similarity_threshold=0.8);
    

    Observe que el identificador para la forma plural patients ahora se compara con la singular patient sin agregar demasiadas otras comparaciones falsas al tiempo de ejecución.

  3. Vuelva a ejecutar con el valor predeterminado name_similarity_threshold=0:

    find_relationships(dirty_tables, verbose=2, name_similarity_threshold=0);
    

    Cambiar name_similarity_threshold a 0 es el otro extremo e indica que se quiere comparar todas las columnas. Esto rara vez es necesario y da como resultado un mayor tiempo de ejecución y coincidencias falsas que deben revisarse. Observe el número de comparaciones en la salida detallada.

Resumen de sugerencias de solución de problemas

  1. Comience desde una coincidencia exacta para las relaciones "m:1" (es decir, los valores predeterminados include_many_to_many=False y coverage_threshold=1.0). Esto suele ser lo que se quiere.
  2. Use un enfoque restringido en los subconjuntos más pequeños de tablas.
  3. Utilice la validación para detectar problemas de calidad de datos.
  4. Use verbose=2 si quiere comprender qué columnas se consideran para la relación. Esto puede dar lugar a una gran cantidad de valores de salida.
  5. Tenga en cuenta las ventajas y desventajas de los argumentos de búsqueda. include_many_to_many=True y coverage_threshold<1.0 pueden producir relaciones falsas que quizá sean más difíciles de analizar y que deberán filtrarse.

Detección de relaciones en el conjunto de datos completo de Synthea

El ejemplo sencillo de base de referencia fue una cómoda herramienta de aprendizaje y solución de problemas. En la práctica, puede empezar desde un modelo semántico como el conjunto de datos completo de Synthea, que tiene muchas más tablas. Explore el conjunto de datos completo de Synthea como se indica a continuación.

  1. Lea todos los archivos del directorio synthea/csv:

    all_tables = {
        "allergies": pd.read_csv('synthea/csv/allergies.csv'),
        "careplans": pd.read_csv('synthea/csv/careplans.csv'),
        "conditions": pd.read_csv('synthea/csv/conditions.csv'),
        "devices": pd.read_csv('synthea/csv/devices.csv'),
        "encounters": pd.read_csv('synthea/csv/encounters.csv'),
        "imaging_studies": pd.read_csv('synthea/csv/imaging_studies.csv'),
        "immunizations": pd.read_csv('synthea/csv/immunizations.csv'),
        "medications": pd.read_csv('synthea/csv/medications.csv'),
        "observations": pd.read_csv('synthea/csv/observations.csv'),
        "organizations": pd.read_csv('synthea/csv/organizations.csv'),
        "patients": pd.read_csv('synthea/csv/patients.csv'),
        "payer_transitions": pd.read_csv('synthea/csv/payer_transitions.csv'),
        "payers": pd.read_csv('synthea/csv/payers.csv'),
        "procedures": pd.read_csv('synthea/csv/procedures.csv'),
        "providers": pd.read_csv('synthea/csv/providers.csv'),
        "supplies": pd.read_csv('synthea/csv/supplies.csv'),
    }
    
  2. Busque relaciones entre las tablas mediante la función find_relationships de SemPy:

    suggested_relationships = find_relationships(all_tables)
    suggested_relationships
    
  3. Visualice las relaciones:

    plot_relationship_metadata(suggested_relationships)
    

    Captura de pantalla de las relaciones entre las tablas.

  4. Cuente cuántas relaciones "m:m" nuevas se detectarán con include_many_to_many=True. Estas relaciones se agregan a las relaciones "m:1" mostradas anteriormente; por lo tanto, debe filtrar por multiplicity:

    suggested_relationships = find_relationships(all_tables, coverage_threshold=1.0, include_many_to_many=True) 
    suggested_relationships[suggested_relationships['Multiplicity']=='m:m']
    
  5. Puede ordenar los datos de relación por varias columnas para comprender mejor su naturaleza. Por ejemplo, podría optar por ordenar la salida por Row Count From y Row Count To, lo que ayuda a identificar las tablas más grandes.

    suggested_relationships.sort_values(['Row Count From', 'Row Count To'], ascending=False)
    

    En un modelo semántico diferente, quizás sería importante centrarse en el número de valores null Null Count From o Coverage To.

    Este análisis puede ayudarle a comprender si alguna de las relaciones podría no ser válida y si necesita quitarla de la lista de candidatos.

Consulte otros tutoriales para el vínculo semántico/SemPy: