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が便利で、世界のプログラマーに支持されているのが肯けるな。コミット権が ないから、これを作者に送れば良いのか。