医薬品副作用データベース②
前回の投稿で医薬品副作用データベース(JADER)について紹介し、公開されているデータ(csvファイル)を結合してテーブルを作成しました。今回はそのテーブルから、必要な情報を抽出していきます。
医薬品名から検索(副作用報告件数)
side_effects.dbの中には、副作用報告の各種情報を結合したjoin_tableが含まれています。
import sqlite3
import pandas as pd
db = sqlite3.connect('side_effects.db')
c = db.cursor()
医薬品検索する時に、特定の銘柄で検索したい場合もあれば、同一成分で検索したい場合もあります。従って、抽出する範囲は一般名もしくは販売名となっていた方が使い勝手が良さそうです。
また、一般名は「○○水和物」「××塩酸塩」、販売名では普通錠と口腔内崩壊錠の区別や屋号(メーカー名)の区別といったように正確に表記しないと抽出されない可能性もあるため、ワイルドカード(%)とLIKE句を使用したあいまい検索ができると良さそうです。詳細は以前の投稿をご参照ください。
drug_name = input('一般名もしくは販売名')
c.execute(f"""
SELECT *
FROM join_table
WHERE (`医薬品(一般名)` LIKE ? OR `医薬品(販売名)` LIKE ?)
AND 医薬品の関与 = '被疑薬'
""",
[f'%{drug_name}%', f'%{drug_name}%'])
results = c.fetchall()
print(f'{drug_name}を被疑薬とする副作用報告は{len(results)}件です。')
上記を実行すると、join_tableから任意の文字列入力(input())で入力した医薬品名で抽出することができます(AND条件で"被疑薬"という条件も入っています)。
例えば、「ニボルマブ」と入力すると、
「ニボルマブを被疑薬とする副作用報告は857件です。」と副作用の報告件数が返ってきます。
※JADERのcsvファイルは全ファイルが毎回更新されるため、ダウンロードタイミングによって、抽出結果が異なる場合があります。
※SQL文は日本語が入っていても大丈夫ですが、スペースや特殊文字、予約語が含まれると上手くいかない場合があります。`医薬品(一般名)`のようにバッククオテーションで囲むことで正確に認識されるようになります。
詳細データの表示
抽出実行結果は、resultsという変数に格納されているので、for文で取り出すことができます。
for result in results:
print(result)
SQL文で"SELECT * …"としているので、全カラムが表示されるため見にくいですね。SELECT句で必要なカラムを絞るかprint()を工夫する必要があります。下記のサンプルコードでは、f-stringsを使用することで「識別番号: 有害事象(転帰)」となるように表示しています。
for result in results:
print(f"{result[0]}: {result[13]}({result[14]})")
データの重複について DISTINCT
結果をよく見ると、同じ識別番号のレコードが並んでいます。上の赤枠のように、同じ識別番号でも別の有害事象が登録されていれば別レコードになります。
では、下の赤枠はどうでしょう?CA-17502033の中身を確認すると理解できます。副作用情報テーブル(reac)を表示してみましょう。
c.execute("""
SELECT * FROM reac
WHERE 識別番号 = "CA-17502033"
""")
for row in c.fetchall():
print(row)
どうやら、"有害事象連番"というカラムの違いで2レコードあったようです。1対多で結合しているので、このように複数レコード存在する場合もあります。
どのように情報を活用するか次第ですが、重複したレコードが不要な場合は、SELET句にDISTINCTを入れることで重複を削除することも出来ます。
c.execute(f"""
SELECT DISTINCT *
FROM join_table
WHERE (`医薬品(一般名)` LIKE ? OR `医薬品(販売名)` LIKE ?)
AND 医薬品の関与 = '被疑薬'
""",
[f'%{drug_name}%', f'%{drug_name}%'])
results = c.fetchall()
print(f'{drug_name}を被疑薬とする副作用報告は{len(results)}件です。')
>「ニボルマブを被疑薬とする副作用報告は854件です。」
for result in results:
print(f"{result[0]}: {result[13]}({result[14]})")
DISTINCTを入れることで、件数が857→854件になり、詳細表示でも重複は解消されていることが確認できます。上の赤枠は完全一致ではないので、どちらのレコードも残ります。
データの集計
GROUP BY
見やすくなったのは良いですが、800件以上もあると全体の傾向がつかみにくいので集計したくなりますね。そんな時に便利なのが、GROUP BYです。GROUP BYを使うと、指定したカラムの値ごとに行をまとめて、まとめた行ごとに集計処理が可能になります。集計処理には、以下の集計関数が使用できます。
COUNT(): 行数をカウント
SUM(): 合計値を計算
AVG(): 平均値を計算
MAX(): 最大値を取得
MIN(): 最小値を取得
サブクエリ
上述のクエリ結果(join_tableから任意の医薬品名でかつ被疑薬)をGROUP BYで集計することになるので、上述のクエリ結果をテーブルとして作成しGROUP BY構文に入れる必要があります。
しかし、結果を得るための過程の1つなので、新規テーブルとして残しておく必要性もなさそうです。そのような場合、サブクエリとしてクエリ構文の中に別のクエリを組み込むことで解決できます。
drug_name = input('一般名もしくは販売名')
c.execute("""
SELECT `有害事象`, count()
FROM (
SELECT DISTINCT * FROM join_table
WHERE (`医薬品(一般名)` LIKE ? OR `医薬品(販売名)` LIKE ?)
AND 医薬品の関与 = '被疑薬'
) AS subquery
GROUP BY `有害事象`
ORDER BY count() DESC
""", [f'%{drug_name}%', f'%{drug_name}%'])
results = c.fetchall()
print(f'<"{drug_name}"が被疑薬の有害事象>')
for result in results:
print(f"{result[0]}: {result[1]}件")
FROM ( ) AS subquery としている括弧内が上述のクエリと同じもので、本来「テーブル名」が入るところにサブクエリが入っています。
また、集計結果(今回は有害事象ごとのカウント数)を多い順に並べるために、ORDER BYを使用しています。
実行すると、以下のように有害事象ごと集計された結果が多い順に表示されます。
今回は、JADERから入手可能なcsvファイルを用いて、医薬品名を入力するとその医薬品が被疑薬として報告された有害事象とその件数を集計・表示するコードを作成しました。
SQL文の構文さえある程度理解すれば、このようなデータベースから検索するスクリプトやアプリを作成することも可能です。是非ご自身のニーズに合わせてデータベースの整備やコード作成を行ってみてください!