医療通訳支援Webアプリ第1弾 視力換算システム

以前投稿で視力の伝え方について書きました。 healthcareit-interpreter.hatenablog.com

iOSのアプリでも作ってみるかと構想しておりましたが、友人(こちらのブログの著者

stagira.hatenablog.com

)からWebアプリにした方が楽だという助言をいただきました。 それで出来上がったのがこちらです。

視力換算システム

https://riow1983.github.io/visual_acuity_conversion/

JavaScriptのライブラリvue.jsを使った実装の大方はその友人によるものです。私はレイアウトを少しいじった程度です。じっくり腰を据えてFlask(Pythonのライブラリ)あたりを使って作ろうと思っていたのにこんなに早く出来上がるなんて。JavaScripterに完敗です。

さてさて、それではこれを眼科の先生に見せて感想を聞いてみることにしますか。

病院の医療情報部門の端末払出しアルゴリズム

スマホ5台新規で払出してください。」 「電子カルテ端末が足りないのよ。あと3台出して。」

こんな要望を受けて、さて在庫数は○○台だから、いくつ払出せるな・・・とその度に頭を悩ませることが多いのが病院の医療情報部門のあるあるではないでしょうか?基準が無くて恣意的で人治主義的なさじ加減で無駄に幅を効かせている総務課の事務員ならまだしも、病院のIT化を推進する医療情報部門の医療情報技師ならドクターの診断アルゴリズムも顔負けな”端末払出しアルゴリズム”で払出業務の透明化を図るべきでしょう。

f:id:HealthcareIT_interpreter:20170425223051p:plain

さて、数学を使わなければならない局面というのはこういう所に出てくるんですね。払出優先度指数の算出はともかく、当月最大払出可能数については数式を考える前にフリーハンドで「在庫管理にはこういう曲線が望ましいよな」と線を描いてから、さてこういうカーブの曲線を描く式ってなんだ?とネットで調べて行き当たるという体験をしました。(今回はロジスティック曲線というものらしいです。)兵站業務(logistics)に向いている曲線だからlogistic曲線というのか?と一瞬思ってしまうほどの運命的な出会いでした。 そういった意味合いはないのですが語源としては同源のようですね。 http://www.weblio.jp/content/%E3%83%AD%E3%82%B8%E3%82%B9%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF

外国人観光客が病院に受診した際、病院の医事課が旅行保険会社に確認すべき事項

通訳に呼ばれた外国人患者が観光のため来日した旅行者だった場合、医療通訳担当者にお会計のことで医事課から色々と注文がついて面倒なことになります。 (うちの病院はスキー場に近いことから、スキー観光客が多いようです。) 外国人観光客が旅行保険に加入していた場合、旅行保険会社から病院に電話してもらいます。日本人スタッフがいれば日本語で電話対応可能ですが(その場合は速攻で医事課に転送しましょう)、英語で対応する必要がある可能性もあります。医療通訳担当者が旅行保険会社に確認すべき事項をまとめてみました。

旅行保険会社に確認すべき事項 (Things to confirm with a travel insurance company):

「まず最初に1つ確認させてください。日本語を話せるスタッフはいますか?」(まだ諦めていません)

"First let me verify one thing. Does your company have any personnel who speak Japanese?"

・会計の方法(The method for billing):

「会計に必要なものを教えてください。請求書、診断書、その他にありますか?」

"Let me know things we must have to prepare for billing. We suppose an invoice, a certificate, and others?"

