読者です 読者をやめる 読者になる 読者になる

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

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

EFファイルを自力で統合する(2017年版)

どうも最近、EFファイル統合でこのブログに来られる方が多いようです。

stagira.hatenablog.com

こんな記事も書きましたが、中身に関しては「適当にコード読んどいてね!」と投げっぱなし感溢れてましたので、ちゃんとした解説を書くことにしました。 ちょっと長いですが、EFファイル統合の流れを追っていきます。

方針

  • 実装はPythonのライブラリ、Pandasに全面的に頼る。
  • ライブラリの便利メソッドにおんぶにだっこ、極力難しいことはしない。

ということでPython環境のない方はAnaconndaを導入して下さい。
EFファイル統合スクリプトこちらにあります。並行してご覧ください。

おさらい

EファイルとFファイルに共通する項目で、重要なのはデータ識別番号 入院年月日 データ区分 順序番号の4項目です。
実際には、この4項目をキーにして結合を行い、同一の順序番号の中で更に行為明細番号を割り振ります。 図にするとこんな感じ。

データ識別番号  入院年月日  データ区分 順序番号 行為明細番号
000001 (Eファイル由来) 2017-04-01 60 1    0
000001 (Fファイル由来) 2017-04-01 60 1 1
000001 (Fファイル由来) 2017-04-01 60 1 2

重要なことは行為明細番号はFファイルにしかなく、Eファイル由来の行の行為明細番号は0になるということです。 これは、この後再び言及します。統合のミソです。

下準備

まず、カラム名のリストを用意します。

e_names = ['施設コード','データ識別番号','退院年月日','入院年月日','データ区分','順序番号',
'病院点数マスタコード','レセプト電算コード','解釈番号','診療明細名称','行為点数','行為薬剤料',
'行為材料料','円点区分','行為回数','保険者番号','レセプト種別コード','実施年月日',
'レセプト科区分  ','診療科区分','医師コード','病棟コード','病棟区分','入外区分','施設タイプ']
#正しくはEファイルには「診療明細名称」ではなく「診療行為名称」だが、統合のため変えている

f_names = ['施設コード','データ識別番号','退院年月日','入院年月日','データ区分',
'順序番号','行為明細番号','病院点数マスタコード','レセプト電算コード','解釈番号','診療明細名称',
'使用量','基準単位','行為明細点数','行為明細薬剤料','行為明細材料料','円点区分',
'出来高実績点数','行為明細区分情報']

続いて、EファイルとFファイルを読み込みます。医師コードを文字列(str)で読み込んでいるのは、病院によって数字のみだったり英数字で管理していたりとバラバラだからです。
shift_jisx0213でエンコード指定していますが、単にshift-jisでも行けるかも知れません。

edata = pd.read_csv('DRGE.TXT',delimiter='\t',parse_dates=['入院年月日','実施年月日'],dtype={'医師コード':str},
    encoding = 'shift_jisx0213',names=e_names)

fdata = pd.read_csv('DRGF.TXT',delimiter='\t',parse_dates=['入院年月日'],dtype={'行為明細区分情報':str},
    encoding = 'shift_jisx0213',names=f_names)

EFファイルの「明細点数・金額」は、Fファイルの'行為明細点数','行為明細薬剤料','行為明細材料料を一緒くたにしたものです。
ここでは、まず3つを単純に足し合わせた新たなカラムを作り、元の3つをdropしています。

fdata['明細点数・金額'] = fdata['行為明細点数'] + fdata['行為明細薬剤料'] + fdata['行為明細材料料']
fdata.drop(['行為明細点数','行為明細薬剤料','行為明細材料料'],axis=1,inplace = True)

下準備が終わりました。ここからは結合処理に入ります。

結合処理と行為明細番号

efdata = pd.concat([edata,fdata])

まずはpd.concatで縦方向に連結します。この時点では、本当に

Eファイル ↓ Fファイル

の順で縦に繋げただけです。
実際には [‘データ識別番号’,‘入院年月日’,‘データ区分’,‘順序番号’,‘行為明細番号’]の順番に並べる必要があります。
さっそく並べましょう!…と言いたいのですが、Eファイルには行為明細番号がありません。
そう、初っ端に言及した通り、Eファイル由来の行は行為明細番号0になります。並べる前に埋めておきましょう。

