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, você aprenderá a:

  • Use componentes da biblioteca Python do link semântico (SemPy) 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 notebook.
    • 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 Microsoft Fabric. Ou inscreva-se para obter uma avaliação gratuita do Microsoft Fabric.

  • Inicie sessão em Microsoft Fabric.

  • Use o seletor de experiência no canto inferior esquerdo da página inicial para alternar para o Fabric.

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

  • Selecione Workspaces 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 caderno relationships_detection_tutorial.ipynb acompanha este tutorial.

Configurar o portátil

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

  1. Instale SemPy do PyPI usando a capacidade de instalação em linha %pip 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 Synthea conjunto de dados de registros médicos sintéticos (versão pequena para simplificar):

    download_synthea(which='small')
    

Detetar relações num pequeno subconjunto de tabelas Synthea de

  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 tabela encounters resolve uma relação muitos-para-muitos entre patients e providers e 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 find_relationships do SemPy:

    suggested_relationships = find_relationships([patients, providers, encounters])
    suggested_relationships
    
  3. Visualize as relações DataFrame como um gráfico, usando a função plot_relationship_metadata do 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" na parte esquerda utilizam as suas chaves estrangeiras para referir-se às suas tabelas de dependência "para" na parte direita. 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 relações

O exemplo de referência mostra um reconhecimento de padrões bem-sucedido 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 find_relationships do 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".

Utilizar validação

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

  • Relata claramente por que um relacionamento específico não segue as regras da chave estrangeira e, portanto, não é possível 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 por 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 os patients passaram a ser cobertos por encounters (Coverage From é 1,0) enquanto encounters são apenas parcialmente cobertos por patients (Coverage To = 0,85), uma vez que faltam filas de pacientes.
    • Ocorre uma correspondência acidental numa coluna GENDER de baixa cardinalidade, que acontece de coincidir por nome e valor em ambas as tabelas, mas não se trata de uma relação 'm:1' de interesse. A cardinalidade baixa é indicada pelas colunas Unique Count From e Unique Count To.
  2. Execute novamente find_relationships para procurar apenas relações "m:1", mas com um coverage_threshold=0.5mais baixo:

    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 para 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 tanto include_many_to_many=True quanto 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:

    • Existe uma correspondência de baixa cardinalidade em GENDER.
    • Uma correspondência de cardinalidade mais alta "m:m" em ORGANIZATION apareceu, indicando claramente que ORGANIZATION é provavelmente uma coluna desnormalizada em 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ódigos de status do pedido, poderão ser confundidos entre si ao se olhar apenas para os 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' identifica-se com um atributo chamado "coluna" (também "COLUNA" ou "Coluna") na entidade 'bar'.
  • Um atributo chamado 'coluna' na entidade 'foo' alinha-se com um atributo chamado 'column_id' em 'bar'.
  • Um atributo chamado 'bar' na entidade 'foo' corresponde a um atributo denominado 'code' em 'bar'.

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

  1. Faça corresponder os nomes das colunas:

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

    Executar com 100% de semelhança% não considera 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 name_similarity_threshold=0.8padrão.

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

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

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

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

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

    Alterar 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, as include_many_to_many=False e coverage_threshold=1.0padrão). 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 para entender quais colunas são consideradas para relacionamento. Isso pode resultar numa grande quantidade de resultados.
  5. Esteja ciente das compensações dos argumentos de pesquisa. include_many_to_many=True e coverage_threshold<1.0 podem 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 completo do Synthea

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 completo Synthea, que tem muito mais tabelas. Explore o conjunto de dados completo synthea 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 find_relationships do 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=True. Estas relações são adicionais às relações "m:1" mostradas anteriormente; Portanto, você tem que filtrar 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 diferente, talvez fosse importante focar no número de nulos Null Count From 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: