2016

いつのまにか2016年になっていた。
このブログも1年半も更新していなかったらしい。
3~4カ月ほど前からmysqlが落ちていてブログそのものが閲覧不能になっていたのに気づいて修復。
せめて読んだ本の感想くらいは書いていきたいとは思う。

とりあえず最近読んだ本のメモ:

  • 白と黒のとびら・オートマトンと形式言語をめぐる冒険
  • 理論から学ぶデータベース実践入門
  • 正規表現技術入門
  • 検索の新地平

「白と黒のとびら」はかなりキてる本で個人的には2015年の本の中では最も衝撃的だった。
一見ライトノベル風の世界のように見えるが(実際に舞台はそうなのだが)オートマトンをダンジョンに見立てて、扉(記号)の通りに部屋(状態)を進んでいき出口(受理状態)にたどり着けるかどうか、というアナロジーで物語が進んでいく。
詳しくはまた今度書く、かもしれない。

巨大なテーブルから SELECT する際のメモ

python経由で MySQL, PostgreSQL のメモリが爆発するレベルの巨大なテーブルからデータを取得する際のメモ(一千万件以上のレコードがあるテーブルから select するようなケースを想定)

現象

fetchall() を使わずに fetchone() / fetchmany() を使っているのに out of memory 的なエラーでプロセスが落ちる。
この現象は python の MySQLdb ライブラリ、および psycopg2 ライブラリで確認した。なお MySQL の場合、import MySQLdb ではなく、公式の import mysql.connector の場合はこの現象は生じない(のでこちらを使うべき?)。

原因

デフォルトのカーソルがクライアントサイドカーソルになっている。なので cursor.execute(sql) した時点ですべてのデータをクライアントに持ってくるので out of memory が発生する(このケースでは fetchone, fetchmany は「一つ(複数)ずつデータを取得する」という挙動をエミュレートしているに過ぎない)。

対策

サーバサイドカーソルを使う。やり方は以下のとおり。

MySQLdb

まずは失敗するパターン。

import MySQLdb
connection=MySQLdb.connect(
    host="host",user="user",
    passwd="passwd",db="db")
cursor=connection.cursor()
cursor.execute("select * from very_huge_table")  # 失敗!
row = cursor.fetchone()
while row is not None:
    print row
    row = cursor.fetchone()

SSCursor を使えばうまくいく。

import MySQLdb
import MySQLdb.cursors   # 追加!
connection=MySQLdb.connect(
    host="host",user="user",
    passwd="passwd",db="db",
    cursorclass = MySQLdb.cursors.SSCursor) # 追加!
cursor=connection.cursor()
cursor.execute("select * from very_huge_table")
row = cursor.fetchone()
while row is not None:
    print row
    row = cursor.fetchone()

psycopg2

psycopg2 の場合はカーソルオブジェクトを生成する際に name を指定するとサーバサイドカーソルになる、という記述があった。

参考URL : http://initd.org/psycopg/docs/connection.html#connection.cursor

If name is specified, the returned cursor will be a server side cursor (also known as named cursor). Otherwise it will be a regular client side cursor.

以下のコードは execute の時点でクライアントサイドにすべてのデータを引っ張ってこようとして失敗する。

import psycopg2
connection = psycopg2.connect("dbname=db host=host user=user")
cursor=connection.cursor()
cursor.execute("select * from very_huge_table")  # 失敗!
row = cursor.fetchone()
while row is not None:
    print row
    row = cursor.fetchone()

named cursor を使えばOK。

import psycopg2
connection = psycopg2.connect("dbname=db host=host user=user")
cursor=connection.cursor(name="thecursor") # 違いはここだけ。名前は PostgreSQL の非予約語ならなんでも
cursor.execute("select * from very_huge_table")
row = cursor.fetchone()
while row is not None:
    print row
    row = cursor.fetchone()

image.plot の目盛を axis 関数で描く

Rのfieldsパッケージのimage.plot関数で目盛に数値ではなくテキストを書き込みたい。axis関数を使っても何故か反映されないというバグ?がある(fieldsのバージョンは6.9.1)。例えば以下の例:

library(fields)

x <- matrix(rnorm(25),nc=5)

colnames <- paste0("col",1:5)
rownames <- paste0("raw",1:5)

# Fail
image.plot(1:5, 1:5, x, xaxt="n", yaxt="n", xlab="X-axis", ylab="Y-axis")
axis(side=2,at=1:5,labels=rownames)
axis(side=1,at=1:5,labels=colnames)

image_plot_fail

確かに最後の二行の axis 関数の結果が反映されていない(x軸, y軸の目盛に文字が書けていない)。

r- how to edit elements on x axis in image.plot
この問題はここでも議論されていて、普通のimageを使って、凡例だけimage.plotを使え、とある。

もう少し簡単に解決する方法を見つけた。

# Success
image.plot(1:5, 1:5, x, xaxt="n", yaxt="n", xlab="X-axis", ylab="Y-axis")
text(-100,-100,".")  # This is dummy, but required for drawing axis
axis(side=2,at=1:5,labels=rownames)
axis(side=1,at=1:5,labels=colnames)