efdata['行為明細番号'].fillna(0,inplace=True)

fillnaメソッドは単純な穴埋めから高度な補完まで使えます。ここでは単純に0での穴埋めをしていますが、この先もうちょっと高度なことをやってもらいます。
続いては単純な並び替えです。

efdata.sort_values(['データ識別番号','入院年月日','データ区分','順序番号','行為明細番号'],inplace = True)
efdata.reset_index(inplace=True)

項目ごとの穴埋め

EFファイルにしかない項目を、穴埋めリストにしてみます。

fill_list='行為回数','実施年月日','診療科区分','医師コード','病棟コード','病棟区分'

さて、各項目がEファイル由来ということは、必ずレコードの先頭にある(=行為明細番号が0)ということです。 その後の空白行については、直近の一番上のデータから穴埋めしていけばよいのです。
fillna(method='pad')正にそのような挙動をしてくれます。

for val in fill_list:
    efdata[val].fillna(method='pad',inplace=True)

さて、これで殆ど出来上がりですが、私の環境では

データ区分、データ識別番号、レセプト電算コード…

のようにカタカナ順になり、続いて漢字…という並び方になってしまいました。
これでは困るので、続いてカラムの順番を変えます。

sort_list = '施設コード','データ識別番号','退院年月日','入院年月日','データ区分',\
'順序番号','行為明細番号','病院点数マスタコード','レセプト電算コード','解釈番号','診療明細名称',\
'使用量','基準単位','明細点数・金額','円点区分','出来高実績点数','行為明細区分情報','行為点数','行為薬剤料',\
'行為材料料','行為回数','保険者番号','レセプト種別コード','実施年月日','レセプト科区分',\
'診療科区分','医師コード','病棟コード','病棟区分','入外区分','施設タイプ'


efdata_sorted = efdata.reindex(columns=sort_list)

df.reindexはcolumnsに指定してやることで、カラムの指定が可能です。

おまけ

DB取り込み用に、0をinfinityに置換してやります。
また、入院年月日に0が入っていた場合は、こちらも'epoch'に置換してやります(多くの場合、この手順はスキップして 構いません。通常は起きない筈なので…)

efdata_sorted['退院年月日'] = efdata_sorted['退院年月日'].replace(0,'infinity')
efdata_sorted['入院年月日'] = efdata_sorted['入院年月日'].replace('0','epoch')

最後に空白のままになっている項目を、便宜上0で埋めておきます。

Fillzero = ['行為点数','行為薬剤料','行為材料料','使用量','基準単位','明細点数・金額','出来高実績点数']
for fillzero in Fillzero:
    efdata_sorted[fillzero].fillna(0,inplace=True)

あとはCSVに吐き出せばおしまいです。
GitHubのコードでは医師コードをいじってますが、環境によって先頭に空白が交じることがあるからで、特に意味はありません。
(悪さをするようなコードでもないです)
実際には、思わぬエラーが出てくることもあり得ますので、適宜修正するのがオススメです。

注記

この統合はDB登録を目的にしたもので、データ提出を視野に入れていません!
データ提出に使うと文字コード絡みで確実に弾かれますので、注意して下さい。

さいきん読んだ本など

あるごりずむ

特にネタがないので(おい)、今日は最近読んだ本の話でも。
私はどんな問題も

  1. 楽ができる機能がないか探す
  2. 楽ができるライブラリを探す
  3. StackOverFlowで答えを探す

という方法で解決するゆとりコーダーです。はい、あまりプログラムは出来ません…
友人に「例えばリストの中身をひっくり返すとしたら、どうする?」と聞かれて「り、list.reverse()メソッドがあるしおすし…」と回答するくらいには無能です。
ということで、

なっとく! アルゴリズム

なっとく! アルゴリズム

こんな本を買いました。そもそもアルゴリズムってちゃんと勉強したことないですしね。
本の内容ですが、分割統治の例などかなり格好良く、読んでて飽きません。気の抜けたイラストが、いい感じに肩の力を抜いてくれます。文字数もそんなにないですが、とはいえ漫画というほど軽くもない、絶妙なところですね。
例となるコードは全てPythonになっており、Pythonユーザーには優しいです。

