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

電子カルテの利用者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.

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

愛用の12インチMacbookにTensorFlowをインストール

今回が2回目の投稿ですがいきなりプログラミングのお話です。

Python機械学習ライブラリTensorFlowを愛用のMacBookにインストールしてみました。 手順は以下の通りです。 Installing TensorFlow on Mac OS X  |  TensorFlow

ちなみにTensorFlowは今までWindowsに対応していませんでしたが、バージョン 0.12からWindowsをサポートするようになりました。これにより、Windows機でもVirtualBoxやDockerを使う必要がなくなりました。 yaju3d.hatenablog.jp

Windows用のTensorFlowのパッケージはPython3.5の64bitが必要ということで、32bitのWindowsは未対応とのことです。(職場のWindowsは32bit版なのでダメですね。まだMacを使うしかありません。) www.yujakudo.com

さて本題に入ります。私のPython環境はAnaconda依存にしてますのでTensorFlowのインストール手順もAnaconda環境に配慮したものになります。手順書でいうと以下の部分からスタートになります。 f:id:HealthcareIT_interpreter:20170326110031p:plain

Anaconda環境でインストールする方法:

手順1)Terminalで次のコマンドを入力し、tensorflowという名前のconda環境を作成します。

$ conda create -n tensorflow

手順2)次のコマンドでconda環境をアクティブにします。

$ source activate tensorflow

これで先ほど作成したtensorflowという名のconda環境がアクティブになりました。(tensorflow)が表示されているのが分かるかと思います。

 (tensorflow)$ 

手順3)次にこのconda環境下でpipを使ってTensorFlowをインストールします。

(tensorflow)$ pip install --ignore-installed --upgrade $TF_PYTHON_URL 

*$TF_PYTHON_URLは予約語です。この部分にはTensorFlowのパケージが置かれてあるURLを入れてください。例えば、CPU専用バージョンのTensorFlow (Python 3.4, 3.5対応版)をインストールするならURLは以下の通りです。 https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-1.0.1-py3-none-any.whl

GPU対応版は以下の通り: https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow_gpu-1.0.1-py3-none-any.whl

私の場合初めはGPU対応版を入れたのですがなぜか動作しませんでしたので、一旦

$ pip uninstall tensorflow

でアンインストールし、CPU専用バージョンを入れ直しました。

(なおこれは上記で作成したconda環境下で

(tensorflow)$ pip uninstall tensor flow

としなくてもきちんとアンインストールされました。)

横道にそれましたがCPU専用バージョンをインストールする場合は以下の通りコマンド入力してください。

 (tensorflow)$ pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-1.0.1-py3-none-any.whl

以下のようにSuccessfully installedと出れば成功です。 f:id:HealthcareIT_interpreter:20170326112211p:plain

Hi, I'm a medical interpreter. Can I stay here with you?

このフレーズ、毎回使ってます。これで断られたことは今のところ1件だけです。 目下400床程度の急性期病院で医療通訳(英語)業務を行なっています。(この病院、田舎なのにいろんな理由で外国人患者が多いんです。) 本格的に始めたのが2016年10月頃から。現在は平均して1日2〜3件の医療通訳依頼が来ています。私の本来業務は医療情報関連のお仕事なんですが、医事課の患者対応の一環として通訳業務もになうようになりました。初めの内は保険証の確認や窓口受付、会計の辺りのコミュニケーションサポートを想定していたのですが、次第に診察室内に呼ばれるようになり、患者とドクターの間に立って通訳をするようになって来ました。これって完全に医療通訳の分野じゃん、私には無理!と最初は二の足を踏みましたが、他にやれる人も無く、もうやるしかありません。それからまもなく半年。それなりに経験値(というか失敗談)が溜まって来たのでここらでいっそブログにでも書いてみるか、ということで始めました。日毎更新は初めから無理だと諦めます。土日だけ書くスタイル、週末ブロガーで行こうかなと思っています。あと本来業務である医療情報関連やプログラミング(業務でPython, jupyter notebookを使用しています)の話題も時々スパイス程度に入れていけたらなと思っています。英語とプログラミングを日々の仕事でスキルアップしていきます。