DPCデータの分析とかやるブログ

DPCデータの分析なんかをテキトーにやってます。

DPCチェッカーElectron版を公開

今回はこちらです。 ZIPをダウンロードして解凍して、dpc.exeを叩いて下さい。ブラウザではなく、ちゃんとアプリケーションが走る筈です。 何だか一ヶ月ほど前に、「Windows版を作りました!」と自慢げにエントリ書いてますが、ええと、忘れて下さい。
前回のはかなり試験的なリリースで、何せファイルサイズは展開後で400MB超、exeを叩くとまずDOS窓が開き、続いてブラウザが開くという「すわ、マルウェアか!?」という挙動をしてました。
幾ら私がいい加減でも、「うーん、これアプリケーションの体を成してないよね・・・」というくらいには思います。

Electronを使ったWindowsアプリの開発について

Electronはウェブサイトをそのまんまデスクトップアプリにしてしまうライブラリです。
最近、とうとう日本語で書かれた解説書も出版され、これから盛り上がることは間違い・・・ないんじゃ・・・ないかなあ・・・(尻すぼみ
今回はズルをしていて、PythonのFlaskとよく似たライブラリ、expressでベタ移植をやってます。なのであんまり、本来のElectronの書き方ではないです。実装方法についてはあんまり参考にしないで下さい。

これからの課題は?

えーと、Python版は一時停止することになると思います。
理由は簡単で、PythonGUIライブラリを使って、アプリケーションを作る自信が無いからです。 (これについては別エントリで詳述します)
PythonにはPyQt / Kivyといった有名なGUIライブラリがありますが、結論から言うとNode.js覚えた方が早いですね!(白目
本格的なウェブサイトや複雑なアプリケーションの構築ならいざ知らず、こういう軽量アプリなら、下手に複雑なライブラリを覚えるより、別言語で書いた方が早いと言うことは往々にしてあります。
ただ、フロントをElectronにして、バックエンドでPython走らせる方法もあるので、高度な分析機能を実装するならPython版を持ち出すかも・・・?
そこまで高度な機能なら、正直、ツールにしても仕方ない感じがしますが、まあ予定は未定ってことで・・・

「Electronではじめるアプリ開発 ~JavaScript/HTML/CSSでデスクトップアプリを作ろう」読んでます

年明けに「Webが全てにやってくるぞ!」と書いておきながら、何もしてなかったので本を買いました… というのは冗談で、ずっとNode.jsいじってました。で、Electronでアプリ化するにあたり、適当にブログやStackOverflowの記事なんか読みつつ書いてたんですが、やっぱり英語は辛いですね!
素晴らしいタイミングで日本語の本が出たので飛びつきました。ああ、日本語だ…落ち着く…

が、あんまり「めでたし、めでたし」とゆー話ではありません。

JavaScriptの世界の恐ろしさ

第三章からアプリケーションを作り始めますが、いきなりReactが登場します。え、Reactってなに?という向きはここで篩い落とされるか、公式のチュートリアルを読みに行くことになるでしょう。
そうそう、ReactはJSXっていう、HTMLをJavaScriptに埋め込んだみたいなシンタックスを使うらしいですよ!当然素の状態ではブラウザで読み込めないので、トランスパイラを噛ませる必要があります。
え、トランスパイラって何ですか、って?それは、JavaScriptでないものJavaScriptに変換するか、或いは特定のバージョンのJavaScriptを、別のバージョンのJavaScriptに変換するものです。
10年前に、JavaScriptスクリプト言語だと学んだ気がしますが、今はコンパイラ言語になってたんですね!

はいはい、意地悪を書きましたよ。
別に今書いたようなことは、フロントエンドの世界では当然でしょう。CoffeeScriptは2010年にはもうありましたし、babelは2015年には話題になってましたからね。
しかしまあ、それでも泣き言を言うなら、JavaScriptは言語の外側が複雑過ぎる気がします。
貧弱な標準ライブラリの裏返しなのか、欠陥の多い言語設計をカバーしようとした結果なのかは分かりませんが、うーん、すごいカルチャーではあります。
ジャングル、というのがこの状況を表す最もよい言葉でしょう。

そもそも、デスクトップアプリケーションを取り巻く状況が…

とまあ、恨み言を書きましたが、別にこの本に問題があるわけではありません。
本自体は、読んでいくと今のWeb開発の景色が見えるようで、とても面白いです。日本語で書かれた唯一のElectron入門本ですし、これでReactの説明までしてたらボリュームが倍になったでしょうから、仕方ないと思います。
が、うーん…例えばC#WPFアプリケーションを書くより、Electronの方が絶対楽かと言われると、うーん…
恐らくアプリケーションのパーツ周りを書くのはElectronの方が絶対楽ですが(だってHTMLですし)、環境作りという点ではJavaScriptの負荷は高いなあ、という感じ。
まあそれでも、C#を一から覚えるよりはJavaScriptの環境作りに苦労するほうが楽そうですけど。

ちなみに、C#GUIアプリケーションを書くとすると、WPFが真っ先に候補に上がると思いますが、qiitaで検索すると268件しか出てきません。

qiita.com

JavaならJavaFXですが、こちらは何と111件。

qiita.com

Reactは604件。

qiita.com

そしてElectronは487件です!

qiita.com

みんな、どんだけデスクトップアプリが嫌いか分かりますね

様式1データを扱いやすい形に変形する

前回に引き続き様式1の操作を行ってみます。
今回は実際に様式1ファイルをいじりますが、まずは前回のおさらいから。

{('A000010', 1): '生年月日',
 ('A000010', 2): '性別',
 ('A000010', 3): '患者住所地域の郵便番号',
 ('A000020', 1): '入院年月日',
 ('A000020', 2): '入院経路',
 ('A000020', 3): '他院よりの紹介の有無',
 ('A000020', 4): '自院の外来からの入院',
 ('A000020', 5): '予定・救急医療入院',

前回はペイロードを扱いやすくするため、こんな感じでタプルをキーにしたディクショナリ、paydictを作りました。
さて、では様式1のダミーデータを読み込んでみましょう。

ff1 = pd.read_csv('ff1dummy.csv')

こんな感じのデータです。

施設コード データ識別番号 入院年月日 回数管理番号 統括診療情報番号 コード バージョン 連番 ペイロード1 ペイロード2 ペイロード3 ペイロード4 ペイロード5 ペイロード6 ペイロード7 ペイロード8 ペイロード9
999999999 1111 2017/1/1 1 0 A000010 2014/4/1 0 19800101 1 9999999 NaN NaN NaN NaN NaN NaN
999999999 1111 2017/1/1 1 0 A000020 2014/4/1 0 20170101 1 0 0 101 1 0 9 NaN

日本語で「ペイロード1〜9」となっているのが面倒なので、名称を変更します。

ff1.columns = ['施設コード', 'データ識別番号', '入院年月日', '回数管理番号', '統括診療情報番号', 'コード', 'バージョン', '連番',
       '1', '2', '3', '4', '5', '6', '7',
       '8', '9']

これで単なる1~9の連番になりました。 さて、ここからですが、

  1. ペイロードの全項目を独立のカラムにする(横に200列くらいの巨大テーブルになる)
  2. 横に伸びているペイロードを縦に並び替え、項目名をつけられるようにする

という2つの方法があります。1番は以前書いたことがありますし、あんまりいじりやすい形になりませんので、今回は2番で行ってみましょう。

pd.meltを使う

データ識別番号 ペイロード1 ペイロード2 ペイロード3 ペイロード4 …
ダミー ダミー ダミー ダミー ダミー

となっているデータを、

データ識別番号 ペイロード1
データ識別番号 ペイロード2
データ識別番号 ペイロード3
データ識別番号 ペイロード4 …

こんな形に変形させます。 pandasには様々なテーブル変形方法がありますが、今回のようなケースではpd.meltを使用します。

melted = pd.melt(ff1,id_vars = ['データ識別番号','入院年月日','コード'],
        value_vars=['1','2','3','4','5','6','7','8','9'],var_name = 'ペイロード',value_name = 'データ')

こうすると出来上がるのは

データ識別番号 入院年月日 コード ペイロード データ
1111 2017/1/1 A000010 1 19800101
1111 2017/1/1 A000020 1 20170101
1111 2017/1/1 A000010 2 1
1111 2017/1/1 A000020 2 1

こんなデータです。

これで、「コード」と「ペイロード番号」のペアを並べることが出来ました! あとはこれを使って項目名を追加するだけです。

項目名カラムを追加する

melted['項目名'] = [paydict.get((k,int(y)),'') for k,y in zip(melted['コード'],melted['ペイロード'])]

新たなカラム「項目名」を作成します。「コード」と「ペイロード」のペアのキーをzipでまとめてループし、paydictから該当する項目を抜き出します。
なお、ここでpaydict[(k,y)]とやるとエラーになるので気をつけましょう。なんでかというと…

構造上、ペイロード項目はスカスカになっていて、例えばA000010はペイロード1~3のみに情報があり、4~9は空白となります。 paydictには存在する項目のキーしか入っていないので、例えば(A000010,4)をキーにした問い合わせをやると例外が返ります。 ディクショナリの`.get'メソッドを使うと、存在しないキーが指定された場合、例外を返さずにデフォルト値を返すことが出来るので、今回はそれで逃げを打ちました。
さて、出来上がりはこんな感じです。

データ識別番号 入院年月日 コード ペイロード データ 項目名
1111 2017/1/1 A000010 1 19800101 生年月日
1111 2017/1/1 A000020 1 20170101 入院年月日
1111 2017/1/1 A000010 2 1 性別
1111 2017/1/1 A000020 2 1 入院経路

あとは出来たデータを利用していけば良いでしょう。
様式1ファイルは曲者です。他にも色々方法がある筈ですので、試してみて下さい。

様式1のペイロード項目詳細を扱いやすい形に変換する

前回に引き続き、様式1データを扱います。 まず、前回紹介したペイロードマスタを読み込みましょう。

payload = pd.read_csv('ペイロードマスタ.csv')

payload.head()

コード ペイロー ド種別 レコード必須 条件等 有 連番 ペイ ロード番号 項目 必須 条件 有 項目名
A000010 患者属性 - 1 生年月日
A000010 患者属性 - 2 性別
A000010 患者属性 - 3 患者住所地域の郵便番号
A000020 入院情報 - 1 入院年月日
A000020 入院情報 - 2 入院経路
A000020 入院情報 - 3 ※A 他院よりの紹介の有無
A000020 入院情報 - 4 ※A 自院の外来からの入院
A000020 入院情報 - 5 ※A 予定・救急医療入院
A000020 入院情報 - 6 ※A 救急車による搬送の有無
A000020 入院情報 - 7 ※A 入院前の在宅医療の有無
A000020 入院情報 - 8 ※A 自傷行為・自殺企図の有無

こんな感じのデータが出てくるはずです。 さて、このまんまでは煮ても焼いても食えません。
まずやるべきことは、コードペイロード番号のセットを作り、項目名と紐付けることです。 いかにも辞書型を使うケースですよね。
さて、Pythonの辞書型は{key : value}のようにキーと値を紐付けるものです。
keyの部分は変更不可能なものなら何でも使うことが出来ます。例えば、リストは変更可能なのでキーに使えませんが、タプルは使えます。
(どんな用途があるか思いつきませんが、関数も変更不可能なのでキーに使えます)
なので、今回はコードペイロード番号のタプルをキーにした辞書を作ってみましょう。
辞書内包表記を使うのが簡単です。

paydict = {(k1,k2):v for k1,k2,v in zip(payload['コード'],payload['ペイロード番号'],payload['項目名'])}

とすれば、

{('A000010', 1): '生年月日',
 ('A000010', 2): '性別',
 ('A000010', 3): '患者住所地域の郵便番号',
 ('A000020', 1): '入院年月日',
 ('A000020', 2): '入院経路',
 ('A000020', 3): '他院よりの紹介の有無',
 ('A000020', 4): '自院の外来からの入院',
 ('A000020', 5): '予定・救急医療入院',

このようにコードペイロードのタプルをキーにした、項目リストが作れます。 逆転したものが欲しければ、

reverse = {v:k for k,v in paydict2.items()}

としてやるだけで、

{'24 時間以内の死亡の有無': ('A000030', 4),
 'BurnIndex': ('M160010', 2),
 'Hugh-Jones 分類': ('M040010', 2),
 'ICD10 コード': ('A006040', 2),
 'NYHA 心機能分類': ('M050010', 2),
 'UICC 病期分類(M)': ('CAN0020', 5),
 'UICC 病期分類(N)': ('CAN0020', 4),
 'UICC 病期分類(T)': ('CAN0020', 3),
 'UICC 病期分類(版)': ('CAN0020', 6),
 'がんの初発、再発': ('CAN0010', 3),

このように、キーと値を逆転させた辞書が手に入ります。

「うーん、タプルの順番だけでコードとペイロード番号を管理するのは引っかかるぞ…」という向きには、名前付きタプルが使えます。

from collections import namedtuple
Paykey = namedtuple('payload_key',['code','number'])

名前付きタプルは、その名の通り、タプルの要素に名前を付けて管理できます。ちょっと使い捨てクラスっぽいですね。

paydict = {Paykey(k1,k2):v for k1,k2,v in zip(payload['コード'],payload['ペイロード番号'],payload['項目名'])}

としてやれば、

{payload_key(code='A000010', number=1): '生年月日',
 payload_key(code='A000010', number=2): '性別',
 payload_key(code='A000010', number=3): '患者住所地域の郵便番号',
 payload_key(code='A000020', number=1): '入院年月日',
 payload_key(code='A000020', number=2): '入院経路',
 payload_key(code='A000020', number=3): '他院よりの紹介の有無',
 payload_key(code='A000020', number=4): '自院の外来からの入院',
 payload_key(code='A000020', number=5): '予定・救急医療入院',
 payload_key(code='A000020', number=6): '救急車による搬送の有無',
 payload_key(code='A000020', number=7): '入院前の在宅医療の有無',

このように要素に名前をつけて管理できます。
さて次回は、様式1データそのものの加工を行ってみましょう。

様式1ファイルの項目マスタをアップしました

このブログでは2回ほど様式1について扱ってきました。
が、まあ、触りだけやって特に深掘りはしていません。

stagira.hatenablog.com

今回は深掘りをするための準備作業をしてみます。
何はともあれ、あの忌々しいペイロードの詳細を見ないといけませんね。
ここに資料がありますので見てみると・・・あれ、これってPDF?
うーん、おかしいですね。
厚生労働省のサイトを頑張って探しましたが、どこにもcsvやエクセルファイルが見当たりません・・・

ここでおさらさいをすると、様式1はこんな形をしています。

様式1全体像

大項目 必須 小項目
1.ヘッダ部 (1) 施設コード
1.ヘッダ部 (2) データ識別番号
1.ヘッダ部 (3) 入院年月日
1.ヘッダ部 (4) 回数管理番号
1.ヘッダ部 (5) 統括診療情報番号
2.ペイロード (1) コード
2.ペイロード (2) バージョン
2.ペイロード (3) 連番
2.ペイロード (4) ペイロード 1(日付等)
2.ペイロード (5) ペイロード 2(コード等)
2.ペイロード (6) ペイロード 3
2.ペイロード (7) ペイロード 4
2.ペイロード (8) ペイロード 5
2.ペイロード (9) ペイロード 6
2.ペイロード (10) ペイロード 7
2.ペイロード (11) ペイロード 8
2.ペイロード (12) ペイロード 9(可変長文字列)

ペイロード部のコードペイロード1〜9の組み合わせで、何のデータが入るか決まる、という寸法です。ではどんなデータが入るのでしょうか。

ペイロード部分項目詳細

コード ペイロー ド種別 レコード必須 条件等 有 連番 ペイ ロード番号 項目 必須 条件 有 項目名
A000010 患者属性 - 1 生年月日
A000010 患者属性 - 2 性別
A000010 患者属性 - 3 患者住所地域の郵便番号
A000020 入院情報 - 1 入院年月日
A000020 入院情報 - 2 入院経路
A000020 入院情報 - 3 ※A 他院よりの紹介の有無
A000020 入院情報 - 4 ※A 自院の外来からの入院
A000020 入院情報 - 5 ※A 予定・救急医療入院
A000020 入院情報 - 6 ※A 救急車による搬送の有無
A000020 入院情報 - 7 ※A 入院前の在宅医療の有無
A000020 入院情報 - 8 ※A 自傷行為・自殺企図の有無

こんな感じのが、だいたい150個くらいあります。で、これを手作業でPDFから書き起こせと?
またまたご冗談を。
困ったので、PDF→Excelへの変換サービスを使い、その後手作業で整形してマスタを作りました。 こちらからダウンロード出来ます。 様式1全体像は様式1マスタ.csvに、ペイロード詳細はペイロードマスタ.csvに入っています。
なお、GitHubでは文字コードがUTF8でないと怒られるので、UTF8にしてあります。Excelで開くとたぶん文字化けしますので、様式1マスタ.xlsxも用意しておきました。
あとはDBに取り込むなり、Pandasで利用するなり、好きなように使えます。
次回は実際にこのファイルを使って、様式1データとマスタを組み合わせて好きなデータを抽出してみましょう。


なお、今回の記事作成にあたってはこちらのサービスに大いに助けられました。

www.tablesgenerator.com

こんなでかいテーブルをMarkdownで手打ちするのは苦行ですので・・・ とても素晴らしいサービスです。Markdownでブログを書いている方は是非どうぞ。

Postgres用SQLクエリまとめに追加をしました

このブログでは今まで色々なSQLクエリを紹介してきましたが、殆どがPostgreSQL用でした。
SQLite向けのクエリはDPCチェッカーに突っ込んでいますが、Postgres用のクエリはどこにもまとまっていません。
流石に不味い、ということでこちらに随時アップロードしています。
ご利用ください。

Postgresを使った分析について少し

当ブログでは一貫してDPCデータの分析にはPostgres + Python環境がよいと主張し続けています。
配布の関係上、DPCチェッカーではSQLiteを使っていますが、通常、自院でその時それぞれな分析をするなら、Postgresの方が柔軟で多くの状況に対応できるのは間違いありません。
特に、2つ以上のパラメータを使った条件指定は、DPCデータ分析では頻発します。
(データ識別番号と入院年月日をセットにした条件指定、データ識別番号と実施年月日をセットにした条件指定は非常に多用されます)
その際、Postgresなら(データ識別番号,入院年月日) IN (SELECT ...)のような書き方をして、通常のIN構文の延長線で書けますが、この書き方に対応していないSQLでは面倒くさいことになります。
この記事でも書きましたが、極力Postgresを利用するのがいいでしょう。
ただ、環境構築や細かい注意点は明文化して来ませんでしたので、そろそろ記事を書こうと思います。
そんなに難しいことはありませんが、場合により詰まるところもありますので・・・

DPCチェッカー 0.13 Windowsアプリケーション版を配布開始

例によってこちらです。
一応ver 0.13はダッシュボード機能の追加がメインで、MDC6別の延べ人数/金額トップ10を実装しました。
が、そこはどうでもよく(10分で仕上げたやっつけ機能ですし・・・)、Windowsアプリケーション版が本命です。
ビルドにはPyinstallerの3.3dev版を使っています。
正式リリース版である3.2.1ではビルドエラーを起こすためで、まあ、あんまり安定してないかもですね・・・(おい
あ、あと今更ライセンスを決めました。まあ、普通にMITライセンスとなっておりますので、無償・無制限でご利用ください。

Windowsでの配布について

えー、元々「Pythonアプリです。使うにはAnacondaをインストールしてね!」というのはどうかな、とずっと思っていました。
思っていながらver 0.13まで来たわけですが、言い訳すると何度もビルドで失敗していたのです。
PyinstallerはPythonスクリプトマルチプラットフォームのアプリケーションにするライブラリです。開発もよく続いていて、将来性にはあまり不安がありません。
ですが、Pandas/numpyを含んだスクリプトをビルドすると、エラーが多く、中々安定しませんでした。
今回、開発中の3.3を使ったら、たまたま上手く行ったので配布する、という見切り発車です。動かなかったらごめんなさい。

他に方法として

  • Node.jsとElectronで開発する。ほぼ書き直し。
  • ScalaJavaFXで開発する。日本語のドキュメントがありません・・・

という方法も検討しましたが、まあ、上に書いた理由で諦めました。
一人開発ソフトで、何種類も書くのは間違いですね・・・
ただ、Scalaは書いていて面白いので、そのうち何か小さなアプリでも公開するかも知れません。