DataFrames
ちらほらとjuliaの集積所を見ていると、データフレームなんてのに出くわす。 昔RとかPythonのパンダをやった時に、おめにかかっている。
データを一まとまりにして管理出来たら楽じゃんって事で、考え出されたんだな。
Juliaはその手の言語としては後発組。Rとかから乗り換えて貰うには是非データフレームを 提供しておきたい。 Juliaが素で提供してるのはえーとね、 Mathematics 組に属している統計屋さんしか無いから。素材だけを提供したって、牙城を切り崩せないよ。 そんなんで、信号処理もおまけに付けます。FFTも出来ますから、時系列分析はどうですか。
やっぱり、データフレームだよ。下記がその資料群。
Python(+Pandas), R, Julia(+DataFrames) の比較
DataFrameを使う
この時のために、血圧データを用意してる。1行目に列名を書いておくのはRからの伝統。
julia> using DataFrames julia> bld = readtable("current.csv"); julia> names(bld) 4-element Array{Symbol,1}: :ymdh :hi :low :pls julia> size(bld) (730,4) julia> head(bld,3) 3x4 DataFrames.DataFrame | Row | ymdh | hi | low | pls | |-----|----------|-----|-----|-----| | 1 | 15010104 | 130 | 73 | 54 | | 2 | 15010121 | 120 | 68 | 69 | | 3 | 15010205 | 118 | 73 | 55 | julia> tail(bld,2) 2x4 DataFrames.DataFrame | Row | ymdh | hi | low | pls | |-----|----------|-----|-----|-----| | 1 | 15123105 | 128 | 69 | 54 | | 2 | 15123121 | 115 | 67 | 65 |
だんだん思い出してきた。
julia> bld[150:151, 1:3] 2x3 DataFrames.DataFrame | Row | ymdh | hi | low | |-----|----------|-----|-----| | 1 | 15031621 | 109 | 62 | | 2 | 15031703 | 121 | 70 | julia> colwise(eltype, bld) 4-element Array{Any,1}: [Int32] [Int32] [Int32] [Int32] julia> bld[:hi] 730-element DataArrays.DataArray{Int32,1}: 130 : julia> typeof(bld) DataFrames.DataFrame julia> colwise(typeof, bld) 4-element Array{Any,1}: [DataArrays.DataArray{Int32,1}] [DataArrays.DataArray{Int32,1}] [DataArrays.DataArray{Int32,1}] [DataArrays.DataArray{Int32,1}] julia> summary(bld) "730x4 DataFrames.DataFrame"
行列なんだな。任意のデータを、行列名[行範囲, 列範囲] で取り出せる。インデックス 番号は1から始まる事になってるのは、Fortranの遺産を引き受けているからだな。
julia> describe(bld[:, 2:3]) hi Min 95.0 1st Qu. 115.0 Median 122.0 Mean 121.28356164383561 3rd Qu. 129.0 Max 141.0 NAs 0 NA% 0.0% low Min 51.0 1st Qu. 66.0 Median 71.0 Mean 69.67945205479452 3rd Qu. 74.0 Max 81.0 NAs 0 NA% 0.0%
データの要約は上記で得られる。標準偏差が出てこないのもRを模倣した? そういう時は、素の統計集を活用しよう。
julia> std(bld[:hi]) 9.771764617036302 julia> cor(bld[:hi], bld[:low]) 0.7444939337521826 julia> cor(bld[:hi], bld[:pls]) -0.5259219860732888
ついでに、相関も取ってみた。
julia> hist(bld[:hi]) (90.0:5.0:145.0,[1,17,40,58,85,109,151,135,94,39,1])
ヒストグラムも、分割を適当にやってくれて簡単に取れる。
julia> quantile(bld[:hi], [0.02, 0.16, 0.84, 0.98]) 4-element Array{Float64,1}: 100.0 110.0 132.0 138.0
分位点も、外部から指定して取れる。±σ、±2σの範囲も簡単に算出できた。もっとも データの分布が釣鐘型になってる前提だけど。
gnuplotにGaston経由でグラフ(散布図だけど)を書く時は、ちと面倒だけど、形式変換が必要
julia> plot(Array(bld[:hi]), Array(bld[:low]), "plotstyle", "points")
Gastonが要求するのは、普通のArray。それに対してDataFrameの方はDataArrayだからね。
julia> plot(bld[:hi], bld[:pls], "plotstyle", "points") ERROR: MethodError: `addcoords` has no method matching addcoords(::DataArrays.DataArray{Int32,1}, ::DataArrays.DataArray{Int32,1}, ::Array{Any,1}, ::Gaston.CurveConf) Closest candidates are: addcoords(::Any, ::Any, ::Any) addcoords(::Union{Array{T,1},Array{T,2},Range{T}}, ::Union{Array{T,1},Array{T,2},Range{T}}, ::Array{T,N}, ::Gaston.CurveConf) addcoords(::Any, ::Any) ... in addcoords at /home/sakae/.julia/v0.4/Gaston/src/gaston_midlvl.jl:98 in plot at /home/sakae/.julia/v0.4/Gaston/src/gaston_hilvl.jl:178
簡単に修正出来るかな。やってみる。まず、Gaston.jlに
import DataArrays: DataArray
を追加してあげる。そして使う方は、gaston_types.jl になるな。
Coord = Union{DataArray,Range,Matrix,Vector}
これで良いはず。
julia> plot(1:730, bld[:hi]) julia> plot(bld[100:200, :hi], bld[100:200, :low], "plotstyle", "points") julia> surf(bld[:hi], bld[:low], (x,y)->(y - x),"plotstyle", "points")
これで、取りあえず動いた。Gastonってsplotはサポートしてなくてちょっと焦った。 詳しい説明書は、PDF fileを参照。
julia> super(DataArray) DataArrays.AbstractDataArray{T,N} julia> super(ans) AbstractArray{T,N} julia> super(ans) Any julia> super(ans) Any
一応、定義の階層を遡ってみるか。ansってのは、前の結果なので、一度superを実行後、 後はそれを繰り返すだけ。最上位はAnyだな。逆に、下の階層を見る時は、
julia> subtypes(AbstractDataArray) 2-element Array{Any,1}: DataArrays.DataArray{T,N} DataArrays.PooledDataArray{T,R<:Integer,N}
DataArrayって、有る程度ストックしておいた所から、分けて貰っているのかな。詳しくは、 ソース嫁。
readtable
いきなりソースの海にダイブすると沈んでしまいそうなので、より詳しいデータフレームの説明は、 DataFrames.jl Overview にあるんでチラ見してる。
そしたら、readtableなんていう面白そうなのが載ってた。何が面白いって、Advancedの所。 それとなく、REPLのhelpで調べてみると
help?> readtable search: readtable No documentation found. DataFrames.readtable is a generic Function. # 3 methods for generic function "readtable": readtable(io::IO) at /home/sakae/.julia/v0.4/DataFrames/src/dataframe/io.jl:810 readtable(io::IO, nbytes::Integer) at /home/sakae/.julia/v0.4/DataFrames/src/dataframe/io.jl:810 readtable(pathname::AbstractString) at /home/sakae/.julia/v0.4/DataFrames/src/dataframe/io.jl:878
ドキュメントが無いから、ソースから拾ってきてやったぞと申しております。ソースに 優るドキュメント無しと昔から言うけど、それをちゃんと伝承してて、あんたは偉い!
で、ドキュメントの説明と ソースが乖離してるじゃん。やっぱりソース優先。ちらっと、git logとかしてみたり。。。 readtableを探すと io.jlに有った。TAGSを作っておいたので一発ヒット。 ちら見すると、
function getseparator(filename::AbstractString) m = match(r"\.(\w+)(\.(gz|bz|bz2))?$", filename) ext = isa(m, RegexMatch) ? m.captures[1] : "" if ext == "csv" return ',' elseif ext == "tsv" return '\t' elseif ext == "wsv" return ' ' else return ',' end end
マッチの使い方の実例が載ってた。wsvってサフィックスが何を意味してるか、ソース見て 良く分かった。gnuplotが好きなフォーマットだな。圧縮ファイルも受け付けてくれるとは 親切だな。ASCIIファイルは圧縮率が高いですから。
一方、こんなコードも
# Open an IO stream based on pathname # (1) Path is an HTTP or FTP URL if startswith(pathname, "http://") || startswith(pathname, "ftp://") error("URL retrieval not yet implemented") # (2) Path is GZip file elseif endswith(pathname, ".gz") io = gzopen(pathname, "r") nbytes = 2 * filesize(pathname) # (3) Path is BZip2 file elseif endswith(pathname, ".bz") || endswith(pathname, ".bz2") error("BZip2 decompression not yet implemented") # (4) Path is an uncompressed file else io = open(pathname, "r") nbytes = filesize(pathname) end
HTTPでもftpでもIO族に属していますから、大丈夫ですって。Webで公開されてるデータも 取ってこれるんか。
でも、 Juliaで楽しくWebスクレイピング! を見ると、まだJuliaは弱いみたいだけど。。。
# This trick is ugly, but is ~33% faster than push!() for large arrays macro push(count, a, val, l) count = esc(count) # Number of items in array a = esc(a) # Array to update val = esc(val) # Value to insert l = esc(l) # Length of array quote $count += 1 if $l < $count $l *= 2 resize!($a, $l) end $a[$count] = $val end end
見苦しいけど、速いに越した事は無いって、わざわざ断りを入れているよ。マクロが多用 されていて、勉強になる。
versioninfo(true)
した時にPkgの状況が追加表示される。そこに不審な表示が。。。
Package Directory: /home/sakae/.julia/v0.4 5 required packages: - DataFrames 0.6.10 - Gaston 0.0.0 79034dca (dirty) :
このdirtyって何? 汚れてるってさ。こいつは、versioninfoの中でPkg.status()からの 報告だな。pkg.jlに
status(io::IO=STDOUT) = cd(Entry.status,io) status(pkg::AbstractString = "", io::IO=STDOUT) = cd(Entry.status,io,pkg)
pkg.entry.jl内にお目当てのstatusが有った。(複数有るので、オイラーはCフラフラじゃねぇーぞと叫びたい!) どうやら、雲の向こうのGitHubと手元の物が異なっていると、汚れてるって報告してくるんだな。 ええ、十分に身に覚えがありますから。
これは、Gitにマージせいと言う、暗黙の圧力に違いないな。オイラーはGitのアカウントなんて 持ってないよ。作者に直接、Send PRしちゃ礼儀違反?
そう言えば、gitの事何も知らないな。ツイターやフェース本を覚えてうかれているより、 先にやらんかいって声が聞こえてきそうだな。
そういう時は、体験学習。まずは、git help。そして体験。
[sakae@fedora Gaston]$ git status HEAD detached at 79034dc Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: src/Gaston.jl modified: src/gaston_aux.jl modified: src/gaston_config.jl modified: src/gaston_hilvl.jl modified: src/gaston_lowlvl.jl modified: src/gaston_midlvl.jl modified: src/gaston_test.jl modified: src/gaston_types.jl no changes added to commit (use "git add" and/or "git commit -a")
Pkgの中の内部関数で、git diffしてたんで、手打ちしてみる。
[sakae@fedora Gaston]$ git diff diff --git a/src/Gaston.jl b/src/Gaston.jl index ddf8362..2df428f 100644 --- a/src/Gaston.jl +++ b/src/Gaston.jl @@ -2,6 +2,7 @@ ## ## This file is distributed under the 2-clause BSD License. +__precompile__() module Gaston export closefigure, closeall, clearfigure, figure, plot, histogram, imagesc, : diff --git a/src/gaston_aux.jl b/src/gaston_aux.jl index ea44e2a..cab368c 100644 --- a/src/gaston_aux.jl +++ b/src/gaston_aux.jl @@ -7,10 +7,10 @@ function gnuplot_init() f = C_NULL try # Linux - f = ccall(:popen, Ptr{Int}, (Ptr{Uint8},Ptr{Uint8}), "gnuplot" ,"w") + f = ccall(:popen, Ptr{Int}, (Ptr{UInt8},Ptr{UInt8}), "gnuplot" ,"w") catch # Windows - f = ccall(:_popen, Ptr{Int}, (Ptr{Uint8},Ptr{Uint8}), "gnuplot" ,"w") + f = ccall(:_popen, Ptr{Int}, (Ptr{UInt8},Ptr{UInt8}), "gnuplot" ,"w") end if f == C_NULL error("There was a problem starting up gnuplot.") :
なる程。Gitが便利で、世界のプログラマーに支持されているのが肯けるな。コミット権が ないから、これを作者に送れば良いのか。