Amazon Web Services ブログ

Amazon QuickSight による CDISC 標準 SEND データの可視化で新薬候補物質の安全性・毒性を分析する

現在承認されている医療用医薬品は、一般的に 9 〜 17 年という非常に長い研究開発期間を経て私たちの手元に届いています。新薬開発はいくつかのフェーズに区分されており、その中で薬剤候補物質の安全性・毒性などを調査する非臨床試験があります。非臨床試験で得られたデータは医薬品承認申請のための国際共通化資料(CTD(Common Technical Document))として FDA(Food and Drug Administration)などへの承認申請を行う際に使用されます。この承認審査を効率化するために国際的なデータ標準化(CDISC標準)の検討・活用が進められており、CDISC 標準の非臨床データモデルは Standard Exchange for Non-clinical Data(SEND)と呼ばれています。このブログでは、CDISC 標準 SEND データの前処理と可視化を行う具体的な方法を紹介します。

本ブログの手順に従っていただくことで、SAS xport 形式で出力された SEND データを自動的に加工し、非臨床試験においての薬剤候補物質の有効性・安全性・毒性を視覚的に分析することができるようになります。以下は Amazon QuickSight を使用してラットの肝臓の所見を可視化したサンプルダッシュボードです。
quicksifht_dashboard_sample_1
quicksight_dashboard_sample_2

  • サンプルダッシュボード説明
    • studyid_part:シート上の全てのビジュアルに適用するフィルタ
    • All Counts:対象ラットの数と合計
    • MA::MAドメインにおける剖検所見
    • MI:MIドメインにおける病理組織学的所見
    • OM:OMドメインにおける器官重量(左:オス/右:メス)
    • LB:LBドメインにおけるAST(GOT)値(左:オス/右:メス)

ソリューション概要

上記のサンプルダッシュボードは SAS xport 形式で出力された SEND データを Amazon Simple Storage Service(S3)にアップロードすると自動的に更新されるように設計・構築されています。アーキテクチャの概要図は以下の通りです。
architecture_overview

  1. S3 バケットに SAS xport 形式のデータ(拡張子.xpt)をアップロードすると S3 のイベント通知がトリガーされ、AWS Glue ワークフローを起動します。
  2. AWS Glue ETL ジョブを使用して SAS xport 形式のデータを Apache Parquet 形式に変換し S3 バケットに保存します。また、元データの形式によっては視覚化に適したフォーマットに前処理を行います。
  3. AWS Glue クローラーの実行により、変換後の S3 データを AWS Glue Catalog 内のテーブルの追加パーティションとして認識させます。
  4. Amazon QuickSight から Amazon Athena 経由で変換後の S3 のデータをクエリすることで、Amazon QuickSightのダッシュボードに最新データが反映されます。

以下のチュートリアルの手順に従っていただくことで、本アーキテクチャに準拠したラットの肝臓の所見を視覚化するためのサンプルダッシュボードを作成することができます。

チュートリアル

前提条件

  • Amazon QuickSight のサインアップが終わり、「管理者」または「作成者」ユーザーでログインできること
  • AWS Glue 用の IAM アクセス許可のセットアップが終わり、AWS GlueのETLジョブとCrawlerに付与するロールを作成済みであること

S3 バケットとフォルダの作成

S3 バケットを作成し、4 つのフォルダを作成します。(バケット名・フォルダ名は任意です。)

  • バケット senddata
  • フォルダ rawdata:SASデータ(XPT形式)のアップロード先
  • フォルダ visualize :ETL後のデータ(Parquet形式)の出力先
  • フォルダ scripts:Glue ETLコードの保存場所
  • フォルダ temporary:Glue ETLコードのWorking Directory

AWS Glue ワークフローの作成①

S3 バケットに XPT 形式のデータがアップロードされたことをトリガーとして AWS Glue Workflow を実行させます。ここでは、次の Amazon EventBridge のターゲットとして指定する AWS Glue の Workflow を作成します。

  1. AWS Glue の左側のメニューから Workflows (orchestration) を選択します。
  2. Add workflow を選択して任意の名前のWorkflowを作成します。
    • Name:etl-crawler-workflow(任意の名前)

