Partilhar via


Tutorial: Descubra relações no conjunto de dados Synthea, usando link semântico

Este tutorial ilustra como detetar relações no conjunto de dados público Synthea , usando link semântico.

Quando você está trabalhando com novos dados ou sem um modelo de dados existente, pode ser útil descobrir relacionamentos automaticamente. Essa deteção de relacionamento pode ajudá-lo a:

  • compreender o modelo a um nível elevado,
  • obter mais informações durante a análise exploratória de dados,
  • validar dados atualizados ou dados novos recebidos e
  • dados limpos.

Mesmo que os relacionamentos sejam conhecidos antecipadamente, uma busca por relacionamentos pode ajudar com uma melhor compreensão do modelo de dados ou a identificação de problemas de qualidade de dados.

Neste tutorial, você começa com um exemplo de linha de base simples onde experimenta apenas três tabelas para que as conexões entre elas sejam fáceis de seguir. Em seguida, você mostra um exemplo mais complexo com um conjunto de tabelas maior.

Neste tutorial, irá aprender a:

  • Use componentes da biblioteca Python (SemPy) do link semântico que suportam a integração com o Power BI e ajudam a automatizar a análise de dados. Esses componentes incluem:
    • FabricDataFrame - uma estrutura semelhante a pandas aprimorada com informações semânticas adicionais.
    • Funções para extrair modelos semânticos de um espaço de trabalho do Fabric para o seu bloco de anotações.
    • Funções que automatizam a descoberta e visualização de relacionamentos em seus modelos semânticos.
  • Solucione problemas do processo de descoberta de relacionamentos para modelos semânticos com várias tabelas e interdependências.

Pré-requisitos

  • Obtenha uma assinatura do Microsoft Fabric. Ou inscreva-se para uma avaliação gratuita do Microsoft Fabric.

  • Entre no Microsoft Fabric.

  • Use o seletor de experiência no lado esquerdo da sua página inicial para alternar para a experiência Synapse Data Science.

    Captura de tela do menu do seletor de experiência, mostrando onde selecionar Ciência de Dados.

  • Selecione Espaços de trabalho no painel de navegação esquerdo para localizar e selecionar seu espaço de trabalho. Este espaço de trabalho torna-se o seu espaço de trabalho atual.

Acompanhe no caderno

O notebook relationships_detection_tutorial.ipynb acompanha este tutorial.

Para abrir o bloco de anotações que acompanha este tutorial, siga as instruções em Preparar seu sistema para tutoriais de ciência de dados, para importar o bloco de anotações para seu espaço de trabalho.

Se preferir copiar e colar o código desta página, pode criar um novo bloco de notas.

Certifique-se de anexar um lakehouse ao bloco de anotações antes de começar a executar o código.

Configurar o bloco de notas

Nesta seção, você configura um ambiente de notebook com os módulos e dados necessários.

  1. Instale SemPy a partir do PyPI usando o %pip recurso de instalação em linha no notebook:

    %pip install semantic-link
    
  2. Execute as importações necessárias de módulos SemPy que você precisará mais tarde:

    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 impor uma opção de configuração que ajude na formatação de saída:

    import pandas as pd
    pd.set_option('display.max_colwidth', None)
    
  4. Puxe os dados de exemplo. Para este tutorial, você usa o conjunto de dados Synthea de registros médicos sintéticos (versão pequena para simplificar):

    download_synthea(which='small')
    

Detetar relações em um pequeno subconjunto de tabelas Synthea

  1. Selecione três tabelas de um conjunto maior:

    • patients especifica as informações do paciente
    • encounters especifica os pacientes que tiveram encontros médicos (por exemplo, uma consulta médica, procedimento)
    • providers especifica quais os prestadores de cuidados médicos que atenderam os doentes

    A encounters tabela resolve uma relação muitos-para-muitos entre patients e providers pode ser descrita como uma entidade associativa:

    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. Encontre relações entre as tabelas usando a função do find_relationships SemPy:

    suggested_relationships = find_relationships([patients, providers, encounters])
    suggested_relationships
    
  3. Visualize as relações DataFrame como um gráfico, usando a função do plot_relationship_metadata SemPy.

    plot_relationship_metadata(suggested_relationships)
    

    Captura de tela mostrando relações entre tabelas no conjunto de dados.

    A função estabelece a hierarquia de relacionamento do lado esquerdo para o lado direito, que corresponde às tabelas "de" e "para" na saída. Em outras palavras, as tabelas independentes "de" no lado esquerdo usam suas chaves estrangeiras para apontar para suas tabelas de dependência "para" no lado direito. Cada caixa de entidade mostra colunas que participam no lado "de" ou "para" de uma relação.

    Por padrão, as relações são geradas como "m:1" (não como "1:m") ou "1:1". As relações "1:1" podem ser geradas de uma ou ambas as maneiras, dependendo se a proporção de valores mapeados para todos os valores excede coverage_threshold em apenas uma ou ambas as direções. Mais adiante neste tutorial, você abordará o caso menos frequente de relacionamentos "m:m".

