医療通訳で汗だくになってた医療情報技師が外資系企業に転職したブログ

医療情報技師・診療情報管理士のダブルライセンス達成!意味ない!

ブログタイトルを変更しました

今年2月から転職をし、病院から外資系企業に籍を移すことになりました。もう医療通訳で診察室に呼ばれることはなくなりましたが、その代わり海外のスタッフと英語でやり取りすることが日常的になり、ますます汗だくになっております。特にテレフォンカンファレンス(TC)で相手がインド英語やフランスなまりの英語だったりすると聞き取ることすら難しい状態です。データアナリストとして日々精進していきますので今後もよろしくお願いします。

現在の取得資格: 医療情報技師(2016年8月試験合格), 診療情報管理士(2018年2月試験合格、88期生)

あ、医療情報技師の点数取得どうしよう。医療情報学会行かないと…。その点、診療情報管理士は資格維持コストが無いので楽ですね。同期の皆さんよろしくお願いします。

診療情報管理士認定試験の合格者受験番号一覧をPhantomJSでスクショしようとしたらブロックされた

3月23日に診療情報管理士認定試験の合格者受験番号が発表されました。認定試験のホームページに掲載されております。

f:id:HealthcareIT_interpreter:20180325184734p:plain

よーし、こりゃあWebscrapingしがいがあるのー、ということで、小手調べにPhantomJSでスクリーショットを撮って見よー、と試して見たことろブロックがかかってしまいました。

from selenium import webdriver
from selenium.webdriver.common.keys import Keys as keys
import time

def main():
    url = "https://www.jha-e.com/top/certExams/resultDetails"
    
    driver = webdriver.PhantomJS()
    driver.get(url)
    
    print(driver.current_url)
    
    driver.save_screenshot("testnum.jpg")
    driver.quit()
    
    print("end")


if __name__ == '__main__':
    main()

f:id:HealthcareIT_interpreter:20180325183111j:plain

やっぱセキュリティ気にしてるのかな?このはてなブログにリンク貼ることもできないようです。

www.jha-e.com

ちなみにurlを一つ上の階層の"https://www.jha-e.com/top/certExams/result"にして見たらちゃんとスクショ撮れました。

f:id:HealthcareIT_interpreter:20180325183120j:plain

Microsoft Translator Text API のcategoryを"generalnn"にすると翻訳精度が飛躍的に向上する

Microsoft Translator Text APIを自作のWebアプリに組み入れて翻訳させても翻訳精度が悪くて使い物にならない、というかiOSアプリや Androidアプリと翻訳結果が違っている、という状況に陥っていました。これについて日本マイクロソフトの中の人に直接問い合わせておりましたが、先日日本マイクロソフトの某パートナー企業の人からcategory="generalnn"にすると解消するという貴重な情報をいただき、その通りにすると改善しました。何も指定しない場合は、デフォルトのcategory="general"になるそうです。ちなみに"generalnn"の最後の"nn"というのはニューラル・ネットワークの事でしょうか? こちらに良い記事がありました。

Microsoft Translator launching Neural Network based translations for all its speech languages – Translator

おそらくデフォルトのcategory="general"は、統計学機械翻訳を行うもので、category="generalnn"は、ディープ・ニューラル・ネットワークによる翻訳を行う、という事なのでしょう。

という事で、以前Githubに公開したライブラリにもcategory="generalnn"を加えておきました。

github.com

Microsoft Translator Text APIをPythonから手軽に使えるように実装した

Githubにあげました。 github.com

同様のライブラリはたくさん上がっていますが、Addtranslationメソッドをまともに使えるものが少なかったので自分で実装しました。 (Addtranslationメソッドとは、こういう風に訳してほしいという例文を送信して、次回以降の翻訳の精度を向上させる(カスタマイズする)機能です。)

使用例はこちら↓

gist.github.com

医療通訳支援Webアプリ(The record system)にPlotlyから最近出たDashを使ってインタラクティブなグラフを追加した

以下のエントリーの続編です。

healthcareit-interpreter.hatenablog.com

herokuにデプロイしたページは以下になります。

https://englishtranslationrecord.herokuapp.com/f:id:HealthcareIT_interpreter:20170804005557p:plain

コードはGitHubにあげてあります。
①Dashのページのコード

github.com

医療通訳支援Webアプリ(以下、Webアプリ)のコード

github.com

作成手順(概要)

1)Dashのページを作ってherokuにデプロイ
2)WebアプリにDashのページを埋め込む
という2段階になります。今回も前回に引き続き以下のページをベースに作業を進めました。

Making a Flask app using a PostgreSQL database and deploying to Heroku

それからDash用に以下も参照:

Dash User Guide and Documentation - Dash by Plotly

herokuに置いたDB(PostgreSQL)をDashのページとWebアプリとで共有する

参考にしたページ:

devcenter.heroku.com

$ git init
$ git add .
$ git commit -m "initial commit"
$ heroku create etr-dashapp
$ git push heroku master

とした後に、データベースの手続きをします。ちなみにetr-dashappというのが今回新規作成するDashのページになります。今回はすでに展開してあったWebアプリ用のDBを使い回したいので、