Amazon EventBridge の作成(S3 Object Created ⇒ Glue worklflow 実行)

「rawdata」フォルダにオブジェクトがアップロードされたことをトリガーとして Glue Workflow を実行する Event Bridge ルールを作成します。

  1. Amazon S3 の左側のメニューから バケット を選択します。
  2. 上記で作成したバケットを選択します。
  3. プロパティタブを選択し、Amazon EventBridge の編集を押下し、このバケット内のすべてのイベントについて Amazon EventBridge に通知を送信するオンにして変更を保存します。
  4. Amazon EventBridge の左側のメニューから ルール を選択します。
  5. ルールを作成 を選択して、ルールを作成します。
    • Step1: ルールの詳細を定義します。
      • 名前:ETL-Trigger(任意の名前)
      • イベントバス:default
    • Step2: イベントパターンを構築します。
      • 作成のメソッド:カスタムパターン(JSONエディタ)
      • イベントパターン:(作成したS3バケットの rawdata フォルダにオブジェクトがアップロードされたイベントを記載します。以下、サンプルです。)
      
      {
        	"source": ["aws.s3"],
        	"detail-type": ["Object Created"],
        	"detail": {
          	"bucket": {
      		"name": ["send-data-for-qs"]
          	},
          	"object": {
            		"key": [{
              		"prefix": "rawdata/"
            		}]
          	}
        	}
      }
      			
    • Step3: ターゲットを選択します。
      • ターゲットタイプ:AWS サービス
      • ターゲットを選択:Glue ワークフロー
      • Glue ワークフロー名:etl-crawler-workflow(上記で作成したWorkflowを選択)
      • 実行ロール:新規作成(Amazon_EventBridge_Invoke_Glue_*というロールが作成されます)
    • そのほかの設定はデフォルトのままとします。

AWS Glue ETL Jobの作成(Python Shell)

    1. AWS Glue の左側のメニューから ETL jobs を選択します。
    2. Create job で Python Shell script editor を選択して、Create を押下します。
    3. Scriptタブで、S3 バケットにアップロードされた XPT 形式のデータを Parquet 形式に変換して出力するを Script を記載します。(以下、サンプルコードです)

import os
import boto3
import xport
import pandas as pd

INPUT_BUCKET = '<XPT形式のデータのアップロード先S3バケット>'
INPUT_FOLDER = '<XPT形式のデータのアップロード先フォルダパス>'
OUTPUT_BUCKET = '<Parquet形式のデータの出力先S3バケット>'
OUTPUT_FOLDER = '<Parquet形式のデータの出力先フォルダパス>'

#S3バケットの特定フォルダ配下のオブジェクト一覧を取得
client = boto3.client('s3')
response = client.list_objects_v2(
    Bucket=INPUT_BUCKET,
    Prefix=INPUT_FOLDER
)

