Scalaでcsvをいじってみました
最近Scalaをいじっています。
いえ、嘘をつきました。2ヶ月くらい断続的にいじってました。
遊び半分でいじっていたので、全然進んでいませんが、とても面白いですね。
ScalaはJavaと同じ、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()
とか、PowerShellのImport-Csv
なんかはありません。
JavaのFileReaderを利用してファイルを読み込み、それをCSVReaderでパースする、という処理を明示する必要があります。
ですが、ここで使われている外部ライブラリはCSVのパースに使うopencsv
だけです。
他は全てScala / Javaの標準ライブラリです。
速度はPandasのコードとそう大差ありませんし、PowerShellの例よりはずっと高速です。
しかもこれはScalaもJavaも全然わかってない人間の書いた、最初のコードですから、きっと高速化する余地がありますね!
(注:つまり、このコードはとんでもない基本的な大間違いをしている可能性が存分にあり、かつ、ひどいメモリ使用・誤った考え方を含む可能性が高いです)
配布するのに苦労が無いので、ちょっとしたツールは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データではむしろそこが主眼になり得るからです。
DPCチェッカー 0.12 ダッシュボード機能を書き直し
例によってこちらです。
二日ぶりのアップデートですね!(白目
えー、以前から予告してましたがダッシュボード機能を書き直しました。
以前の仕様は「テンプレートエンジンを使ってJSONをレンダリングし」、それを「HTMLに直書きされたJavaScriptが処理する」という悲惨な代物でした。
ホームページ作りの教科書にある「やってはいけないこと」を全てやってる感じですね。
心を入れ替え反省しましたので、ちゃんとajaxでデータをやり取りする仕様に書き換えてます。
ついでにDPC入院料のグラフも付けてみました。
今回の書き直しで、大分拡張が簡単になったので、色んな可視化を試してみようと思います。
DPCチェッカー0.11 認知症ケア加算のチェック機能を追加
例によってこちらです。
今回は単なるクエリの追加だけですが、SQLiteの仕様について面白いことも分かったので、いずれ別記事で書きます。
また、今後の方針についてですが・・・
今後の方針
- やっぱりexeで走るアプリケーションにしたい
- 中身のコードがひどいのでリファクタリングしたい
- もうちょいマシな統計機能をつけたい
などとなっております。
exeについては、Pyinstallerの開発版を使えば可能だと分かりました。
ええ、開発版です。公式の最新版、3.2.1だとバグってビルド出来ないことは確認してます。
- このままビルドして配布しても、この後継続的に更新出来るか分からない
- 成功したビルドが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部分はあとで書き直します・・・)