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

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

様式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へ型キャストして、それから置換しているからです。
人間は忘れる生き物ですね・・・

SQLiteと日付型データについて

SQLiteは素晴らしいツールです。
単体exeで走り、かつデータベースは一つのファイルに纏まるので、DBをUSBに入れて、実行環境ごと持ち運ぶことが出来ます。
パブリックドメインなので、好きなように使って大丈夫です。Accessと違って、DBのサイズ制限も無いようなもんです。
ここまで書けば、結論は確かですね。最初のDBはSQLiteを使いましょう!

が、このブログではそうしてません。何故でしょうか。

日付型データの問題

SQLiteは基本的に、整数・浮動小数・テキスト、の三種類のデータを扱えます。
何か足りない気がしますね。
そう、日付型です。
日付型がないのはそんなに問題でしょうか? はい、とんでもない問題になります。 例えば

20161231 - 20161201 = 30ですが、

20170101 - 20161231 = 8870になります。

この結果を期待する人はいませんね。 これは致命的な欠点に思えますし、実際ひどいのですが、回避策は(なんと!)あります。
julianday関数です。 データをユリウス通日に変換することで、日付のように振る舞う数字データを入手できます。
やりましたね!

…いえいえ、ちょっと待ってください。ユリウス通日?一体何のことでしょう?
Wikipediaの記載によれば、

ユリウス通日(ユリウスつうじつ、Julian Day、JD)とは、ユリウス暦[注 1]紀元前4713年1月1日(すなわち西暦-4712年1月1日)の正午(世界時)からの日数である[1]。単にユリウス日(ユリウスび)ともいう

だそうです。つまり、その日が紀元前4713年1月1日から、通算して何日目なのかを、「数字として」返す関数だ、ということです。 そりゃまあ、これを使って変換すれば、あとは数字の引き算ですから、

select julianday("2017-01-01") - julianday("2016-12-31")

は、1.0を返します。でも、全ての日付処理をこうやって変換するのは、あまりいいアイディアではありませんね。
というわけで、個人的には初めてのSQLはPostgres辺りをお勧めしています。
日付型データを扱う必要が無ければ、SQLiteでもいいんですが、DPCデータではむしろそこが主眼になり得るからです。