$ heroku addons:attach englishtranslationrecord::DATABASE --app etr-dashapp

$ heroku pg:promote postgresql-hexagonal-12345 --app etr-dashapp

とやってあげます。englishtranslationrecordが既存のWebアプリの名称ということになります。
postgresql-hexagonal-12345はダミーの名称です。herokuのページ行くと確認できる、すでに展開してあるDBの名称になります。
すでに作成済みのDBを使い回すので以下の処理(herokuにDBを新規作成)は不要です。

$ heroku run python
>>> from app import db
>>> db.create_all()
>>> exit()

Procfileの中身

さて上記で、

$ git push heroku master

とやった時点でうまくいけば

https://etr-dashapp.herokuapp.com/

にDashのページが表示されるのですが、Procfileの書き方を間違えていたせいでHeroku deployment error H10 (App crashed)というエラーが出てしまいました。

stackoverflow.com

↑ここにあるように

$ heroku restart

とか色々と試してみたのですがダメでした。頭を抱えた末にたどり着いたページに助けられました:

github.com

このページでは、Procfileの中身は以下のようになっていました。

web gunicorn run:server

うん? runというのはrun.pyのことか。serverというのはrun.py中に記載されたDashのインスタンスのことか。なら自分はdashapp.pyという名称にしていたので

web gunicorn dashapp:server

にしてみるか。動いた!jimmybowに感謝です。 でもよく見たら公式ページにもちゃんと書いてありました。

Procfile
web: gunicorn app:server
(Note that app refers to the filename app.py. server refers to the variable server inside that file).

Webアプリのデプロイのときは純flask製だったのでserverインスタンスではなくappインスタンスだった上、ファイル名称はapp.pyだったので

web: gunicorn app:app

だったとしてもよく分かっていなかったということが露呈した形になります。
app:appはapp.py:appインスタンスということだとようやく分かりました。

WebアプリにDashのページを埋め込む

さて話を本題に戻しましょう。Dashのページはflask本体のようにrender_templateメソッドを(現在のところ)使えないらしいので、
(参考:Dash itself won’t provide compatibility with Jinja templates.)

community.plot.ly

Webアプリ(flaskapp)がrender_templateするindex.html (ここではhome.html)などにiframeを組み込んで、herokuにデプロイしたDashのページのURLをsrcに指定してやる方法が一番簡単そうです。

ということで以下の修正をWebアプリ側に施しました。

<html>

<head>
</head>

<body>
 
<iframe frameborder='0' noresize='noresize' style='position: absolute; background: transparent; width: 100%; height:100%;' src="{{ iframe }}" frameborder="0" id="dash"></iframe>

<script type="text/javascript">
    window.onload = setInterval(function(){
        document.getElementById("dash").src = document.getElementById("dash").src
    }, 60*1000);
</script>

</body>

</html>
@app.route('/')
def home():
    iframe = 'https://etr-dashapp.herokuapp.com/'
    return render_template('home.html', iframe=iframe)

htmlファイルにはJavaScriptで60秒ごとにiframe部分限定のリロードをかけるように仕込んであります。 Dashのページはリロードする度にDBから最新データを取得するように関数化してあります。

def serve_layout():
    getdata()
    return html.Div([
        dcc.Dropdown(
            id='select-person',
            options=[{'label': i, 'value': i} for i in ffc.columns],
            multi=True,
            value=ffc.columns
        ),
        dcc.Graph(
            id='graph-with-range',
            animate=False
        ),
        dcc.Graph(
            id='my-table'
        ),
        dcc.RangeSlider(
            id='month-range',
            marks={str(i): i for i in ffc.index},
            min=ffc.index.min(),
            max=ffc.index.max(),
            value=[ffc.index.min(), ffc.index.max()],
            step=None
        )
    ])

app.layout = serve_layout

しかしDashのコードというのはPythonなのかJavaScriptなのか分からなくなりますね。(実際、HTMLとJavaScriptだけでインタラクティブなページを作ろうとするとこれほど短いコードでは済みませんが。)

matplotlibで複数のグラフを作ったが、上司に報告するときExcelに貼り付けるってどうよ

患者統計(医事統計)の数字がおかしいので調べてくれ

という上司の命令に従い、渋々これまでの月別帳票(月別に別個のフォルダに格納されている)をPython標準ライブラリのglobとosを使って爆速で一括取得し、openpyxl で特定のセルだけ横串にしてmatplotlibで数値の月別変遷を見える化、したものの、

出来上がった複数のpngファイルをExcelに貼り付けて報告

という最後の落ちで笑えない状態になりました。せっかくjupyter notebookで作業してるんだから最後までスマートに行きたいものです。Excelpngファイルを貼り付けって、結局手作業で画像の位置が微妙にずれたりして見た目にも美しくありません。

jupyter notebookからグラフを貼り付けたHTMLファイルを生成し、それで報告する

ここに同じような発想をしている人がいました。

qiita.com

