Tutorial: Descobrir relacionamentos no conjunto de dados Synthea utilizando o link semântico
Este tutorial ilustra como detectar relações no conjunto de dados público Synthea usando o Link Semântico.
Ao trabalhar com novos dados ou sem um modelo de dados existente, descobrir relações automaticamente pode ser útil. Essa detecção de relação pode ajudar você a:
- entender o modelo em um alto nível,
- obter mais insights durante a análise de dados exploratória,
- validar dados atualizados ou novos, dados de entrada e
- Limpar os dados.
Mesmo que as relações sejam conhecidas com antecedência, uma pesquisa por relações pode ajudar a entender melhor o modelo de dados ou na identificação de problemas de qualidade dos dados.
Neste tutorial, você começa com um exemplo de linha de base simples no qual você testa 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 dão suporte à 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 workspace do Fabric para o seu notebook.
- Funções que automatizam a descoberta e a visualização das relações em seus modelos semânticos.
- Solucionar problemas do processo de descoberta de relação 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 alternador de experiência no lado esquerdo da sua página inicial para mudar para a experiência de Ciência de Dados Synapse.
- Selecione Workspaces no painel de navegação esquerdo para localizar e selecionar seu workspace. Esse workspace se torna seu workspace atual.
Acompanhar no notebook
O notebook relationships_detection_tutorial.ipynb acompanha este tutorial.
Para abrir o notebook que acompanha este tutorial, siga as instruções em Preparar seu sistema para tutoriais de ciência de dados para importar os notebooks para seu workspace.
Se preferir copiar e colar o código a partir dessa página, você poderá criar um novo notebook.
Certifique-se de anexar um lakehouse ao notebook antes de começar a executar o código.
Configurar o notebook
Nesta seção, você configura um ambiente de notebook com os módulos e dados necessários.
Instale o
SemPy
por meio do PyPI usando a funcionalidade de instalação em linha%pip
no notebook:%pip install semantic-link
Faça as importações necessárias dos módulos da SemPy de 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 )
Importe o pandas para impor uma opção de configuração que ajuda na formatação de saída:
import pandas as pd pd.set_option('display.max_colwidth', None)
Efetue pull dos dados de exemplo. Para este tutorial, você usará o conjunto de dados Synthea de registros médicos sintéticos (versão pequena para simplificar):
download_synthea(which='small')
Detectar relações em um pequeno subconjunto de tabelas Synthea
Selecione três tabelas de um conjunto maior:
patients
especifica as informações do pacienteencounters
especifica quais pacientes tiveram contato com médicos (por exemplo, uma consulta médica, um procedimento)providers
especifica quais provedores médicos atenderam aos pacientes
A tabela
encounters
resolve uma relação muitos para muitos entrepatients
eproviders
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')
Encontre relações entre as tabelas usando a função
find_relationships
da SemPy:suggested_relationships = find_relationships([patients, providers, encounters]) suggested_relationships
Visualize as relações de DataFrame como um gráfico usando a função
plot_relationship_metadata
da SemPy.plot_relationship_metadata(suggested_relationships)
A função estabelece a hierarquia de relação do lado esquerdo para o direito, o que corresponde a tabelas "de" e "para" na saída. Em outras palavras, as tabelas independentes "de" no lado esquerdo usam suas chaves de referência para apontar para suas tabelas de dependência "para" no lado direito. Cada caixa de entidade mostra colunas que participam do lado "de" ou "para" de uma relação.
Por padrão, as relações são geradas como "m:1" (não "1:m") ou "1:1". As relações "1:1" podem ser geradas de uma ou ambas as formas, dependendo de se a taxa de valores mapeados para todos os valores excede o
coverage_threshold
em apenas uma ou em ambas as direções. Posteriormente neste tutorial, você abordará o caso menos frequente de relações "m:m".
Solucionar problemas de detecção de relação
O exemplo de linha de base mostra uma detecção de relação bem-sucedida em dados limpos Synthea. Na prática, os dados raramente são limpos, o que impede uma detecção bem-sucedida. Há várias técnicas que podem ser úteis quando os dados não são limpos.
Esta seção deste tutorial aborda a detecção de relação quando o modelo semântico está sujo.
Comece trabalhando com os DataFrames originais para obter dados "sujos" e imprima o tamanho desses dados.
# 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))
Para comparação, imprima os tamanhos das tabelas originais:
print(len(patients)) print(len(providers))
Encontre relações entre as tabelas usando a função
find_relationships
da SemPy:find_relationships([patients_dirty, providers_dirty, encounters])
A saída do código mostra que não há relações detectadas devido aos erros introduzidos anteriormente para criar o modelo semântico "sujo".
Usar a validação
A validação é a melhor ferramenta para solucionar problemas de falhas de detecção de relação porque:
- Ela relata claramente por que uma relação específica não segue as regras da Chave de referência e, portanto, não pode ser detectada.
- Ela é executada rapidamente com grandes modelos semânticos porque se concentra apenas nas relações declaradas e não executa uma pesquisa.
A validação pode usar qualquer DataFrame com colunas semelhantes à gerada por find_relationships
. No código a seguir, o DataFrame suggested_relationships
refere-se a patients
, em vez de a patients_dirty
, mas você pode gerar alias dos DataFrames com um dicionário:
dirty_tables = {
"patients": patients_dirty,
"providers" : providers_dirty,
"encounters": encounters
}
errors = list_relationship_violations(dirty_tables, suggested_relationships)
errors
Amenizar os critérios de pesquisa
Em cenários mais obscuros, você pode tentar amenizar os critérios de pesquisa. Esse método aumenta a possibilidade de falsos positivos.
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 foi detectada a relação de
encounters
apatients
, mas há dois problemas:- A relação indica uma direção de
patients
paraencounters
, o que é um inverso da relação esperada. Isso ocorre porque todos ospatients
passaram a ser cobertor porencounters
(Coverage From
é 1,0), enquanto osencounters
são apenas parcialmente cobertos porpatients
(Coverage To
= 0,85), uma vez que as linhas dos pacientes estão ausentes. - Há uma correspondência acidental em uma coluna
GENDER
de baixa cardinalidade, que acaba correspondendo por nome e valor em ambas as tabelas, mas não é uma relação de interesse "m:1". A baixa cardinalidade é indicada pelas colunasUnique Count From
eUnique Count To
.
- A relação indica uma direção de
Execute
find_relationships
novamente para procurar apenas relações "m:1", mas com umcoverage_threshold=0.5
mais 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
paraproviders
. Porém, a relação deencounters
parapatients
não é detectada, porquepatients
não é exclusivo e, portanto, não pode estar no lado "Um" da relação "m:1".Amenizar
include_many_to_many=True
ecoverage_threshold=0.5
:find_relationships(dirty_tables, include_many_to_many=True, coverage_threshold=0.5)
Agora, ambas as relações de interesse estão visíveis, mas há muito mais ruído:
- A baixa correspondência de cardinalidade de
GENDER
está presente. - Apareceu uma maior correspondência de cardinalidade "m:m" de
ORGANIZATION
, tornando evidente queORGANIZATION
é provavelmente uma coluna desnormalizada para ambas as tabelas.
- A baixa correspondência de cardinalidade de
Corresponder nomes de coluna
Por padrão, a SemPy considera como correspondência apenas os atributos que mostram similaridade de nome, aproveitando o fato de que os designers de banco de dados geralmente nomeiam colunas relacionadas da mesma forma. Esse comportamento ajuda a evitar relações espúrias, que ocorrem com mais frequência com chaves inteiras de baixa cardinalidade. Por exemplo, se houver 1,2,3,...,10
categorias de produto e 1,2,3,...,10
códigos de status de pedido, eles serão confundidos entre si quando apenas mapeamentos de valores forem analisados, sem levar em conta os nomes de coluna. Relações espúrias não devem ser um problema com chaves do tipo GUID.
A SemPy analisa uma semelhança entre nomes de coluna e nomes de tabela. A correspondência é aproximada e não diferencia maiúsculas de minúsculas. Ela ignora as subcadeias de caracteres "decorador" encontradas com mais frequência, como "id", "code", "name", "key", "pk" e "fk". Como resultado, os casos de correspondência mais típicos são:
- um atributo chamado “column” na entidade “foo” corresponde a um atributo chamado “column” (também “COLUMN” ou “Column”) na entidade 'bar'.
- um atributo chamado “column” na entidade “foo” corresponde a um atributo chamado “column_id” em “bar”.
- um atributo chamado “bar” na entidade “foo” corresponde a um atributo chamado “code” em “bar”.
Ao corresponder primeiro os nomes de coluna, a detecção é executada mais rapidamente.
Corresponda os nomes das colunas:
- Para entender quais colunas são selecionadas para avaliação adicional, 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 limite de 1 indica que seu interesse é em apenas 100% de correspondência.
find_relationships(dirty_tables, verbose=2, name_similarity_threshold=1.0);
A execução com 100% de similaridade não contabiliza pequenas diferenças entre os nomes. No seu exemplo, as tabelas apresentam um formato plural com sufixo "s", o que não resulta em correspondência exata. Isso é bem resolvido com o padrão
name_similarity_threshold=0.8
.- Para entender quais colunas são selecionadas para avaliação adicional, use a opção
Execute novamente com o padrão
name_similarity_threshold=0.8
:find_relationships(dirty_tables, verbose=2, name_similarity_threshold=0.8);
Observe que a ID do formato plural
patients
agora é comparada ao singularpatient
sem adicionar muitas outras comparações espúrias ao tempo de execução.Execute novamente com o padrão
name_similarity_threshold=0
: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 correspondências espúrias que precisam ser revisadas. Observe o número de comparações na saída detalhada.
Resumo das dicas de solução de problemas
- Comece a partir da correspondência exata para relações "m:1" (ou seja, o padrão
include_many_to_many=False
ecoverage_threshold=1.0
). Geralmente, esse é o resultado desejado. - Use um foco restrito em subconjuntos menores de tabelas.
- Use a validação para detectar problemas de qualidade de dados.
- Use
verbose=2
se quiser entender quais colunas são consideradas para a relação. Isso pode resultar em uma grande quantidade de saída. - Esteja ciente das desvantagens dos argumentos de pesquisa.
include_many_to_many=True
ecoverage_threshold<1.0
podem produzir relações espúrias cuja análise pode ser mais difícil e precisarão ser filtrados.
Detectar relações no conjunto de dados completo Synthea
O exemplo de linha de base simples foi uma ferramenta conveniente de aprendizado e solução de problemas. Na prática, você pode começar a partir de um modelo semântico, como o conjunto de dados do completo Synthea, que tem muito mais tabelas. Explore o conjunto de dados completo synthea da seguinte maneira.
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'), }
Encontre relações entre as tabelas usando a função
find_relationships
da SemPy:suggested_relationships = find_relationships(all_tables) suggested_relationships
Visualizar relações:
plot_relationship_metadata(suggested_relationships)
Conte quantas novas relações "m:m" serão descobertas com
include_many_to_many=True
. Tratam-se de relações além das "m:1" mostradas anteriormente; portanto, você precisa filtrar:multiplicity
suggested_relationships = find_relationships(all_tables, coverage_threshold=1.0, include_many_to_many=True) suggested_relationships[suggested_relationships['Multiplicity']=='m:m']
Você pode classificar os dados de relação em várias colunas para obter uma compreensão mais profunda da natureza deles. Por exemplo, você pode optar por ordenar a saída por
Row Count From
eRow Count To
, o que ajuda a identificar as tabelas maiores.suggested_relationships.sort_values(['Row Count From', 'Row Count To'], ascending=False)
Em um modelo semântico diferente, talvez seja importante se concentrar no número de nulos
Null Count From
ouCoverage To
.Essa análise pode ajudar você a entender se alguma das relações pode ser inválida e se você precisa removê-la da lista de candidatos.
Conteúdo relacionado
Faça o check-out de outros tutoriais sobre link semântico / SemPy: