教程:使用语义链接发现 Synthea 数据集中的关系

本教程演示如何使用语义链接检测公共 Synthea 数据集中的关系。

在使用新数据或不使用现有数据模型的情况下工作时,自动发现关系会很有帮助。 此关系检测可帮助你:

  • 大致了解模型,
  • 在探索数据分析期间获取更多见解,
  • 验证更新的数据或新数据、传入数据以及
  • 清除数据。

即使关系事先已知,搜索关系也有助于更好地了解数据模型或识别数据质量问题。

在本教程中,你将从一个简单的基线示例开始,其中只试验了三个表,以便轻松掌握它们之间的连接。 然后,会显示一个更复杂的示例,其中包含更大的表集。

本教程介绍如何执行下列操作:

  • 使用语义链接的 Python 库 (SemPy) 的组件,它们支持与 Power BI 集成,并帮助自动执行数据分析。 这些核心组件包括:
    • FabricDataFrame - 一种类似 panda 的结构,通过其他语义信息进行了增强。
    • 用于将语义模型从 Fabric 工作区拉取到笔记本中的函数。
    • 对语义模型中的关系自动进行发现和可视化的函数。
  • 对具有多个表和相互依赖性的语义模型的关系发现过程进行故障排除。

先决条件

  • 选择左侧导航窗格中的“工作区”,找到并选中自己的工作区。 此工作区将成为当前工作区。

在笔记本中继续操作

本教程随附 relationships_detection_tutorial.ipynb 笔记本。

若要打开本教程随附的笔记本,请按照让系统为数据科学做好准备教程中的说明操作,将该笔记本导入到工作区。

或者,如果要从此页面复制并粘贴代码,则可以创建新的笔记本

在开始运行代码之前,请务必将湖屋连接到笔记本

设置笔记本

在本部分中,你将使用必要的模块和数据设置笔记本环境。

  1. 使用笔记本中的 %pip 内联安装功能从 PyPI 安装 SemPy

    %pip install semantic-link
    
  2. 导入稍后会用到的 SemPy 模块:

    import pandas as pd
    
    from sempy.samples import download_synthea
    from sempy.relationships import (
        find_relationships,
        list_relationship_violations,
        plot_relationship_metadata
    )
    
  3. 导入 pandas 以强制执行有助于格式化输出的配置选项:

    import pandas as pd
    pd.set_option('display.max_colwidth', None)
    
  4. 拉取示例数据。 在本教程中,你将使用合成医疗记录的 Synthea 数据集(简单起见,称其为小版本):

    download_synthea(which='small')
    

检测一小部分 Synthea 表之间的关系

  1. 从较大的集中选择三个表:

    • patients 指定患者信息
    • encounters 指定有医疗接触的患者(例如就诊、手术)
    • providers 指定哪些医疗提供者为患者提供了服务

    encounters 表解析 patientsproviders 之间的多对多关系,可描述为 关联实体

    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. 使用 SemPy 的 find_relationships 函数查找表之间的关系:

    suggested_relationships = find_relationships([patients, providers, encounters])
    suggested_relationships
    
  3. 使用 SemPy 的 plot_relationship_metadata 函数将关系 DataFrame 可视化为图形。

    plot_relationship_metadata(suggested_relationships)
    

    屏幕截图显示数据集中表之间的关系。

    该函数将关系层次结构从左侧布局到右侧,对应于输出中的“from”和“to”表。 换言之,左侧的独立“from”表使用其外键指向右侧的“to”依赖项表。 每个实体框显示参与关系的“from”或“to”一方的列。

    默认情况下,关系将生成为“m:1”(而不是“1:m”)或“1:1”。 “1:1”关系可以单向或双向生成,具体取决于映射值与所有值的比率是否仅在一个方向或两个方向上都超过 coverage_threshold。 在本教程的后半部分,你将了解不太常见的“m:m”关系的情况。

对关系检测问题进行故障排除

基线示例显示了对干净 Synthea 数据的成功关系检测。 实际上,数据很少干净的,这会阻碍成功检测。 在数据不干净时,有几个方法很有用。

本教程的本节介绍语义模型包含“脏数据”时的关系检测。

  1. 首先操作原始数据帧以获取“脏”数据,并打印脏数据的大小。

    # 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. 为了进行比较,原始表的打印大小为:

    print(len(patients))
    print(len(providers))
    
  3. 使用 SemPy 的 find_relationships 函数查找表之间的关系:

    find_relationships([patients_dirty, providers_dirty, encounters])
    

    代码的输出显示,由于前面引入的错误创建了“脏”语义模型,因此未检测到任何关系。

使用验证

验证是排查关系检测失败的最佳工具,因为:

  • 它清楚地报告了为什么特定关系不遵循外键规则,因此无法得到检测。
  • 它使用大型语义模型快速运行,因为它只关注声明的关系,并且不执行搜索。

验证可以将任何与 find_relationships 生成的列类似的任何 DataFrame。 在以下代码中,suggested_relationships DataFrame 指的是 patients 而不是 patients_dirty,但可以使用字典对 DataFrame 使用别名:

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

errors = list_relationship_violations(dirty_tables, suggested_relationships)
errors

放宽搜索条件