#拡張子が.xptのファイルをターゲットに後続処理を実行
for content in response['Contents']:
    s3_key = content['Key']
    if s3_key.endswith('.xpt'):
        xpt_file_name = os.path.basename(s3_key)
        
        # s3バケットからXPTファイルをダウンロード
        s3 = boto3.resource('s3')
        s3.Bucket(INPUT_BUCKET).download_file(s3_key, xpt_file_name)
        
        # XPT->Parquetへのファイル変換処理を行い、S3にアップロード
        with open(xpt_file_name, 'rb') as f:
            df = xport.to_dataframe(f)
            name = xpt_file_name.split('.')[0]
            output_file_name = name + '.parquet'
            df.to_parquet(output_file_name, index=False, compression='snappy')
            column_name = 'STUDYID'
            for value in df[column_name].unique():
                s3_target_key = OUTPUT_FOLDER + name + '/studyid_part=' + value + '/' + output_file_name
                s3.Bucket(OUTPUT_BUCKET).upload_file(output_file_name, s3_target_key)
            
            # txドメインのみ追加で変換処理と重複削除を実施 
            if name == 'tx':
                df = df[df['TXPARMCD'].isin(['ARMCD','TRTDOS','TRTDOSU'])]
                df = df[['STUDYID', 'SETCD', 'TXPARMCD', 'TXVAL']].pivot(index=['STUDYID', 'SETCD'], columns='TXPARMCD', values='TXVAL').reset_index()
                df = df[['ARMCD', 'TRTDOS', 'TRTDOSU', 'STUDYID']]
                df.drop_duplicates(inplace=True)
                df.reset_index(drop=True, inplace=True)
                output_file_name = 'tx-transform.parquet'
                df.to_parquet(output_file_name, index=False, compression='snappy')
                column_name = 'STUDYID'
                for value in df[column_name].unique():
                    s3_target_key = OUTPUT_FOLDER + 'tx_transform/studyid_part=' + value + '/' + output_file_name
                    s3.Bucket(OUTPUT_BUCKET).upload_file(output_file_name, s3_target_key)
	
  1. Job details タブで以下を設定します。
    • Name:etl-for-qs (任意の名前)
    • IAM Role:(前提条件で作成済みのIAMロール)
    • Python version:Python 3.9(任意のPythonバージョン。上記のサンプルコードでは3.9を使用します。)
    • (Advanced properies) Script path:s3://send-data-for-qs/script/ (S3バケットの作成で作成したフォルダ)
    • (Advanced properies) Temporary path:s3://send-data-for-qs/temporary/ (S3バケットの作成で作成したフォルダ)
    • (Advanced properies) Job Parameters: --additional-python-modules パラメータでコンマ区切りの Python モジュールのリストを指定することで、新しいPythonモジュールを追加することができます。今回はSAS xport形式のデータを扱うために、PyPI(The Python Package Index)の xport モジュールを追加しています。xport モジュールを使用することで SAS xport 形式のデータであれば AWS Glue での ETL 処理を行うことが可能です。
      • Key : --additional-python-modules
      • Value : xport==3.6.1
  2. そのほかの設定はデフォルトのままでSaveします。

AWS Glue Crawler を作成

Amazon Athena からクエリできるようにするために、AWS Glue の Clawler を使って ETL 処理後に出力されたデータのスキャン、分類、スキーマ情報の抽出、そのメタデータの AWS Glue Data Catalog への自動保存をするクローラーを作成します。

  1. AWS Glue の左側のメニューから Databases を選択します。
  2. Add database を選択してメタデータテーブルを作成する先の Database を作成します。
    • Name: db-for-qs (任意の名前)
  3. AWS Glue の左側のメニューから Crawlers を選択します。
  4. Create を選択して S3 の send-data-for-qs/visualize/フォルダ配下のファイルを検出しクロールする Clawler を作成します。
    • Name: crawler-for-qs (任意の名前)
    • Data Source: s3://send-data-for-qs/visualize/ (ETL後のデータ(Parquet形式)の出力先フォルダ)
    • IAM role:Create new IAM role(AWSGlueServiceRole-* を新規作成もしくは前提条件で作成済みのIAMロール)
    • Target database: db-for-qs (上記作成したDatabaseを選択)
    • そのほかの設定はデフォルトのままとします。

AWS Glue ワークフローの作成②

S3バケットにXPT形式のデータがアップロードされたことをトリガーとして、ETL処理を実施しParquet形式のデータをS3に出力し、クローラー処理を実行するワークフローを作成します。

  1. AWS Glue の左側のメニューから Workflows (orchestration) を選択します。
  2. 作成したWorkflowを選択します。(この時点では、Graghタブに何も表示されません)
  3. Graghタブの Add trigger を選択します。
  4. Add New タブを選択し以下を設定して Add します。
    • Name: SEND-Data-Uploaded (任意の名前)
    • Trigger Type: EventBridge event
    • Number of events: 1
  5. SEND-Data-Uploaded の矢印の先に表示される Add node を選択し、Jobsタブから上記で作成したETL Job etl-for-qs を選択してAddします。
  6. SEND-Data-Uploadedetl-for-qs」Workflowが表示されるので、 etl-for-qsアクティビティを選択します。
  7. 矢印の先に表示されるAdd trigger を選択します。
  8. Add New タブを選択し以下を設定してAddします。
    • Name:execute-clawler (任意の名前)
    • Trigger type: Event
    • Trigger Logic: Start after ALL watched event
  9. 追加したトリガーexecute-clawlerの矢印の先に表示される Add node を選択し、Crawlers タブを選択してから上記で作成したクローラー crawler-for-qs を選択して Add します。
  10. 作成した Workflow は以下のようになります。
    glue_workflow
  11. S3バケットのフォルダ rawdata/ にSENDデータをアップロードして、Workflow が実行します。
  12. Workflowの実行後に、フォルダ visualize/ 配下にParquet形式のデータが出力されていることを確認します。

