DataFrames

ちらほらとjuliaの集積所を見ていると、データフレームなんてのに出くわす。 昔RとかPythonのパンダをやった時に、おめにかかっている。

データを一まとまりにして管理出来たら楽じゃんって事で、考え出されたんだな。

Juliaはその手の言語としては後発組。Rとかから乗り換えて貰うには是非データフレームを 提供しておきたい。 Juliaが素で提供してるのはえーとね、 Mathematics 組に属している統計屋さんしか無いから。素材だけを提供したって、牙城を切り崩せないよ。 そんなんで、信号処理もおまけに付けます。FFTも出来ますから、時系列分析はどうですか。

やっぱり、データフレームだよ。下記がその資料群。

10 Minutes to DataFrames.jl

above slide

データフレーム

Python(+Pandas), R, Julia(+DataFrames) の比較

MATLAB移民のためのJulia tips

Julia でデータのセーブとロード

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

Julia package

readme

電子書籍「人工知能、ロボット、人の心。」の無料配布

テクニカルナレッジ

経済の仕組み

FORTH の情報まとめてみた 2016 年版

古典コンパイラ入門(#JuliaLang)