Solucionar problemas de deteção de relacionamento

O exemplo de linha de base mostra uma deteção de relacionamento bem-sucedida em dados Synthea limpos. Na prática, os dados raramente são limpos, o que impede uma deteção bem-sucedida. Existem várias técnicas que podem ser úteis quando os dados não estão limpos.

Esta seção deste tutorial aborda a deteção de relacionamento quando o modelo semântico contém dados sujos.

  1. Comece manipulando os DataFrames originais para obter dados "sujos" e imprima o tamanho dos dados sujos.

    # 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 comparação, tamanhos de impressão das tabelas originais:

    print(len(patients))
    print(len(providers))
    
  3. Encontre relações entre as tabelas usando a função do find_relationships SemPy:

    find_relationships([patients_dirty, providers_dirty, encounters])
    

    A saída do código mostra que não há relações detetadas devido aos erros introduzidos anteriormente para criar o modelo semântico "sujo".

Validação de uso

A validação é a melhor ferramenta para solucionar problemas de falhas de deteção de relacionamento porque:

  • Ele relata claramente por que um relacionamento específico não segue as regras da Chave Estrangeira e, portanto, não pode ser detetado.
  • Ele é executado rapidamente com grandes modelos semânticos porque se concentra apenas nas relações declaradas e não realiza uma pesquisa.

A validação pode usar qualquer DataFrame com colunas semelhantes à gerada pelo find_relationships. No código a seguir, o suggested_relationships DataFrame refere-se a patients em vez de patients_dirty, mas você pode alias os DataFrames com um dicionário:

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

errors = list_relationship_violations(dirty_tables, suggested_relationships)
errors

Afrouxar os critérios de pesquisa

Em cenários mais obscuros, você pode tentar afrouxar seus critérios de pesquisa. Este método aumenta a possibilidade de falsos positivos.

  1. Defina include_many_to_many=True e avalie se isso ajuda:

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

    Os resultados mostram que a relação de encounters para patients foi detetada, mas há dois problemas:

    • A relação indica uma direção de patients para encounters, que é um inverso da relação esperada. Isso ocorre porque todos patients foram cobertos por encounters (Coverage From é 1,0) enquanto encounters são apenas parcialmente cobertos por patients (Coverage To = 0,85), uma vez que as filas de pacientes estão faltando.
    • Há uma correspondência acidental em uma coluna de cardinalidade GENDER baixa, que acontece de corresponder por nome e valor em ambas as tabelas, mas não é uma relação de interesse "m:1". A cardinalidade baixa é indicada por Unique Count From e Unique Count To colunas.
  2. Execute find_relationships novamente para procurar apenas relações "m:1", mas com um menor coverage_threshold=0.5:

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

    O resultado mostra a direção correta das relações de encounters até providers. No entanto, a relação de encounters para patients não é detetada, porque patients não é única, então não pode estar no lado "Um" da relação "m:1".

  3. Solte ambos include_many_to_many=True e coverage_threshold=0.5:

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

    Agora ambas as relações de interesse são visíveis, mas há muito mais ruído:

    • O jogo de baixa cardinalidade está GENDER presente.
    • Uma correspondência de cardinalidade mais alta "m:m" apareceu ORGANIZATION , tornando aparente que é provável que ORGANIZATION uma coluna seja desnormalizada para ambas as tabelas.

Corresponder nomes de colunas

Por padrão, o SemPy considera como correspondências apenas atributos que mostram semelhança de nomes, aproveitando o fato de que os designers de banco de dados geralmente nomeiam colunas relacionadas da mesma maneira. Esse comportamento ajuda a evitar relacionamentos espúrios, que ocorrem com mais frequência com chaves inteiras de baixa cardinalidade. Por exemplo, se houver 1,2,3,...,10 categorias de produtos e 1,2,3,...,10 código de status do pedido, eles serão confundidos uns com os outros quando apenas olharem para mapeamentos de valor sem levar em conta os nomes das colunas. Relações espúrias não devem ser um problema com chaves semelhantes a GUID.