Amazon Athena でクエリを実行

ETL処理後のデータに対してAthenaでクエリを実行して結果を確認します。

  1. Amazon Athena の左側のメニューから クエリエディタ を選択します。
  2. 設定 タブを選択し、管理を選択します。
  3. 任意の S3 バケットのフォルダをクエリの結果の格納先を指定します。
  4. エディタ タブで以下を選択します。
    • データソース:AwsDataCatalog
    • データベース:db-for-qs (AWS Glue Crawlerを作成で作成したデータベース)
  5. テーブル 欄に表示されるクローラーにより作成されたテーブルに対してクエリを実行します。ここでは、SENDデータのドメインごとにフォルダを分けてデータを出力しているので、ドメインごとにテーブルが作成されています。(以下は、「mi」テーブルに対してのサンプルクエリです)
    
    SELECT * FROM "db-for-qs"."mi" limit 10;
    
  6. クエリ結果 タブに結果が出力されることを確認します。これで無事にSENDデータがETL処理によりParquet形式に変換されクエリを実行できることが確認できました。

Amazon QuickSight ダッシュボードの作成

出力されたParquet 形式のデータに対して Athena 経由でクエリを実行し、QuickSight 上でデータの可視化を行います。ここでは、例として肝臓のデータを使用し、病理組織学的所見としてMIドメインのピボットテーブルと器官重量として OM ドメインのボックスプロットを作成します。

データセットの追加

  1. QuickSight を開き、左側のメニューのデータセットを選択し、新しいデータセットを選択します。
  2. Athena を選択し、データソース名(例:MI_data)を入力してデータソースを作成を押下します。
  3. テーブルの選択画面で以下を選択し、カスタムSQLを使用を選択します。
    • カタログ:AwsDataCatalog
    • データベース:db-for-qs (AWS Glue Crawlerを作成で作成したデータベース)
    • テーブル:mi (任意のテーブル)
  4. カスタム SQL を使って複数のテーブルを結合させるために、データの編集/プレビューを選択します。
    • カスタム SQL 名:任意の名前
    • カスタムクエリ:クエリ文(以下は、MIドメインのデータに TXドメインと DM ドメインを結合させ、armcd 試験群にサンプルがない場合でもテーブル項目として表示させるためのDummyデータを追加するカスタムクエリのサンプルコードになります)
    
    SELECT "dm"."usubjid","dm"."studyid","dm"."studyid_part", "dm"."armcd", "dm"."sex","ma"."mastresc", "ma"."maspec", "ma"."masev", "ma"."mady", "ma"."matest", "ma"."domain","tx_transform"."trtdos", "tx_transform"."trtdosu", 0 as "dummy"
    FROM "AwsDataCatalog"."send-data-for-qs"."dm"
    JOIN "AwsDataCatalog"."send-data-for-qs"."ma"
    ON "dm"."studyid_part" = "ma"."studyid_part" AND "dm"."usubjid" = "ma"."usubjid"
    JOIN "AwsDataCatalog"."send-data-for-qs"."tx_transform"
    ON "dm"."studyid_part" = "tx_transform"."studyid_part" AND "dm"."armcd" = "tx_transform"."armcd"
    UNION ALL
    SELECT "dm"."usubjid", "dm"."studyid","dm"."studyid_part", "dm"."armcd", "dm"."sex", "ma"."mastresc", "ma"."maspec", "ma"."masev", "ma"."mady", "ma"."matest", "ma"."domain","tx_transform"."trtdos", "tx_transform"."trtdosu", 1 as "dummy"
    FROM "AwsDataCatalog"."send-data-for-qs"."dm"
    JOIN "AwsDataCatalog"."send-data-for-qs"."ma"
    ON "dm"."studyid_part" = "ma"."studyid_part" AND "dm"."usubjid" = "ma"."usubjid"
    JOIN "AwsDataCatalog"."send-data-for-qs"."tx_transform"
    ON "dm"."studyid_part" = "tx_transform"."studyid_part" AND "dm"."armcd" = "tx_transform"."armcd"
    WHERE ("dm"."usubjid", "dm"."studyid") in (
    SELECT min("dm"."usubjid"), "dm"."studyid"
    FROM "AwsDataCatalog"."send-data-for-qs"."dm"
    GROUP BY "dm"."studyid", "dm"."armcd", "dm"."sex"
    )
    			
  5. 追加を選択し、計算フィールドを追加を選択します。
    • フィールド名:COUNT
    • 計算式:1
  6. 保存して公開を選択します。

