Compartir a través de


Tutorial: Descubrir relaciones en el conjunto de datos Synthea mediante el vínculo semántico

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

Al trabajar con nuevos datos o trabajar 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 y
  • 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 muestra un ejemplo más complejo con un conjunto de tablas más grande.

En este tutorial, aprenderá a:

  • Use componentes de la biblioteca de Python del vínculo semántico (SemPy) que admiten la integración con Power BI y ayudan 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 espacio de trabajo de Fabric en tu cuaderno.
    • Funciones que automatizan la detección y visualización de relaciones en los modelos semánticos.
  • Solucionar problemas en el proceso de descubrimiento de relaciones para los modelos semánticos con múltiples tablas e interdependencias.

Prerrequisitos

  • 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.

Configuración del cuaderno

En esta sección, configurará un entorno de cuaderno con los módulos y 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 los 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 Synthea de registros médicos sintéticos (versión pequeña 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 del paciente
    • encounters especifica los pacientes que tuvieron encuentros médicos (por ejemplo, una cita médica, un procedimiento)
    • providers especifica qué proveedores médicos atendieron 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 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 "from" y "to" 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ás el caso menos frecuente de las relaciones "m:m".

Solución de problemas de detección de relaciones

En el ejemplo de referencia se muestra una detección exitosa de relaciones en datos limpios 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 los datos sucios.

    # 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, los tamaños de impresión 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 detectan relaciones 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 foránea, por lo cual no puede ser detectada.
  • Se ejecuta rápidamente con modelos semánticos grandes porque 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 suggested_relationships DataFrame hace referencia a patients en lugar de 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 flexible

En situaciones más confusas, puede intentar relajar 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 a:

    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 un inverso de la relación esperada. Esto se debe a que todos los patients resultaron estar cubiertos por encounters (Coverage From es 1,0), mientras que encounters solo están cubiertos parcialmente por patients (Coverage To = 0,85), ya que faltan filas correspondientes a los pacientes.
    • Se produce una coincidencia accidental en una columna de cardinalidad baja GENDER, 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 Unique Count From y Unique Count To columnas.
  2. Vuelva a ejecutar find_relationships para buscar solo las relaciones "m:1", pero con un coverage_threshold=0.5inferior:

    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.

Empareja los nombres de columnas

De forma predeterminada, SemPy considera que solo coincide con atributos que muestran similitud de nombres, aprovechando el hecho de que los diseñadores de bases de datos suelen asignar nombres a las columnas relacionadas de la misma manera. 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 1,2,3,...,10 categorías de productos y 1,2,3,...,10 códigos de estado de pedido, se confundirán entre sí al examinar solo las asignaciones de valores sin tener en cuenta los nombres de las columnas. Las relaciones falsas no deberían ser un problema con las claves de tipo GUID.

SemPy examina 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 buscar nombres de columna coincidentes primero, la detección se ejecuta más rápido.

  1. Empareja los nombres de las columnas:

    • 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 tu ejemplo, las tablas tienen una forma plural con sufijo "s", lo que resulta en que no hay una coincidencia exacta. Esto se controla bien con la name_similarity_threshold=0.8predeterminada.

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

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

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

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

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

    Cambiar name_similarity_threshold a 0 es el otro extremo e indica que desea comparar todas las columnas. Esto rara vez es necesario y da como resultado un mayor tiempo de ejecución y coincidencias espurias 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, el include_many_to_many=False y el coverage_threshold=1.0predeterminados). Esto suele ser lo que quieres.
  2. Use un enfoque estrecho en subconjuntos más pequeños de tablas.
  3. Use 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 resultados.
  5. Tenga en cuenta las desventajas de los argumentos de búsqueda. include_many_to_many=True y coverage_threshold<1.0 pueden producir relaciones falsas que pueden ser más difíciles de analizar y que deban filtrarse.

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

El ejemplo de línea base simple era una herramienta de aprendizaje y solución de problemas cómoda. En la práctica, puede empezar desde un modelo semántico, como el conjunto de datos completo Synthea, que tiene muchas más tablas. Explore el conjunto de datos completo 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. Visualizar relaciones:

    plot_relationship_metadata(suggested_relationships)
    

    Captura de pantalla de las relaciones entre tablas.

  4. Cuente cuántas relaciones "m:m" nuevas se detectarán con include_many_to_many=True. Estas relaciones son adicionales 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, puede 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 quitarlas de la lista de candidatos.

Explora otros tutoriales de enlace semántico / SemPy: