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

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

DPCチェッカー 0.12 ダッシュボード機能を書き直し

例によってこちらです。
二日ぶりのアップデートですね!(白目
えー、以前から予告してましたがダッシュボード機能を書き直しました。

以前の仕様は「テンプレートエンジンを使ってJSONレンダリングし」、それを「HTMLに直書きされたJavaScriptが処理する」という悲惨な代物でした。
ホームページ作りの教科書にある「やってはいけないこと」を全てやってる感じですね。
心を入れ替え反省しましたので、ちゃんとajaxでデータをやり取りする仕様に書き換えてます。
ついでにDPC入院料のグラフも付けてみました。
今回の書き直しで、大分拡張が簡単になったので、色んな可視化を試してみようと思います。

DPCチェッカー0.11 認知症ケア加算のチェック機能を追加

例によってこちらです。

今回は単なるクエリの追加だけですが、SQLiteの仕様について面白いことも分かったので、いずれ別記事で書きます。
また、今後の方針についてですが・・・

今後の方針

  • やっぱりexeで走るアプリケーションにしたい
  • 中身のコードがひどいのでリファクタリングしたい
  • もうちょいマシな統計機能をつけたい

などとなっております。
exeについては、Pyinstaller開発版を使えば可能だと分かりました。
ええ、開発版です。公式の最新版、3.2.1だとバグってビルド出来ないことは確認してます。

  1. このままビルドして配布しても、この後継続的に更新出来るか分からない
  2. 成功したビルドが400MB近くある

などの問題があり、もんにょりしてます。
これはPyinstallerの問題というよりは、Pandas/numpyとPyinstallerの相性問題でして、うーん、ちょっと先行きが分からないですね。
(ちなみに書いておくと、Pyinstallerは標準ライブラリで組まれたコードをアプリ化する限り、殆ど問題を起こしません)

「改訂新版JavaScript本格入門」を読みました

今書いているアプリケーションでは、ブラウザを使ったデータの可視化をやってます。
残念ながらその部分はJavaScriptに頼らざるを得ず、コピペと当てずっぽうではコードが動かないので、本を買いました。 「改訂新版JavaScript本格入門」は、痒いところに手の届く参考書です。
実は「JavaScript : The Good Parts」も読んだんですが、流石にもう2017年です。今なら「改訂新版JavaScript本格入門」がオススメですね。

名著「The Good Parts」よりこちらを勧めるのはなんで?

JavaScriptの言語仕様が変わりすぎだからです。
とりわけECMAScript2015での追加は凄まじく、「本格入門」でも章の半分が「今までの話」でもう半分が「これからの話」に分かれてるとかザラでした。Python界隈が未だに 2.x系と3.x系で議論してるのが、微笑ましくなるような強烈さです。
Wikipediaで追加事項を見てみましょう。

クラス、モジュール、イテレータ、for/ofループ、Pythonスタイルのジェネレータ、アロー関数、2進数および8進数の整数リテラル、Map、Set、WeekMap、WeekSet、プロキシ、テンプレート文字列、let、const、型付き配列、デフォルト引数、Symbol、Promise、分割代入、可変長引数

さらっと「クラス、モジュール、イテレータ」とか出てきてますね。
今までどうしてたんでしょうか・・・と思ったら、プロトタイプチェーンなるものが使われていたそうです。
また、アロー関数は冗長なfunction () { ... }記法を簡略化したもので、() => {....}(場合により{}省略可)というモダン何だかScalaっぽい記法が使えます。
constは変更不可能な変数(というか定数)定義で、やっぱりScalaのvalっぽい関数型言語で流行のイミュータブルな奴ですね。
私のような、ECMA2015以降にJavaScriptを触り始めた、完全な門外漢でも驚く変わりようです。
(一応書いておくと、クラスは今までのオブジェクト仕様のシンタックスシュガーだそうで、根本から入れ替えたというわけでもなさそうですが・・・)
正直、以前はJavaScriptに手を出すつもりはありませんでしたが、ブラウザに手を出せば絶対に逃げられない言語です。何かの機会に学ばざる得なくなるので、解説本をざっと読むのはいいと思いました。 (自分が公開しているDPCチェッカーの、あまりに酷いJavaScript部分はあとで書き直します・・・)

行値式を使った条件指定について(Postgres SQL前提)

保険算定というのは奇々怪々です。
あまりにも制度が複雑になりすぎて、「○○を行った患者さんで、××は算定してなくて、でも△△を算定してる人が知りたい」みたいな問題が割と出てきます。
一つ一つの条件は単純ですが、重なってくると面倒ですね。
さて、入院データを元に抽出をかけるとすると、「一人」ではなく「一入院」を単位とするのが一般的です。
つまり、先ほどのめんどくさいパターンマッチは、「その人」ではなく「その入院」に対してかけないといけません。
具体的に言えば、データ識別番号だけでは駄目で、入院年月日も使って探す必要があります。
(DPCデータの分析では、まずデータ識別番号と入院年月日を組み合わせてキーを作るのが基本です) では実際にやってみましょう。こんな感じでしょうか。

select *
from ef1612
Where 診療明細名称 LIKE '%探しているもの%'
AND (データ識別番号,入院年月日) NOT IN (select distinct データ識別番号,入院年月日
from efile
Where 診療明細名称 LIKE '%算定してない条件%')
AND (データ識別番号,入院年月日) IN (select distinct データ識別番号,入院年月日
from efile 
Where 診療明細名称 LIKE '%算定してる条件%')

--以下延々と続く

さて、上記のSQLは一体何をしているのでしょう。

AND (データ識別番号,入院年月日) IN (select distinct データ識別番号,入院年月日
from efile
Where 診療明細名称 LIKE '%なんとか%')

SQLでは、INを使って一定の条件に当てはまるデータを抽出出来ます。イメージとしては

123 in [123,456,789]

のような処理を考えてみましょう。上の例では123は右側のリストの先頭にあります。 同じように上記の例では、データ識別番号,入院年月日ペアが存在するかどうかを探しているのです。
INの横で唐突にSELECT文が走っていますが、よく見ると'データ識別番号,入院年月日'のペアを引っ張るようになっています。
逆に特定条件を満たしていないものを探したければ、NOT INを使えばいいだけです。 この例で分かるとおり、ブロックを並び続けることで、いくらでも条件を追加出来ます。 ただし、上記の()を使ってペア(もしくはそれ以上の複数条件)を使う記法は行値式といって、Postgresなど一部SQLにしか実装されていません。 (代表的なところでは、MicrosoftSQL Serverでは未対応です)
その場合はEXISTSなどを使って書き換える必要があります。 非常に便利で、自分は実務で連発してますが、案外使わないもんなんでしょうか・・・?

DPC疾患の月別推移を可視化する(修正版)

前回はタイトル通り、DPC疾患の月別推移を可視化する関数を書きました。
あれは実行するとデータを作り、同時にグラフを描画するというひどい代物で、流石に反省しましたのでクラスに書き換えてみました。
何気にこのブログでクラスを書くのは初めてですね。
今までいかにサボっていたかよく分かります・・・

class diff_dpc:
        
    '''前月との疾患別比較を出す。funcはmean,sum,count辺りがいいが、
    groupbyで使えるなら何でもよい
    kaはオプションだが、あった方がグラフが見やすい'''
    
    def __init__(self,df,ka=None,func='sum'):
    
        self.df = df
        self.ka = ka
        self.func = func
        if self.ka:
            self.df = self.df[self.df['診療科区分'] == self.ka]
        else:
            pass

        self.df['MDC6'] = [x[0:6] for x in self.df['分類番号']]
        self.result = self.df.groupby(['month','MDC6']).agg(self.func)
        self.result.reset_index(inplace=True)
    
    
    def plot(self):
        ax = sns.barplot(x='MDC6',y='dpc総点数',hue='month',data=self.result)
        for item in ax.get_xticklabels():
            item.set_rotation(90)
        plt.show()
    
    def getdata(self):
        return self.result

使い方は test = diff_dpc(df,ka=110,func='count') のようにしてやります。すると二つのメソッドを持ったオブジェクトが出来ますので、 test.plot()でグラフを描画、'test.getdata()'でデータの取り出しが出来ます。
(別にインスタンス変数を指定しても同じ事なので、'test.result'としても問題ありません)

DPC疾患の月別推移を可視化する

今回はDPC入院料の分析のお話です。
DPC入院料の推移を見ると、時折奇妙な動きをすることがあります。特定の科の数字が跳ね上がったり、下がったりです。
もちろん中身をチェックすることになりますが、毎回集計して目で見るのは面倒ですね。自動化しましょう。 まず、元のデータとしてこんなものを用意します。

data = psql.read_sql("select to_char(実施年月日,'YYYYMM') AS month,データ識別番号,実施年月日,診療科区分,分類番号, \
sum(行為点数*行為回数) AS DPC総点数 \
from dtable \
Where データ区分 = 93 \
group by month,データ識別番号,実施年月日,診療科区分,分類番号",connection)

これで、診療月/診療科区分/分類番号を含むデータが得られました。 さて、次に出すのは分類別の集計です。DPC分類を使うとデータが細かくなりすぎますので、MDC6を使うことにします。
(分類番号の頭6桁を切り出すだけです)
点数合計の推移が見たいときもあれば、延べ件数が見たいとき、平均が見たいときなどもあるでしょう。 グラフに出来るといいですね。
深く考えずに全部突っ込んでみます。

def diff_dpc(df,ka=None,func='sum'):
    '''月別・MDC6別に集計したDataFrameを返す。funcはmean,sum,count辺りが良いが、
    groupbyで使えるなら何でもいい。
  同時に比較結果をグラフにして表示する。
    kaはオプションだが、無いと項目数が多すぎてグラフが見づらい'''
    if ka:
        df = df[df['診療科区分'] == ka]
    else:
        pass
    
    df['MDC6'] = [x[0:6] for x in df['分類番号']]
    
    result = df.groupby(['month','MDC6']).agg(func)
    result.reset_index(inplace=True)

    ax = sns.barplot(x='MDC6',y='dpc総点数',hue='month',data=result)
    for item in ax.get_xticklabels():
        item.set_rotation(90)
    plt.show()
    
    return result

実際には diff_dpc(data,ka=110,func='count') のようにして使います。 グラフの描画は完全に副作用ですし、引数で指定するかclassに書き換えて独立メソッドにした方がいい気もしますね…

認知症ケア加算の算定ミスをチェックする

明けましておめでとうございます。
つい数日前のエントリで、今年はSQL紹介だけの単調なエントリはやらないぞ!と書いておいてアレですが、算定ミスチェックのSQLでございます。
認知症ケア加算は、「入院後14日以前か、15日以降か」で算定する点数が変わります。
残念ながら、手入力をしていると、わりかしポコポコ算定ミスが出てきます。そこで、簡単なチェックを作ってみました。

SELECT データ識別番号,入院年月日,実施年月日,診療明細名称
FROM EFILE
WHERE 診療明細名称 LIKE '%認知症ケア%14日%'
AND 行為明細番号 = 0
AND 実施年月日 - 入院年月日 + 1 > 14
ORDER BY データ識別番号

認知症ケア加算には1と2がありますが、どちらを算定する病院でも使えます。
ここでは、入院15日目以降にも14日以内のコードを算定しているものを拾っています。
新年早々こんなんで申し訳ありませんが、今年もよろしくお願いいたします。