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

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

様式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は書いていて面白いので、そのうち何か小さなアプリでも公開するかも知れません。

Scalaでcsvをいじってみました

最近Scalaをいじっています。
いえ、嘘をつきました。2ヶ月くらい断続的にいじってました。
遊び半分でいじっていたので、全然進んでいませんが、とても面白いですね。
ScalaJavaと同じ、JVMで動く言語です。そのため、コンパイルした結果はそのまま(Javaが走る環境ならどこでも)走ります。
今時はJavaが入ってないマシンの方が珍しいので、事実上どこでも動くと言って良いでしょう。

試しに書いてみる

先週は、PowerShellを使って「EF/Dファイルの退院年月日'00000000'を置換する」スクリプトを書きました。
Powershellでは驚くほど単純に書けましたが、Scalaだとこんな感じです。

package example

object ModifyEF extends helperfunc with App {

import java.io.FileReader
import collection.JavaConversions._
import com.opencsv.CSVReader
import com.opencsv.CSVWriter
import java.io.FileWriter
import scala.collection.JavaConverters._


val reader = new CSVReader(new FileReader("edata.txt"),'\t')
val test = reader.readAll.toList

val changed = test.transpose.map(matchlist(_))
val writing = changed.transpose.map(_.toArray)

val writer = new CSVWriter(new FileWriter("out.txt"), '\t')
writer.writeAll(writing.asJava)
writer.flush


}

trait helperfunc {
def changenull(list:List[String]):List[String] = {
    list.map({case "00000000" => "infinity"; case x => x})
}

def matchlist(list:List[String]):Array[String] = {list.head match {
    case "退院年月日" => changenull(list).toArray
    case _ => list.toArray
}}

}

うーん、ぱっと見恐ろしく見えますね。
csvの読み込み操作をワンライナーで済ましてくれる、Pandasのpd.read_csv()とか、PowerShellImport-Csvなんかはありません。
JavaのFileReaderを利用してファイルを読み込み、それをCSVReaderでパースする、という処理を明示する必要があります。
ですが、ここで使われている外部ライブラリはCSVのパースに使うopencsvだけです。
他は全てScala / Javaの標準ライブラリです。
速度はPandasのコードとそう大差ありませんし、PowerShellの例よりはずっと高速です。
しかもこれはScalaJavaも全然わかってない人間の書いた、最初のコードですから、きっと高速化する余地がありますね!
(注:つまり、このコードはとんでもない基本的な大間違いをしている可能性が存分にあり、かつ、ひどいメモリ使用・誤った考え方を含む可能性が高いです)
配布するのに苦労が無いので、ちょっとしたツールはScalaで書いてjarファイルで公開するかも知れません。
そこまでたどり着けるかどうか不明ですが・・・

EFファイル/DファイルをPowershellで扱う

なんでいきなりPowershellなの?

えーと、ご存じない方のために説明すると、Powershellコマンドプロンプトの強化版みたいな奴です。
将来的にはコマンドプロンプトを置き換える方向に進むみたいですね。

さて、なんでPowershellかと言うと・・・

Import-Csvコマンドが便利

Import-Csvコマンドは、かなり大きなサイズのCSVでも読み込めて、しかも何の苦労も要りません。 例えば、

$x = Import-Csv efile.txt -Delimiter "`t" -Encoding Default

と打てば、同じディレクトリにあるefile.txtをタブ区切りテキスト形式で読み込んで、変数$xに格納できます。
読み込んだデータはテキストでは無くオブジェクトになります。
ヘッダがある場合、ヘッダに対して各列が紐付く形になります。
つまりどういうことかと言えば、 $x.データ識別番号などと打てば、データ識別番号の列だけを抽出できるんですね。
これは超便利です。PandasのDataFrameの簡易版みたいに使えます。
この関数がC#にあれば普通にC#使ってたかも…

メソッドが便利

上記の通り、データがオブジェクトになるため、メソッドが扱えます。

$x.退院年月日.replace("00000000","infinity")

上の例では、$xの属性退院年月日replaceメソッドをかけて、文字列00000000をinfinityに置換しています。
ちなみに目的はPostgres読み込み前の前処理ですね。
Postgresで00000000を日付データとして読み込もうとするとエラーになるので・・・

データの上書き(2017-03-02追記)

データの上書きを行うには、一度foreachループを回す必要があります。 例えば

$x | foreach {
$_.退院年月日 = $_.退院年月日.replace("00000000","infinity")
}

という感じです。
実際にEF/Dファイルの退院年月日を修正するスクリプトGitHubに置きましたので、参考までにどうぞ。

普段Pythonを触っていて、オブジェクトという概念に馴染みがあれば、Powershellは使いやすいシェルだと思います。
時間があれば触ってみるといいかも知れません。

PostgreSQL用のクエリをまとめてます(進行形)

このブログではずっとPostgreSQL用にクエリを書き連ねて来ましたが、そろそろいい数になりましたのでGitHubに上げることにしました。
こちらです。 まだ数は多くありませんが、これから順次増やしていく予定です。
また、よくよく考えるとPostgresの環境構築とか、その辺の話を一度も書いていませんでしたので、これから少しずつ書いていく・・・はず・・・

EFファイル・DファイルをPostgreSQLに読み込む際の注意点

「退院年月日」について、当該月に退院していないデータには0を記載する仕様00000000を記載する仕様になっています。
(2017-02-22修正。何故か0だと思い込んでましたが、正しくは00000000です)

他のSQLではどうか知りませんが、Postgresでは日付型データに00000000があれば当然エラーになります。読み込めません。
なので、読み込み前に000000000をPostgresで読める形に変換しておく必要があります。 自分はinfinityで置換していますが、この辺は好みですね。
一度Excelで開いて置換してもいいですが、面倒なので変換用のPythonスクリプトを書きました。
先ほどのリポジトリに入ってますので、併せて利用してください。


2017-02-22追記。最初のポストで00000000が正解なところを、0と勘違いしてました。
が、Pythonスクリプトの方は、書いた当時の私がちゃんとしてたので、正しく動作します。
具体的には退院年月日カラムを整数値として読み込むことで、文字列00000000から整数値0へ型キャストして、それから置換しているからです。
人間は忘れる生き物ですね・・・