スタンドアローンのHTMLファイルのソースコードを開いて、markdownで必要なテキストやデータテーブルを記載するというものです。これは素晴らしい! このスタンドアローンHTMLファイルをテンプレートにしてjupyter notebookからmatplotlibで生成したグラフをbase64形式にして貼り付ければ

Excel方眼紙を完全に駆逐

できます。pngファイルの残骸も発生することがありません。

ということでjupyter notebookに実装しました。

gistc542f40ad8221297a5c2436dbcceb410

matplotlibのグラフを任意の数だけを貼り付けたHTMLファイルがカレントディレクトリに生成されます。

f:id:HealthcareIT_interpreter:20170723171351p:plain

このファイルをエディタで開いてmarkdown記載領域に必要な説明文を追加すれば以下のようになります。う、美しい!

f:id:HealthcareIT_interpreter:20170723171358p:plain

はいサイナラExcel

OpenCVをWindows Server 2012 R2に入れてPythonで使う、だけの話なのに死ぬ思いした話

Windowsの場合、
結論から言うと、必要なものは、
・Anaconda3(前提)
・opencv3 (Pythonライブラリ)
OpenCV 3.2.0 (本体)
の3つだけです。cv2.pydのコピぺとか環境変数の追加とか一切必要ありません。

1. opencv3のインストー

$conda install --channel https://conda.anaconda.org/menpo opencv3

2. OpenCV 3.2.0 のダウンロード

github.com

3. opencv-3.2.0-vc14.exeを実行し、得られたファイルを任意の場所に格納する

以上。

参考ページ:

qiita.com ただし、カスケードファイルが
C:\Users[username]\Anaconda3\envs[envname]\Library\etc
に入っていると書いてありますが、私が見たところ空でした。(うん?この記事はもしかしてPythonのライブラリであるopencv3と本体のOpenCV(3.1)を混同していないか?)
ですので(カスケードファイルを取得するために(?))OpenCV本体が必要になります。
opencv-3.2.0-vc14.exe実行後取得できるファイルの格納先はCドライブ直下にしました。以下のようにカスケードファイルの絶対パスをコード中に書いてやれば使いたい放題です。

import cv2, sys, re

# 入力ファイルを指定 
if len(sys.argv) <= 1:
    print("no input file")
    quit()
image_file = sys.argv[1] 

# 出力ファイル名
output_file = re.sub(r'\.jpg|jpeg|png$', '-mosaic.jpg', image_file)
mosaic_rate = 30 

# カスケードファイルのパスを指定
cascade_file = "C:¥opencv¥sources¥data¥haarcascades¥haarcascade_frontalface_alt.xml"

# 画像の読み込み 
image = cv2.imread(image_file)
image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # グレイスケール変換

# 顔認識を実行 
cascade = cv2.CascadeClassifier(cascade_file)
face_list = cascade.detectMultiScale(image_gs,
    scaleFactor=1.1,
    minNeighbors=1,
    minSize=(100,100))

if len(face_list) == 0:
    print("no face")
    quit()

# 認識した部分にモザイクをかける 
print(face_list)
color = (0, 0, 255)
for (x,y,w,h) in face_list:
    # 顔を切り抜く 
    face_img = image[y:y+h, x:x+w]
    # 切り抜いた画像を指定倍率で縮小 
    face_img = cv2.resize(face_img, (w//mosaic_rate, h//mosaic_rate))
    # 縮小した画像を元のサイズに戻す 
    face_img = cv2.resize(face_img, (w, h), 
        interpolation=cv2.INTER_AREA)
    # 元の画像に貼り付ける 
    image[y:y+h, x:x+w] = face_img
# 描画結果をファイルに書き込む
cv2.imwrite(output_file, image)

今回もこの本に書いてあるコードを拝借してます。

以上、超シンプルなやり方以外にも色々とインストール方法はたくさんあるようで、今回はそんな多岐にわたるマニュアル(ブログ)に翻弄されて時間と労力を消費してしまいました。

実はmac だと

$ brew tap homebrew/science
$ brew install opencv
$ pip install opencv-python

の連続コンボだけですぐに使えます。非常にシンプルです。ところがWindowsだと色々と面倒な処置が必要なようです。特に私が今回最初に参照したページにはシステム環境編集のPathをいじれとか書いてある。

www.buildinsider.net

googleで「opencv install windows」で検索すると一番先頭に出てくるページですから参照しようとしたのも無理はありません。素直に従ってPathの内容をここに記載してある通り書き換えて、他のPathが全部消えてしまったとしても無理はありません。(←死ぬ思いしたのここ)

qiita.com

ここもひどいもんです。曰く、「opencv/build/python/2.7/x64/以下にあるcv2.pydファイルをクリップボードにコピーします。このファイルをWinPython(ここはAnacondaに読み替え)のモジュール管理フォルダに格納します。」

↓そもそも公式ページからして良くわかりません。どこからダウンロードすればいいのか。

opencv.org

Pythonのライブラリも名称がはっきりしません。opencv3なのかcv2なのか。使うときは

import cv2

ですが、conda listで見るとopencv3と表記されています。

先述の本にはWindowsでのインストールの方法が記載されていなかったんです。そのせいで大変な思いをしました。なんとかして欲しかったものです。