image.plot と axis の間にダミーでtext関数を実行する(三行目)。以下のとおり不思議なことにこれで解決した(理由は未調査)。

image_plot_success

重点サンプリング (2)

前回は正規分布の裾の積分をなんとなく決めた提案分布による重点サンプリングで求めた。今回は提案分布の違いがどのような誤差の違いを生むのかについて実験した。ただし、今回の積分範囲は [4,∞) とした。ようするに \(f\) を標準正規分布のpdfとして

\begin{align}
\int_4^\infty f(x)dx
\end{align}

を求める問題。
“重点サンプリング (2)”の続きを読む

モデル選択の実験 (BIC を追加)

前回の記事 では AIC と AICc を比較した。今回はそれに BIC を追加してみた。BICはあまり使ったことがなかったが、個人的には結構おどろきの結果が得られた。

BIC は以下で定義される。n はデータ数、k はモデルのパラメータ数。

\begin{align}
BIC=-2\times\{\text{Maximum Likelihood}\}+(\log n)\times k
\end{align}

実際のデータ分析では当然、n は固定なので AIC とのちがいは k の前の係数が 2 という定数か、 \(\log n\) という定数か、の違いがあるが、これって同じようなもんでしょ、と思って BIC はわりとノーマークだったけど、今回実験してみて考えを改めることなった。
“モデル選択の実験 (BIC を追加)”の続きを読む

モデル選択の実験 (AIC vs AICc / R の AICcmodavg パッケージ)

前回の「モデル選択の実験 (AIC vs LOOCV)」の続きです。

小標本の場合は、AIC じゃなくて AICc を使うといいよとのことなので、今回は、前回同様の方法で AIC と AICc を比較してみた。

真のモデルやその他のモデルの設定などは前回と全く同様。図の見方も前回と同様です。LOOCV の結果も並べてみたかったが計算量の関係で断念。

いろんな意味で手抜き気味です。あしからず。

AICc

「正規ノイズの線形モデル」のケースでは以下で定義される情報量基準。

\begin{align}
AICc = AIC + \frac{2k(k+1)}{n-k-1}
\end{align}
k : パラメータ数、n : データ数。

GLM のケースではこの定式化は使えないらしい[1]。 そんなこんなで GLM の場合は R の AICcmodavg パッケージをつかう。

glm.fit <- glm(...)
AICc(glm.fit)

とすることで AICc の値を良きに計らって計算してくれる。
“モデル選択の実験 (AIC vs AICc / R の AICcmodavg パッケージ)”の続きを読む

  1. [1] でも実は間違えてこの定式化でやってみたけどそれなりに良い結果 (AIC よりもよい結果) が得られた。

モデル選択の実験 (AIC vs LOOCV)

最近読んだ「データ解析のための統計モデリング入門――一般化線形モデル・階層ベイズモデル・MCMC (→ amazon)」に感化されて、モデル選択基準である、AIC、LOOCV (Leave One-Out Cross Validation) について実験してみた(参考:モデル選択の周辺の話の整理)。

LOOCV と AIC はある意味で両極端だ。LOOCV は利用するのに必要な仮定が少なく、直感的にも何をやっているのかわかりやすい(ただし計算量は非常に大きい)。一方で AIC は評価式の簡単さ(計算量の少なさ)とは裏腹に、背後には正則条件だとかネストだとかの直感的とは言いがたい仮定をしたりする。

まあ AIC とか LOOCV とかが常に正しいモデルを選択するわけじゃない、というのは直感的にはわかるけど、実際に試してみたことはなかったので、真のモデルを知っているという神の視点からみて、こいつらがどんな挙動を示すのかをシミュレーションで確かめてみた。

“モデル選択の実験 (AIC vs LOOCV)”の続きを読む

Ruby CGI で Insecure operation error

久しぶりの ruby ネタ。以下のような ruby CGI で 500 internal error が出た。

#!/usr/bin/ruby

require "cgi"
cgi = CGI.new
params = cgi.params

open(params["filename"],"w") { |f| f.puts "hoge" }
cgi.out { "hoge" }

apache のログを見てみるとファイルを作成する部分 (open) で Insecure operation というエラーが出ている。

いろいろ調べてみると、どうやら ruby CGI で URL のパラメータを使ったパス名でファイルを書き込もうとしたことが原因のようだ。パス名に空白とか入っていると悪さができそう、ということらしい。ごもっともです。
“Ruby CGI で Insecure operation error”の続きを読む

[R] データフレームを可視化するtabplotパッケージについて

データの列数が多くなってくる (高次元になってくる) とデータの全体像が捉えにくくなる.R-bloggers を読んでたら,Rのデータフレームを可視化するためのパッケージ tabplot なんてのがあるらしい,ということで試してみた.コレを使うとデータを効率的に可視化できるので,よくわからないデータをとりあえず可視化してみて,それからあれこれ考えてみると捗るかもしれない.

CRAN から tabplot パッケージをインストールすればすぐに使える (類似の名前で tableplot なんてのがあるので注意).
“[R] データフレームを可視化するtabplotパッケージについて”の続きを読む