O SemPy analisa uma semelhança entre nomes de colunas e nomes de tabelas. A correspondência é aproximada e não diferencia maiúsculas de minúsculas. Ele ignora as substrings "decorador" mais frequentemente encontradas, como "id", "code", "name", "key", "pk", "fk". Como resultado, os casos de correspondência mais típicos são:

  • um atributo chamado 'coluna' na entidade 'foo' corresponde a um atributo chamado 'coluna' (também 'COLUNA' ou 'Coluna') na entidade 'bar'.
  • Um atributo chamado 'coluna' na entidade 'foo' corresponde a um atributo chamado 'column_id' em 'barra'.
  • Um atributo chamado 'bar' na entidade 'foo' corresponde a um atributo chamado 'code' em 'bar'.

Ao corresponder os nomes das colunas primeiro, a deteção é executada mais rapidamente.

  1. Corresponder aos nomes das colunas:

    • Para entender quais colunas são selecionadas para avaliação posterior, use a verbose=2 opção (verbose=1 lista apenas as entidades que estão sendo processadas).
    • O name_similarity_threshold parâmetro determina como as colunas são comparadas. O limite de 1 indica que você está interessado apenas em 100% de correspondência.
    find_relationships(dirty_tables, verbose=2, name_similarity_threshold=1.0);
    

    Correr a 100% de semelhança não leva em conta pequenas diferenças entre os nomes. No seu exemplo, as tabelas têm uma forma plural com sufixo "s", o que resulta em nenhuma correspondência exata. Isso é bem tratado com o padrão name_similarity_threshold=0.8.

  2. Execute novamente com o padrão name_similarity_threshold=0.8:

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

    Observe que o Id para a forma patients plural agora é comparado ao singular patient sem adicionar muitas outras comparações espúrias ao tempo de execução.

  3. Execute novamente com o padrão name_similarity_threshold=0:

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

    Mudar name_similarity_threshold para 0 é o outro extremo e indica que você deseja comparar todas as colunas. Isso raramente é necessário e resulta em maior tempo de execução e partidas espúrias que precisam ser revistas. Observe o número de comparações na saída detalhada.

Resumo das dicas de solução de problemas

  1. Comece a partir da correspondência exata para relações "m:1" (ou seja, o padrão include_many_to_many=False e coverage_threshold=1.0). Isso geralmente é o que você quer.
  2. Use um foco estreito em subconjuntos menores de tabelas.
  3. Use a validação para detetar problemas de qualidade de dados.
  4. Use verbose=2 se quiser entender quais colunas são consideradas para relacionamento. Isso pode resultar em uma grande quantidade de saída.
  5. Esteja ciente das compensações dos argumentos de pesquisa. include_many_to_many=True e coverage_threshold<1.0 pode produzir relações espúrias que podem ser mais difíceis de analisar e precisarão ser filtradas.

Detetar relações no conjunto de dados Synthea completo

O exemplo de linha de base simples foi uma ferramenta conveniente de aprendizagem e solução de problemas. Na prática, você pode começar a partir de um modelo semântico, como o conjunto de dados Synthea completo, que tem muito mais tabelas. Explore o conjunto de dados synthea completo da seguinte maneira.

  1. Leia todos os arquivos do diretório 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. Encontre relações entre as tabelas, usando a função do find_relationships SemPy:

    suggested_relationships = find_relationships(all_tables)
    suggested_relationships
    
  3. Visualize relacionamentos:

    plot_relationship_metadata(suggested_relationships)
    

    Captura de ecrã das relações entre tabelas.

  4. Conte quantas novas relações "m:m" serão descobertas com include_many_to_many=Trueo . Estas relações são adicionais às relações "m:1" mostradas anteriormente; portanto, você tem que filtrar em multiplicity:

    suggested_relationships = find_relationships(all_tables, coverage_threshold=1.0, include_many_to_many=True) 
    suggested_relationships[suggested_relationships['Multiplicity']=='m:m']
    
  5. Você pode classificar os dados de relacionamento por várias colunas para obter uma compreensão mais profunda de sua natureza. Por exemplo, você pode optar por ordenar a saída por Row Count From e Row Count To, que ajudam a identificar as tabelas maiores.

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

    Em um modelo semântico Null Count From diferente, talvez fosse importante focar no número de nulos ou Coverage To.

    Essa análise pode ajudá-lo a entender se algum dos relacionamentos pode ser inválido e se você precisa removê-los da lista de candidatos.

Confira outros tutoriais para link semântico / SemPy: