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

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

2017年振り返りと、2018年の展望

はい、今年の後半は本当にブログ書いてませんでした!ごめんなさい!
暇ができたら、レセ電ファイルを読み取るパーサーでも書こうと思ってましたが……うーん、全然時間が取れませんでしたね。
で、何をやってたかと言うと…

本ばっかり読んでいた

私は読書が趣味です。出身が人文学部なので、とにかく沢山読みます。
元々は西洋哲学や、ヨーロッパ史を中心に読み漁ってましたが、この数年はプログラムの本を読むのが趣味になりました。
ええ、趣味です。結局肝心のコードを書けてないので、実益は(殆ど)ありません。
ということで、以下に最近買った本をつらつらと。

Fluent Python ―Pythonicな思考とコーディング手法

Fluent Python ―Pythonicな思考とコーディング手法

Pythonによるテキストマイニング入門

Pythonによるテキストマイニング入門

基礎からわかる Scala

基礎からわかる Scala

Javaの絵本 第3版 Javaが好きになる新しい9つの扉

Javaの絵本 第3版 Javaが好きになる新しい9つの扉

経済・ファイナンスデータの計量時系列分析 (統計ライブラリー)

経済・ファイナンスデータの計量時系列分析 (統計ライブラリー)

いや、これは無理でしょう

今年の教訓は「自分の頭に入るだけの本を買いなさい」ですね!
どれもすごくいい本で、頭の中が整理されますが(特にFluent Pythonは素晴らしい)、いかんせん数が多すぎます。 途中で止まってる本も多いし……

2018年はどうするか

2016年の終わりには、「Webが全てにやって来るぞ!」と書きました。
マイクロソフトSkypeをElectronで書き直したと聞いた時は、椅子から転げ落ちそうになりましたね。何だかんだ言いつつ、Node.jsは避け得ないものになってますし、Reactは凄い存在感で……ん?

はい。2017年はこのブログが何度も「DPCデータ分析」から脱線しまくった1年でした(白目

2018年はそろそろデータ分析に戻ります……うん、戻る、はず……
ただ、流石にネタ切れなので、ブログのタイトルは変えます。DPCデータ分析=>医療データ分析、くらいに風呂敷を広げるか、もうちょい広い(そしてテキトーな)タイトルにするかも知れません。
一応予定しているリストをば。

  • 時系列分析。本まで買ってますし、記事は書きます。ただ、理解するのにすごい苦労してますが…

  • Pythonをシェル代わりに使おう話。オライリーPython自動化本で、os.walkの使い方を読んだ時は目からウロコでした。

  • 事務仕事はPythonで片付けましょう講座。

結局、機械学習でDPCデータ分析をしよう!という抱負は生煮えで終わってしまったので、2018年こそは何か形にしたいですね。
何だかんだ、グダグダな2017年でありましたが、2018年はブログの名前も変えますし、色々方向を変えようと思っております。
それでは皆様、よいお年を。

ICD10を正規表現でパースする

またもやお久しぶりとなってしまいました… さて、今回のお題はICD10のパースです。以前、「I20$のような、I200~209を指定するケースはどうしよう?」という問題を記事にしました。その時は、ゴリ押しでI200~I209までを連番生成して済ませてます。
が、今回(実際にあった話ですが)、「I20$もしくは、I50$もしくは、I1$のどれかに当てはまる疾患を探してね!」という案件が参りました。うーん、どうしたもんでしょうか。

正規表現を使わざるを得ないとき

問題に直面したとき、ある人々はこう考える……「そうか、正規表現を使うんだ!」そして問題が2つに増える

インターネットには賢者の箴言がありますが、そうも言っていられません。
上記のような問題に出くわしたとき、正規表現ではI20[0-9]|I50[0-9]|I1[0-9]のように表記します。 |はor条件を意味しており、[0-9]は0から9の数字に当てはまるもの、という意味です。 上記ですと、正に「I20$もしくは、I50$もしくは、I1$のどれか」という分けですね。 まあ、実際にはI500だけではなく、I5000にも当てはまっちゃうので、本当にそれでいいのかと言われるとアレですが…(この辺は、ICDのコード体系におんぶに抱っこしてます)

例としてPythonで書くとこんな感じですね。

import re
pattern = r"I20[0-9]|I50[0-9]|I1[0-9]"
re.findall(pattern,"I209,I11,I300")

out: ['I209,'I11']

実務でICD10の抽出をするときは、患者単位であることが殆どです。
その場合、患者別にICDのリストを作るのか(例:['I209','I11','I300'])、複数のICDを連結して文字列にしてしまうか(例:"I209,I11,I300")は、ケースバイケースでしょう。
私が関わった案件では単純に連結してしまいましたが、リストのほうが扱いやすいケースもある筈です。

pandasで移動平均を出してみる

お久しぶりです。
ブログはサボってましたが、仕事はサボれず、色々やっていました。
現在、時系列データに取り組んでいますが、全く経験がないので四苦八苦してます。
単に推移を見るだけならプロットすればオシマイですが、突発的な変動は無視して、傾向を見たいとなると、移動平均というものを扱う必要が出てきます。

エクセルでどうやるのかは知りませんが、pandasなら簡単!

ということで、以下の通りすぐに出来ました。
移動平均は一定期間(例えば3ヶ月毎)の平均を、ずらしながら取っていくという数字です。 統計WEBに記事があります
wikipediaにも記事がありますが……
うーん、これ読むよりはいきなりグラフ描いたほうが分かりやすいかも…
なお、wikipediaで紹介されている指数加重移動平均もpandasに実装されています。
数式の理解はともかく、実際にはどっちも使ってみればいいんじゃないでしょうか。使うのは簡単だし。

Pandas rolling_mean & emma example

scikit-learn 勉強ノート(2)

前回から大分間が空きましたが、scikit-learnの勉強メモです。
おさらいすると、サンプルとしてIrisデータセットを使い、アヤメの品種を分類していたのでした。
データセットは、がく片や花びらの長さが記録されたXと正解ラベルが記載されたyに分かれています。
これを分割して、

  • X_train, y_train

  • X_test, y_test

の2ペアにし、trainデータで学習して、testデータで正解率を見る、というのが流れです。

前回はRandom Forestモデルを使いましたが、今回はsvmモデルを使用することにします。

from sklearn.svm import LinearSVC
svc = LinearSVC()

さて早速学習しましょう…と言いたいところですが、ここで注意点。
Random Forestは特例で、データが数値データであろうがカテゴリデータであろうが、また桁違いのデータが混在していようが、一切構わず、そのまま投げ込んでも結果が出てしまいます。
機械学習のモデルの中では例外中の例外と言ってよく、他のモデルは

  • そもそもカテゴリデータは(そのままでは)扱えない

  • データのスケールが違う場合は、同じスケールになるよう変換する必要がある

ことが殆どです。
今回使うSVMも、これに該当します。手作業で変換するのは御免なので、ライブラリを使います。
SVMではMinMaxScalerを使うそうなので、早速インポートしましょう。

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(X_train)

学習のときと同じで、.fitメソッドを訓練データに適用します。
後の流れもよく似ています。

X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

これで、同サイズに変換されたデータが手に入りました。 あとは

svc.fit(X_train_scaled,y_train)
svc.score(X_test_scaled,y_test)

とするだけです。scikit-learnは異なるモデルでも同じようなインターフェースを持っているので、一度基本を学ぶと色んなモデルを試してみることが出来ます。

例えばロジスティック回帰なら、

from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression()
logreg.fit(X_train_scaled, y_train)

logreg.score(X_test_scaled, y_test)

と、殆ど同じコードで比較が出来ます。
実際にはもちろん、モデル毎に意味合いの異なるパラメータをいじる必要がありますが、モデルの比較が簡単に出来ていいですね。

[読書] 「達人に学ぶDB設計」「The Hitchhiker's Guide to Python」

特にネタもないので、最近読んだ本の話でも。

達人に学ぶDB設計

達人に学ぶDB設計 徹底指南書

達人に学ぶDB設計 徹底指南書

SQL解説本でご存じ、ミック先生のDB設計本です。
ひとまずSQL文を書けるようになる、くらいになった後で、「さてDBはどう設計したらいんだろ?」と思うことは、割とあります。
「そういえば正規化ってどうすればいいんだろ?」とか。
この本には、今まで読んだ中で一番わかりやすい正規化の説明があります。
そもそもデータをどのように保存すべきか、という基本中の基本から話が始まるので、SQLを始めるときにもいい本でしょう。

  • コンピュータが扱いやすい表とは何か

というのは、非常に重要な問題ですが、特に義務教育で教えることもなく、実務で学ぶしかありません。
こういう本で学べるなら、それに越したことはないでしょう。

The Hitchhiker’s Guide to Python

The Hitchhiker's Guide to Python: Best Practices for Development

The Hitchhiker's Guide to Python: Best Practices for Development

突然の洋書。
実際のところ、通読する気は更々無くて、Chapter 5 “Reading Great Code"のために買いました。
勉強がてらhowdoiのコードを読んだりしてたんですが、やっぱり解説書欲しいなーと思ったので・・・
本書のChapter5 では、howdoi, Diamond, Tablib, Requests, Werkzeug, Flaskのコードが解説されてます。
ちなみに、Chapter5以外の部分は、翻訳が無償公開されています。
「洋書なんて読んでられっか!」という向きは、こちらをどうぞ。

ICD→MDC6の変換表を作成する

色々あって、ICD10→DPCの頭6桁の変換を行う必要に迫られました。 とはいえ、電子点数表がありますから、楽勝でしょう。
そう思ってデータを見ると…ん…あれ…?

I20$ → 050050

とかありますね。 困りました。実データは、I209とかなのです。これを050050に変換するには、どうすればいいのでしょうか?

ICD表を拡張する

しょうがないので表を拡張することにします。 要は I200 I201 I202 ~以下省略

と、0~9まで増やしてやればよいのです。
ということで、以下のように作業してみました。
もっとスマートな方法がありそうなもんですが、とりあえずはこれで凌ぐことにします。

ICD10 => MDC6変換マスタ作成

EF/Dファイルのテーブル定義について

今更ですが、EFファイル/DファイルをDBに読み込む際のテーブル定義について、少し見直しをしていました。

今まで、「主キーとすべき項目が見当たらない」という理由で、id列を連番で振っていましたが、冷静になってみると複合主キー使えばいいんですね…

Postgresのドキュメントを見てみましょう。

5.3. 制約

主キーも複数の列に渡ることがあり、その構文は一意性制約に似ています。
CREATE TABLE example ( a integer, b integer, c integer, PRIMARY KEY (a, c) );

と、はっきり例が載ってます。
ということで、自動付番のid列を作るのを止めたのが以下の例です。

  • EFファイル
CREATE TABLE ETABLE
(施設コード VARCHAR(255),
データ識別番号 INTEGER,
退院年月日 DATE,
入院年月日 DATE,
データ区分 DOUBLE PRECISION, 
順序番号 DOUBLE PRECISION,
行為明細番号 DOUBLE PRECISION,
病院点数マスタコード DOUBLE PRECISION,
レセプト電算コード DOUBLE PRECISION,
解釈番号 VARCHAR(255),
診療明細名称 VARCHAR(255),
使用量 DOUBLE PRECISION,
基準単位 DOUBLE PRECISION,
明細点数・金額 DOUBLE PRECISION,
円点区分 DOUBLE PRECISION,
出来高実績点数 DOUBLE PRECISION,
行為明細区分情報 CHAR(12),
行為点数 DOUBLE PRECISION,
行為薬剤料 DOUBLE PRECISION,
行為材料料 DOUBLE PRECISION,
行為回数 DOUBLE PRECISION,
保険者番号 DOUBLE PRECISION,
レセプト種別コード DOUBLE PRECISION,
実施年月日 DATE,
レセプト科区分 DOUBLE PRECISION,
診療科区分 DOUBLE PRECISION,
医師コード VARCHAR(255),
病棟コード VARCHAR(255),
病棟区分 DOUBLE PRECISION,
入外区分 VARCHAR(8),
施設タイプ VARCHAR(8),
PRIMARY KEY(データ識別番号,データ区分,順序番号,行為明細番号,実施年月日));
  • Dファイル
CREATE TABLE DTABLE
(施設番号 VARCHAR(255),
データ識別番号 INTEGER,
退院年月日 DATE,
入院年月日 DATE,
データ区分 DOUBLE PRECISION, 
順序番号 DOUBLE PRECISION,
点数マスタコード DOUBLE PRECISION,
レセ電算処理コード DOUBLE PRECISION,
解釈番号 VARCHAR(255),
診療行為名称 VARCHAR(255),
行為点数 DOUBLE PRECISION,
行為薬剤料 DOUBLE PRECISION,
行為材料料 DOUBLE PRECISION,
円点区分 DOUBLE PRECISION,
行為回数 DOUBLE PRECISION,
保険者番号 DOUBLE PRECISION,
レセプト種別コード DOUBLE PRECISION,
実施年月日 DATE,
レセプト科区分 DOUBLE PRECISION,
診療科区分 DOUBLE PRECISION,
医師コード VARCHAR(255),
病棟コード VARCHAR(255),
病棟区分 DOUBLE PRECISION,
入外区分 DOUBLE PRECISION,
施設タイプ DOUBLE PRECISION,
算定開始日 VARCHAR(8),
算定終了日 VARCHAR(8),
算定起算日 VARCHAR(8),
分類番号 VARCHAR(255),
医療機関係数 VARCHAR(255),
PRIMARY KEY(データ識別番号,データ区分,順序番号,実施年月日));

ご覧のとおり、

  • EFファイルはデータ識別番号,データ区分,順序番号,行為明細番号,実施年月日

  • Dファイルはータ識別番号,データ区分,順序番号,実施年月日

の組み合わせを主キーとしています。仕様書を読む限り、この組み合わせでデータが一意に定まる…はず…
もし実際に使って問題が起きたら、ご連絡頂けると助かります。
2,3ヶ月分これで登録出来たので、多分大丈夫だとは思うんですが…