在更模糊的方案中,可以尝试放宽搜索条件。 此方法会增加假正的可能性。

  1. 设置 include_many_to_many=True 并评估它是否有帮助:

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

    结果显示检测的到从 encounterspatients 的关系,但存在两个问题:

    • 该关系指示从 patientsencounters的方向,这与预期关系的方向相反。 这是因为所有 patients 都发生在 encounters (Coverage From 为 1.0) 中,而 patients (Coverage To = 0.85) 仅部分覆盖 encounters,因为患者行缺失。
    • 在低基数 GENDER 列中发生意外匹配,这两个表中的名称和值恰好匹配,但它不是所关注的的“m:1”关系。 低基数由 Unique Count FromUnique Count To 列指示。
  2. 重新运行 find_relationships 仅查找“m:1”关系,但 coverage_threshold=0.5 较低:

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

    结果显示从 encountersproviders 的关系的正确方向。 但是,不会检测到从 encounterspatients 的关系,因为 patients 并不唯一,因此它不能位于“m:1”关系的“一”侧。

  3. include_many_to_many=Truecoverage_threshold=0.5 均放宽:

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

    现在,两种关注的关系都可见,但有了更多的噪音:

    • 存在 GENDER 上的低基数匹配。
    • ORGANIZATION 上出现更高的基数“m:m”匹配,因此很明显,ORGANIZATION 可能是两个表的去规范化列。

匹配列名称

默认情况下,SemPy 仅将显示名称相似性的属性视为匹配项,利用数据库设计人员通常以相同方式命名相关列这一事实。 此行为有助于避免出现虚假关系,这些关系最常出现在低基数整数键中。 例如,如果存在 1,2,3,...,10 产品类别和 1,2,3,...,10 订单状态代码,则仅查看值映射而不考虑列名时,它们将相互混淆。 虚假的关系不应该是 GUID 类似键的问题。

SemPy 会查看列名和表名之间的相似性。 匹配是近似的,不区分大小写。 它忽略最常遇到的“修饰器”子字符串,例如“id”、“code”、“name”、“key”、“pk”、“fk”。 因此,最典型的匹配情况是:

  • 实体“foo”中名为“column”的属性与实体“bar”中名为“column”(也可为“COLUMN”或“Column”)的属性匹配。
  • 实体“foo”中名为“column”的属性与“bar”中名为“column_id”的属性匹配。
  • 实体“foo”中名为“bar”的属性与“bar”中名为“code”的属性匹配。

通过首先匹配列名,检测运行速度会更快。

  1. 匹配列名称:

    • 若要了解会选择哪些列进行进一步评估,请使用 verbose=2 选项(verbose=1 仅列出正在处理的实体)。
    • name_similarity_threshold 参数会确定列的比较方式。 阈值为 1 表示仅关注 100% 匹配。
    find_relationships(dirty_tables, verbose=2, name_similarity_threshold=1.0);
    

    以 100% 的相似性运行时,无法考虑到名称之间的细微差异。 在示例中,表具有带“s”后缀的复数形式,因此无法完全匹配。 这在默认情况 name_similarity_threshold=0.8 下处理得很好。

  2. 使用默认 name_similarity_threshold=0.8 重新运行:

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

    请注意,复数形式 patients 的 ID 现会与单一 patient 进行比较,而不会在执行时间内增加过多其他虚假比较。

  3. 使用默认值 name_similarity_threshold=0 重新运行:

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

    name_similarity_threshold 更改为 0 是另一个极端值,它指示要比较所有列。 很少有必要进行此操作,并且会导致增加执行时间和需要审查的虚假匹配。 观察详细输出中的比较数。

故障排除提示摘要

  1. 从“m:1”关系的完全匹配开始(即默认 include_many_to_many=Falsecoverage_threshold=1.0)。 这通常是你想要的。
  2. 对较小的表子集使用窄焦点。
  3. 使用验证检测数据质量问题。
  4. 如果要了解哪些列被视为关系列,请使用 verbose=2。 这可能会导致大量的输出。
  5. 请注意对搜索参数的权衡。 include_many_to_many=Truecoverage_threshold<1.0 可能会产生难以分析和筛选的虚假关系。

检测完整 Synthea 数据集上的关系

简单的基线示例是一种方便的学习和故障排除工具。 实际上,可以从语义模型(例如完整的 Synthea 数据集)开始,它具有更多的表。 按如下所示浏览完整的 synthea 数据集。

  1. 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. 使用 SemPy 的 find_relationships 函数,查找表之间的关系:

    suggested_relationships = find_relationships(all_tables)
    suggested_relationships
    
  3. 将关系可视化:

    plot_relationship_metadata(suggested_relationships)
    

    表之间关系的屏幕截图。

  4. 计算使用 include_many_to_many=True 会发现多少个新“m:m”关系。 这些关系是之前显示的“m:1”关系之外的关系;因此,必须对 multiplicity 文件进行筛选:

    suggested_relationships = find_relationships(all_tables, coverage_threshold=1.0, include_many_to_many=True) 
    suggested_relationships[suggested_relationships['Multiplicity']=='m:m']
    
  5. 可以按各种列对关系数据进行排序,以便更深入地了解其性质。 例如,可以选择通过 Row Count FromRow Count To 对输出进行排序,这有助于识别最大的表。

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

    在不同的语义模型中,应该着重关注 Null Count FromCoverage To 的 null 数。

    此分析可以帮助你了解是否有任何关系可能无效,以及是否需要将其从候选项列表中删除。

查看有关语义链接/SemPy 的其他教程: