Julia で統計解析 第 3 章 データフレームの取り扱い¶
Version()
最新バージョン 2024-10-29 13:47
airquality
データセットは,1973 年の 5 月から 9 月の間の日々のオゾン濃度,日射量,風速,気温の記録である。
iris
データセットは,アヤメ科の 3 種,それぞれ 50 個の花の 4 個の測定値と種名のデータである。
なお,R におけるデータフレームの列名と違う場合があるので注意が必要である。
たとえば,Solar.R
という列名が Solar_R
という列名であったり,Sepal.Length
という列名が SepalLength
という列目であったりするので,読み込んだ後で確認するほうがよい。
RDatasets パッケージの dataset() を使う場合¶
using RDatasets データフレーム名 = dataset("datasets", "データセット名");
何らかの事情でこの方法が使えない場合(macOS の Julia の場合)は,次の方法を試してみてほしい。
using RDatasets
airquality = dataset("datasets", "airquality");
first(airquality, 3) # 最初の 3 行を示す
Row | Ozone | Solar.R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
using RDatasets
iris = dataset("datasets", "iris");
first(iris, 3) # 最初の 3 行を示す
Row | SepalLength | SepalWidth | PetalLength | PetalWidth | Species |
---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Cat… | |
1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
2 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
3 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
RCall パッケージの rcopy() を使う場合¶
using RCall データフレーム名 = rcopy(R"データセット名")
using RCall
airquality = rcopy(R"airquality")
first(airquality, 3) # 最初の 3 行を示す
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
using RCall
iris = rcopy(R"iris")
first(iris, 3) # 最初の 3 行を示す
Row | Sepal_Length | Sepal_Width | Petal_Length | Petal_Width | Species |
---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Cat… | |
1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
2 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
3 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
自前のデータ¶
データフレームの入出力については「Julia で統計解析--その2 データの取扱」を参照のこと。
まず最初に,DataFrames パッケージの使用する場合,以下の 1 行が必要である。
using DataFrames
DataFrame()
に 変数名(列名)= データベクトル
の形式で列挙すれば,簡単なデータフレームを作ることができる。
Julia におけるデータベクトルは,要素をカンマで区切り列挙したものを [ ] で囲んだものである。要素は,整数,実数,文字列,文字などである。
以下の例示で,ベクトル |> println
としたのは,REPL 環境では単に ベクトル
だけを入力すると複数行の出力になるのを防ぐためである。println(ベクトル)
のように書けば同じ効果が得られるが,表示したいものを強調するために |> println
を使う。
[2, 4, 3, 6, 1] # 数値ベクトル
5-element Vector{Int64}: 2 4 3 6 1
[2, 4, 3, 6, 1] |> println # 数値ベクトル
[2, 4, 3, 6, 1]
println([2, 4, 3, 6, 1]) # 数値ベクトル
[2, 4, 3, 6, 1]
["foo", "bar", "baz", "boo", "vax"] |> println # 文字列ベクトル
["foo", "bar", "baz", "boo", "vax"]
1:5 |> println # 等差数列
1:5
collect(1:5) |> println # 等差数列 1:5 とほとんど同じ
[1, 2, 3, 4, 5]
'a':'e' |> println # 文字ベクトル
'a':1:'e'
collect('a':'e') |> println # 文字ベクトル 'a':'e' とほとんど同じ
['a', 'b', 'c', 'd', 'e']
rand(3) |> println # [0, 1) の一様乱数
[0.1685273947698881, 0.08239455213768898, 0.2330147400788538]
rand(1:100, 3) |> println # 1:100 から無作為抽出
[51, 3, 58]
randn(3) |> println # μ=0, σ=1 の標準正規乱数
[0.10522287393415958, 0.45968886484651456, 1.5449057305845453]
randn(3) .* 10 .+ 50 |> println # μ=50, σ=10 の規乱数
[55.25276330519599, 51.69563962013977, 37.14137856837596]
df = DataFrame(a = [2, 4, 3, 6, 1], b = ["foo", "bar", "baz", "boo", "vax"])
Row | a | b |
---|---|---|
Int64 | String | |
1 | 2 | foo |
2 | 4 | bar |
3 | 3 | baz |
4 | 6 | boo |
5 | 1 | vax |
df |> println
5×2 DataFrame Row │ a b │ Int64 String ─────┼─────────────── 1 │ 2 foo 2 │ 4 bar 3 │ 3 baz 4 │ 6 boo 5 │ 1 vax
データフレームの大きさ¶
データセットの大きさは size()
で得られる。結果はタプル (行数, 列数) で返される。
size(airquality) |> println
(153, 6)
データフレームの行数は nrow()
で得られる。
nrow(airquality) |> println
153
データフレームの列数は ncol()
で得られる。
ncol(airquality) |> println
6
データフレームの変数名¶
変数名は names()
で得られる。
names(airquality) |> println
["Ozone", "Solar_R", "Wind", "Temp", "Month", "Day"]
names(iris) |> println
["Sepal_Length", "Sepal_Width", "Petal_Length", "Petal_Width", "Species"]
シンボリック名は propertynames()
で得られる。
propertynames(airquality) |> println
[:Ozone, :Solar_R, :Wind, :Temp, :Month, :Day]
propertynames(iris) |> println
[:Sepal_Length, :Sepal_Width, :Petal_Length, :Petal_Width, :Species]
データフレームの表示¶
データフレームの最初の $n$ 行,最後の $m$ 行を表示するには,first(df, n)
, last(df, m)
のように指定する。
n
, m
が省略された場合は 1 が仮定される。
first(airquality, 6)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
4 | 18 | 313 | 11.5 | 62 | 5 | 4 |
5 | missing | missing | 14.3 | 56 | 5 | 5 |
6 | 28 | missing | 14.9 | 66 | 5 | 6 |
last(iris, 3)
Row | Sepal_Length | Sepal_Width | Petal_Length | Petal_Width | Species |
---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Cat… | |
1 | 6.5 | 3.0 | 5.2 | 2.0 | virginica |
2 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
3 | 5.9 | 3.0 | 5.1 | 1.8 | virginica |
データフレームの表示の 1 行目は,列名(変数名),2 行目はそれぞれのデータの型である。
Int64
は整数型,Float64
は実数型,String
,String3
,String7
などは文字列型である。なお,それぞれの型名の後に ?
がついている場合は,データ中に欠損値 missing
が含まれることを表している。
データフレームのコピーは copy()
で¶
データフレーム df1
のコピーを df2 = df1
で作ったつもりになって,コピーされたデータフレーム df2
を書き換えると df1
も同じように書き換わってしまう。
代入してできた df2
は実は df1
の言ってみれば別名なのだ。df1 を変更しても,df2 を変更しても,もう一方のデータフレームも同じように変更されているように見える。
df1 = DataFrame(a = [1, 2], b = ['a', 'b'])
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
df2 = df1
df2[1, 1] = 99999
df2
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 99999 | a |
2 | 2 | b |
df1
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 99999 | a |
2 | 2 | b |
df2 === df1 # df2 と df1 はコンピュータのメモリ上で同じ(`===`)ものか? ---> true
true
df2 = copy(df1)
とすれば,df1
と df2
は別物になり,このようなことにはならない。
df1 = DataFrame(a = [1, 2], b = ['a', 'b'])
df2 = copy(df1)
df2[1, 1] = 99999
df2
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 99999 | a |
2 | 2 | b |
df1
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
df2 === df1 # df2 と df1 は同じものか? ---> false 別物
false
空データフレーム¶
指定されたデータフレームと同じ列名と同じ型を持つ 0 行のデータフレームを作る。
empty(airquality)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 |
empty!(df)
により,df
の列名だけを残し,すべての行を削除する。意図しないなら危険!注意!
df1 = DataFrame(a = [1, 2], b = ['a', 'b'])
empty!(df1)
df1
Row | a | b |
---|---|---|
Int64 | Char |
データフレームの列の参照¶
Julia では,列を参照するときに何通りかの方法がある。
以下の方法は,1 つの列全体を参照する。
列名の前に :
を付けたもの,以下の例では :Ozone
は Julia では「シンボル」と呼ばれる(必ずしもデータフレームの取り扱いのときだけでなく,整数や文字列のようなものとして扱われる)。
[:, 列]
のような指定において,:
はすべての行を表す。
airquality.Ozone;
airquality."Ozone"
airquality[:, :Ozone];
airquality[:, "Ozone"];
firstcolumn = :Ozone
airquality[:, firstcolumn];
airquality[:, 1];
1 番目の指定法が一番わかり易いと思うが,例からも想像できるように,変数名の中に .
があるとエラーになる。そのような場合には前もって列名を変更する(「1.3. データフレームの列名の変更」参照)か 2 番目の方法すなわち引用符でくくればよい。
行や列を範囲で参照するときには [行範囲, 列範囲]
のように指定する。
範囲の指定は,数1:数2
のように表したときは数1から数2までの整数,[数1, 数2, …]
あるいは [シンボル1, シンボル2, …]
あるいは [文字列1, 文字列2, …]
のように表した場合はそれぞれをまとめたもの(集合)として扱われる。特に,:
が使われる場合はその行や列全部を表す。
なお,文字列での指定ととシンボルでの指定は混ぜて使用することはできない。
first(airquality[:, [1, 3]], 5) # 1,3列のすべての行
Row | Ozone | Wind |
---|---|---|
Int64? | Float64 | |
1 | 41 | 7.4 |
2 | 36 | 8.0 |
3 | 12 | 12.6 |
4 | 18 | 11.5 |
5 | missing | 14.3 |
first(airquality[:, ["Solar_R", "Month"]], 5) # "Solar.R" と "Month" のすべての行
Row | Solar_R | Month |
---|---|---|
Int64? | Int64 | |
1 | 190 | 5 |
2 | 118 | 5 |
3 | 149 | 5 |
4 | 313 | 5 |
5 | missing | 5 |
データフレームの行の参照¶
データフレームを [a, b]
で指定する場合,第 1 引数は行,第 2 引数は列を指定する。
airquality[1:5, [1, 3]] # 1〜5 行の 1, 3 列
Row | Ozone | Wind |
---|---|---|
Int64? | Float64 | |
1 | 41 | 7.4 |
2 | 36 | 8.0 |
3 | 12 | 12.6 |
4 | 18 | 11.5 |
5 | missing | 14.3 |
第 1 引数は :
で表す他に !
で表すこともある。:
はデータフレームをコピーして参照するが,!
は直接参照する。
下の 2 つの例において,test
の第 1 列は元のままの Int64?
であるが,test2
の第 1 列は,test2
の第 2 列と同じ Float64
になる。
この違いは,大したことではないこともあるが,大きな落とし穴になることもあるので注意が必要である。
test = airquality[1:5, [1, 3]]
test[:, 1] = test[:, 2] .* 10
test
Row | Ozone | Wind |
---|---|---|
Int64? | Float64 | |
1 | 74 | 7.4 |
2 | 80 | 8.0 |
3 | 126 | 12.6 |
4 | 115 | 11.5 |
5 | 143 | 14.3 |
test2 = airquality[1:5, [1, 3]]
test2[!, 1] = test2[:, 2] .* 5
test2
Row | Ozone | Wind |
---|---|---|
Float64 | Float64 | |
1 | 37.0 | 7.4 |
2 | 40.0 | 8.0 |
3 | 63.0 | 12.6 |
4 | 57.5 | 11.5 |
5 | 71.5 | 14.3 |
たとえば,下の例では test3[:, 1]
は InexactError: Int64(57.5)
というエラーを発生する。つまり,「元のままの Int64?
に 57.5
の整数部 Int(57.5)
を代入しているが精度が失われるよ」というエラーである。
大雑把な方針としては,左辺のデータフレームには !
を使うほうが自然かもしれない。
test3 = airquality[1:5, [1, 3]]
test3[:, 1] = test3[:, 2] .* 5
test3
InexactError: Int64(57.5) Stacktrace: [1] Int64 @ ./float.jl:994 [inlined] [2] convert @ ./number.jl:7 [inlined] [3] convert @ ./missing.jl:70 [inlined] [4] setindex! @ ./array.jl:976 [inlined] [5] macro expansion @ ./multidimensional.jl:981 [inlined] [6] macro expansion @ ./cartesian.jl:64 [inlined] [7] _unsafe_setindex!(::IndexLinear, A::Vector{Union{Missing, Int64}}, x::Vector{Float64}, I::Base.Slice{Base.OneTo{Int64}}) @ Base ./multidimensional.jl:979 [8] _setindex! @ ./multidimensional.jl:967 [inlined] [9] setindex!(A::Vector{Union{Missing, Int64}}, v::Vector{Float64}, I::Function) @ Base ./abstractarray.jl:1413 [10] setindex!(df::DataFrame, v::Vector{Float64}, row_inds::Colon, col_ind::Int64) @ DataFrames ~/.julia/packages/DataFrames/kcA9R/src/dataframe/dataframe.jl:731 [11] top-level scope @ In[45]:2
データフレームの列名の変更¶
列名の変更は rename!()
による。
Julia の一般的な規約であるが,関数名の最後が !
になっている場合は,関数への引数がインプレースで変更されるので代入の必要はない。
逆に言えば,元の引数が変更されてしまうので,前もってコピーをとっておかない限りもとに戻せなくなるので注意が必要である。
第 2 引数以降に,元の名前 => 新しい名前
の形式で列挙する。
airquality2 = rename(airquality, Dict("Solar_R" => "SolarR"));
first(airquality2)
Row | Ozone | SolarR | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
rename()
の場合は,引数として指定した元のデータフレームは変更されない。したがって,もし変更結果を保存したい場合はデータフレーム(自分自身でもよい)に保存しなければならない
first(airquality)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
rename!()
では,引数として指定した元のデータフレームが変更される。
rename!(airquality, Dict("Solar_R" => "SolarR"));
first(airquality)
Row | Ozone | SolarR | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
rename!(airquality, Dict("SolarR" => "Solar_R"));
first(airquality)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
列の抽出¶
select()
,select!()
は列の抽出を行う。抽出した後の順序は指定したとおりになる(単純に並べ替えるだけということも可)。
列の指定は何通りかある。
df = select(airquality, [4, 3, 2, 1]); # 列番号で指定
first(df)
Row | Temp | Wind | Solar_R | Ozone |
---|---|---|---|---|
Int64 | Float64 | Int64? | Int64? | |
1 | 67 | 7.4 | 190 | 41 |
df = select(airquality, [:Wind, :Temp, :Ozone, :Solar_R]); # シンボルで指定
first(df)
Row | Wind | Temp | Ozone | Solar_R |
---|---|---|---|---|
Float64 | Int64 | Int64? | Int64? | |
1 | 7.4 | 67 | 41 | 190 |
列を(名前を変えて)コピーしたり,変数変換することもできる。ByRow()
はすべての行に対して,引数で指定する関数または無名関数を適用する。
たとえば,元の Temp
を華氏温度 Fahrenheit
に名前を変えて,Temp
を変換して摂氏温度 Celcius
を作成する。
x -> 5(x-32)/9
が無名関数で,引数を x
として 5(x-32)/9
を計算する。
関数で定義するとすれば func(x) = 5(x-32)/9
と同じである。
df = select(airquality, :Ozone, :Solar_R, :Wind,
:Temp => :Fahrenheit, :Temp => ByRow(x -> 5(x-32)/9) => :Celcius);
first(df, 5)
Row | Ozone | Solar_R | Wind | Fahrenheit | Celcius |
---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Float64 | |
1 | 41 | 190 | 7.4 | 67 | 19.4444 |
2 | 36 | 118 | 8.0 | 72 | 22.2222 |
3 | 12 | 149 | 12.6 | 74 | 23.3333 |
4 | 18 | 313 | 11.5 | 62 | 16.6667 |
5 | missing | missing | 14.3 | 56 | 13.3333 |
select(airquality, Not(:Ozone))[1:3, :] # 最初の 3 行だけ示す
Row | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|
Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 190 | 7.4 | 67 | 5 | 1 |
2 | 118 | 8.0 | 72 | 5 | 2 |
3 | 149 | 12.6 | 74 | 5 | 3 |
:Month
と :Day
を除く。
select(airquality, Not([:Month, :Day]))[1:3, :] # 最初の 3 行だけ示す
Row | Ozone | Solar_R | Wind | Temp |
---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 |
2 | 36 | 118 | 8.0 | 72 |
3 | 12 | 149 | 12.6 | 74 |
行の抽出¶
行を指定するときに,任意のベクトルを書けば,その要素に対応する行が抽出される。
airquality[1:3, :] # 1 〜 3 行目を抽出
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
airquality[1:20:end, :] # 1行目 から 20 行おきに抽出
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 1 | 8 | 9.7 | 59 | 5 | 21 |
3 | 39 | 323 | 11.5 | 87 | 6 | 10 |
4 | missing | 138 | 8.0 | 83 | 6 | 30 |
5 | 63 | 220 | 11.5 | 85 | 7 | 20 |
6 | 110 | 207 | 8.0 | 90 | 8 | 9 |
7 | 118 | 225 | 2.3 | 94 | 8 | 29 |
8 | 13 | 27 | 10.3 | 76 | 9 | 18 |
airquality[vcat(1:2, 5...), :] # 1, 2, 5 行を抽出
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | missing | missing | 14.3 | 56 | 5 | 5 |
行を表す位置に論理式を書くことができる。この場合には,条件を満たす行が抽出される。
airquality[airquality.Wind .> 20, :] # Wind が 20 以上の行を抽出する .> はベクトル演算ゆえ . が付く
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 8 | 19 | 20.1 | 61 | 5 | 9 |
2 | 37 | 284 | 20.7 | 72 | 6 | 17 |
これを次のように書くこともできる。
row -> row.Wind > 20
は無名関数である。row
を引数として row.Wind > 20
という条件式を書いている。
filter(row -> row.Wind > 20, airquality) # .> ではなく > である
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 8 | 19 | 20.1 | 61 | 5 | 9 |
2 | 37 | 284 | 20.7 | 72 | 6 | 17 |
以下の例では,Wind > 15
かつ Temp <= 65
の行を抽出する。
airquality[(airquality.Wind .> 15) .&& (airquality.Temp .<= 65), :]
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 8 | 19 | 20.1 | 61 | 5 | 9 |
2 | 6 | 78 | 18.4 | 57 | 5 | 18 |
3 | missing | 66 | 16.6 | 57 | 5 | 25 |
4 | 14 | 20 | 16.6 | 63 | 9 | 25 |
上の例では,airquality
を 3 回も書かなければならないが,下のように書くと 1 回で済む(ただし慣れないと読むのが難しい)。
filter([:Wind, :Temp] => (x, y) -> x > 15 && y <= 65, airquality)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 8 | 19 | 20.1 | 61 | 5 | 9 |
2 | 6 | 78 | 18.4 | 57 | 5 | 18 |
3 | missing | 66 | 16.6 | 57 | 5 | 25 |
4 | 14 | 20 | 16.6 | 63 | 9 | 25 |
行の削除¶
「条件を満たさない行を削除する」ことは,「条件を満たす行を抽出する」ことと同じである。
df = DataFrame(a = 1:4, b = 'a':'d')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
3 | 3 | c |
4 | 4 | d |
「a
が 偶数」という条件を満たさない行を削除する,つまり,「a
が 奇数」という行を削除するということは,「a
が 偶数」という条件を満たす行を抽出することである。
df[iseven.(df.a), :]
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 2 | b |
2 | 4 | d |
重複を除き,ユニークな行のみを含むデータフレームを作る¶
unique()
/unique!()
を使う。unique!()
はインプレースで作用する。
見た目が同じ行が削除されるのであって,本来別々の測定対象がたまたま全く同じ測定値になったような場合でも重複とみなされて削除されてしまう。 測定対象を識別するユニークなコードも含めて重複の有無を判定すべきである。
df = DataFrame(a = [1, 2, 1], b = ['a', 'b', 'a'])
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
3 | 1 | a |
unique(df) # 3 行目は 1 行目と見た目が同じなので削除される
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
欠損値を含む行か含まない行か¶
欠損値を含まない行なら 1(true
),含む行なら 0(false
) の Bool 型のベクトルを返す。
df = DataFrame(a = [1, 2, 1], b = ['a', missing, 'c'])
Row | a | b |
---|---|---|
Int64 | Char? | |
1 | 1 | a |
2 | 2 | missing |
3 | 1 | c |
completecases(df) |> println
Bool[1, 0, 1]
欠損値を含まない行を抽出する¶
前節の completecases()
と行の選択を併せて,欠損値を含まない行を抽出することができる。
df = DataFrame(a = [1,2,1], b = ['a', 'b', 'a'])
df[completecases(df), :]
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
3 | 1 | a |
dropmissing(df) # および dropmissing!(df)
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
3 | 1 | a |
データフレームの列方向連結¶
既存のデータフレームの右に別のデータフレームを連結するのは hcat()
を使う。インプレイスでの連結ではないので,連結結果を後で利用したい場合は結果を代入する。
行数が異なるデータフレームを連結しようとするとエラーになる。Julia のデータフレームは行名を持たないので,連結しようとする 2 つのデータフレームの各行が同じ対象のものであることを保証するのはユーザの責任である。
特定の列名を基準としてデータフレームをマージする場合は hcat()
ではなく innerjoin
,leftjoin
などを使う。
df1 = DataFrame(a = [2, 1, 4], b = ["ab", "cde", "fg"])
Row | a | b |
---|---|---|
Int64 | String | |
1 | 2 | ab |
2 | 1 | cde |
3 | 4 | fg |
df2 = DataFrame(c = [1.2, 3.4, 5.6])
Row | c |
---|---|
Float64 | |
1 | 1.2 |
2 | 3.4 |
3 | 5.6 |
df3 = hcat(df1, df2)
Row | a | b | c |
---|---|---|---|
Int64 | String | Float64 | |
1 | 2 | ab | 1.2 |
2 | 1 | cde | 3.4 |
3 | 4 | fg | 5.6 |
連結するデータフレームに同じ列名があるとエラーになるが,makeunique = true
を指定しておくと,列名_番号
のように名前を変えて連結することができる。
df4 = hcat(df1, df2, df2, makeunique = true)
Row | a | b | c | c_1 |
---|---|---|---|---|
Int64 | String | Float64 | Float64 | |
1 | 2 | ab | 1.2 | 1.2 |
2 | 1 | cde | 3.4 | 3.4 |
3 | 4 | fg | 5.6 | 5.6 |
データフレームの行方向連結¶
連結しようとしている 2 つのデータフレーム df1
, df2
に含まれる列名の集合を set1
, set2
とする。
append!(df1, df2)
により,df1
の後に df2
を連結する。
append!()
はインプレースの連結なので,df1 は 結果で書き換えられる(結果を別のデータフレームに代入することもできる)。
vcat(df1, df2)
も同じように,df1
の後に df2
を連結するが,vcat()
はインプレースではないので,後で結果を利用したい場合は別のデータフレームに代入しなければならない。
append!()
も vcat()
も,第 3 引数 cols
で連結する列に関する詳細な設定ができる。
cols = :setequal, cols=:orderequal
¶
append!(df1, df2, cols=:setequal)
で df1
の後に df2
を連結する。
cols=:setequal
はデフォルトで,df1
, df2
の列数と名前は同じでなければならないことを表す。順序は違ってもよい。
cols=:orderequal
を指定すると,順序も同じでなければならないことを表す(順序が違えばエラーになる)。
以下の例は,df1
も df2
も 列名 a
, b
を持つ場合である。
df1 = DataFrame(a = 1:3, b = 'A':'C')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
df2 = DataFrame(b = ['X', 'Y'], a = 11:12)
Row | b | a |
---|---|---|
Char | Int64 | |
1 | X | 11 |
2 | Y | 12 |
append!(df1, df2,);
df1
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
4 | 11 | X |
5 | 12 | Y |
cols = :intersect
¶
cols=:setequal
の場合には列名の集合は同じでなければならないが,cols=:intersect
の場合には df1
の列名の集合が df2
の列名の集合に包含されていればよい。df1
に含まれるすべての列名が df2
にも含まれていなければならない。
df1 = DataFrame(a = 1:3, b = 'a':'c')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
3 | 3 | c |
df2 = DataFrame(a = 11:12, b = ['X', 'Y'], c = [1.2, 3.4])
Row | a | b | c |
---|---|---|---|
Int64 | Char | Float64 | |
1 | 11 | X | 1.2 |
2 | 12 | Y | 3.4 |
append!(df1, df2, cols = :intersect);
df1
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | a |
2 | 2 | b |
3 | 3 | c |
4 | 11 | X |
5 | 12 | Y |
cols = :subset
¶
cols=:subset
の場合には df1
と df2
に共通する列名のデータはそのまま連結されるが,df1
にはあるが df2
にはない列名のデータは missing
になる。
df1 = DataFrame(a = 1:3, b = 'A':'C')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
df2 = DataFrame(a = 11:12, c = [1.2, 3.4])
Row | a | c |
---|---|---|
Int64 | Float64 | |
1 | 11 | 1.2 |
2 | 12 | 3.4 |
append!(df1, df2, cols = :subset);
df1
Row | a | b |
---|---|---|
Int64 | Char? | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
4 | 11 | missing |
5 | 12 | missing |
df1 = DataFrame(a = 1:3, b = 'A':'C')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
df2 = DataFrame(a = 11:12, c = [1.2, 3.4])
Row | a | c |
---|---|---|
Int64 | Float64 | |
1 | 11 | 1.2 |
2 | 12 | 3.4 |
append!(df1, df2, cols = :union);
df1
Row | a | b | c |
---|---|---|---|
Int64 | Char? | Float64? | |
1 | 1 | A | missing |
2 | 2 | B | missing |
3 | 3 | C | missing |
4 | 11 | missing | 1.2 |
5 | 12 | missing | 3.4 |
データフレームの最終行の次に 1 行追加する¶
push!(df1, 追加行)
のようにしてインプレースで追加する。
追加行は Dict()
またはデータフレームの一行で指定する。前節の append!()
と同じく,cols
引数が使える。
df1 = DataFrame(a = 1:3, b = 'A':'C')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
push!(df1, Dict(:a => 5, :b => 'X', :c => 1.23), cols = :union)
Row | a | b | c |
---|---|---|---|
Int64 | Char | Float64? | |
1 | 1 | A | missing |
2 | 2 | B | missing |
3 | 3 | C | missing |
4 | 5 | X | 1.23 |
push!(df1, df1[1, :])
Row | a | b | c |
---|---|---|---|
Int64 | Char | Float64? | |
1 | 1 | A | missing |
2 | 2 | B | missing |
3 | 3 | C | missing |
4 | 5 | X | 1.23 |
5 | 1 | A | missing |
行を繰り返してデータフレームを作る¶
repeat()
でデータの繰り返しを作る。
df = DataFrame(a = 1:2, b = 3:4)
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 3 |
2 | 2 | 4 |
repeat(df, inner = 2, outer = 3)
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 3 |
2 | 1 | 3 |
3 | 2 | 4 |
4 | 2 | 4 |
5 | 1 | 3 |
6 | 1 | 3 |
7 | 2 | 4 |
8 | 2 | 4 |
9 | 1 | 3 |
10 | 1 | 3 |
11 | 2 | 4 |
12 | 2 | 4 |
df = DataFrame(a = 1:2, b = 3:4);
repeat(df, 2)
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 3 |
2 | 2 | 4 |
3 | 1 | 3 |
4 | 2 | 4 |
df = DataFrame(a = 1:2, b = 3:4);
repeat!(df, inner = 2, outer = 3); # 破壊的
first(df, 10)
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 3 |
2 | 1 | 3 |
3 | 2 | 4 |
4 | 2 | 4 |
5 | 1 | 3 |
6 | 1 | 3 |
7 | 2 | 4 |
8 | 2 | 4 |
9 | 1 | 3 |
10 | 1 | 3 |
df = DataFrame(a = 1:2, b = 3:4);
repeat!(df, 2); # 破壊的
df
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 3 |
2 | 2 | 4 |
3 | 1 | 3 |
4 | 2 | 4 |
データフレームに列を挿入する¶
insertcols!(df1, 挿入位置, 列名1 => データ1, …)
により,df1
の挿入位置へ,順に列をインプレースで挿入する。複数の列を挿入できる。
df1 = DataFrame(a = 1:3, b = 'A':'C')
Row | a | b |
---|---|---|
Int64 | Char | |
1 | 1 | A |
2 | 2 | B |
3 | 3 | C |
insertcols!(df1, 2, :col2 => [1.2, 3.4, 5.67], :col3 => ["No1", "No2", "No3"])
Row | a | col2 | col3 | b |
---|---|---|---|---|
Int64 | Float64 | String | Char | |
1 | 1 | 1.2 | No1 | A |
2 | 2 | 3.4 | No2 | B |
3 | 3 | 5.67 | No3 | C |
データフレームの要素から新しいデータフレームを作る¶
combine()
は既存の列変数から新しいデータフレームを作る。
first(airquality, 3)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
:Wind
は mile/hour, :Temp
は 華氏温度なので,それぞれ m/sec,摂氏温度に変換し,名前もつける。
x -> 0.4470389x
,x -> 5(x.-32)/9
は無名関数
df = combine(airquality, :Wind => (x -> 0.4470389x) => :m_by_sec, "Temp" => (x -> 5(x.-32)/9) => :℃)
first(df, 3)
Row | m_by_sec | ℃ |
---|---|---|
Float64 | Float64 | |
1 | 3.30809 | 19.4444 |
2 | 3.57631 | 22.2222 |
3 | 5.63269 | 23.3333 |
データフレームの各列に関数を施す¶
mapcols()
/mapcols!()
の第 1 引数は関数または無名関数である。その関数をデータフレームの各列に適用する。
mapcols()
は元のデータフレームは元のまままで。結果を利用したい場合はデータフレームに代入しなければならない。
mapcols!()
はインプレースで列の変更が行われるので代入は不要であるが,データフレームを書き換えてはいけない場合には注意が必要である。
df = DataFrame(x = 1:4, y = 11:14)
Row | x | y |
---|---|---|
Int64 | Int64 | |
1 | 1 | 11 |
2 | 2 | 12 |
3 | 3 | 13 |
4 | 4 | 14 |
mapcols(a -> a.^2, df) # df は元のまま。利用したければデータフレームに代入する
df
Row | x | y |
---|---|---|
Int64 | Int64 | |
1 | 1 | 11 |
2 | 2 | 12 |
3 | 3 | 13 |
4 | 4 | 14 |
df2 = mapcols(a -> a.^2, df) # df は元のまま。利用したければデータフレームに代入する
df2
Row | x | y |
---|---|---|
Int64 | Int64 | |
1 | 1 | 121 |
2 | 4 | 144 |
3 | 9 | 169 |
4 | 16 | 196 |
mapcols!(a -> a.^2, df) # インプレースで関数が適用され,df は破壊される
df
Row | x | y |
---|---|---|
Int64 | Int64 | |
1 | 1 | 121 |
2 | 4 | 144 |
3 | 9 | 169 |
4 | 16 | 196 |
ソート(並べ替え)¶
df = DataFrame(a = [1,1,2,2,3,3], b = [1,2,1,2,4,3],
c = [11,12,13,14,15,16], d = [21,-22,-23,24,25,26],
name = 'f':-1:'a')
Row | a | b | c | d | name |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Char | |
1 | 1 | 1 | 11 | 21 | f |
2 | 1 | 2 | 12 | -22 | e |
3 | 2 | 1 | 13 | -23 | d |
4 | 2 | 2 | 14 | 24 | c |
5 | 3 | 4 | 15 | 25 | b |
6 | 3 | 3 | 16 | 26 | a |
ソートされているかをチェックする¶
issorted(df, :name) # :name 列は昇順にソートされているか?(降順にソートされているので false)
false
issorted(df, 3) # 3 列目は昇順にソートされているか? true
true
ソートの順番を決める¶
sort!()
はインプレースでソートされる。
sort(df, order(:name)) # :name を昇順にソート
Row | a | b | c | d | name |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Char | |
1 | 3 | 3 | 16 | 26 | a |
2 | 3 | 4 | 15 | 25 | b |
3 | 2 | 2 | 14 | 24 | c |
4 | 2 | 1 | 13 | -23 | d |
5 | 1 | 2 | 12 | -22 | e |
6 | 1 | 1 | 11 | 21 | f |
sort(df, order(:b, rev=true)) # :b 列を降順にソート
Row | a | b | c | d | name |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Char | |
1 | 3 | 4 | 15 | 25 | b |
2 | 3 | 3 | 16 | 26 | a |
3 | 1 | 2 | 12 | -22 | e |
4 | 2 | 2 | 14 | 24 | c |
5 | 1 | 1 | 11 | 21 | f |
6 | 2 | 1 | 13 | -23 | d |
sort(df, order(:d, by=abs)) # :d 列の絶対値をとって降順にソート
Row | a | b | c | d | name |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Char | |
1 | 1 | 1 | 11 | 21 | f |
2 | 1 | 2 | 12 | -22 | e |
3 | 2 | 1 | 13 | -23 | d |
4 | 2 | 2 | 14 | 24 | c |
5 | 3 | 4 | 15 | 25 | b |
6 | 3 | 3 | 16 | 26 | a |
並べ替えベクトルを返す¶
o = sortperm(df, :d)
o |> println
[3, 2, 1, 4, 5, 6]
sortperm()
で求めたベクトルに基づいて,ソートすることができる。
df[o, :]
Row | a | b | c | d | name |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Char | |
1 | 2 | 1 | 13 | -23 | d |
2 | 1 | 2 | 12 | -22 | e |
3 | 1 | 1 | 11 | 21 | f |
4 | 2 | 2 | 14 | 24 | c |
5 | 3 | 4 | 15 | 25 | b |
6 | 3 | 3 | 16 | 26 | a |
sort(df, :d) # o = sortperm(df, :d); df[o, :] と同じ
Row | a | b | c | d | name |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Char | |
1 | 2 | 1 | 13 | -23 | d |
2 | 1 | 2 | 12 | -22 | e |
3 | 1 | 1 | 11 | 21 | f |
4 | 2 | 2 | 14 | 24 | c |
5 | 3 | 4 | 15 | 25 | b |
6 | 3 | 3 | 16 | 26 | a |
行の包含¶
df1 = DataFrame(ID = [1, 2, 3], Name = ["Karl Pearson", "Maurice Kendall", "Charles Spearman"])
Row | ID | Name |
---|---|---|
Int64 | String | |
1 | 1 | Karl Pearson |
2 | 2 | Maurice Kendall |
3 | 3 | Charles Spearman |
df2 = DataFrame(ID = [1, 2, 4], Job = ["Lawyer", "Doctor", "Farmer"])
Row | ID | Job |
---|---|---|
Int64 | String | |
1 | 1 | Lawyer |
2 | 2 | Doctor |
3 | 4 | Farmer |
df1
と df2
のすべての組み合わせを作る crossjoin
¶
R の expand.grid
に相当するもの。引数はデータフレームでなければならない。
crossjoin(DataFrame(a = df1.Name), DataFrame(b = df2.Job))
Row | a | b |
---|---|---|
String | String | |
1 | Karl Pearson | Lawyer |
2 | Karl Pearson | Doctor |
3 | Karl Pearson | Farmer |
4 | Maurice Kendall | Lawyer |
5 | Maurice Kendall | Doctor |
6 | Maurice Kendall | Farmer |
7 | Charles Spearman | Lawyer |
8 | Charles Spearman | Doctor |
9 | Charles Spearman | Farmer |
df1
の行のうち,df2
に含まれない行を抽出する antijoin
¶
on
で指定する行列をキーとして,df1
の行のうち,df2
に含まれない行を抽出する
antijoin(df1, df2, on=:ID)
Row | ID | Name |
---|---|---|
Int64 | String | |
1 | 3 | Charles Spearman |
df1
に,df2
をマージする innerjoin
¶
on
で指定する行列をキーとして,df1
と df2
に共通する行を取り出す。
innerjoin(df1, df2, on=:ID)
Row | ID | Name | Job |
---|---|---|---|
Int64 | String | String | |
1 | 1 | Karl Pearson | Lawyer |
2 | 2 | Maurice Kendall | Doctor |
df1
に,df2
をマージする leftjoin
¶
on
で指定する行列をキーとして,df1
に df2
をマージする。df2
にない項目は missing
になる。
leftjoin(df1, df2, on=:ID)
Row | ID | Name | Job |
---|---|---|---|
Int64 | String | String? | |
1 | 1 | Karl Pearson | Lawyer |
2 | 2 | Maurice Kendall | Doctor |
3 | 3 | Charles Spearman | missing |
df1
に,df2
をマージする outerjoin
¶
on
で指定する行列をキーとして,df1
に df2
をマージする。どちらかのデータフレームにない項目は missing
になる。
outerjoin(df1, df2, on=:ID)
Row | ID | Name | Job |
---|---|---|---|
Int64 | String? | String? | |
1 | 1 | Karl Pearson | Lawyer |
2 | 2 | Maurice Kendall | Doctor |
3 | 3 | Charles Spearman | missing |
4 | 4 | missing | Farmer |
df2
に,df1
をマージする rightjoin
¶
on
で指定する行列をキーとして,df1
に df2
をマージする。df1
にない項目は missing
になる。
rightjoin(df1, df2, on=:ID)
Row | ID | Name | Job |
---|---|---|---|
Int64 | String? | String | |
1 | 1 | Karl Pearson | Lawyer |
2 | 2 | Maurice Kendall | Doctor |
3 | 4 | missing | Farmer |
両方に存在する項目のみでマージする semijoin
¶
on
で指定する行列をキーとして,df1
と df2
の両方に存在する項目のみでマージする。
semijoin(df1, df2, on=:ID)
Row | ID | Name |
---|---|---|
Int64 | String | |
1 | 1 | Karl Pearson |
2 | 2 | Maurice Kendall |
欠損値のリストワイズ除去¶
欠損値のリストワイズ除去とは,1 行中に 1 つでも欠損値を含む行を除去することである。
airquality2 = dropmissing(airquality);
size(airquality) |> println
size(airquality2) |> println
first(airquality2, 5)
(153, 6) (111, 6)
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64 | Int64 | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
4 | 18 | 313 | 11.5 | 62 | 5 | 4 |
5 | 23 | 299 | 8.6 | 65 | 5 | 7 |
dropmissing()
では欠損値を除去した結果を別のデータフレームに(同じでもよいが)代入しなければ結果は保存されない。
一方 dropmissing!()
では引数で指定したデータフレームが直接書き換えられる。
dropmissing!(airquality);
size(airquality) |> println
(111, 6)
ロングフォーマット(縦長データフレーム)に変換する¶
df = DataFrame(a = [1,1,2,2,3,3], b = [1,1,1,2,2,2],
c = [11,12,13,14,15,16], d = [21,22,23,24,25,26],
e = ["a", "b", "c", "d", "e", "f"])
Row | a | b | c | d | e |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | String | |
1 | 1 | 1 | 11 | 21 | a |
2 | 1 | 1 | 12 | 22 | b |
3 | 2 | 1 | 13 | 23 | c |
4 | 2 | 2 | 14 | 24 | d |
5 | 3 | 2 | 15 | 25 | e |
6 | 3 | 2 | 16 | 26 | f |
df2 = stack(df, [:c, :d])
Row | a | b | e | variable | value |
---|---|---|---|---|---|
Int64 | Int64 | String | String | Int64 | |
1 | 1 | 1 | a | c | 11 |
2 | 1 | 1 | b | c | 12 |
3 | 2 | 1 | c | c | 13 |
4 | 2 | 2 | d | c | 14 |
5 | 3 | 2 | e | c | 15 |
6 | 3 | 2 | f | c | 16 |
7 | 1 | 1 | a | d | 21 |
8 | 1 | 1 | b | d | 22 |
9 | 2 | 1 | c | d | 23 |
10 | 2 | 2 | d | d | 24 |
11 | 3 | 2 | e | d | 25 |
12 | 3 | 2 | f | d | 26 |
ワイドフォーマット(横長データフレーム)に変換する¶
前節でロングフォーマットに変換したデータフレームを元のワイドフォーマットに変換する(元に戻る)。
unstack(df2)
Row | a | b | e | c | d |
---|---|---|---|---|---|
Int64 | Int64 | String | Int64? | Int64? | |
1 | 1 | 1 | a | 11 | 21 |
2 | 1 | 1 | b | 12 | 22 |
3 | 2 | 1 | c | 13 | 23 |
4 | 2 | 2 | d | 14 | 24 |
5 | 3 | 2 | e | 15 | 25 |
6 | 3 | 2 | f | 16 | 26 |
データフレームをグループ変数に基づいて分割する¶
groupby()
により,第 1 引数のデータフレームを,第 2 引数で指定する列名でグループ分けする。列名は複数をベクトルで与えることもできる。
:Month
が 5, 6, 7, 8, 9 の 5 つのデータフレームに分割する。
gd = groupby(airquality, :Month);
グループ分けされたデータフレームを抽出する¶
get()
により,グループ分けに使った名前 :Month
の列の値が 5 であるデータフレームを取り出す(最初の 3 行だけを示す)。
get(gd, (Month=5,), nothing)[1:3,:]
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64 | Int64 | Float64 | Int64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
グループ分けに使った列の値が 6 のデータフレームを取り出す(最初の 3 行だけを示す)。
get(gd, (6,), nothing)[1:3,:]
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64 | Int64 | Float64 | Int64 | Int64 | Int64 | |
1 | 29 | 127 | 9.7 | 82 | 6 | 7 |
2 | 71 | 291 | 13.8 | 90 | 6 | 9 |
3 | 39 | 323 | 11.5 | 87 | 6 | 10 |
グループ分けに使った列の値が 10 のデータフレームを取り出そうとすると,なにもないので結果が返ってこない。実は 3 番目の引数は,条件を満たすデータフレームがない場合に何を返すかを指定している。
get(gd, (10,), "なにもない")
"なにもない"
size(gd) はグループ化されたデータファイルの個数を返す。
size(gd)
(5,)
したがって,gd[n] のように指定すれば n 番目のグループ化されたデータフレームが得られる。
gd[2][1:3, :] # 最初の 3 行だけ示す
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64 | Int64 | Float64 | Int64 | Int64 | Int64 | |
1 | 29 | 127 | 9.7 | 82 | 6 | 7 |
2 | 71 | 291 | 13.8 | 90 | 6 | 9 |
3 | 39 | 323 | 11.5 | 87 | 6 | 10 |
要するに,データフレームを要素とする配列なので,他に,gd[end]
,last(gd)
,gd[(Month=7,)]
,gd[Dict("Month" => 8)]
,gd[(9,)]
などの指定ができる。
グループ化されたデータフレームに関する情報¶
groupcols())
はグループ化に使われた列名を返す。
groupcols(gd) |> println
[:Month]
groupindices()
は元のデータフレムの各行が何番目のグループ化されたデータフレームに属するかのベクトルを返す(長さは元のデータフレームの行数)。
groupindices(gd) |> println
Union{Missing, Int64}[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
keys()
はグループデータフレームのキーの値を返す。
keys(gd)
5-element DataFrames.GroupKeys{GroupedDataFrame{DataFrame}}: GroupKey: (Month = 5,) GroupKey: (Month = 6,) GroupKey: (Month = 7,) GroupKey: (Month = 8,) GroupKey: (Month = 9,)
k = keys(gd)[1]
GroupKey: (Month = 5,)
keys(k), values(k)
([:Month], (5,))
values(k)
(5,)
NamedTuple(k)
(Month = 5,)
k.Month, k[:Month], k[1]
(5, 5, 5)
グループ化に使われなかった列名を返す。
valuecols(gd) |> println
[:Ozone, :Solar_R, :Wind, :Temp, :Day]
親データフレームを返す¶
グループ化されたデータフレームをまとめて元のデータフレームを再現する。
parent(gd)[end-4:end, :] # 最後の 5 行を示す
Row | Ozone | Solar_R | Wind | Temp | Month | Day |
---|---|---|---|---|---|---|
Int64 | Int64 | Float64 | Int64 | Int64 | Int64 | |
1 | 14 | 20 | 16.6 | 63 | 9 | 25 |
2 | 30 | 193 | 6.9 | 70 | 9 | 26 |
3 | 14 | 191 | 14.3 | 75 | 9 | 28 |
4 | 18 | 131 | 8.0 | 76 | 9 | 29 |
5 | 20 | 223 | 11.5 | 68 | 9 | 30 |
列ごとに関数を適用する¶
eachcol()
using Statistics
df = DataFrame(a = 1:5, b = 11:15)
mean.(eachcol(df))
2-element Vector{Float64}: 3.0 13.0
std.(eachcol(df))
2-element Vector{Float64}: 1.5811388300841898 1.5811388300841898
map(eachcol(df)) do col
maximum(col) - minimum(col)
end
2-element Vector{Int64}: 4 4
map(x -> mean(skipmissing(x)), eachcol(airquality))
6-element Vector{Float64}: 42.0990990990991 184.80180180180182 9.939639639639632 77.7927927927928 7.216216216216216 15.945945945945946
行ごとに関数を適用する¶
sum.(eachrow(df))
5-element Vector{Int64}: 12 14 16 18 20
mean.(eachrow(df))
5-element Vector{Float64}: 6.0 7.0 8.0 9.0 10.0
クエリーによる操作例¶
スタンドアローンのクエリー演算子はパイプ演算子 |>
で組み合わせて使われる。
using Query, DataFrames, Statistics
df = DataFrame(a = [1, 1, 2, 3], b = [4, 5, 6, 8])
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 4 |
2 | 1 | 5 |
3 | 2 | 6 |
4 | 3 | 8 |
df2 = df |>
@groupby(_.a) |>
@map({a=key(_), b=mean(_.b)}) |>
@filter(_.b > 5) |>
@orderby_descending(_.b) |>
DataFrame
Row | a | b |
---|---|---|
Int64 | Float64 | |
1 | 3 | 8.0 |
2 | 2 | 6.0 |
要素に関数を適用する @map コマンド¶
source |> @map(element_selector)
source
は,クエリーできるものなら何でもよい(これ以降の全てのコマンドの場合も同じ)。
下の例ではベクトルが対象にされている。
以下の例は,ベクトルから要素を 1 個ずつ取り出し 2 乗して,結果を集め,x に代入する。
data = [1,2,3];
x = data |> @map(_^2) |> collect
x |> println
[1, 4, 9]
条件を満たす行を抽出 @filter コマンド¶
source |> @filter(filter_condition)
filter_condition
は,ソースから 1 つの要素を受け取りその要素を保持する場合に true
,フィルターされる場合には false
を返す無名関数
df = DataFrame(name=["John", "Sally", "Kirk"],
age=[23., 42., 59.], children=[3,5,2])
Row | name | age | children |
---|---|---|---|
String | Float64 | Int64 | |
1 | John | 23.0 | 3 |
2 | Sally | 42.0 | 5 |
3 | Kirk | 59.0 | 2 |
x = df |> @filter(_.age > 30 && _.children > 2) |> DataFrame
Row | name | age | children |
---|---|---|---|
String | Float64 | Int64 | |
1 | Sally | 42.0 | 5 |
データフレームのグループ化 @groupby コマンド¶
source |> @groupby(key_selector)
key_selector
は,ソースの要素がグループ化されるときに使われる値を返す無名関数
source |> @groupby(source, key_selector, element_selector)
element_selector
は,ソースの要素がグループ分けされる前にグループの前に挿入される要素に適用される無名関数
@group
の戻り値はグループのイテラブルである。
df = DataFrame(name=["John", "Sally", "Kirk"],
age=[23., 42., 59.], children=[3,2,2])
Row | name | age | children |
---|---|---|---|
String | Float64 | Int64 | |
1 | John | 23.0 | 3 |
2 | Sally | 42.0 | 2 |
3 | Kirk | 59.0 | 2 |
x = df |>
@groupby(_.children) |> # children でグループ化
@map({Key=key(_), Count=length(_)}) |> # Count はサイズ
DataFrame
Row | Key | Count |
---|---|---|
Int64 | Int64 | |
1 | 3 | 1 |
2 | 2 | 2 |
データのソート @orderby
,@orderby_descenidng
,@thenby
,@thenby_descending
コマンド¶
@orderby
コマンド
@orderby_descenidng
コマンド
データのソート(昇順,降順)
@thenby
コマンド
@thenby_descending
コマンド
直前のデータソートに続いてデータのソートを行う(昇順,降順)。直前のソートで同順位があった場合の処理を定義する。
source |> @orderby(key_selector)
@orderby(key_selector) |> @thenby(key_selector2)
df = DataFrame(a=[2,1,1,2,1,3],b=[2,2,1,1,3,2])
x = df |> @orderby_descending(_.a) |> @thenby(_.b) |> DataFrame
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 3 | 2 |
2 | 2 | 1 |
3 | 2 | 2 |
4 | 1 | 1 |
5 | 1 | 2 |
6 | 1 | 3 |
データフレームのマージ @groupjoin コマンド¶
outer |> @groupjoin(inner, outer_selector, inner_selector, result_selector)
outer
,inner
はクエリーできるものなら何でもよい
df1 = DataFrame(a=[1,2,3], b=[1.,2.,3.])
Row | a | b |
---|---|---|
Int64 | Float64 | |
1 | 1 | 1.0 |
2 | 2 | 2.0 |
3 | 3 | 3.0 |
df2 = DataFrame(c=[2,4,2], d=["John", "Jim","Sally"])
Row | c | d |
---|---|---|
Int64 | String | |
1 | 2 | John |
2 | 4 | Jim |
3 | 2 | Sally |
x = df1 |> @groupjoin(df2, _.a, _.c, {t1=_.a, t2=length(__)}) |> DataFrame
Row | t1 | t2 |
---|---|---|
Int64 | Int64 | |
1 | 1 | 0 |
2 | 2 | 2 |
3 | 3 | 0 |
データフレームの連結 @join コマンド¶
df1 = DataFrame(a=[1,2,3], b=[1.,2.,3.])
df2 = DataFrame(c=[2,4,2], d=["John", "Jim","Sally"])
x = df1 |> @join(df2, _.a, _.c, {_.a, _.b, __.c, __.d}) |> DataFrame
Row | a | b | c | d |
---|---|---|---|---|
Int64 | Float64 | Int64 | String | |
1 | 2 | 2.0 | 2 | John |
2 | 2 | 2.0 | 2 | Sally |
キーと値のペアを展開 @mapmany コマンド¶
source = Dict(:a=>[1,2,3], :b=>[4,5])
Dict{Symbol, Vector{Int64}} with 2 entries: :a => [1, 2, 3] :b => [4, 5]
q = source |> @mapmany(_.second, {Key=_.first, Value=__}) |> DataFrame
Row | Key | Value |
---|---|---|
Symbol | Int64 | |
1 | a | 1 |
2 | a | 2 |
3 | a | 3 |
4 | b | 4 |
5 | b | 5 |
要素を取り出す @take コマンド¶
最初から幾つの要素を取り出すかを指示する
source = [1,2,3,4,5]
q = source |> @take(3) |> collect
3-element Vector{Int64}: 1 2 3
要素を捨てる @drop コマンド¶
最初から幾つの要素を捨てるかを指示する。
source = [1,2,3,4,5]
q = source |> @drop(3) |> collect
2-element Vector{Int64}: 4 5
重複データを除く @unique コマンド¶
重複データを取り除く(ユニークなデータのみを残す)
source = [1,1,2,2,3]
q = source |> @unique() |> collect
3-element Vector{Int64}: 1 2 3
列の選択 @select コマンド¶
列の選択
source |> @select(selectors...)
selector
は名前,位置,述語関数で選択,排除を指定する,これらは順に適用される(順序が変更されることはない)。
df = DataFrame(fruit=["Apple","Banana","Cherry"],amount=[2,6,1000],
price=[1.2,2.0,0.4],isyellow=[false,true,false])
q1 = df |> @select(2:3, occursin("ui"), -:amount) |> DataFrame
Row | price | fruit |
---|---|---|
Float64 | String | |
1 | 1.2 | Apple |
2 | 2.0 | Banana |
3 | 0.4 | Cherry |
df = DataFrame(fruit=["Apple","Banana","Cherry"],amount=[2,6,1000],
price=[1.2,2.0,0.4],isyellow=[false,true,false])
q2 = df |> @select(!endswith("t"), 1) |> DataFrame
Row | price | isyellow | fruit |
---|---|---|---|
Float64 | Bool | String | |
1 | 1.2 | false | Apple |
2 | 2.0 | true | Banana |
3 | 0.4 | false | Cherry |
df = DataFrame(fruit=["Apple","Banana","Cherry"],amount=[2,6,1000],
price=[1.2,2.0,0.4],isyellow=[false,true,false])
q = df |> @rename(:fruit => :food, :price => :cost, :food => :name) |> DataFrame
Row | name | amount | cost | isyellow |
---|---|---|---|---|
String | Int64 | Float64 | Bool | |
1 | Apple | 2 | 1.2 | false |
2 | Banana | 6 | 2.0 | true |
3 | Cherry | 1000 | 0.4 | false |
変数変換 @mutate コマンド¶
数式で変数変換
df = DataFrame(fruit=["Apple","Banana","Cherry"],amount=[2,6,1000],
price=[1.2,2.0,0.4],isyellow=[false,true,false])
q = df |> @mutate(price = 2 * _.price + _.amount, isyellow = _.fruit == "Apple") |> DataFrame
Row | fruit | amount | price | isyellow |
---|---|---|---|---|
String | Int64 | Float64 | Bool | |
1 | Apple | 2 | 4.4 | true |
2 | Banana | 6 | 10.0 | false |
3 | Cherry | 1000 | 1000.8 | false |
欠損値行を除く @dropna コマンド¶
欠損値のある行を除く
source |> @dropna(columns...)
引数なしで @dropna()
のように使用されるときは,全ての列が対象になる。
df = DataFrame(a=[1,2,3], b=[4,missing,5])
q = df |> @dropna() |> DataFrame
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 4 |
2 | 3 | 5 |
q = df |> @dropna(:b) |> DataFrame
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 4 |
2 | 3 | 5 |
q = df |> @dropna(:b, :a) |> DataFrame
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 4 |
2 | 3 | 5 |
欠損値を含まないデータフレーム @dissallowna コマンド¶
欠損値を含まないデータフレームとする
source |> @dissallowna(columns...)
@dissallowna()
のように引数なしで使われると全ての列を対象にする。
なお,以下で Query.isna() としているが,これは同じ名前の関数が同時に使用している RCall パッケージにもあるため,「Query の isna() である」ことを明示しているだけなので,重複がなければ isna() だけでよい。
df = DataFrame(a=[1,missing,3], b=[4,5,6])
q = df |> @filter(!Query.isna(_.a)) |> @dissallowna() |> DataFrame
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 4 |
2 | 3 | 6 |
df = DataFrame(a=[1,2,missing], b=[4,missing,5])
q = df |> @filter(!Query.isna(_.b)) |> @dissallowna(:b) |> DataFrame
Row | a | b |
---|---|---|
Int64? | Int64 | |
1 | 1 | 4 |
2 | missing | 5 |
df = DataFrame(a=[1,missing,3], b=[4,5,6])
q = df |> @replacena(0) |> DataFrame
Row | a | b |
---|---|---|
Int64 | Int64 | |
1 | 1 | 4 |
2 | 0 | 5 |
3 | 3 | 6 |
df = DataFrame(a=[1,2,missing], b=["One",missing,"Three"])
q = df |> @replacena(:b=>"Unknown", :a=>0) |> DataFrame
Row | a | b |
---|---|---|
Int64 | String | |
1 | 1 | One |
2 | 2 | Unknown |
3 | 0 | Three |
データフレームを二次元配列に変換する¶
Matrix() を使用する。データフレームの各列の型が違う場合(特に数値型と文字列型が混在するような場合)には,R のように最上位の型に統一されることはない。
M = Matrix(airquality);
M[1:5, :]
5×6 Matrix{Float64}: 41.0 190.0 7.4 67.0 5.0 1.0 36.0 118.0 8.0 72.0 5.0 2.0 12.0 149.0 12.6 74.0 5.0 3.0 18.0 313.0 11.5 62.0 5.0 4.0 23.0 299.0 8.6 65.0 5.0 7.0
M2 = Matrix(iris[1:3, :])
3×5 Matrix{Any}: 5.1 3.5 1.4 0.2 "setosa" 4.9 3.0 1.4 0.2 "setosa" 4.7 3.2 1.3 0.2 "setosa"