自分用の備忘録として作成しましたが、ずっとストレージの奥で眠っていたままでした。
Julia 1.6.3で動作確認したので公開致します。
長いのでインラインで説明しますのでご容赦下さい。
サンプル用のデータは一番下に記述してあります。
サンプルの中にある文字列は、Juliaの解説文からコピペしてあります。
DataFrameMemo01.jl
#=────────────────────────────────────────────────────
表題
DataFrame.jl使用方法のまとめ
目的
自分用のsnippet, Memo and 備忘録として
参照サイト
DataFramesの本家サイト
https://juliadata.github.io/DataFrames.jl/stable/
第1版 MAY/21/2020 ドラフト作成
第2版 MAY/31/2020 説明順の整理
第3版 OCT/28/2021 動作確認
最終検証環境
Julia 1.6.3 (2021-09-23)
DataFrame.jl v1.2.2
MacOS 12.0.1
第1章 DataFrame型の宣言方法
第2章 DataFrame全体の種々な情報を得る
第3章 カラム名の変更
第4章 データ取り出し系
第5章 データ加工系
第6章 using Statistics.jlと組み合わせる簡単なテスト
───────────────────────────────────────────────────────=#
# 使用パッケージ群
using DataFrames # DataFrameを使用するためのパッケージ
#using DataFramesMeta
using CSV # CSVを読むためのパッケージ
using Statistics # 統計パッケージ(サンプルで使用)
#using CategoricalArrays
#using Query
#using SQLite
#=────────────────────────────────────────────────────
第1章 DataFrame型の宣言方法
1-1. 直接生成する方法
1-2. 空のデータフレーム型の変数を用意して追加していく方法
1-3. dictionaryから作成する方法
1-4. 行列から作る方法
1-5. CSVファイルから作る方法
───────────────────────────────────────────────────────=#
#=======================================================
1-1. 直接作成する方法。
========================================================#
# カラムとデータを指定して作る方法
df = DataFrame(COL1 = 1:4, COL2 = ["AA", "BB", "CC", "DD"])
df = DataFrame(COL1 = rand(Int64, 10))
df = DataFrame([rand(Int64, 10)],[:COL1])
df = DataFrame([rand(Int64, 10),rand(Int64, 10)],[:COL1,:COL2])
df = DataFrame(COL1 = rand(Int64, 10),COL2 = rand(Int64, 10))
#=======================================================
1-2. 空のDataFrame型の変数を用意して追加していく方法
========================================================#
df = DataFrame()
df.A = 1:10
df.B = 11:20
#=======================================================
1-3. dictionaryから作成する方法
========================================================#
df = DataFrame(:A => 1:5,:B => rand(5),:C => ["A", "B", "C", "A", "B"])
#=======================================================
1-4. 行列から作る方法
========================================================#
data = [
2 1.002 "AA002"
3 1.003 "AA003"
4 1.004 "AA004"
5 1.005 "AA005"
];
df = DataFrame(data,:auto)
#=======================================================
1-5. CSVファイルから作る方法
(システム設計業としては、最も実用的と考えます)
私は、DBからCSVを生成して、Excel等で確認してから利用する事が多いです。
他にも方法が有るようです。
========================================================#
# CSVとして読み込んでDataFrameに変換する例
df = CSV.File("dftest.csv") |> DataFrame
# DataFrameとして返すように指定する例
df = CSV.read("dftest.csv", DataFrame)
#= 処理時間を計測しましたが、データ量が少ないためかほとんど差がありませんでした。
メモリの使用量を考慮すると大きなデータの場合 readを使う方がベターと考えます。
ただし、一時的な利用なので気にしないという選択も有り。
julia> @time df = CSV.File("dftest.csv") |> DataFrame ;
0.000637 seconds (449 allocations: 37.078 KiB)
julia> @time CSV.read("dftest.csv", DataFrame) ;
0.000696 seconds (440 allocations: 33.188 KiB)
=#
# データの中身を変更/削除した時に備えて、復旧作業用にバックアップを用意しておく
dfBackup = copy(df)
#=─────────────────────────────────────────────────────
第2章 DataFrame全体の種々な情報を得る
2-1. 大きさを求める
2-2. カラム名を求める
2-3. データ型を取得
2-4. 簡易統計値を求める
───────────────────────────────────────────────────────=#
#=======================================================
2-1. 大きさを求める
========================================================#
size(df, 1) # 行数
size(df, 2) # 列数
size(df) # 行数、列数をタプルで返す
# 当然、以下の指定方法でも問題ない
# julia> size(df)[1] # 40
# julia> size(df)[2] # 10
#=======================================================
2-2. カラム名を求める
========================================================#
names(df) # カラム名をベクトルで求める
names(df)[2:5] # 指定したカラム名を求める
#=======================================================
2-3. データ型を取得
========================================================#
eltype.(eachcol(df)) # カラムの各型をベクトルで求める
eltype.(eachcol(df))[1:4] # 指定した連続したカラムのデータ型を求める
eltype(df[:,1]) # 指定した1つのカラムのデータ型を求める
eltype(df[!,1]) # 上記と同じ
#=======================================================
2-4. 簡易統計値を求める
========================================================#
describe(df) # 全体の簡易統計値を求める
describe(df[:,[1]]) # 指定したカラムの簡易統計値を求める
describe(df[!,[1]]) # 上記と同じ
describe(df[:,1:2]) # 上記と同じ
describe(df[!,1:2]) # 上記と同じ
#=======================================================
julia> println(describe(df))
10×7 DataFrame
Row │ variable mean min median max nmissing eltype
│ Symbol Union… Any Union… Any Int64 Type
─────┼─────────────────────────────────────────────────────────────────────────────────────────
1 │ COL01 20.5 1 20.5 40 0 Int64
2 │ COL02 1.0205 1.001 1.0205 1.04 0 Float64
3 │ COL03 AA001 AA040 0 String
4 │ COL04 BB0001 BB0040 0 String
5 │ COL05 1.0002e5 100001 1.0002e5 100040 0 Int64
6 │ COL06 -1.0002e5 -100040 -1.0002e5 -100001 0 Int64
7 │ COL07 Scientific yet 0 String
8 │ COL08 48.448 4.16775 53.877 98.7265 0 Float64
9 │ COL09 -0.104227 -2.46575 -0.0754626 2.02717 0 Float64
10 │ COL10 51.7617 4.01664 51.5799 99.2162 8 Union{Missing, Float64}
[注意事項]
- mean は算術平均という意味。相加平均とも言う。要素の合計を要素の数で割った値
averageは平均と訳され算術平均を示す事が多いが、概念的にはもっと広い意味を持ちます。例えば加重平均とか。
統計ではmeanを使う事が多い。
財務系英語でもよく使われるが、もっと明確な名称を使う事もある。Arithmetic meanとか。
使った場合には、ちょっと意識高い系に見られる・・・・かも。
他の意味と混同しないように the mean と記述する文書も多い
- COL10のeltypeが、Union{Missing, Float64}となっているが、Missing型, Float64型の混在という意味(わざとそういうデータを用意した)
- 代表値として表現する時はは、使用する分野によって期待されるものが異なるので、確認をした方が良い
========================================================#
describe(df)[!,2] # mean列の取り出し
describe(df)[!,:mean]
describe(df)[1,2] # 1行目(COL01)のmeanの取り出し
describe(df)[1,:mean]
describe(df)[1,:] # COL01の統計データ取り出し
describe(df)[1:3,:] # COL01〜3の統計データ取り出し(意味があるのか?)
# 統計計算は、describe関数より、Statistics.jlを使った方が
# 応用が利くような気がする
# '第6章 using Statistics.jlと組み合わせる簡単なテスト'を参照してください。
#=────────────────────────────────────────────────────
第3章 カラム名の変更
3-1. リストで変更
3-2. 辞書で変更
3-3. 一つだけ直接変更する例
───────────────────────────────────────────────────────=#
#=======================================================
3-1. リストで変更
========================================================#
#=
rename 関数
例1. リストで変更
rename!(df,[:A,:B,:C,:D,:E,:F,:G,:H,:I,:J])
julia> nc = [:W, :X, :Y, :Z];
julia> rename!(df, ncolname)
6×4 DataFrame
│ Row │ W │ X │ Y │ Z │
│ │ Int64 │ Float64 │ String │ Int64 │
├─────┼───────┼──────────┼────────┼───────┤
│ 1 │ 3 │ 0.858239 │ C │ 120 │
│ 2 │ 4 │ 0.578579 │ A │ 160 │
│ 3 │ 1 │ 0.558038 │ A │ 40 │
│ 4 │ 2 │ 0.319128 │ B │ 80 │
│ 5 │ 5 │ 0.142839 │ X │ 200 │
│ 6 │ 10 │ 0.1 │ C │ 20 │
#=======================================================
3-2. 辞書で変更
========================================================#
rename( df, [2 => :N02, 3 => :N03]) # 新しいデータセットを返す
rename!(df, [2 => :N02, 3 => :N03]) # dfを書き換える
julia> nc = Dict(:W=>:A, :X =>:B, :Y=>:C, :Z=>:D);
julia> rename!(df, nc)
6×4 DataFrame
│ Row │ A │ B │ C │ D │
│ │ Int64 │ Float64 │ String │ Int64 │
├─────┼───────┼──────────┼────────┼───────┤
│ 1 │ 3 │ 0.858239 │ C │ 120 │
│ 2 │ 4 │ 0.578579 │ A │ 160 │
│ 3 │ 1 │ 0.558038 │ A │ 40 │
│ 4 │ 2 │ 0.319128 │ B │ 80 │
│ 5 │ 5 │ 0.142839 │ X │ 200 │
│ 6 │ 10 │ 0.1 │ C │ 20 │
#=======================================================
3-3. 一つだけ直接変更する例
========================================================#
# 地味に便利
julia> rename!(df,:COL01 => :a)
40×10 DataFrame
Row │ a COL02 COL03 COL04 COL05 COL06 COL07 COL08 COL09 COL10
│ Int64 Float64 String String Int64 Int64 String Float64 Float64 Float64?
─────┼─────────────────────────────────────────────────────────────────────────────────────────────────
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
40 rows omitted
julia> rename!(df,:1 => :COL01)
40×10 DataFrame
Row │ COL01 COL02 COL03 COL04 COL05 COL06 COL07 COL08 COL09 COL10
│ Int64 Float64 String String Int64 Int64 String Float64 Float64 Float64?
─────┼─────────────────────────────────────────────────────────────────────────────────────────────────
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
40 rows omitted
=#
#=────────────────────────────────────────────────────
第4章 データ取り出し系
4-1. その前に行と列
4-2. 指定列の取り出し
4-3. 指定行の取り出し
4-4. 矩形の取り出し
4-5. 条件をつけて抽出する
───────────────────────────────────────────────────────=#
#=======================================================
4-1. その前に行と列
========================================================#
#=
その前に、各セル(敢えてセルと呼びますが)へのアクセスはdf[行、列]と記述します。
[重要事項説明]
データの参照とコピー
データ群を取り出すと操作は2種類の結果を発生させます。
1. コピー
取り出されたデータは、オリジナルとは'別の領域'に確保されます。
当然、コピーされたデータの内容を変更してもオリジナルに影響を与えません。
2. 参照
取り出されたデータの内容は、オリジナルデータと'同じ領域'を参照しています。
結果、データを変更するとオリジナルデータも変更されます。
このような作用を持つ関数に対して、関数名の後ろに '!'を付けるのが通例になっていますが、
全てそうなっているようではないので、使用する際には確認した方がベターです。
下記例では、 参照系は # ref, コピー系は # copy と記述しておきます。
これは親切ですね。ただ残念なことに面倒なので記述するのは最初だけです。
実際に使用する場合、簡単な試験を行うことをお勧めします。
=#
#=======================================================
4-2. 指定列の取り出し
========================================================#
df[!,1] # ref 戻り値が参照(型?)なので戻り値を修正すると元のデータセットも修正される
df[!,"COL01"] # ref
df[!,:COL01] # ref
df.COL01 # ref
df."COL01" # ref
df.:COL01 # ref
df[:,1] # copy 戻り値はコピー(型?)なので、修正しても元のデータセットに影響しない
df[:,"COL01"] # copy
df[:,:COL01] # copy
select(df,1) # copy
select(df,"COL01") # copy
select(df,:COL01) # copy
# 連続列の取り出し
# 2列目から5列までを取り出す例(2,3,4,5列)
df[:,2:5] # copy
select(df,2:5) # copy
# 結果をMatrixに変換したければ、以下の操作が可能です。
select(df,2:5) |> Matrix
#=
julia> select(df,2:5) |> Matrix
40×4 Matrix{Any}:
1.001 "AA001" "BB0001" 100001
1.002 "AA002" "BB0002" 100002
⋮
1.039 "AA039" "BB0039" 100039
1.04 "AA040" "BB0040" 100040
julia> size(select(df,2:5) |> Matrix )
(40, 4)
=#
# 列を取り出す
# 1から10までにStep 3 で取り出す
# 1,4,7,10
df[:,1:3:10]
df[:,1:3:end]
select(df,1:3:10)
# 1:3:end のように 'end'は使えない。
# どうしても'end'相当を指定したい場合には、以下のように記述する
select(df,1:3:size(df, 2))
#指定列を指定順に取り出す
df[:,[4,2,1]]
df[:,["COL04","COL02","COL01"]]
df[:,[:COL04,:COL02,:COL01]]
# 列名を変数経由でカラム名で指定する事も出来る
sel1 = [4,2,1]
df[:,sel1] # 4,2,1列を指定順に取り出す。
sel2 = ["COL04","COL02","COL01"] # 列名を別途、文字列で指定する事も出来る
df[:,sel2]
sel3 = [:COL04,:COL02,:COL01]
df[:,sel3]
# select文を使った例
# select!を使うと以下例のdfに上書きされる。
select(df,[4,2,1])
select(df,Not([4,2,1])) # 4,2,1【以外】 <- 地味に便利
select(df,["COL04","COL02","COL01"])
select(df,[:COL04,:COL02,:COL01])
select(df,sel2)
select(df,Not(sel2))
select(df,sel3)
#=======================================================
4-3. 指定行の取り出し
========================================================#
# 行の指定 (無条件)
df[1,:] # 1行目のみ
df[Not(1),:] # 1行目以外
df[[end],:] # 最後の行
first(df) # 最初の行の取り出し
first(df,3) # 最初の3行の取り出し
last(df) # 最後の行の取り出し
last(df,2) # 最後の2行の取り出し
# 連続行の取り出し (無条件)
# 2行目から5行までを取り出す例(2,3,4,5行)
df[2:5,:]
#指定列を指定順に取り出す (無条件)
df[[4,2,1],:]
# 矩形の取出し (無条件)
# 2から5行までの取出しと、1から3列までの矩形を得る
df[2:5, 1:3]
# 1,3,7行目の1,3,6列の矩形を撮り出す
df[[1,3,7], [1,3,6]]
# 【条件】をつけて抽出する <- 地味に便利
df[df.:COL01.>10,:]
df[(df.:COL01.>10) .& (df.COL07 .== "to"),:]
df[(df.:COL01.>10) .& (df.COL08 .> 50),:]
df[df.:COL01.==10,:] # 値が10の行を取り出す
#=────────────────────────────────────────────────────
第5章 データ加工系
5-1. ソーティング
5-2. 特定CELL (データ)の書換え
5-3. 特定列データの書換え
5-4. 条件付き列データの書換え
5-5. 行削除系
5-6. 行追加系
5-7. 列削除系
5-8. DataFrame連結系(横)
5-9. DataFrame連結系(縦)
5-10. join
5-11. groupby
───────────────────────────────────────────────────────=#
#=======================================================
5-1. ソーティング (ソーティングがデータ加工なのか議論は別にして)
========================================================#
# 6列目をキーに昇順に並べかける
# COL06は逆順でデータが用意されているので
# 結果として COL01が逆順に表示されれば正解
sort(df,6)
sort(df,:COL06)
# 逆順にソートするには、rev=trueを指定する
# COL01は昇順データなので、逆順で表示されれば正解
sort(df,1,rev=true)
# COL08を1stキー、COOL01を2ndキーに昇順でソートする
sort(df,[8,1])
sort(df,[8,1],rev=true)
#=
一度に複雑なソートを行う事は出来ないので、複数回実行する事になる(はず)
私の場合、DBから持ってくることが多いので、事前にSQL等々で並べ替えておくことが多い。
=#
#=======================================================
5-2. 特定CELL (データ)の書換え
========================================================#
df[3,1] = 30 # 値3 を 30に書き換える
df[4,:COL01] = 40
df.COL01[5] = 50
df.:COL01[6]=60
df."COL01"[7]=70
# データを初期化する
df = copy(dfBackup)
#=======================================================
5-3. 特定列データの書換え 無条件バージョン
========================================================#
# COL01の値に 10を掛ける(左辺右辺で :,! の混在可能ですが、統一した方が良いと思います。)
df[:,1] = df[:,1] .*10
df[!,1] = df[!,1] .*10
df[:,:COL01] = df[:,:COL01] .*10
df[!,:COL01] = df[!,:COL01] .*10
# データを初期化する
df = copy(dfBackup)
#=======================================================
5-4. 条件付き列データの書換え
データの値を元に判断し、演算した結果でデータを書き換えるという方式
実業務で割と多い処理がこの処理なのかと思います。
さらに効率良い方式を検討中です
========================================================#
# 前処理として書換え作業用ヘルパー関数を作成する
# 2倍にして返す関数
function helper1(args)
return args * 2 # 私は必ず return を記述する(ように心がけている)
end
# 条件付きで2倍にして返す
function helper2(args)
if(args > 10)
return args * 2
end
return args
end
# COL01列をデータ変換を適用する
df[!,1] = helper1.(df[!,1])
df[!,:COL01] = helper1.(df[!,:COL01])
# こっちの方がシンプルで明確です。(私好み?)
df.:COL01 = sin.(df.:COL01)
df.:COL01 = helper1.(df.:COL01)
df.COL01 = sin.(df.COL01)
df.COL01 = helper1.(df.COL01)
# 条件付きで抽出 値が10以上のデータを取り出す
df[df.:COL01.>10,:]
# 条件付きで書換える 値が10以上の時にゼロを代入する
df[(df.:COL01 .> 10),:COL01] .= 0 # セル内のデータを参照していない事に着目
# セル内のデータを参照して値を変更。ただし条件式を helper2に記述する事になる
df = copy(dfBackup)
df.:COL01 = helper2.(df.:COL01)
#=======================================================
5-5. 行削除系
========================================================#
# データを初期化する
df = copy(dfBackup)
# 削除系
# 行を削除する deleterows!が非推奨になっているのでdelete!を使う
delete!(df, 4) # 先頭の4行を削除
# 連続して行を削除する
delete!(df,1:10)
delete!(df,1:2:10) # ただし、delete!(df,1:2:end)は使えない
# 条件付き行削除 filter系は その2 でもっと説明する予定
filter(row -> row[:COL01] > 10, df) # 値が10以上の行を削除したデータセットを作成
filter!(row -> row[:COL01] > 10, df) # 値が10以上の行を削除しdfを上書きする
#=======================================================
5-6. 行追加系
========================================================#
df = copy(dfBackup) # データを初期化してから試す
# push!
push!(df,df[1,:])
#=======================================================
5-7. 列追加系
5-8.も参照ください。
========================================================#
# 単純に列を右端に追加する場合
# 条件としては、列の長さが一致している事(この場合には40)
df = copy(dfBackup) # データを初期化してから試す
df.COL11 = rand(40) # 40個もサンプルを作るのが面倒なので手抜き
df.COL12 = rand(size(df)[1]) # もっと手抜きかもしれない
#=======================================================
5-8. DataFrame連結系(横)
========================================================#
# データを初期化する
df = copy(dfBackup)
# サンプル用のDataFrameを左右に分割したデータを作成する
dfleft = df[:,1:5]
dfright = df[:,6:end]
# makeunique=trueの時は重複したカラムに _ を先頭に追加してUniqueに名前をつけて追加処理する
hcat(dfleft,dfright, makeunique=true) # copy
hcat(dfleft,dfright, copycols=true) # copy デフォルト
hcat(dfleft,dfright, copycols=false) # ref
#=
** 重要 **
[copycols=false]の場合、
hcatが返すDataFrameには、ソースデータフレームからコピーされたカラムが含まれます。
この意味は、結合されたデータセットは、元になる2つのデータセットを参照するデータセットとして
構成される。生成されたデータセットを変更すると、対応する元のデータセットも変更される
取り扱いが面倒なので、明確な自分の意図がある場合以外は使わない方がベターな気がする。
=#
#=======================================================
5-9. DataFrame連結系(縦)
========================================================#
# データを初期化する
df = copy(dfBackup)
# サンプル用のDataFrameを上下に分割したデータを作成する
upperdf = df[1:20,:]
lowerdf = df[21:end,:]
# upperdfにlowerdfが追加される
vcat(upperdf,lowerdf) # 追加された結果が返される(upperdef,lowerdfには影響ない)
append!(upperdf, lowerdf) # upperdfに追加されるので注意
#=======================================================
5-10. join
========================================================#
#=
Joinは複数のパターンがあり、それぞれ関数が用意されている
Joinの基本は、キーとするカラム指定してDataFrameの連結を行うことである。
RDBMS(Relational DataBase Management System)の基本機能の一つです。
ここでは、データを新規に作成するのが面倒なので、DataFrames.jlのドキュメントから
引用して説明します。https://dataframes.juliadata.org/stable/man/joins/
この手のRDBMS系の処理の場合は事前にDBを利用して処理をしておくのが望ましいし汎用性が高いと思う。
特にデータ量が多い場合にはDB利用がベターと考えます。
=#
#=======================================================
5-10-1. innerjoin
========================================================#
#=
内部結合とも呼ばれる。渡されたすべてのDataFrameに存在するキーの値の行が出力される
innerjoin(people, jobs, on = :ID)は、キーとしてIDを指定してDataFrameを結合する
二つのDataFrameのIDで共通するのは、ID=20だけなので、得られたDataFrameは以下になる。
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
innerjoin(people, jobs, on = :ID)
#=
1×3 DataFrame
Row │ ID Name Job
│ Int64 String String
─────┼─────────────────────────
1 │ 20 John Doe Lawyer
=#
#=======================================================
5-10-2. leftjoin
========================================================#
#=
左外部結合とも呼ばれる。
1番目(左)の引数に存在するキーの値の行が,2番目(右)の引数にその値が存在するかどうかにかかわらず出力される
例では、peopleには、ID=60が存在するが、jobsには無い。従って連結されたDataFrameのID=60のJobは'missing'となる
また、jobsには、ID=40が存在するが、peopleには、存在しないの為、連結されたDataFrameには含まれない
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
leftjoin(people, jobs, on = :ID)
#=
2×3 DataFrame
Row │ ID Name Job
│ Int64 String String?
─────┼──────────────────────────
1 │ 20 John Doe Lawyer
2 │ 60 Jane Doe missing
=#
#=======================================================
5-10-3. rightjoin
========================================================#
#=
右外部結合とも呼ばれる。
leftjoinに渡すDataFrameの順が入れ替わった物と同等
2番目(右)の引数に存在するキーの値の行が,1番目(左)の引数にその値が存在するかどうかにかかわらず出力される
例では、peopleには、ID=40が存在するが、jobsには無い。従って連結されたDataFrameのID=40のJobは'missing'となる
また、jobsには、ID=60が存在するが、peopleには、存在しないの為、連結されたDataFrameには含まれない
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
rightjoin(people, jobs, on = :ID)
#=
2×3 DataFrame
Row │ ID Name Job
│ Int64 String? String
─────┼─────────────────────────
1 │ 20 John Doe Lawyer
2 │ 40 missing Doctor
=#
#=======================================================
5-10-4. outerjoin
========================================================#
#=
外部結合とも呼ばれる。内部結合の機能+片方にしか存在しないDataFrameのデータも結合する
得られたDataFrameの'missing'に着目してください。
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
outerjoin(people, jobs, on = :ID)
#=
3×3 DataFrame
Row │ ID Name Job
│ Int64 String? String?
─────┼──────────────────────────
1 │ 20 John Doe Lawyer
2 │ 60 Jane Doe missing
3 │ 40 missing Doctor
=#
#=======================================================
5-10-5. semijoin
========================================================#
#=
準結合とも呼ばれる
innerjoinと似ていますが、出力は最初の(左)引数の列に制限されます。
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
# peopleに定義されているIDとNameだけが出力されます。
semijoin(people, jobs, on = :ID)
#=
1×2 DataFrame
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 20 John Doe
=#
# innerjoinとの違いを確認してください。Jobが含まれています。
innerjoin(people, jobs, on = :ID)
#=
1×3 DataFrame
Row │ ID Name Job
│ Int64 String String
─────┼─────────────────────────
1 │ 20 John Doe Lawyer
=#
#=======================================================
5-10-6. antijoin
========================================================#
#=
最初(左)の引数には存在するが、2番目(右)の引数には存在しないキーの値の行を含みます。
jobsには含まれているが、peopleには含まれていないIDに対して出力されている点に着目
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
antijoin(people, jobs, on = :ID)
#=
1×2 DataFrame
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 60 Jane Doe
=#
#=======================================================
5-10-7. antijoin
========================================================#
#=
交差結合、デカルト積(Cartesian)とも呼ばれる
2つのDataFrameの全ての組み合わせを取得するものです。
makeunique : false(デフォルト)の場合,結合されていない列で重複した名前が見つかった場合にエラーが発生します.
trueの場合,重複した名前には_iというサフィックスが付きます(iは最初の重複に対して1から始まります)
=#
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"])
people = DataFrame(ID = [20, 60], Name = ["John Doe", "Jane Doe"])
crossjoin(people, jobs, makeunique = true)
#=
4×4 DataFrame
Row │ ID Name ID_1 Job
│ Int64 String Int64 String
─────┼────────────────────────────────
1 │ 20 John Doe 20 Lawyer
2 │ 20 John Doe 40 Doctor
3 │ 60 Jane Doe 20 Lawyer
4 │ 60 Jane Doe 40 Doctor
=#
#=======================================================
5-11. groupby
========================================================#
#=
指定したカラムでグループ化されたDataFrameを作成する
複数のカラムを指定する場合には、[:ID, :Name]の形式で記述する
=#
groupby(people, :ID)
#=
GroupedDataFrame with 2 groups based on key: ID
First Group (1 row): ID = 20
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 20 John Doe
⋮
Last Group (1 row): ID = 60
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 60 Jane Doe
=#
#上記のように複数のグループが生成された場合、以下の方法でグループ数を得ることが出来る
length(groupby(people, :ID)) # 2
#実際にインデックスを使用してアクセスしてみる
groupby(people, :ID)[1]
#=
1×2 SubDataFrame
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 20 John Doe
=#
groupby(people, :ID)[2]
#=
1×2 SubDataFrame
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 60 Jane Doe
=#
#GroupKeyを求めることが出来る
gd = groupby(people,:ID)
keys(gd)
#=
julia> keys(gd)
2-element DataFrames.GroupKeys{GroupedDataFrame{DataFrame}}:
GroupKey: (ID = 20,)
GroupKey: (ID = 60,)
=#
#指定した値を持つキーのグループを求めることが出来る
#キーの指定には、:IDという形式は使えないので注意
gd[(ID=20,)]
#=
1×2 SubDataFrame
Row │ ID Name
│ Int64 String
─────┼─────────────────
1 │ 20 John Doe
=#
#groupbyの戻り値は、iteratorが返る。
#従って、以下の操作が可能
for g in gd
println(g)
end
#=
他にも多数の細かい機能があるが、活用範囲が狭いと勝手に判断して省略
https://dataframes.juliadata.org/stable/lib/functions/#Grouping
を参照してください。
=#
#=======================================================
第6章 using Statistics.jlと組み合わせる簡単なテスト
========================================================#
# データを初期化する
df = copy(dfBackup)
Statistics.mean(df[!,2]) # 平均値 1.0205
Statistics.std(df[!,2]) # 標本標準偏差 0.011690451944500135
#当然、以下の記述方法でも良い
mean(df[!,2]) # 平均値 1.0205
std(df[!,2]) # 標本標準偏差 0.011690451944500135
#=
参考までに、実行時間を計測してみた。
可能であれば、Statistics.jlと組み合わせた方が高速な模様
julia> @time describe(df)[2,:mean]
0.001829 seconds (445 allocations: 31.516 KiB)
1.0205
julia> @time mean(df[!,2])
0.000011 seconds (1 allocation: 16 bytes)
1.0205
=#
# That's all folks.
dftest.csv
COL01,COL02,COL03,COL04,COL05,COL06,COL07,COL08,COL09,COL10
1,1.001,AA001,BB0001,100001,-100001,Scientific,70.69986743,0.044937759,44.6451354
2,1.002,AA002,BB0002,100002,-100002,computing,61.83559738,0.929936427,35.42127868
3,1.003,AA003,BB0003,100003,-100003,has,69.54531749,-0.118914299,29.48884286
4,1.004,AA004,BB0004,100004,-100004,traditionally,51.13573566,-0.759068012,72.27979404
5,1.005,AA005,BB0005,100005,-100005,required,6.409457393,0.217406525,
6,1.006,AA006,BB0006,100006,-100006,the,29.98944376,1.862862366,54.62233332
7,1.007,AA007,BB0007,100007,-100007,highest,9.903665267,-0.032010992,6.051888675
8,1.008,AA008,BB0008,100008,-100008,performance,7.035021201,0.655275961,4.016636045
9,1.009,AA009,BB0009,100009,-100009,yet,98.16743639,-0.631077379,41.89747567
10,1.01,AA010,BB0010,100010,-100010,domain,32.13860274,-0.896925919,
11,1.011,AA011,BB0011,100011,-100011,experts,13.69807297,0.345574404,76.65766038
12,1.012,AA012,BB0012,100012,-100012,have,56.13645843,0.390749008,78.9712095
13,1.013,AA013,BB0013,100013,-100013,largely,87.65709496,0.888892285,14.47981707
14,1.014,AA014,BB0014,100014,-100014,moved,73.10098029,-0.179115018,74.69039311
15,1.015,AA015,BB0015,100015,-100015,to,55.5790018,0.073310327,
16,1.016,AA016,BB0016,100016,-100016,slower,52.17499597,-0.723347271,38.8461184
17,1.017,AA017,BB0017,100017,-100017,dynamic,8.30929389,-1.749679357,94.51462905
18,1.018,AA018,BB0018,100018,-100018,languages,60.7422794,-2.465752475,27.60465437
19,1.019,AA019,BB0019,100019,-100019,for,4.167751043,-1.949011477,53.0392168
20,1.02,AA020,BB0020,100020,-100020,daily,23.36087689,-1.77912943,
21,1.021,AA021,BB0021,100021,-100021,work,69.88469321,-1.384898203,91.77742191
22,1.022,AA022,BB0022,100022,-100022,We,15.00871805,1.357613675,50.54396377
23,1.023,AA023,BB0023,100023,-100023,believe,14.35184944,0.300479502,7.598185455
24,1.024,AA024,BB0024,100024,-100024,there,75.95567102,0.025411352,95.99298018
25,1.025,AA025,BB0025,100025,-100025,are,42.19017132,-0.588811056,
26,1.026,AA026,BB0026,100026,-100026,many,8.541933347,2.014685991,99.21623692
27,1.027,AA027,BB0027,100027,-100027,good,76.08871377,-0.759723444,50.60489219
28,1.028,AA028,BB0028,100028,-100028,reasons,98.7264632,-1.85147392,77.80579996
29,1.029,AA029,BB0029,100029,-100029,to,73.98967366,-0.15801635,52.55488528
30,1.03,AA030,BB0030,100030,-100030,prefer,32.34986628,1.179267357,
31,1.031,AA031,BB0031,100031,-100031,dynamic,34.48369753,1.231877572,87.02051893
32,1.032,AA032,BB0032,100032,-100032,languages,8.254392767,0.176278728,59.79199992
33,1.033,AA033,BB0033,100033,-100033,for,77.16210947,-1.070119656,38.73583079
34,1.034,AA034,BB0034,100034,-100034,these,68.51901323,-1.444477782,75.4299695
35,1.035,AA035,BB0035,100035,-100035,applications,14.36468924,-0.295251541,
36,1.036,AA036,BB0036,100036,-100036,and,94.59111813,2.027168945,88.5285774
37,1.037,AA037,BB0037,100037,-100037,we,61.96451307,-0.294078773,5.896181252
38,1.038,AA038,BB0038,100038,-100038,do,32.73941878,1.184557187,8.090276187
39,1.039,AA039,BB0039,100039,-100039,not,94.42291271,0.670205518,19.5597116
40,1.04,AA040,BB0040,100040,-100040,expect,72.54202401,-0.614707451,