Julia で統計解析 第 4 章 一変量統計¶
Version()
最新バージョン 2024-10-29 13:50
一変量統計¶
サンプルサイズ,(算術)平均値,不偏分散,標準偏差は基本統計量と呼ばれる。その他に五数要約値である最小値,第一四分位数,中央値(第二四分位数),第三四分位数,最大値も必要である。
R に用意されている airquality
データセットを用いて例示する。
using RCall
airquality = rcopy(R"airquality");
println("size: ", size(airquality))
first(airquality, 5)
size: (153, 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 |
Ozone
, Solar_R
は欠損値を含むので取り扱いに注意が必要である。dropmissing()
により欠損値をリストワイズ除去したデータフレームも作っておく。同時に,分析に必要のない変数を「選択しない」(それ以外を選択する)ために select()
を使う。
using DataFrames
airquality2 = dropmissing(select(airquality, Not([:Month, :Day])))
# airquality2 = dropmissing(select(airquality, [1, 2, 3, 4])) などと同じ
println("size: ", size(airquality2))
first(airquality2, 5)
size: (111, 4)
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 |
4 | 18 | 313 | 11.5 | 62 |
5 | 23 | 299 | 8.6 | 65 |
一変数の場合¶
基礎統計量¶
単変量の基礎統計量を求める関数として,sum()
, mean()
, var()
, std()
, median()
, quantile()
がある。
個別の変数に対して適用する場合は以下のようにする。
using Statistics
mean(airquality.Ozone)
missing
Ozone
には欠損値 missing
が含まれているので,平均値を求めるためには前もって skipmissing()
を適用しなければならない。
mean(skipmissing(airquality.Ozone))
42.12931034482759
何回も Ozone
について統計処理をする場合,使用するたびに skipmissing()
するのは無駄なので,結果を変数に代入しておけばよい。
なお,以下の使用例で最後に |> plintln
をつけているのはこの資料作成ツールのくせに対応するためなので,REPL モードで実行する場合は不要である。
x = skipmissing(airquality.Ozone);
mean(x) |> println
var(x) |> println
std(x) |> println
42.12931034482759 1088.2005247376317 32.98788451443396
なお,airquality2
は一つでも欠損値を含む行は削除しているので,airquality2
においては skipmissing()
を使う必要はないが,サンプルサイズが異なるので,結果が違うことに注意する必要がある。
mean(airquality2.Ozone)
42.0990990990991
データの個数は length()
で確認できるが skipmissing()
で得られるデータは使用できない。以下で定義するような filter()
を使う必要がある。filter()
は,欠損値を除くデータを作る。skipmissing()
は欠損値を含むデータというデータ型を作るだけで,実際に missing
が除去されるわけではない。しかし,他の統計関数では skipmissing()
による場合も filter(!ismissing, )
による場合も結果は同じである。
y = filter(!ismissing, airquality.Ozone)
mean(y) |> println
var(y) |> println
std(y) |> println
length(y) |> println
42.12931034482759 1088.200524737631 32.987884514433944 116
パーセンタイル値¶
パーセンタイル値は quantile()
で求める。第 2 引数で 任意の q
値をベクトルで指定できる。
using StatsBase
quantile(skipmissing(airquality.Ozone), [0, 0.25, 0.5, 0.75, 1])
5-element Vector{Float64}: 1.0 18.0 31.5 63.25 168.0
quantile(airquality2.Ozone, [0, 0.25, 0.5, 0.75, 1])
5-element Vector{Float64}: 1.0 18.0 31.0 62.0 168.0
度数分布¶
カテゴリー変数の度数分布は freqtable()
で求める。freqtable()
は欠損値 missing
の個数もカウントする。
chickwts
は,餌の添加物(feed
)の種類による雛鳥の体重(weight
)のデータセットである。
using RCall
chickwts = rcopy(R"chickwts")
using FreqTables
freqtable(chickwts.feed)
6-element Named Vector{Int64} Dim1 │ ──────────┼─── casein │ 12 horsebean │ 10 linseed │ 12 meatmeal │ 11 soybean │ 14 sunflower │ 12
airquality = rcopy(R"airquality")
first(airquality, 10)
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 |
7 | 23 | 299 | 8.6 | 65 | 5 | 7 |
8 | 19 | 99 | 13.8 | 59 | 5 | 8 |
9 | 8 | 19 | 20.1 | 61 | 5 | 9 |
10 | missing | 194 | 8.6 | 69 | 5 | 10 |
M = mean.(eachcol(airquality));
M
6-element Vector{Union{Missing, Float64}}: missing missing 9.957516339869281 77.88235294117646 6.993464052287582 15.803921568627452
REPL での出力と,println()
での出力形式が異なるので,DataFrame()
にしてから println()
すればよい。
なお,そのままでは各行の変数名が表示されないので,DataFrame()
で,variable=names(データフレーム名)
とすれば 1 列目に変数名が付加される。
DataFrame()
の中での,a=b
という指定は,「変数 b
を列名 a
としてデータフレームに含める」という意味である(a
と b
が同じでも構わない)。
DataFrame(variable=names(airquality), Mean=M) |> println
6×2 DataFrame Row │ variable Mean │ String Float64? ─────┼───────────────────────── 1 │ Ozone missing 2 │ Solar_R missing 3 │ Wind 9.95752 4 │ Temp 77.8824 5 │ Month 6.99346 6 │ Day 15.8039
各変数に欠損値が含まれる場合には skipmissing()
した後に統計関数を適用しなければならないので,以下のようにする。
map()
は,第 2 引数のそれぞれの要素に対して 第 1 引数で定義される関数を適用する。関数は普通の関数と無名関数の場合がある。無名関数は「仮引数 -> 関数定義」の形をしている。
M = map(x -> mean(skipmissing(x)), eachcol(airquality))
M
6-element Vector{Float64}: 42.12931034482759 185.93150684931507 9.95751633986928 77.88235294117646 6.993464052287582 15.803921568627452
上の例で使用した無名関数と同じことをする普通の関数として定義し,それを map()
で使用する場合は以下のようになる。
mean2(x) = mean(skipmissing(x))
M = map(mean2, eachcol(airquality))
6-element Vector{Float64}: 42.12931034482759 185.93150684931507 9.95751633986928 77.88235294117646 6.993464052287582 15.803921568627452
複数の統計量を計算してそれらを DataFtame()
でまとめて一覧表にすることができる。
M = map(x -> mean(skipmissing(x)), eachcol(airquality))
V = map(x -> var(skipmissing(x)), eachcol(airquality))
S = map(x -> std(skipmissing(x)), eachcol(airquality))
n = map(x -> length(filter(!ismissing, x)), eachcol(airquality))
DataFrame(variable=names(airquality), n=n, Mean=M, Var=V, SD=S)
Row | variable | n | Mean | Var | SD |
---|---|---|---|---|---|
String | Int64 | Float64 | Float64 | Float64 | |
1 | Ozone | 116 | 42.1293 | 1088.2 | 32.9879 |
2 | Solar_R | 146 | 185.932 | 8110.52 | 90.0584 |
3 | Wind | 153 | 9.95752 | 12.4115 | 3.523 |
4 | Temp | 153 | 77.8824 | 89.5913 | 9.46527 |
5 | Month | 153 | 6.99346 | 2.00654 | 1.41652 |
6 | Day | 153 | 15.8039 | 78.5797 | 8.86452 |
このように map()
を使う方法は,同じような指定を何回も繰り返さなければならないが,必要な処理をまとめて関数化しておけば 1 行の関数呼び出しで実行できるので簡単に使用できる。
using Statistics
function describe2(df)
M = map(x -> mean(skipmissing(x)), eachcol(df))
V = map(x -> var(skipmissing(x)), eachcol(df))
S = map(x -> std(skipmissing(x)), eachcol(df))
n = map(x -> length(filter(!ismissing, x)), eachcol(df))
DataFrame(variable=names(df), n=n, Mean=M, Var=V, SD=S)
end
describe2 (generic function with 1 method)
describe2(airquality) |> println
6×5 DataFrame Row │ variable n Mean Var SD │ String Int64 Float64 Float64 Float64 ─────┼────────────────────────────────────────────────── 1 │ Ozone 116 42.1293 1088.2 32.9879 2 │ Solar_R 146 185.932 8110.52 90.0584 3 │ Wind 153 9.95752 12.4115 3.523 4 │ Temp 153 77.8824 89.5913 9.46527 5 │ Month 153 6.99346 2.00654 1.41652 6 │ Day 153 15.8039 78.5797 8.86452
describe2(airquality2) |> println
4×5 DataFrame Row │ variable n Mean Var SD │ String Int64 Float64 Float64 Float64 ─────┼───────────────────────────────────────────────── 1 │ Ozone 111 42.0991 1107.29 33.276 2 │ Solar_R 111 184.802 8308.74 91.1523 3 │ Wind 111 9.93964 12.6573 3.55771 4 │ Temp 111 77.7928 90.8203 9.52997
describe()
を使う¶
describe()
では,より簡単に複数の変数に対して複数の統計量をまとめて求めることができる。
必要な統計量は第 2 引数以降に任意の順序でシンボルによって列挙する。指定できるのは :mean
, :std
, :min
, :q25
, :median
, :q75
, :max
, :nunique
, :nmissing
, :first
, :last
, :eltype
である。
すべての統計量を計算するときは :all
を指定すればよい。
describe()
には総和 sum()
,不偏分散 var()
は実装されていない。
describe()
ではパーセンタイル値は q0
(min
), q25
, q50
(median
), q75
, q100
(max
) を求めることができる。
なお,REPL では結果の出力桁数(幅)により表示が打ち切られる。
以下のように printlins()
で明示的に出力すれば打ち切られることはない。
ただし,物理的な出力桁数の制限により折り返し表示されるので見にくくなることはある。
println(describe(airquality, :all))
6×16 DataFrame Row │ variable mean std min q25 median q75 max sum nunique nuniqueall nmissing nnonmissing first last eltype │ Symbol Float64 Float64 Real Float64 Float64 Float64 Real Real Nothing Int64 Int64 Int64 Real Real Type ─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ Ozone 42.1293 32.9879 1 18.0 31.5 63.25 168 4887 67 37 116 41 20 Union{Missing, Int64} 2 │ Solar_R 185.932 90.0584 7 115.75 205.0 258.75 334 27146 117 7 146 190 223 Union{Missing, Int64} 3 │ Wind 9.95752 3.523 1.7 7.4 9.7 11.5 20.7 1523.5 31 0 153 7.4 11.5 Float64 4 │ Temp 77.8824 9.46527 56 72.0 79.0 85.0 97 11916 40 0 153 67 68 Int64 5 │ Month 6.99346 1.41652 5 6.0 7.0 8.0 9 1070 5 0 153 5 9 Int64 6 │ Day 15.8039 8.86452 1 8.0 16.0 23.0 31 2418 31 0 153 1 30 Int64
sum()
, var()
に対応する関数 describe2()
を定義しておく。指定できるのは :sum
, :mean
, :var
, :std
, :min
, :q25
, :median
, :q75
, :max
, :nmissing
である。
すべての統計量を計算するときは,統計量の指定をしないようにするか :all
を指定すればよい。
function describe2(df, arg...=:all)
q25(x) = quantile(x, 0.25)
q75(x) = quantile(x, 0.75)
nmissing(x) = nrow(df) - length(ismissing.(x))
arg0 = [:sum, :mean, :var, :std, :min, :q25, :median, :q75, :max, :nmissing]
func = [sum, mean, var, std, minimum, q25, median, q75, maximum, nmissing]
funcname = ["sum", "mean", "var", "std", "minimum", "q25", "median", "q75", "maximum", "nmissing"]
arg[1] == :all && (arg = arg0)
res = DataFrame(variable = Symbol.(names(df)))
n = ncol(df)
m = length(arg)
body = Array{Float64, 2}(undef, n, m)
loc = indexin(arg, arg0)
for i in 1:n
x = skipmissing(df[!, i])
body[i, :] = [func[j](x) for j in loc]
end
bodydf = DataFrame(body, funcname[loc])
pos = indexin(["nmissing"], funcname[loc])[1]
isnothing(pos) || (bodydf[!, pos] = Int.(bodydf[:, pos]))
hcat(res, bodydf)
end
describe2(airquality) |> println; println()
describe2(airquality, :all) |> println; println()
describe2(airquality, :q25, :q75, :nmissing) |> println; println()
describe2(airquality2, :q25, :q75, :nmissing) |> println; println()
6×11 DataFrame Row │ variable sum mean var std minimum q25 median q75 maximum nmissing │ Symbol Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Int64 ─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ Ozone 4887.0 42.1293 1088.2 32.9879 1.0 18.0 31.5 63.25 168.0 37 2 │ Solar_R 27146.0 185.932 8110.52 90.0584 7.0 115.75 205.0 258.75 334.0 7 3 │ Wind 1523.5 9.95752 12.4115 3.523 1.7 7.4 9.7 11.5 20.7 0 4 │ Temp 11916.0 77.8824 89.5913 9.46527 56.0 72.0 79.0 85.0 97.0 0 5 │ Month 1070.0 6.99346 2.00654 1.41652 5.0 6.0 7.0 8.0 9.0 0 6 │ Day 2418.0 15.8039 78.5797 8.86452 1.0 8.0 16.0 23.0 31.0 0 6×11 DataFrame Row │ variable sum mean var std minimum q25 median q75 maximum nmissing │ Symbol Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Int64 ─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ Ozone 4887.0 42.1293 1088.2 32.9879 1.0 18.0 31.5 63.25 168.0 37 2 │ Solar_R 27146.0 185.932 8110.52 90.0584 7.0 115.75 205.0 258.75 334.0 7 3 │ Wind 1523.5 9.95752 12.4115 3.523 1.7 7.4 9.7 11.5 20.7 0 4 │ Temp 11916.0 77.8824 89.5913 9.46527 56.0 72.0 79.0 85.0 97.0 0 5 │ Month 1070.0 6.99346 2.00654 1.41652 5.0 6.0 7.0 8.0 9.0 0 6 │ Day 2418.0 15.8039 78.5797 8.86452 1.0 8.0 16.0 23.0 31.0 0 6×4 DataFrame Row │ variable q25 q75 nmissing │ Symbol Float64 Float64 Int64 ─────┼────────────────────────────────────── 1 │ Ozone 18.0 63.25 37 2 │ Solar_R 115.75 258.75 7 3 │ Wind 7.4 11.5 0 4 │ Temp 72.0 85.0 0 5 │ Month 6.0 8.0 0 6 │ Day 8.0 23.0 0 4×4 DataFrame Row │ variable q25 q75 nmissing │ Symbol Float64 Float64 Int64 ─────┼────────────────────────────────────── 1 │ Ozone 18.0 62.0 0 2 │ Solar_R 113.5 255.5 0 3 │ Wind 7.4 11.5 0 4 │ Temp 71.0 84.5 0
元の describe()
に :sum
と :var
にも対応する別バージョンの関数を呈示しておく。
function getcol(df, sym, newsym)
df2 = describe(df, sym, :nmissing)
if sym == :mean
n = nrow(df)
df2[!, sym] .*= (n .- df2.nmissing)
else
df2[!, sym] .*= df2[:, sym]
end
rename!(df2, sym => newsym)
end
function describe3(df, arg...)
length(arg) == 1 && arg[1] == :all && (arg = (:sum, :mean, :var, :std, :min, :q25, :median, :q75, :max, :nunique, :nmissing, :first, :last, :eltype))
df2 = DataFrame(variable = Symbol.(names(df)))
for a in arg
if a == :sum
df3 = getcol(df, :mean, :sum)
elseif a == :var
df3 = getcol(df, :std, :var)
else
df3 = describe(df, a)
end
df2 = hcat(df2, select(df3, 2))
end
df2
end
describe3(airquality, :all)
Row | variable | sum | mean | var | std | min | q25 | median | q75 | max | nunique | nmissing | first | last | eltype |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Symbol | Float64 | Float64 | Float64 | Float64 | Real | Float64 | Float64 | Float64 | Real | Nothing | Int64 | Real | Real | Type | |
1 | Ozone | 4887.0 | 42.1293 | 1088.2 | 32.9879 | 1 | 18.0 | 31.5 | 63.25 | 168 | 37 | 41 | 20 | Union{Missing, Int64} | |
2 | Solar_R | 27146.0 | 185.932 | 8110.52 | 90.0584 | 7 | 115.75 | 205.0 | 258.75 | 334 | 7 | 190 | 223 | Union{Missing, Int64} | |
3 | Wind | 1523.5 | 9.95752 | 12.4115 | 3.523 | 1.7 | 7.4 | 9.7 | 11.5 | 20.7 | 0 | 7.4 | 11.5 | Float64 | |
4 | Temp | 11916.0 | 77.8824 | 89.5913 | 9.46527 | 56 | 72.0 | 79.0 | 85.0 | 97 | 0 | 67 | 68 | Int64 | |
5 | Month | 1070.0 | 6.99346 | 2.00654 | 1.41652 | 5 | 6.0 | 7.0 | 8.0 | 9 | 0 | 5 | 9 | Int64 | |
6 | Day | 2418.0 | 15.8039 | 78.5797 | 8.86452 | 1 | 8.0 | 16.0 | 23.0 | 31 | 0 | 1 | 30 | Int64 |
複数の変数に対して,任意の q
値に対するパーセンタイル値を求めるには,以下のような関数を定義すればよい。missing
を除去して計算される。
function quantile2(df, qs=[0, 0.25, 0.5, 0.75, 1])
Names =["q$(Int(100q))" for q in qs]
Names = replace(Names, "q0" => "min", "q50" => "median", "q100" => "max")
qtile = map(x -> quantile(skipmissing(x), qs), eachcol(df))
df2 = DataFrame(Matrix(reshape(vcat(qtile...), length(qs), :)'), Names)
n = map(x -> length(filter(!ismissing, x)), eachcol(df))
insertcols!(df2, 1, :variable => names(df), :n => n)
df2
end
quantile2(airquality) |> println; println()
quantile2(airquality, [0.05, 0.25, 0.75, 0.95]) |> println; println()
6×7 DataFrame Row │ variable n min q25 median q75 max │ String Int64 Float64 Float64 Float64 Float64 Float64 ─────┼────────────────────────────────────────────────────────────── 1 │ Ozone 116 1.0 18.0 31.5 63.25 168.0 2 │ Solar_R 146 7.0 115.75 205.0 258.75 334.0 3 │ Wind 153 1.7 7.4 9.7 11.5 20.7 4 │ Temp 153 56.0 72.0 79.0 85.0 97.0 5 │ Month 153 5.0 6.0 7.0 8.0 9.0 6 │ Day 153 1.0 8.0 16.0 23.0 31.0 6×6 DataFrame Row │ variable n q5 q25 q75 q95 │ String Int64 Float64 Float64 Float64 Float64 ─────┼───────────────────────────────────────────────────── 1 │ Ozone 116 7.75 18.0 63.25 108.5 2 │ Solar_R 146 24.25 115.75 258.75 311.5 3 │ Wind 153 4.6 7.4 11.5 15.5 4 │ Temp 153 60.2 72.0 85.0 92.0 5 │ Month 153 5.0 6.0 8.0 9.0 6 │ Day 153 2.0 8.0 23.0 29.4
function quantile3(df, q=[0, 0.25, 0.5, 0.75, 1])
res = DataFrame(variable = Symbol.(names(df)))
n = ncol(df)
m = length(q)
body = Array{Float64, 2}(undef, n, m)
for i in 1:n
body[i, :] = quantile(skipmissing(df[!, i]), q)
end
colnames = ["q$(Int(q0*100))" for q0 in q]
replace!(colnames, "q0" => "min", "q50" => "median", "q100" => "max")
hcat(res, DataFrame(body, colnames))
end
quantile3(airquality) |> println; println()
quantile3(airquality2) |> println; println()
6×6 DataFrame Row │ variable min q25 median q75 max │ Symbol Float64 Float64 Float64 Float64 Float64 ─────┼─────────────────────────────────────────────────────── 1 │ Ozone 1.0 18.0 31.5 63.25 168.0 2 │ Solar_R 7.0 115.75 205.0 258.75 334.0 3 │ Wind 1.7 7.4 9.7 11.5 20.7 4 │ Temp 56.0 72.0 79.0 85.0 97.0 5 │ Month 5.0 6.0 7.0 8.0 9.0 6 │ Day 1.0 8.0 16.0 23.0 31.0 4×6 DataFrame Row │ variable min q25 median q75 max │ Symbol Float64 Float64 Float64 Float64 Float64 ─────┼─────────────────────────────────────────────────────── 1 │ Ozone 1.0 18.0 31.0 62.0 168.0 2 │ Solar_R 7.0 113.5 207.0 255.5 334.0 3 │ Wind 2.3 7.4 9.7 11.5 20.7 4 │ Temp 57.0 71.0 79.0 84.5 97.0
パーセンタイル値を求める q
値を指定できる。
quantile2(airquality2, (1:2:9)./10)
Row | variable | n | q10 | q30 | median | q70 | q90 |
---|---|---|---|---|---|---|---|
String | Int64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | Ozone | 111 | 11.0 | 20.0 | 31.0 | 49.0 | 89.0 |
2 | Solar_R | 111 | 37.0 | 137.0 | 207.0 | 248.0 | 285.0 |
3 | Wind | 111 | 5.7 | 8.0 | 9.7 | 11.5 | 14.9 |
4 | Temp | 111 | 64.0 | 73.0 | 79.0 | 83.0 | 90.0 |
分析に使用する変数は以下のように指定できるので,新しくデータフレームを作る必要はない。
quantile2(airquality[:, [1, 3, 4]])
Row | variable | n | min | q25 | median | q75 | max |
---|---|---|---|---|---|---|---|
String | Int64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | Ozone | 116 | 1.0 | 18.0 | 31.5 | 63.25 | 168.0 |
2 | Wind | 153 | 1.7 | 7.4 | 9.7 | 11.5 | 20.7 |
3 | Temp | 153 | 56.0 | 72.0 | 79.0 | 85.0 | 97.0 |
combine(), select()/select!(), transform()/transform!() を使う¶
Hadley Wickham が書いた論文 "The Split-Apply-Combine Strategy for Data Analysis" にあるように,統計解析は「データをグループ化し(split),それぞれのグループに関数を適用し(apply),結果をまとめ上げる(combine)」という三段階からなる。
Julia ではこれを,combin2
, select
/select!
, transform
/transform!
で行う。
combine()
は,第 1 引数で渡されたデータフレームについて,第 2 引数以降で指示された関数を適用し,結果をデータフレームにまとめる。
本来は複数のサブグループに対して用いるものであるが,データフレーム全体も 1 個のサブグループに過ぎないので,適用してもなんの問題もない。
第 2 引数は何通りかの書き方がある。
nrow
のようにデータフレームを引数とする関数名だけを書く場合。nrow()
はデータフレームに作用する関数で,データフレームの行数を返す。結果のデータフレームで,nrow
列に 153 が書かれている。結果は 1 個の数値に限らない。たとえばsize()
はデータフレームの行数と列数をタプルで返すので,結果の x1 の列に (153, 6) が書かれている。列名のx1
はデフォルトである。quantil()
はベクトルを返すが,tuple に変換しないと結果が見にくくなる。nrow => :名前
のように書くと,列名を定義できる。:nrow
と書いたので,結果のデータフレームで,列名がn
になっている。- 最初の
=>
の左にあるものはデータフレームの列を示す。シンボル(:
がついている)や文字列(" "
で囲まれている)や数字(列の番号)などの場合もある。 - 最初の
=>
の右にあるのものは関数(自作の関数も可)または無名関数である。 - 関数を何段階か順番に適用するときには無名関数で定義する(
=>
で関数を数珠つなぎにすることはできない)。 - 関数/無名関数の後に
=>
に続くものがある場合は,それは結果のデータフレームの列名になる(重複する名前は指定できない)。ない場合にはデフォルトでもっともらしい名前が付けられる。
function my_std(x)
mean = sum(x) / length(x)
var = sum((x .- mean).^2) / (length(x) - 1)
sqrt(var)
end
results = combine(airquality,
nrow,
size,
nrow => :n,
:Wind => std,
"Wind" => var => :var_wind,
3 => var => "var-wind",
3 => (x -> sqrt(var(x))) => "std-wind",
:Wind => my_std => "std-wind-2",
:Ozone => (x -> length(filter(!ismissing, x))) => "valid cases",
:Ozone => (x -> mean(skipmissing(x))) => :Mean,
:Temp => (x -> tuple(quantile(x, [0.25, 0.5, 0.75]))) => "%-tiles(25, 50, 75)"
)
typeof(results) |> println; println()
results |> println
DataFrame 1×11 DataFrame Row │ nrow x1 n Wind_std var_wind var-wind std-wind std-wind-2 valid cases Mean %-tiles(25, 50, 75) │ Int64 Tuple… Int64 Float64 Float64 Float64 Float64 Float64 Int64 Float64 Tuple… ─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ 153 (153, 6) 153 3.523 12.4115 12.4115 3.523 3.523 116 42.1293 ([72.0, 79.0, 85.0],)
グループごとの記述統計量¶
データフレームをグループを表す変数でグループ化し,それぞれのサブグループごとに統計量を求める。
airquality
データセットの :Month
でグループ化する。
select()
/select!()
はデータフレームの列を選択する。Not(シンボル)
,Not(シンボルベクトル)
はそれらを含まないことを表す。
groupby(データフレーム,グループ化に使う列の指定)
でグループ化する。列の指定はベクトルでもよい。
using RCall
airquality = rcopy(R"airquality");
gdf = groupby(select(airquality, Not(:Day)), :Month);
println("number of subgroup = ", length(gdf))
number of subgroup = 5
gdf[1]
は :Month
が 5 の場合のサブグループである。
first(gdf[1], 5)
Row | Ozone | Solar_R | Wind | Temp | Month |
---|---|---|---|---|---|
Int64? | Int64? | Float64 | Int64 | Int64 | |
1 | 41 | 190 | 7.4 | 67 | 5 |
2 | 36 | 118 | 8.0 | 72 | 5 |
3 | 12 | 149 | 12.6 | 74 | 5 |
4 | 18 | 313 | 11.5 | 62 | 5 |
5 | missing | missing | 14.3 | 56 | 5 |
describe() を使う¶
groupby()
により分割されたそれぞれの gdf[i]
は,データフレームなので,前節に述べた方法をそのまま適用できる。
describe(select(gdf[1], Not(:Month)))
Row | variable | mean | min | median | max | nmissing | eltype |
---|---|---|---|---|---|---|---|
Symbol | Float64 | Real | Float64 | Real | Int64 | Type | |
1 | Ozone | 23.6154 | 1 | 18.0 | 115 | 5 | Union{Missing, Int64} |
2 | Solar_R | 181.296 | 8 | 194.0 | 334 | 4 | Union{Missing, Int64} |
3 | Wind | 11.6226 | 5.7 | 11.5 | 20.1 | 0 | Float64 |
4 | Temp | 65.5484 | 56 | 66.0 | 81 | 0 | Int64 |
describe2(select(gdf[1], Not(:Month)))
Row | variable | sum | mean | var | std | minimum | q25 | median | q75 | maximum | nmissing |
---|---|---|---|---|---|---|---|---|---|---|---|
Symbol | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Int64 | |
1 | Ozone | 614.0 | 23.6154 | 493.926 | 22.2244 | 1.0 | 11.0 | 18.0 | 31.5 | 115.0 | 5 |
2 | Solar_R | 4895.0 | 181.296 | 13242.4 | 115.075 | 8.0 | 72.0 | 194.0 | 284.5 | 334.0 | 4 |
3 | Wind | 360.3 | 11.6226 | 12.4711 | 3.53145 | 5.7 | 8.9 | 11.5 | 14.05 | 20.1 | 0 |
4 | Temp | 2032.0 | 65.5484 | 46.9892 | 6.85487 | 56.0 | 60.0 | 66.0 | 69.0 | 81.0 | 0 |
quantile(select(gdf[end], Not(:Month)).Wind)
5-element Vector{Float64}: 2.8 7.550000000000001 10.3 12.325 16.6
quantile2(select(gdf[end], Not(:Month)))
Row | variable | n | min | q25 | median | q75 | max |
---|---|---|---|---|---|---|---|
String | Int64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | Ozone | 29 | 7.0 | 16.0 | 23.0 | 36.0 | 96.0 |
2 | Solar_R | 30 | 14.0 | 116.75 | 192.0 | 234.5 | 259.0 |
3 | Wind | 30 | 2.8 | 7.55 | 10.3 | 12.325 | 16.6 |
4 | Temp | 30 | 63.0 | 71.0 | 76.0 | 81.0 | 93.0 |
for
ループで,すべてのサブグループについて分析できる。
for df in gdf
println("\nMonth: ", df[1, :Month], "\n", describe(select(df, Not(:Month))))
end
Month: 5 4×7 DataFrame Row │ variable mean min median max nmissing eltype │ Symbol Float64 Real Float64 Real Int64 Type ─────┼─────────────────────────────────────────────────────────────────────────── 1 │ Ozone 23.6154 1 18.0 115 5 Union{Missing, Int64} 2 │ Solar_R 181.296 8 194.0 334 4 Union{Missing, Int64} 3 │ Wind 11.6226 5.7 11.5 20.1 0 Float64 4 │ Temp 65.5484 56 66.0 81 0 Int64 Month: 6 4×7 DataFrame Row │ variable mean min median max nmissing eltype │ Symbol Float64 Real Float64 Real Int64 Type ─────┼─────────────────────────────────────────────────────────────────────────── 1 │ Ozone 29.4444 12 23.0 71 21 Union{Missing, Int64} 2 │ Solar_R 190.167 31 188.5 332 0 Union{Missing, Int64} 3 │ Wind 10.2667 1.7 9.7 20.7 0 Float64 4 │ Temp 79.1 65 78.0 93 0 Int64 Month: 7 4×7 DataFrame Row │ variable mean min median max nmissing eltype │ Symbol Float64 Real Float64 Real Int64 Type ─────┼──────────────────────────────────────────────────────────────────────────── 1 │ Ozone 59.1154 7 60.0 135 5 Union{Missing, Int64} 2 │ Solar_R 216.484 7 253.0 314 0 Union{Missing, Int64} 3 │ Wind 8.94194 4.1 8.6 14.9 0 Float64 4 │ Temp 83.9032 73 84.0 92 0 Int64 Month: 8 4×7 DataFrame Row │ variable mean min median max nmissing eltype │ Symbol Float64 Real Float64 Real Int64 Type ─────┼──────────────────────────────────────────────────────────────────────────── 1 │ Ozone 59.9615 9 52.0 168 5 Union{Missing, Int64} 2 │ Solar_R 171.857 24 197.5 273 3 Union{Missing, Int64} 3 │ Wind 8.79355 2.3 8.6 15.5 0 Float64 4 │ Temp 83.9677 72 82.0 97 0 Int64 Month: 9 4×7 DataFrame Row │ variable mean min median max nmissing eltype │ Symbol Float64 Real Float64 Real Int64 Type ─────┼─────────────────────────────────────────────────────────────────────────── 1 │ Ozone 31.4483 7 23.0 96 1 Union{Missing, Int64} 2 │ Solar_R 167.433 14 192.0 259 0 Union{Missing, Int64} 3 │ Wind 10.18 2.8 10.3 16.6 0 Float64 4 │ Temp 76.9 63 76.0 93 0 Int64
変数ごとに統計量を一覧表示¶
変数ごとに一覧表示するには,いわゆるロングフォーマットにして処理をする必要がある。
long = stack(select(airquality, Not(:Day)), [:Ozone, :Solar_R, :Wind, :Temp])
gdf2 = groupby(long, [:variable, :Month]);
first(gdf2[1], 5)
Row | Month | variable | value |
---|---|---|---|
Int64 | String | Float64? | |
1 | 5 | Ozone | 41.0 |
2 | 5 | Ozone | 36.0 |
3 | 5 | Ozone | 12.0 |
4 | 5 | Ozone | 18.0 |
5 | 5 | Ozone | missing |
result = combine(gdf2, nrow,
:value => (x -> length(filter(!ismissing, x))) => :n,
:value => (x -> mean(skipmissing(x))) => :Mean,
:value => (x -> std(skipmissing(x))) => :SD,
);
result |> println
20×6 DataFrame Row │ variable Month nrow n Mean SD │ String Int64 Int64 Int64 Float64 Float64 ─────┼───────────────────────────────────────────────────── 1 │ Ozone 5 31 26 23.6154 22.2244 2 │ Ozone 6 30 9 29.4444 18.2079 3 │ Ozone 7 31 26 59.1154 31.6358 4 │ Ozone 8 31 26 59.9615 39.6812 5 │ Ozone 9 30 29 31.4483 24.1418 6 │ Solar_R 5 31 27 181.296 115.075 7 │ Solar_R 6 30 30 190.167 92.883 8 │ Solar_R 7 31 31 216.484 80.5683 9 │ Solar_R 8 31 28 171.857 76.8349 10 │ Solar_R 9 30 30 167.433 79.1183 11 │ Wind 5 31 31 11.6226 3.53145 12 │ Wind 6 30 30 10.2667 3.76923 13 │ Wind 7 31 31 8.94194 3.03598 14 │ Wind 8 31 31 8.79355 3.22593 15 │ Wind 9 30 30 10.18 3.46125 16 │ Temp 5 31 31 65.5484 6.85487 17 │ Temp 6 30 30 79.1 6.59859 18 │ Temp 7 31 31 83.9032 4.31551 19 │ Temp 8 31 31 83.9677 6.58526 20 │ Temp 9 30 30 76.9 8.35567
必要なら小分けにして表示すればよい。
result.variable
の型は String
なので,Wind
の結果だけを抽出するには result.variable .== "Wind"
とする(:Wind
ではないので注意)。
result[result.variable .== "Wind", [:n, :Mean, :SD]]
Row | n | Mean | SD |
---|---|---|---|
Int64 | Float64 | Float64 | |
1 | 31 | 11.6226 | 3.53145 |
2 | 30 | 10.2667 | 3.76923 |
3 | 31 | 8.94194 | 3.03598 |
4 | 31 | 8.79355 | 3.22593 |
5 | 30 | 10.18 | 3.46125 |
function temp(i, sym1, df)
combine(filter(x -> x[sym1] == i, df),
:Wind => (x -> length(filter(!ismissing, x))) => :n,
:Wind => mean,
:Wind => std)
end
result2 = temp(5, :Month, airquality);
for i in 6:9
append!(result2, temp(i, :Month, airquality))
end
result2
Row | n | Wind_mean | Wind_std |
---|---|---|---|
Int64 | Float64 | Float64 | |
1 | 31 | 11.6226 | 3.53145 |
2 | 30 | 10.2667 | 3.76923 |
3 | 31 | 8.94194 | 3.03598 |
4 | 31 | 8.79355 | 3.22593 |
5 | 30 | 10.18 | 3.46125 |
月ごとの結果が必要なら Month
でソートして表示する。
sort(result, :Month)
Row | variable | Month | nrow | n | Mean | SD |
---|---|---|---|---|---|---|
String | Int64 | Int64 | Int64 | Float64 | Float64 | |
1 | Ozone | 5 | 31 | 26 | 23.6154 | 22.2244 |
2 | Solar_R | 5 | 31 | 27 | 181.296 | 115.075 |
3 | Wind | 5 | 31 | 31 | 11.6226 | 3.53145 |
4 | Temp | 5 | 31 | 31 | 65.5484 | 6.85487 |
5 | Ozone | 6 | 30 | 9 | 29.4444 | 18.2079 |
6 | Solar_R | 6 | 30 | 30 | 190.167 | 92.883 |
7 | Wind | 6 | 30 | 30 | 10.2667 | 3.76923 |
8 | Temp | 6 | 30 | 30 | 79.1 | 6.59859 |
9 | Ozone | 7 | 31 | 26 | 59.1154 | 31.6358 |
10 | Solar_R | 7 | 31 | 31 | 216.484 | 80.5683 |
11 | Wind | 7 | 31 | 31 | 8.94194 | 3.03598 |
12 | Temp | 7 | 31 | 31 | 83.9032 | 4.31551 |
13 | Ozone | 8 | 31 | 26 | 59.9615 | 39.6812 |
14 | Solar_R | 8 | 31 | 28 | 171.857 | 76.8349 |
15 | Wind | 8 | 31 | 31 | 8.79355 | 3.22593 |
16 | Temp | 8 | 31 | 31 | 83.9677 | 6.58526 |
17 | Ozone | 9 | 30 | 29 | 31.4483 | 24.1418 |
18 | Solar_R | 9 | 30 | 30 | 167.433 | 79.1183 |
19 | Wind | 9 | 30 | 30 | 10.18 | 3.46125 |
20 | Temp | 9 | 30 | 30 | 76.9 | 8.35567 |
以下のようなプログラムを書くこともできる。
a = [describe(df) for df in gdf]
variable = [:Ozone, :Solar_R, :Wind, :Temp]
for i in variable
df = DataFrame[]
for j in 1:length(a)
pos = indexin([Symbol(i)], a[j][:, 1])[1]
ismissing(pos) && continue
row = DataFrame(a[j][pos, :])
if df == [] # first
df = row
else
append!(df, row)
end
end
println("\n", df[1,1])
rename!(df, 1 => :GroupKey)
nam = [replace("$key", "GroupKey: " => "") for key in keys(gdf)]
df[!, 1] = nam
println(df)
end
Ozone 5×7 DataFrame Row │ GroupKey mean min median max nmissing eltype │ String Float64 Real Float64 Real Int64 Type ─────┼───────────────────────────────────────────────────────────────────────────── 1 │ (Month = 5,) 23.6154 1 18.0 115 5 Union{Missing, Int64} 2 │ (Month = 6,) 29.4444 12 23.0 71 21 Union{Missing, Int64} 3 │ (Month = 7,) 59.1154 7 60.0 135 5 Union{Missing, Int64} 4 │ (Month = 8,) 59.9615 9 52.0 168 5 Union{Missing, Int64} 5 │ (Month = 9,) 31.4483 7 23.0 96 1 Union{Missing, Int64} Solar_R 5×7 DataFrame Row │ GroupKey mean min median max nmissing eltype │ String Float64 Real Float64 Real Int64 Type ─────┼───────────────────────────────────────────────────────────────────────────── 1 │ (Month = 5,) 181.296 8 194.0 334 4 Union{Missing, Int64} 2 │ (Month = 6,) 190.167 31 188.5 332 0 Union{Missing, Int64} 3 │ (Month = 7,) 216.484 7 253.0 314 0 Union{Missing, Int64} 4 │ (Month = 8,) 171.857 24 197.5 273 3 Union{Missing, Int64} 5 │ (Month = 9,) 167.433 14 192.0 259 0 Union{Missing, Int64} Wind 5×7 DataFrame Row │ GroupKey mean min median max nmissing eltype │ String Float64 Real Float64 Real Int64 Type ─────┼──────────────────────────────────────────────────────────────── 1 │ (Month = 5,) 11.6226 5.7 11.5 20.1 0 Float64 2 │ (Month = 6,) 10.2667 1.7 9.7 20.7 0 Float64 3 │ (Month = 7,) 8.94194 4.1 8.6 14.9 0 Float64 4 │ (Month = 8,) 8.79355 2.3 8.6 15.5 0 Float64 5 │ (Month = 9,) 10.18 2.8 10.3 16.6 0 Float64 Temp 5×7 DataFrame Row │ GroupKey mean min median max nmissing eltype │ String Float64 Real Float64 Real Int64 Type ─────┼────────────────────────────────────────────────────────────── 1 │ (Month = 5,) 65.5484 56 66.0 81 0 Int64 2 │ (Month = 6,) 79.1 65 78.0 93 0 Int64 3 │ (Month = 7,) 83.9032 73 84.0 92 0 Int64 4 │ (Month = 8,) 83.9677 72 82.0 97 0 Int64 5 │ (Month = 9,) 76.9 63 76.0 93 0 Int64
using DataFrames, Statistics
for variable in [:Ozone, :Solar_R, :Wind, :Temp]
println("\n$variable")
result = DataFrame[]
for i in 1:length(gdf)
each = dropmissing(select(gdf[i], [:Month, variable]))
res = combine(each, :Month => first => :Month, nrow => :n, variable => mean => :Mean, variable => std => :SD)
if result == []
result = res
else
result = vcat(result, res)
end
end
println(result)
end
Ozone 5×4 DataFrame Row │ Month n Mean SD │ Int64 Int64 Float64 Float64 ─────┼──────────────────────────────── 1 │ 5 26 23.6154 22.2244 2 │ 6 9 29.4444 18.2079 3 │ 7 26 59.1154 31.6358 4 │ 8 26 59.9615 39.6812 5 │ 9 29 31.4483 24.1418 Solar_R 5×4 DataFrame Row │ Month n Mean SD │ Int64 Int64 Float64 Float64 ─────┼───────────────────────────────── 1 │ 5 27 181.296 115.075 2 │ 6 30 190.167 92.883 3 │ 7 31 216.484 80.5683 4 │ 8 28 171.857 76.8349 5 │ 9 30 167.433 79.1183 Wind 5×4 DataFrame Row │ Month n Mean SD │ Int64 Int64 Float64 Float64 ─────┼───────────────────────────────── 1 │ 5 31 11.6226 3.53145 2 │ 6 30 10.2667 3.76923 3 │ 7 31 8.94194 3.03598 4 │ 8 31 8.79355 3.22593 5 │ 9 30 10.18 3.46125 Temp 5×4 DataFrame Row │ Month n Mean SD │ Int64 Int64 Float64 Float64 ─────┼──────────────────────────────── 1 │ 5 31 65.5484 6.85487 2 │ 6 30 79.1 6.59859 3 │ 7 31 83.9032 4.31551 4 │ 8 31 83.9677 6.58526 5 │ 9 30 76.9 8.35567
二変量統計¶
2 変量の統計量は,カテゴリー変数の場合はクロス集計表,間隔尺度変数の場合はピアソンの積率相関係数 cor()
,共分散 var()
,順序尺度変数の場合はスピアマンの順位相関係数 corspearman()
,ケンドールの順位相関係数 corkendall()
がある。
二重クロス集計表¶
2 変量の少なくとも 1 つがカテゴリー変数(名義尺度変数,順序尺度変数)の場合に用いる。対になる変数が間隔尺度変数,非尺度変数の場合は,階級分けして集計しなければならない。
用いるのは 1 変量の場合の度数分布を作成するのと同じ freqtable()
である。
esoph
はフランスにおける喫煙・飲酒と食道癌のケース・コントロール研究の結果である。集計データなので,元データに展開したものを用いる。
esoph = rcopy(R"esoph")
insertcols!(esoph, 6, :which => " ")
size(esoph) |> println
first(esoph, 2) |> println
(sum(esoph.ncases), sum(esoph.ncontrols)) |> println
(88, 6) 2×6 DataFrame Row │ agegp alcgp tobgp ncases ncontrols which │ Cat… Cat… Cat… Float64 Float64 String ─────┼──────────────────────────────────────────────────────── 1 │ 25-34 0-39g/day 0-9g/day 0.0 40.0 2 │ 25-34 0-39g/day 10-19 0.0 10.0 (200.0, 775.0)
ESOPH = DataFrame()
for row in eachrow(esoph)
row.which = "Case"
for j in 1:row.ncases
push!(ESOPH, row)
end
row.which = "Control"
for j in 1:row.ncontrols
push!(ESOPH, row)
end
end
freqtable(ESOPH.which)
2-element Named Vector{Int64} Dim1 │ ────────┼──── Case │ 200 Control │ 775
select!(ESOPH, Not([:ncases, :ncontrols])); # 症例数の 2 列を除く
size(ESOPH) |> println
first(ESOPH, 5) |> println
(975, 4) 5×4 DataFrame Row │ agegp alcgp tobgp which │ Cat… Cat… Cat… String ─────┼───────────────────────────────────── 1 │ 25-34 0-39g/day 0-9g/day Control 2 │ 25-34 0-39g/day 0-9g/day Control 3 │ 25-34 0-39g/day 0-9g/day Control 4 │ 25-34 0-39g/day 0-9g/day Control 5 │ 25-34 0-39g/day 0-9g/day Control
freqtable(ESOPH.agegp, ESOPH.alcgp)
6×4 Named Matrix{Int64} Dim1 ╲ Dim2 │ 0-39g/day 40-79 80-119 120+ ────────────┼─────────────────────────────────────────── 25-34 │ 61 45 5 5 35-44 │ 89 80 20 10 45-54 │ 78 81 39 15 55-64 │ 89 84 43 26 65-74 │ 71 53 29 8 75+ │ 27 12 2 3
freqtable(ESOPH.agegp, ESOPH.tobgp)
6×4 Named Matrix{Int64} Dim1 ╲ Dim2 │ 0-9g/day 10-19 20-29 30+ ────────────┼─────────────────────────────────────── 25-34 │ 70 19 11 16 35-44 │ 109 46 27 17 45-54 │ 104 57 33 19 55-64 │ 117 65 38 22 65-74 │ 99 38 20 4 75+ │ 26 11 3 4
using RCall
airquality = rcopy(R"airquality");
airquality2 = dropmissing(select(airquality, Not([:Month, :Day])));
相関係数,共分散¶
欠損値を含まない場合¶
それぞれの関数を使う¶
変数の対ごとに,関数を適用する。変数の指定法は色々ある。
cor(airquality2.Ozone, airquality2.Temp) |> println
corspearman(airquality2.Ozone, airquality2.Temp) |> println
corkendall(airquality2.Ozone, airquality2.Temp) |> println
cov(airquality2.Ozone, airquality2.Temp) |> println
cov(airquality2[!, :Ozone], airquality2.Temp) |> println
cov(airquality2[!, "Ozone"], airquality2.Temp) |> println
cov(airquality2[!, 1], airquality2.Temp) |> println
0.6985414096486394 0.7729319330689582 0.5861471249834473 221.52072072072067 221.52072072072067 221.52072072072067 221.52072072072067
combine() を使う¶
combine(airquality2,
[:Ozone, :Temp] => cor,
[:Ozone, :Wind] => cor,
[:Ozone, :Solar_R] => cor,
[:Ozone, :Solar_R] => corspearman,
)
Row | Ozone_Temp_cor | Ozone_Wind_cor | Ozone_Solar_R_cor | Ozone_Solar_R_corspearman |
---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | |
1 | 0.698541 | -0.612497 | 0.348342 | 0.348186 |
欠損値を含む場合¶
それぞれの関数を使う¶
欠損値が含まれるデータの場合は,両方の変数がともに missing
でないデータ対のみが分析に使われる。
方法はいくつかあるが,airquality2
を作ったときと同じように,select()
で 2 変数のみを含むデータフレームを作り,dropmissing()
で欠損値を含むデータを除くのがもっとも簡単であろう。
df = dropmissing(airquality[:, [:Ozone, :Temp]]);
first(df, 3)
Row | Ozone | Temp |
---|---|---|
Int64 | Int64 | |
1 | 41 | 67 |
2 | 36 | 72 |
3 | 12 | 74 |
cor(df.Ozone, df.Temp) |> println
corspearman(df.Ozone, df.Temp) |> println
corkendall(df.Ozone, df.Temp) |> println
cov(df.Ozone, df.Temp) |> println
0.6983603421509317 0.7740429554613014 0.5862988215264407 218.5212143928036
combine()
を使う¶
欠損値処理を行う無名関数の記述が長いので,何回も使う場合は関数定義をしておくとよい。
cor2(x, y) = cor(Matrix(dropmissing(DataFrame(x=x, y=y))))[1,2]
cov2(x, y) = cov(Matrix(dropmissing(DataFrame(x=x, y=y))))[1,2]
combine(airquality,
[:Ozone, :Temp] => ((x, y) -> cor(Matrix(dropmissing(DataFrame(x=x, y=y))))[1,2]),
[:Ozone, :Temp] => cor2,
[:Ozone, :Wind] => cor2,
[:Ozone, :Wind] => ((x, y) -> cov(Matrix(dropmissing(DataFrame(x=x, y=y))))[1,2]),
[:Ozone, :Wind] => cov2,
)
Row | Ozone_Temp_function | Ozone_Temp_cor2 | Ozone_Wind_cor2 | Ozone_Wind_function | Ozone_Wind_cov2 |
---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | 0.69836 | 0.69836 | -0.601547 | -70.9385 | -70.9385 |
corspearman(airquality2.Ozone, airquality2.Temp) |> println
corspearman(df.Ozone, df.Temp) |> println
0.7729319330689582 0.7740429554613014
cor(Matrix(airquality2)) |> display
4×4 Matrix{Float64}: 1.0 0.348342 -0.612497 0.698541 0.348342 1.0 -0.127183 0.294088 -0.612497 -0.127183 1.0 -0.49719 0.698541 0.294088 -0.49719 1.0
corspearman(Matrix(airquality2)) |> display
4×4 Matrix{Float64}: 1.0 0.348186 -0.605136 0.772932 0.348186 1.0 -0.0616964 0.209537 -0.605136 -0.0616964 1.0 -0.499323 0.772932 0.209537 -0.499323 1.0
corkendall(Matrix(airquality2)) |> display
4×4 Matrix{Float64}: 1.0 0.240319 -0.440459 0.586147 0.240319 1.0 -0.0430135 0.142902 -0.440459 -0.0430135 1.0 -0.362387 0.586147 0.142902 -0.362387 1.0
cov(Matrix(airquality2)) |> display
4×4 Matrix{Float64}: 1107.29 1056.58 -72.5112 221.521 1056.58 8308.74 -41.2448 255.468 -72.5112 -41.2448 12.6573 -16.8572 221.521 255.468 -16.8572 90.8203
欠損値を含む場合¶
対処法は 2 つある。1 つは,dropmissing()
によって欠損値を含まない(リストワイズ除去)データフレームにして 「2.2.1. 欠損値を含まない場合」 を適用する。
もう 1 つは,すべての 2 変数の組み合わせにおいて,2 変数の場合に示したペアワイズ除去により統計量を計算する。
そのための関数を以下に示しておく。
function multi(df, FUNC; samplesize=false)
n = ncol(df)
A = ones(n, n)
N = zeros(Int, n, n)
for i in 1:n
x = filter(!ismissing, df[:, i])
FUNC == cov && (A[i, i] = var(x))
N[i, i] = length(x)
end
for i in 1:n-1
for j in i+1:n
df2 = Matrix(dropmissing(df[:, [i, j]]))
A[j, i] = A[i, j] = FUNC(df2[:, 1], df2[:, 2])
N[j, i] = N[i, j] = size(df2, 1)
end
end
return (samplesize ? (A, N) : A)
end;
samplesize=false
を指定すれば,2変数の組み合わせにおける有効サンプルサイズの行列を返す。そのときには,戻り値を 2 個受け取るので,左辺には 2 個の変数を指定しなければならない。1 つ目は統計量の行列,2 つ目がサンプルサイズ行列である。
df = select(airquality, Not([:Month, :Day]))
A, N = multi(df, cor, samplesize=true)
A|> display
N|> display
4×4 Matrix{Float64}: 1.0 0.348342 -0.601547 0.69836 0.348342 1.0 -0.0567917 0.27584 -0.601547 -0.0567917 1.0 -0.457988 0.69836 0.27584 -0.457988 1.0
4×4 Matrix{Int64}: 116 111 116 116 111 146 146 146 116 146 153 153 116 146 153 153
multi(df, corspearman) |> display
4×4 Matrix{Float64}: 1.0 0.348186 -0.590155 0.774043 0.348186 1.0 -0.000977333 0.207428 -0.590155 -0.000977333 1.0 -0.446541 0.774043 0.207428 -0.446541 1.0
multi(df, corkendall) |> display
4×4 Matrix{Float64}: 1.0 0.240319 -0.42836 0.586299 0.240319 1.0 0.00067856 0.144234 -0.42836 0.00067856 1.0 -0.322242 0.586299 0.144234 -0.322242 1.0
multi(df, cov) |> display
4×4 Matrix{Float64}: 1088.2 1056.58 -70.9385 218.521 1056.58 8110.52 -17.946 229.16 -70.9385 -17.946 12.4115 -15.2721 218.521 229.16 -15.2721 89.5913
多変量統計¶
x = freqtable(ESOPH.agegp, ESOPH.alcgp, ESOPH.tobgp)
for i in 1:size(x, 3)
println("\nESOPH.tobgp: ", names(x)[3][i])
display(x[:, :, i])
end
ESOPH.tobgp: 0-9g/day
6×4 Named Matrix{Int64} Dim1 ╲ Dim2 │ 0-39g/day 40-79 80-119 120+ ────────────┼─────────────────────────────────────────── 25-34 │ 40 27 2 1 35-44 │ 60 35 11 3 45-54 │ 46 38 16 4 55-64 │ 49 40 18 10 65-74 │ 48 34 13 4 75+ │ 18 5 1 2
ESOPH.tobgp: 10-19
6×4 Named Matrix{Int64} Dim1 ╲ Dim2 │ 0-39g/day 40-79 80-119 120+ ────────────┼─────────────────────────────────────────── 25-34 │ 10 7 1 1 35-44 │ 14 23 6 3 45-54 │ 18 21 14 4 55-64 │ 22 21 15 7 65-74 │ 14 10 12 2 75+ │ 6 3 1 1
ESOPH.tobgp: 20-29
6×4 Named Matrix{Int64} Dim1 ╲ Dim2 │ 0-39g/day 40-79 80-119 120+ ────────────┼─────────────────────────────────────────── 25-34 │ 6 4 0 1 35-44 │ 7 14 2 4 45-54 │ 10 15 5 3 55-64 │ 12 17 6 3 65-74 │ 7 9 3 1 75+ │ 0 3 0 0
ESOPH.tobgp: 30+
6×4 Named Matrix{Int64} Dim1 ╲ Dim2 │ 0-39g/day 40-79 80-119 120+ ────────────┼─────────────────────────────────────────── 25-34 │ 5 7 2 2 35-44 │ 8 8 1 0 45-54 │ 4 7 4 4 55-64 │ 6 6 4 6 65-74 │ 2 0 1 1 75+ │ 3 1 0 0