同様の手順で他のドメインのデータセットを追加することができます。

病理組織学的所見ピボットテーブルの作成

  1. QuickSight のホーム画面から左側のメニューの分析を選択し、新しい分析を選択します。
  2. データセット MI_data (上記で作成したデータセット)を選択します。
  3. 分析で使用を選択し、インタラクティブシート(デフォルト)が選択された状態で作成を選択します。
  4. ビジュアルタイプのピボットテーブルを選択します。
  5. 計算フィールドを使用して、misev の値を (empty) から空欄になるように、以下を追加します。
    • フィールド名:sev
    • 計算式:ifelse(misev=''," ",misev)
  6. 計算フィールドを使用して、mistrescNORMALを一番上にし他をアルファベット順になるように、以下を追加します。
    • フィールド名:stresc
    • 計算式:ifelse(mistresc='NORMAL',1,2)
  7. 計算フィールドを使用して、armcd 試験群とmidy ごとのラット数が 2 以下の場合は異常値とみなしてビジュアルには含めないようにするために、以下を追加します。
    • フィールド名:sum_midy
    • 計算式:sum(COUNT, [armcd,midy])
  8. 以下のようなフィールドウェルを設定します。
    • 行:mistresc, sev
    • 列:armcd,trtdos,trtdosu,sex
    • 値:COUNT(合計)
  9. mistrescの列をNORMALを一番上にし他をアルファベット順になるように、行ヘッダーまたは列ヘッダーを使用したピボットテーブルのソートを使って、mistresc並べ替えstresc昇順を選択します。
  10. フィルタを使って対象データをフィルタします。ここでは、mispec (部位)と studyid (実験ごとに付与されるID) でフィルタしています。
  11. 途中死亡などの理由により集計から除外する動物がいるケースを想定して、動物をフィルタを使って対象データをフィルタします。ここでは、armcd (試験群) と midy ごとのラット数が 2 以下の場合は異常値とみなしてビジュアルには含めないようにするために、追加したフィールド sum_midy に対して合計が2より大きいとフィルタしています。
  12. ビジュアルのフォーマットを使用して、ヘッダーの列の背景色を変更します。
  13. ビジュアルのフォーマットを使用して、行の小計を追加します。
  14. 計算フィールドを使用して、集計項目のNORMALを一番上にして他はアルファベット順にします。
  15. 条件付き書式設定を使用して、列COUNTのグラデーションをつけます。

これで病理所見の数を群と性別ごとに見ることのできるピボットテーブルができました。

