4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DataFrame.jl使用方法のまとめ(備忘録として)

Posted at

自分用の備忘録として作成しましたが、ずっとストレージの奥で眠っていたままでした。
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,

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?