
今週のプロジェクトとして、Call centerの電話の履歴をとってきて分析をしている。ただし、今日は大いにはまって、3時間ほど時間をとられてしまった。
それは何かというとCATIシステムは全ての電話の履歴を管理しており、何月何日の何時何分何秒に電話をかけたという履歴がある、また1つの番号に何度も電話をかけるので、データの形としてはcall level dataすなわち、同じ電話番号が当然何度も登場する。このデータをperson level dataにまず変換するのだけどそれが一苦労だった、基本的には同じ番号のレコードの中から時間が一番新しいものだけを残せばよいのである。CATI systemはデータをMicrosoft SQL serverに保存してあり、ODBCというデータベースとwindows applicationの橋渡しをするプログラムの設定をした後、Stataにデータを読み込める。
Stataにおいて時間変数が例えば time と呼ばれていたのであれば
gen date = day(time) とか Year = yeah(time) とかすれば日にちや年が取り出せるのは知っていたのだけれども、どうやって分や秒を取り出せるかは今まで試したことが無かったので分からなかった。Stataのマニュアルのdate & timeの章は非常に分厚くて読む気になれないが仕方ないので読む、でもまったく意味不明である。
時間変数を普通のreal型で取り出すとその整数部分は 17476である、しばらく考えて365でこれを割ってみると47.879である、マニュアルを見てみるとStataの時間は1960年の1月1日を起点にしているとあるので、2007年はちょうど47年後であるからこの変数は単なる「経過日」であることがわかる。この、小数点部分をとってきて 24を掛ければ時間が得られ、その整数部分を捨ててさらに小数部分だけをとって、60を掛ければ分が手に入り、同様に小数部分だけにまた60を掛ければ秒が手に入ることが判明。またこうして得られた秒はSQLサーバーで表示されているものと同じである。
ただし、以上の処理は大変面倒くさいので必ず別のやり方があるはずだと思って調べると、Stataは大まかに分けて6通りの時間の数え方のフォーマットを持っており、経過年は %tdであり、これを秒を単位にした時間や、週や月や四半期に変換も出来ることが判明。それぞれに返還するための関数の変換表も data management manualにある。ここまで理解するのに1時間半ぐらいかかった。このマニュアルは非常に分かりにくいのである。
そうして、計算したもののなぜだか秒がずれてしまう。何度やっても秒がずれる、これはどこかでrounding errorが起きているはずだと思い、SQLからODBCさらにデータを読み込む過程のどこでこれが起きているかなどを一通り見るが分からない。
しばらくして、作った変数を眺めていると、データの型がfloatになっていることに気がついた。Floatでも小数点の38桁まで扱えるはずなので、この程度の誤差なら問題ないはずである。1秒は0.01666分でありそれは、0.0000694444日であり小数点の8桁ぐらいあれば十分なはずであるとPOST ITの裏に計算してみる。それでもと念のためにdoubleで変数を作ってみると解決した。ここまで3時間。Stataは内部では、1960年の1月1日からの経過の1000分の1秒単位で計算しているらしく、double型で変数を作らないと精度が保てないらしい。
tempvar tc_time hour min sec
gen double `tc_time' = cofd(asktime)
gen `hour' = hh(`tc_time')
gen `min' = mm(`tc_time')
gen `sec' = ss(`tc_time')
desc
普通そんなこと思いつかんよ、まったく。だいたいどんな状況で1000分の1秒単位の分析をStataでやらないといけないのか、エンジニアはC++か何かで計算するだろうし、余り細かすぎるのも困ったものである。