チュートリアル: セマンティック リンクを使用して Synthea データセット内のリレーションシップを検出する
この記事では、セマンティック リンクを使用して、パブリック Synthea データセット内のリレーションシップを検出する方法について説明します。
新しいデータを操作する場合や、既存のデータ モデルを使用せずに作業する場合は、リレーションシップを自動的に検出すると便利です。 このリレーションシップ検出は、次の場合に役立ちます:
- モデルを大まかに理解する
- 探索的データ分析中により多くの分析情報を得る
- 更新されたデータまたは新規、受信データを検証する
- データをクリーンアップします。
リレーションシップが事前にわかっていても、リレーションシップの検索は、データ モデルの理解やデータ品質の問題の特定に役立ちます。
このチュートリアルでは、単純なベースラインの例から始めます。ここでは、3 つのテーブルのみを使用して実験し、それらの間の接続を簡単に追跡できるようにします。 次に、より大きなテーブル セットを使用して、より複雑な例を示します。
このチュートリアルでは、次の作業を行う方法について説明します。
- Power BI との統合をサポートし、データ分析の自動化に役立つセマンティック リンクの Python ライブラリ (SemPy) のコンポーネントを使用します。 コンポーネントには、次が含まれます。
- FabricDataFrame - 追加のセマンティック情報で強化された pandas のような構造体。
- Fabric ワークスペースからノートブックにセマンティック モデルをプルする関数。
- セマンティック モデル内のリレーションシップの検出と視覚化を自動化する関数。
- 複数のテーブルとの相互依存関係があるセマンティック モデルのリレーションシップ検出プロセスのトラブルシューティングを行います。
前提条件
Microsoft Fabric サブスクリプションを取得します。 または、無料の Microsoft Fabric 試用版にサインアップします。
Microsoft Fabric にサインインします。
ホーム ページの左側にある環境スイッチャーを使って、Synapse Data Science 環境に切り替えます。
- 左側のナビゲーション ペインから [ワークスペース] を選択して、お使いのワークスペースを見つけて選択します。 このワークスペースが現在のワークスペースになります。
ノートブックで作業を進める
このチュートリアルには、relationships_detection_tutorial.ipynb ノートブックが付属しています。
このチュートリアルに付随するノートブックを開くには、「データ サイエンス用にシステムを準備する」チュートリアル の手順に従い、ノートブックをお使いのワークスペースにインポートします。
このページからコードをコピーして貼り付ける場合は、[新しいノートブックを作成する] ことができます。
コードの実行を開始する前に、必ずレイクハウスをノートブックにアタッチしてください。
ノートブックを設定する
このセクションでは、必要なモジュールとデータを含むノートブック環境を設定します。
ノートブック内の
%pip
インライン インストール機能を使用して PyPI からSemPy
をインストールします:%pip install semantic-link
後で必要になる SemPy モジュールの必要なインポートを実行します:
import pandas as pd from sempy.samples import download_synthea from sempy.relationships import ( find_relationships, list_relationship_violations, plot_relationship_metadata )
出力の書式設定に役立つ構成オプションを適用するために pandas をインポートします:
import pandas as pd pd.set_option('display.max_colwidth', None)
サンプル データをプルします。 このチュートリアルでは、合成医療記録の Synthea データセットを使用します (わかりやすくするために小さいバージョン):
download_synthea(which='small')
Synthea テーブルの小さなサブセット リレーションシップを検出する
より大きなセットから 3 つのテーブルを選択します:
patients
は患者情報を指定しますencounters
は、医療面談 (例えば、診察予約、処置) を受けた患者を特定しますproviders
は、どの医療提供者が患者に出席するかを指定します
encounters
テーブルは、patients
とproviders
の間の多対多の関係を解決し、連想エンティティとして記述できます:patients = pd.read_csv('synthea/csv/patients.csv') providers = pd.read_csv('synthea/csv/providers.csv') encounters = pd.read_csv('synthea/csv/encounters.csv')
SemPy の
find_relationships
関数を使用してテーブル間のリレーションシップを検索します:suggested_relationships = find_relationships([patients, providers, encounters]) suggested_relationships
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 データでの正常なリレーションシップ検出を示しています。 実際には、データがクリーンになることはほとんどありません。そうなると検出が成功しません。 データがクリーンでない場合に役立つ手法がいくつかあります。
このチュートリアルのこのセクションでは、セマンティック モデルにダーティ データが含まれる場合のリレーションシップの検出について説明します。
まず、元の DataFrame を操作して "ダーティ" データを取得し、ダーティ データのサイズを出力します。
# 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))
比較のために、元のテーブルのプリント サイズ:
print(len(patients)) print(len(providers))
SemPy の
find_relationships
関数を使用してテーブル間のリレーションシップを検索します:find_relationships([patients_dirty, providers_dirty, encounters])
コードの出力は、"ダーティな" セマンティック モデルを作成することになった以前に生じたエラーにより、リレーションシップが検出されていないことを示しています。
検証機能の使用
検証は、次の理由から、リレーションシップ検出エラーのトラブルシューティングに最適なツールです:
- 特定のリレーションシップが外部キー ルールに従っていないため、検出できない理由が明確に報告されます。
- 宣言されたリレーションシップのみに焦点を当て、検索を実行しないため、大規模なセマンティック モデルでは高速に実行されます。
検証では、find_relationships
によって生成されたものと同様の列を持つ任意の DataFrame を使用できます。 次のコードでは、suggested_relationships
DataFrame は patients_dirty
ではなく patients
を参照しますが、ディクショナリを使用して DataFrame のエイリアスを設定できます:
dirty_tables = {
"patients": patients_dirty,
"providers" : providers_dirty,
"encounters": encounters
}
errors = list_relationship_violations(dirty_tables, suggested_relationships)
errors
検索条件の緩和
より不明確なシナリオでは、検索条件を緩めてみてください。 この方法では、誤検知の可能性が高くなります。
include_many_to_many=True
を設定し、それが役立つかどうかを評価します:find_relationships(dirty_tables, include_many_to_many=True, coverage_threshold=1)
結果は、
encounters
からpatients
への関係が検出されたことを示していますが、次の 2 つの問題があります:- リレーションシップは、
patients
からencounters
への方向を示します。これは、予想されるリレーションシップの逆です。 これは、すべてのpatients
がencounters
(Coverage From
は1.0) でカバーされ、encounters
はpatients
(Coverage To
= 0.85) で部分的にしかカバーされていないためです。 - カーディナリティが低い
GENDER
列に誤って一致します。これは両方のテーブルの名前と値で一致しますが、関心のある "m:1" リレーションシップではありません。 カーディナリティが低い場合は、Unique Count From
列とUnique Count To
列で示されます。
- リレーションシップは、
"m:1" リレーションシップのみを検索するには
find_relationships
を再実行しますが、coverage_threshold=0.5
は低くします:find_relationships(dirty_tables, include_many_to_many=False, coverage_threshold=0.5)
結果は、
encounters
からproviders
へのリレーションシップの正しい方向を示します。 ただし、encounters
からpatients
へのリレーションシップは検出されません。patients
は一意ではないので、"m:1" リレーションシップの "1" 側にすることはできません。include_many_to_many=True
とcoverage_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' という属性と一致します。
列名を最初に照合すると、検出の実行速度が速くなります。
列名を照合します:
- さらに評価するためにどの列が選択されているかを理解するには、
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
で適切に処理されます。- さらに評価するためにどの列が選択されているかを理解するには、
既定の
name_similarity_threshold=0.8
を使用して再実行します:find_relationships(dirty_tables, verbose=2, name_similarity_threshold=0.8);
複数形
patients
の ID が、実行時間に他のスプリアス比較を追加しすぎることなく、単数形のpatient
と比較されるようになりました。既定の
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=False
とcoverage_threshold=1.0
) の完全一致から開始します。 これは通常、必要な操作です。 - テーブルの小さなサブセットに狭いフォーカスを使用します。
- 検証を使用して、データ品質の問題を検出します。
- リレーションシップと見なされる列を理解したい場合は、
verbose=2
を使用します。 これにより、大量の出力が発生する可能性があります。 - 検索引数のトレードオフに注意してください。
include_many_to_many=True
とcoverage_threshold<1.0
は、分析が難しい可能性があり、フィルター処理する必要があるスプリアス リレーションシップを生成する可能性があります。
完全な Synthea データセットのリレーションシップを検出する
簡単なベースラインの例は、便利な学習およびトラブルシューティング ツールでした。 実際には、ずっと多くのテーブルを持つ完全な Synthea データセットなどのセマンティック モデルから開始できます。 次のように、完全な synthea データセットを調べます。
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'), }
SemPy の
find_relationships
関数を使用してテーブル間のリレーションシップを検索します:suggested_relationships = find_relationships(all_tables) suggested_relationships
リレーションシップを視覚化する:
plot_relationship_metadata(suggested_relationships)
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']
リレーションシップ データをさまざまな列で並べ替えて、その性質をより深く理解することができます。 たとえば、最大のテーブルを識別するのに役立つ
Row Count From
とRow Count To
で出力を並べ替えることもできます。suggested_relationships.sort_values(['Row Count From', 'Row Count To'], ascending=False)
別のセマンティック モデルでは、null
Null Count From
またはCoverage To
の数に焦点を当てることが重要な場合があります。この分析は、いずれかのリレーションシップが無効である可能性があるかどうか、および候補の一覧から削除する必要があるかどうかを理解するのに役立ちます。
関連するコンテンツ
セマンティック リンク や SemPy については、他のチュートリアルを確認してください。