えりくさー

プログラミングElixir

プログラミングElixir

Programming Phoenix: Productive |> Reliable |> Fast

Programming Phoenix: Productive |> Reliable |> Fast

なぜかElixirを勉強し始めました。
ElixirはRubyっぽい文法で書けるErlangです…という説明で通じたら苦労はいりませんね。
えーと、なんか並行処理にすごく強くて速い、厨二病かっけーな言語です(違
2012年に登場したばかりの、かなり若い言語ですが、もうWebフレームワークが作られています。
Phoenixというのですが、これが非常に便利で、Flaskよりずっと楽…というか、Python界隈がWebフレームワークに恵まれてないんじゃ…?と思うくらいには楽です。
DBの面倒はぜんぶフレームワークが見てくれますし(自分でPostgresやSQLiteを見る必要がない)、いー感じにフレームワークがコードを自動生成して面倒見てくれます。
Flaskでは全てが手作業で、だからこそ挙動を把握できるところもありますから、良し悪しもありますが…

Electronアプリ DPCチェッカー0.2を公開

えー、前回のリリースから一ヶ月以上サボってましたが、0.2が出来ましたので公開します。
例によってこちらからどうぞ。

変更点

Python版にあったダッシュボード機能を移植しました。
今回こんなに時間がかかったのは、方向性を決めあぐねて、色々フレームワークをいじって遊んでたからです。
JavaScriptの世界は本当にジャングルみたいに繁栄してますね・・・
一時期は「フロント部分をReactで書き直す」とか正気じゃないことを考えてました。
その後は「Reactはやり過ぎだ。Vue.jsで書き直せばいいだろう」と思ってました。うーん、迷走ですね!
(そもそも力を入れるべきはそこではない)

今後の方向

いい加減単月データだけを見る構造は卒業して、最低でも1年分は保存出来るようにしたいですが、設計どうするか迷っています。
割と見切り発車でアップデートするかも知れません。
あとPythonベースの高度なダッシュボードも作ってみたいなあ・・・

Pandasでささっと前年度比較

毎月レセプトが終わると、前年同月比較をやることが多いです。
まあ、手術がガクンと落ちたり上がったり、どうしても波があるからです。
今回は、EFからささっとデータを取ってくる方法を考えてみました。 わかりやすく、今年のデータはdf17、去年のデータはdf16としましょう。

df17 = pd.read_sql("""select 診療科区分,解釈番号,診療明細名称,出来高実績点数,行為回数
from etable17
Where データ区分 BETWEEN 50 AND 59
AND 解釈番号 LIKE 'K%'
AND 解釈番号 NOT LIKE 'K92%'""",connection)

df16 = pd.read_sql("""select 診療科区分,解釈番号,診療明細名称,出来高実績点数,行為回数
from etable16
Where データ区分 BETWEEN 50 AND 59
AND 解釈番号 LIKE 'K%'
AND 解釈番号 NOT LIKE 'K92%'""",connection)

ひとまず手術手技だけに着目することにします。
解釈番号がKで始まるものに固定し、かつK92系(輸血です)を除いて、純粋に手術手技だけを見てみましょう。

df16['year'] = 16
df17['year'] = 17

df_merged = pd.concat([df16,df17])

あとで集計しやすいように、yearカラムを追加して年を入れます。
pd.concatはデータフレームを縦方向に連結する関数です。SQLでいうところのUNIONですね。

df_merged.groupby(['診療科区分','year']).describe()

で診療科区分別の状態を、年別にサクッと確認できます。
また、

df_merged.sort_values(by='出来高実績点数', ascending=False)

とすれば、手技の高い順にソートをすることも出来ます。

また、メソッドは連結が可能です。
つまり、

df16.groupby(['診療科区分','診療明細名称']).sum().sort_values(by='出来高実績点数', ascending=False)

こうすれば、診療科区分・手技別に総計を出し、金額順に並び替える、という処理がワンライナーで出来ます。
こんな風に、その場その場の問題をぱぱっと解決するのにPandasは便利ですね。

EFファイル統合スクリプトをアップしました

4月分のレセプトチェックのため、EF統合ツールを使ったところ「2017年度以降のデータが入っています」と蹴られました。 あれ?と思って調べてみたら…PRISMのEF統合ツール、今年度分は6月公開予定なんですね!(白目

うーん、困りました。そういえば、去年も同じようなことがあって困ってた記憶があります。
ほじくり返すと、どうやらEF統合を自力でやろうとしてたみたいですね。
仕方ないので今年もそうしますが、去年のに少しだけ手を加えて、ヘッダを用意しなくても使えるように修正しました。
こちらEF統合(2016).pyをご利用ください。
EファイルとFファイルのあるフォルダに置いて実行すれば、outputEF.txtが出力されます。

なお、このスクリプトの用途はあくまでDB取り込み用であり、提出仕様ではないのでご注意を。
エンコーディングUTF-8になってるので、このまま提出すれば100%弾かれます。

Bokehで動的なグラフを描画する

そもそもBokehって?

大分前に一度紹介しましたが、PythonのグラフライブラリにBokehというものがあります。
Pythonのデータ可視化というと、matplotlibが圧倒的なデファクトになってます。
そのラッパーであるseabornも有名で、最近のPython本を見るとseabornを使う例も多いですね。
今回紹介するBokehはかなり違った出自で、HTMLを吐き出すのが特徴です。ライブラリはPython側とJavaScript側に分かれており、実際の描画はJavaScript側が行います。
また、グラフの描画レベルが2つに分かれているのも特徴です。

ローレベルのbokeh.plotting

  • 折れ線グラフ、単純な散布図、棒や円などを描画できる
  • D3.jsほどローレベルではないが、割りと近い感じ
  • 当然自由度は高いが、これでグラフを組み上げるのは大変そう…

ハイレベルのbokeh.charts

  • 既に用意された形式で、棒グラフやヒストグラム、散布図など一通り描画できる
  • ユーザーサイドから見ると、seabornと似たような感覚で使える

Bokehのいいところ

グラフは動的にいじれるし、見栄えも良い。HTMLとJavaScriptなので、Webアプリにも簡単に組み込める。

Bokehの悪いところ

ライブラリの性質上、データはPython側で用意し、描画のコードを書くのだが、実際にはJavaScriptにコンバートされることになる。 Python側のデータとJavaScript側のデータは必ずしも同一ではない(!)のに注意しないといけない。
400列の巨大データから、2行だけ使ってグラフを描画するケースなどもあるので、DataFrameを全てJavaScript側に流し込むわけにはいかない、という話だけど…



こんな感じでしょうか。 後は下にJupiter Notebookを貼っておきますので、マウスでいじってみて下さい。 (前回みたいにGistも用意したんですが、JavaScriptのロードでコケました…nbviewerだと動くのは、何でなんでしょうね?)

Jupyter Notebook Viewer

DPC入院料を月別/科別に可視化する

さて今日も元気にJavaScriptでガンガンフロントを書いていきましょう!
…という趣旨の記事を書きそうになって正気に返ったスタゲイラです。こんばんは。
えーと。ここしばらくはこんな本を読んでました。

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門

Reactビギナーズガイド ―コンポーネントベースのフロントエンド開発入門

このままだと、ブログが「日曜プログラマのWebサイト作り」になってしまうので、路線を戻します。
今回はSQLからは離れて、既に「DPC入院料」「科名」「診療月」が入ったデータセットがあるものと仮定し、簡単な可視化をやってみましょう。
使うのはpandasのプロット機能と、seabornです。
Jupyter Notebookをそのまま載せてみました。実務でも、大体こんな感じでやってます。

DPC入院料のグラフ化テスト

実務では月別の推移を見ることが多いです。
そして、大雑把に「今月は入院料が高い」「今月はなんか低い」と分かったら、そこから細かく見ていくわけですが、真っ先に切り分けるのは診療科であることが殆どです。
このように時系列を折れ線グラフで描いたり、分布をヒストグラムや箱ひげ図で確認するのはとても重要です。 分布に異常がないかヒストグラムでチェックして、箱ひげ図で異常値がないか見る感じですね。
最後に外れ値除去を行って再び箱ひげ図を見る、なんてこともやってます。