器官重量ボックスプロットの作成

  1. 分析へのデータセットへの追加から、他のドメインのデータセットを同様に追加することができます。ここでは、データセット OM_data を追加します。
  2. ビジュアルの追加により、データセットリストから OM_data を選択して、追加アクションからビジュアルを追加を選択します。
  3. ビジュアルタイプのボックスプロットを選択します。
  4. 以下のようなフィールドウェルを設定します。
    • Group by:armcd
    • Value:omstresn
  5. 計算フィールドを使用して、visitdy (見かけ上の試験日数) に値が入っていた場合には visitdy を使い、入っていない場合は omdy (試験日数)を試験日数として使うようにします。(SENDIG v3.0に準拠して作成されたSENDデータの場合。SENDIG v3.1以降の場合は、omnomdy を使います)
    • フィールド名:visitdyORdy
    • 計算式:ifelse(isNull(visitdy),lbdy,visitdy)
  6. 計算フィールドを使用して、armcd試験群と visitdyORdyごとのラット数が2以下の場合は異常値とみなしてビジュアルには含めないようにするために、以下を追加します。
    • フィールド名:sum_dy
    • 計算式:sum(COUNT, [armcd,visitdyORdy])
  7. フィルタを使って対象データをフィルタします。ここでは、sex(性別)でフィルタしています。
  8. フィルタを使って対象データをフィルタします。ここでは、armcd (試験群) と visitdyORdy (試験日数) ごとのラット数が2以下の場合は異常値とみなしてビジュアルには含めないようにするために、追加したフィールド visitdyORdy に対して合計が 2 より大きいとフィルタしています。
  9. ヴィジュアルフォーマットを使って、ビジュアルのフォーマットを更新します。
    • ボックスプロット:ボックスを塗りつぶし外れ値を表示とすべてのデータポイントを表示を選択します。
    • Y軸:自動 (データ範囲に基づく) を選択します。
  10. ボックスプロットのボックス部分を選択して色を変更します。

これで肝臓の重量を群ごとにプロットしたボックスプロットができました。

シート内の全てのビジュアルに適用するフィルタを追加

  1. どれか1つのビジュアルを選択し、分析へのフィルターの追加によって studyid フィルタを追加します。
  2. 右側にある 3 つのドットを選択し、編集を選択します。
  3. 適用先として該当する全てのビジュアルを選択し、適用を選択します。
  4. フィルタコントロールのカスタマイズにより、シートに追加されたフィルタビジュアルのフォーマットコントロールを選択し、コントロールオプションからリスト-複数選択を選択します。
  5. これで、フィルタビジュアルに表示される studyid を選択することで、シート上の studyid フィールドのある全てのビジュアルに適用されるようになりました。

シートの更新が完了したら、右上のダッシュボードの公開を選択して任意の名前のダッシュボードを付けて公開します。これで、QuickSightのダッシュボードが作成されました。

QuickSight ダッシュボードの閲覧

公開されたQuickSightのダッシュボードを閲覧します。閲覧者は、ダッシュボード上で Studyid (実験ごとに付与されるID) を選択することで、ダッシュボード上の全てのビジュアルに対するフィルタに適用させることができるので、閲覧対象の実験を選択することができます。

手順は以上になります。

結論と展望

本ブログの手順に従っていただくことで、CDISC 標準 SEND データを自動的に加工し、非臨床試験においての薬剤候補物質の安全性・毒性を視覚的に分析できることをお示ししました。サンプルダッシュボードとしてラットの肝臓の所見を視覚化しておりますが、同様の手順で腎臓・心臓など別の臓器を視覚化することや、その他の生理学的なインサイトを得ることも可能です。また、今回は CDISC 標準の非臨床試験データモデルの SEND を対象にしましたが、臨床試験データモデルの Study Data Tabulation Model(SDTM)も同様のアーキテクチャで可視化することが可能です。

Amazon QuickSight / AWS Glue は研究領域において、幅広いユースケースで利用できます。Amazon QuickSight / AWS Glueを利用したい場合やご質問がある場合、また、本ブログに記載しました CDISC 標準データの可視化にご質問のある場合は こちらにご連絡ください。


著者について

岡田 渚(Nagisa Okada)

アマゾン ウェブ サービス ジャパン合同会社 ソリューションアーキテクト

ヘルスケア・ライフサイエンスチームのソリューションアーキテクトです。カスタマーへのAWSでのシステム設計や構築支援に従事しています。 プライベートでは旅行と子育てを楽しんでいます。

宮崎 友貴(Yuki Miyazaki)

アマゾン ウェブ サービス ジャパン合同会社 ソリューションアーキテクト

通信・メディアチームのソリューションアーキテクトです。カスタマーへのAWSでのシステム設計や構築支援に従事しています。 プライベートでは、自転車と温泉を楽しんでいます。