「御社が指定する様式の診断書はありますか?(御社のメールアドレスをメールしてもらえますか?当院のメールアドレスはfoo@hogeです。

"Should we make a private style document you specify? (Please send your e-mail to our address. The address is foo@hoge.)"

「患者の要望で作成した書類は保険適用されますか?」

"Are documents on patient’s demand covered by the insurance?"

「請求書の送付先を教えてください。患者による立替払いになりますか?それとも保険会社による直接支払いでしょうか?」

"To whom should we bill to? Is it an advance payment on behalf of the insurer or a direct payment from the insurer?"

(保険会社による直接支払いの場合)「保険会社様の社名、ご住所、患者様のお名前、案件番号を確認させてください。」

(In case of a direct payment) "Can I verify the company’s name and address, the patient’s name, and case No.?"

「他に準備すべきものはありますか?」

"Is there anything else we should prepare?"

・その他の費用について (About other expenses):

「松葉杖や付き添い寝具など入院中に発生する(した)その他の費用はどのように扱われますでしょうか?それらは患者様負担でしょうか?それとも保険会社様支払いでしょうか?」

"How do you deal with other expenses incurred during admission, namely a crutch, a bedding for rent? Are these things to be paid by the patient himself (herself) or by the the insurer? "

・・・と、こんな感じです。こうやって準備万端にしておけば少しは気が楽になります。

ランダムパスワードの生成

電子カルテの利用者IDの初期パスワードとして、大文字アルファベットと数字で構成された6桁のランダムパスワードを設定する方針が決定されました。え!今更かよ! さて生成コードをPythonで実装してみます。

import string
from random import choice

def get_PW(figures):
    pw = ''.join([choice(string.ascii_uppercase.replace('I','').replace('O','').replace('Z','')+string.digits.replace('0','').replace('1','').replace('2','')) for i in range(figures)])
    if pw.isdigit():#数字だけの場合
        return 'A'+pw[1:]
    elif set(pw).issubset(set(string.ascii_uppercase)):#文字だけの場合
        return "9"+pw[1:]
    else:#数字と文字ともに含有している場合
        return pw

get_PW(6)

数字だけの場合はint(pw)としても良いです。文字だけの場合はどうするか悩みましたが、文字列を集合に変換し、集合「パスワード」が集合「大文字アルファベット」のサブセット(部分集合)であればすなわちパスワードが全て大文字アルファベットで構成されていることになる、という性質を利用することにしました。 あとは、紛らわしい文字と数字の組み合わせ(1とI, 0とO,2とZ)を除外しておいて視認性を高める工夫をしています。 後はこれをVBAで実装し直すだけですね(笑)!

疾病別患者分布をジオプロットする方法

4月になり新採用職員の皆さんが多数入職されました。そんな彼らを前に電子カルテ研修と題してjupyter notebookのスライド機能(Reveal.js)を使ったプレゼンを行ってきました。それについてはまた別途記事にしたいと思いますが、ここではそのプレゼン資料にさらっと挟んでおいた疾病別患者分布の作り方を覚書きしておきます。

ステップ1:ジオコーディング

私の使用言語はPythonです。DWHから抽出してきた疾病別郵便番号一覧をあらかじめ五大疾病だけに絞り込んでおきます。絞り込みが完了したファイルを"五大疾病データ.xlsx"とします。それをPythonのライブラリpandasで読み取り、dataframeにした上で、ジオコーディング(郵便番号を緯度経度に変換)用公開API(http://geoapi.heartrails.com/api/json)に郵便番号を一つずつ投げていく関数を作り実行させます。

import requests
import json
import pandas as pd

df = pd.read_excel("五大疾病データ.xlsx")
df["latitude"] = ""
df["longitude"] = ""

def getlocation(dx):
    url = 'http://geoapi.heartrails.com/api/json'
    payload = {'method':'searchByPostal'}
    for i in dx.index:
        payload['postal'] = dx.loc[i,"郵便番号"]
        res = requests.get(url, params=payload).json()["response"]["location"][0]
        dx.loc[i,"latitude"] = res['y']
        dx.loc[i,"longitude"] = res['x']

getlocation(df)

さらっと書きましたが、実際には一気に処理させるとエラーになってしまいました。全部で9000レコード超だったのですが、500レコードずつ分割してgetlocation()を複数回実行させるという地道な作業が必要でした。(daskというpandasのdataframeを分割並列処理するライブラリが功を奏すか考えもしたのですが実際には試しませんでした。)

ステップ2:ジオプロット

次に、取得した経緯緯度を元に地図上に点描(ジオプロット)していく処理を行います。 前処理として、ステップ1で取得したdataframeを疾病ごとに分割し余分なカラムをそぎ落とします。

mf = df
cancer = mf[mf["疾病分類"] == "がん"]
ci = mf[mf["疾病分類"] == "脳卒中"]
mi = mf[mf["疾病分類"] == "急性心筋梗塞"]
di = mf[mf["疾病分類"] == "糖尿病"]
si = mf[mf["疾病分類"] == "精神疾患"]

ll_cancer = cancer[["latitude","longitude"]]
ll_ci = ci[["latitude","longitude"]]
ll_mi = mi[["latitude","longitude"]]
ll_di = di[["latitude","longitude"]]
ll_si = si[["latitude","longitude"]]

疾病ごとに緯度経度のみのdataframeが取得できました。これで準備万端です。

gmapsでやる場合

googlemapにジオプロットできるgmapsというライブラリを使う場合は以下の通りコードを書いていきます。

import gmaps
gmaps.configure(api_key="YOUR_GOOGLE_MAPS_API")

add_layer_cancer = gmaps.symbol_layer(ll_cancer, fill_color="red", stroke_color="red", scale=2)
add_layer_ci = gmaps.symbol_layer(ll_ci, fill_color="blue", stroke_color="blue", scale=2)
add_layer_mi = gmaps.symbol_layer(ll_mi, fill_color="green", stroke_color="green", scale=2)
add_layer_di = gmaps.symbol_layer(ll_di, fill_color="yellow", stroke_color="yellow", scale=2)
add_layer_si = gmaps.symbol_layer(ll_si, fill_color="purple", stroke_color="purple", scale=2)

m = gmaps.Map()

m.add_layer(add_layer_cancer) 
m.add_layer(add_layer_ci) 
m.add_layer(add_layer_mi) 
m.add_layer(add_layer_di) 
m.add_layer(add_layer_si) 

m

これで疾病ごとに色分けされたドットがプロットされたグーグルマップを表示することができます。(筆者はjupyter notebook上で実行し、inlineにマップが表示されることを確認しました。)

Foliumでやる場合

gmapsと異なりライブラリ内蔵の専用地図にジオプロットできるのが、Foliumというライブラリです。こちらはアウトプットをHTMLファイル保存できます。以下の通りコードを書いていきます。

import folium

map_ = folium.Map(location=[YOUR_LAT, YOUR_LON],
           tiles='Stamen Terrain',
           zoom_start=9)

for i in ll_cancer.index:
    x = ll_cancer.loc[i,"latitude"]
    y = ll_cancer.loc[i,"longitude"]
    folium.CircleMarker([x,y],
                 radius=4,
                 popup="がん",
                 color="#011efe",
                 fill_color="#011efe"
                 ).add_to(map_)

for i in ll_ci.index:
    x = ll_ci.loc[i,"latitude"]
    y = ll_ci.loc[i,"longitude"]
    folium.CircleMarker([x,y],
                 radius=4,
                 popup="脳卒中",
                 color="#fe0000",
                 fill_color="#fe0000"
                 ).add_to(map_)

for i in ll_mi.index:
    x = ll_mi.loc[i,"latitude"]
    y = ll_mi.loc[i,"longitude"]
    folium.CircleMarker([x,y],
                 radius=4,
                 popup="急性心筋梗塞",
                 color="#0bff01",
                 fill_color="#0bff01"
                 ).add_to(map_)

for i in ll_di.index:
    x = ll_di.loc[i,"latitude"]
    y = ll_di.loc[i,"longitude"]
    folium.CircleMarker([x,y],
                 radius=4,
                 popup="糖尿病",
                 color="#fdfe02",
                 fill_color="#fdfe02"
                 ).add_to(map_)

for i in ll_si.index:
    x = ll_si.loc[i,"latitude"]
    y = ll_si.loc[i,"longitude"]
    folium.CircleMarker([x,y],
                 radius=4,
                 popup="精神疾患",
                 color="#fe00f6",
                 fill_color="#fe00f6"
                 ).add_to(map_)

map_.save(outfile="fivemap_circle.html")

ちなみに保存したHTMLファイルをjupyter notebook上に表示する場合は

from IPython.display import HTML
HTML('<iframe src=./fivemap_circle.html width=800 height=500></iframe>')

とすればOKです。

考察:患者分布図で個人が特定されることはないか?

経度緯度を取得すると言っても、郵便番号が素ですから個人宅がピンポイントに指し示されることはありません。また病名についても五大疾病という大きな分類で表していますので、マイナーかつ詳細病名が地図上に表示されて推定されてしまうこともまずありません。(ただしアウトプットをネット公開することは控えたほうがいいかも知れません。あくまで院内資料ということで。)

患者分布図を作成する有料サービスも存在しているようですが今やオープンソースのライブラリで手軽に実行できる時代になっています。病院のデータ分析担当者は自らのスキルを院内にアピールし、病院長が無駄なコンサルタント料を支払うことの無いよう監視して行かなければなりません。

妊婦健診時の通訳

本日は妊婦健診の通訳に呼ばれました。場所は産婦人科外来です。医療情報技師で病院の医療情報部門で働く私(男性)にとって、産婦人科外来で電子カルテ端末のトラブルがあったときでも入るのに躊躇する部署ですが、女性の通訳担当がいない当院ではいたしかたありません。 さて妊婦健診は英語で

prenatal checkup

と言います。ドクターがいる診察室ではない、助産師が助産記録を作成する小部屋で話をしました。助産記録を取る時必ず聞くのが経産婦か否かという点です。これは英語でいうと

primipara, bipara or multipara

となります。つまり初産婦が

primipara

二回経産婦が

bipara

そして三回以上の経産婦が

multipara

となるそうですが、ちょっと混乱してきました。0,1,2,3とカウントするはずですが、0と1の両方とも初産婦ということになっているような気がします。気のせいでしょうか。 さてここはあまり突っ込まないことにします。どっちにしてもこれらの単語を並べたところで妊婦さんは「??」という顔をします。ですので結局こう聴き直すことになります

Is this your first delivery or more than that?

これで明確になります。 さて今回はそういった話はなく胎位調整のための体操の説明でした。胎位は

fetal position

でいいかと思います。看護師さんが妊婦さんに「ベッドの上に四つん這いになってください」と言いました。これが難しかった。

Please put your arms and legs on the bed.

と苦し紛れに言ったところなんとか通じましたが本当は

Please get down on all fours on the bed.

だそうです。更に困ったのが「右側に倒れてください」です。

Please get the right side of the body on the bed.

でいいんでしょうか?また「腕をこのようにしてください」というのも中々難しく感じました。

Get both arms bent like this.

でしょうか?「クッションを腰に当てて」というフレーズも出てきましたが、

Put a cushion on the back, ...

で良さそうです。クッションはそのまま英語で使えます。 何とか体操の説明は終了し、最後に入院証書の説明のところで連帯保証人の説明のところで「同居でなく生計を別にしている人」というのが出てきましたが、

Do you have any relatives who live separately and make their own living?

で良さそうです。ちなみに保証人と連帯保証人は

guarantor, joint guarantor

となります。

視力の伝えかた

眼科に呼ばれると英語で視力を伝える場面に遭遇します。例えば視力1.5とか視力0.3とか小数点表記は日本独自の表記法で、そのまま英訳することはできません。分数表記に換算してあげる必要があります。例えば視力1.5の場合は6/4(six four vision)となります。ちなみにこれはメートル法の表記になります。患者さんがアメリカ合衆国リベリアミャンマー出身の場合はヤード・ポンド法になるため20/13(twenty thirteen vision)と言ってやる必要があります。(それにしてもヤード・ポンド法がここまでマイナーになっているとは知りませんでした。) この換算については毎回考えるのが面倒、というか医療通訳の現場で計算している暇もなし、眼科医に聞いてもすぐに換算値が出てこない、ということで換算表を自作してみました。

JapaneseNotation FractionalVisualAcuity(yard-pound system) FractionalVisualAcuity(metric system) YourFeet(yard-pound system) YourMeters(metric system) StandardFeet(yard-pound system) StandardMeters(metric system)
2 20/10 6/3 20 6 10 3
1.5 20/13 6/4 20 6 13 4
1.2 20/16 6/5 20 6 16 5
1 20/20 6/6 20 6 20 6
0.7 20/28 6/9 20 6 28 9
0.5 20/40 6/12 20 6 40 12
0.3 20/66 6/20 20 6 66 20
0.1 20/200 6/60 20 6 200 60
0.01 20/2000 6/600 20 6 2000 600

この記事を書いてて思いついたのですが、換算システムをスマホアプリ化して無料で配ってみようかなと。患者さんの出身国をまず選択し、それによってメートル法ヤードポンド法かが自動選択され、空欄に日本表記の視力を入力すると換算値及び英語表記(更には英語発語も加えられたら格好いい)が出るという仕組みです。ちょっと作ってみますか。
→作ってみました(2019-07-29追記):

riow1983.github.io

ところで換算値を伝えたとしてもそれがどういう意味なのか患者さんが分からないというパターンも考えられます。その場合は次のように伝える想定でいます。

1)視力1.0が基準値です。 2)基準値の視力とは、20フィート(6メートル)離れた場所から1/3インチの文字を認識できる能力です。 3)例えばその人が視力0.5(20/40(6/12) vision)の場合、その人は1/3インチの文字を認識するためには20フィート(6メートル)まで近づかなければなりませんが、基準値の視力を持つ人ならそこまで近づく必要はなく、40フィート(12メートル)の距離で同じ文字を認識することができることになります。

(英訳) First, 20/20 (6/6) vision is a standard visual acuity. Second, 20/20 (6/6) vision is an ablility to recognize 1/3 inch letter from 20 feet (6 meters) distance. Third, for example, when you got 20/40 (6/12) vision, you would have to get closer to the same sized letter within 20 feet (6 meters) distance to recognize that, on the other hand the person who has 20/20 (6/6) vision doesn't need to do that. He or she can recognize the letter from 40 feet (12 meters) distance.

これで分かってもらえるといいんですが。次呼ばれた時に試してみます。