今回は Julia の複素数について簡単にまとめてみました。
complex(a, b)
julia> complex(1, 2)
1 + 2im
julia> complex(1.0, 2)
1.0 + 2.0im
julia> complex(1.0)
1.0 + 0.0im
julia> typeof(1 + 2im)
Complex{Int64}
julia> typeof(1.0 + 2im)
Complex{Float64}
julia> a = 1 + 2im
1 + 2im
julia> b = 1 - im
1 - 1im
julia> a + b
2 + 1im
julia> a - b
0 + 3im
julia> a * b
3 + 1im
julia> a / b
-0.5 + 1.5im
julia> a * a
-3 + 4im
julia> a ^ 2
-3 + 4im
julia> a == a
true
julia> a != a
false
julia> a != b
true
julia> real(a)
1
julia> imag(a)
2
julia> abs(a)
2.23606797749979
julia> abs2(a)
5
julia> abs(b)
1.4142135623730951
julia> abs2(b)
2
julia> angle(a)
1.1071487177940904
julia> angle(b)
-0.7853981633974483
julia> cis(0)
1.0 + 0.0im
julia> cis(pi/4)
0.7071067811865476 + 0.7071067811865475im
julia> cis(pi/2)
6.123233995736766e-17 + 1.0im
julia> cis(pi)
-1.0 + 1.2246467991473532e-16im
julia> cis(angle(a))
0.44721359549995804 + 0.8944271909999159im
julia> cis(angle(b))
0.7071067811865476 - 0.7071067811865475im
julia> sqrt(-1 + 0im)
0.0 + 1.0im
julia> sqrt(-2 + 0im)
0.0 + 1.4142135623730951im
julia> sqrt(-1 + 0im) ^ 2
-1.0 + 0.0im
julia> sqrt(-2 + 0im) ^ 2
-2.0000000000000004 + 0.0im
julia> exp(complex(0, pi/4))
0.7071067811865476 + 0.7071067811865475im
julia> exp(complex(0, pi)) + 1
0.0 + 1.2246467991473532e-16im
julia> using LinearAlgebra
julia> a = [1 + 2im, 3 + 4im]
2-element Vector{Complex{Int64}}:
1 + 2im
3 + 4im
julia> b = [5 + 6im, 7 + 8im]
2-element Vector{Complex{Int64}}:
5 + 6im
7 + 8im
julia> conj.(a)
2-element vector{Complex{Int64}}:
1 - 2im
3 - 4im
julia> conj.(b)
2-element Vector{Complex{Int64}}:
5 - 6im
7 - 8im
julia> a + b
2-element Vector{Complex{Int64}}:
6 + 8im
10 + 12im
julia> a - b
2-element Vector{Complex{Int64}}:
-4 - 4im
-4 - 4im
julia> a .* b
2-element Vector{Complex{Int64}}:
-7 + 16im
-11 + 52im
julia> a ./ b
2-element Vector{Complex{Float64}}:
0.2786885245901639 + 0.06557377049180328im
0.4690265486725664 + 0.035398230088495575im
julia> a .+ c
2-element Vector{Complex{Int64}}:
2 + 1im
4 + 3im
julia> a .- c
2-element Vector{Complex{Int64}}:
0 + 3im
2 + 5im
julia> a .* c
2-element Vector{Complex{Int64}}:
3 + 1im
7 + 1im
julia> a ./ c
2-element Vector{Complex{Float64}}:
-0.5 + 1.5im
-0.5 + 3.5im
julia> dot(a, b)
70 - 8im
julia> dot(b, a)
70 + 8im
julia> dot(a, a)
30 + 0im
julia> dot(b, b)
174 + 0im
julia> norm(a)
5.477225575051661
julia> norm(b)
13.19090595827292
julia> a = [1+1im 1-1im; 2+3im 2+1im]
2×2 Matrix{Complex{Int64}}:
1+1im 1-1im
2+3im 2+1im
julia> transpose(a)
2×2 transpose(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
1+1im 2+3im
1-1im 2+1im
julia> a'
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
1-1im 2-3im
1+1im 2-1im
julia> adjoint(a)
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
1-1im 2-3im
1+1im 2-1im
julia> a''
2×2 Matrix{Complex{Int64}}:
1+1im 1-1im
2+3im 2+1im
julia> b = [1+0im 1+1im; 1-1im 0+1im]
2×2 Matrix{Complex{Int64}}:
1+0im 1+1im
1-1im 0+1im
julia> (a + b)'
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
2-1im 3-2im
2+0im 2-2im
julia> a' + b'
2×2 Array{Matrix{Int64}}:
2-1im 3-2im
2+0im 2-2im
julia> c = 1 - 1im
1 - 1im
julia> (c .* a)'
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
2+0im 5-1im
0+2im 3+1im
julia> conj(c) .* a'
2×2 Matrix{Complex{Int64}}:
2+0im 5-1im
0+2im 3+1im
julia> (a * b)'
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
1+1im 5-2im
1-3im -2-7im
julia> b' * a'
2×2 Matrix{Complex{Int64}}:
1+1im 5-2im
1-3im -2-7im
julia> det(a)
-4.0 + 2.0000000000000004im
julia> det(a')
-4.0 - 2.0im
julia> conj(det(a))
-4.0 - 2.0000000000000004im
julia> inv(a)
2×2 Matrix{Complex{Float64}}:
-0.3-0.4im 0.3-0.1im
0.1+0.8im -0.1-0.3im
julia> inv(a)'
2×2 adjoint(::Matrix{ComplexF64}) with eltype ComplexF64:
-0.3+0.4im 0.1-0.8im
0.3+0.1im -0.1+0.3im
julia> inv(a')
2×2 adjoint(::Matrix{ComplexF64}) with eltype ComplexF64:
-0.3+0.4im 0.1-0.8im
0.3+0.1im -0.1+0.3im
julia> u = [1+2im, 3+4im]
2-element Vector{Complex{Int64}}:
1 + 2im
3 + 4im
julia> v = [5+6im, 7+8im]
2-element Vector{Complex{Int64}}:
5 + 6im
7 + 8im
julia> a * u
2-element Vector{Complex{Int64}}:
6 + 4im
-2 + 18im
julia> dot(a * u, v)
184 - 126im
julia> dot(u, a' * v)
184 - 126im
DataFrames.jl は、Julia で二次元の表形式データを効率的に扱うためのライブラリです。各列は異なる型 (数値、文字列、ブール値など) を保持でき、列ごとに名前 (シンボル) を付けることができます。Julia 本体の性能を活かし、大規模なデータセットに対しても高速な計算や統計解析が可能です。また、ライブラリ CSV.jl を使えば、CSV 形式のファイルの読み書きも簡単に行うことができます。
今回は DataFrames.jl の基本的な使い方を簡単に説明します。
Julia のパッケージモード (REPL で ] キーを押す) で次のコマンドを実行します。
(@v1.12) pkg> add DataFrames (@v1.12) pkg> add CSV
DataFrames と CSV をインストールします。インストール終了後 st で状態を表示すると、M.Hiroi の環境では次のように表示されます。
(@v1.12) pkg> st Status `~/.julia/environments/v1.12/Project.toml` [336ed68f] CSV v0.10.16 [a93c6f00] DataFrames v1.8.2 [91a5bcdd] Plots v1.41.6 [f3b207a7] StatsPlots v0.15.8
インストールが終了したら、Backspace または Ctrl + C を押してください。通常の julia> プロンプトに戻ります。
データフレームはコンストラクタ DataFrame() で生成します。列ごとにデータを指定して作成するのが一般的です。
DataFrame() : 空のデータフレームを生成
DataFrame(x=type, ...) : 列名 x とデータ型 type を指定
DataFrame(x=data, ...) : 列名 x とデータ data (vector, range, or constan) を指定
DataFrame("x" => data, ...) : 列名 x とデータをペアで指定する
DataFrame([(x=1, y=2), (x=3, y=4), ...]) : 行データ (名前付きタプル) を格納したベクタで指定する
DataFrame(matrix, [:x, :y, ...]) : 行列と列名を格納したベクタ (または :auto) を指定する
列名 = data 以外の方法でデータを指定する場合、列名には文字列またはシンボルを使います。簡単な使用例を示します。
julia> using DataFrames
julia> DataFrame()
0×0 DataFrame
julia> DataFrame(x=Int64[], y=Float64[])
0×2 DataFrame
Row │ x y
│ Int64 Float64
───┴─────────
julia> DataFrame(x=["foo","bar","baz"], y=[10,20,30], z=0.0)
3×3 DataFrame
Row │ x y z
│ String Int64 Float64
───┼──────────────
1 │ foo 10 0.0
2 │ bar 20 0.0
3 │ baz 30 0.0
julia> DataFrame([(x=11, y=12), (x=13, y=14)])
2×2 DataFrame
Row │ x y
│ Int64 Int64
───┼────────
1 │ 11 12
2 │ 13 14
julia> DataFrame(rand(3,3), :auto)
3×3 DataFrame
Row │ x1 x2 x3
│ Float64 Float64 Float64
───┼────────────────────
1 │ 0.00647265 0.175429 0.668197
2 │ 0.655034 0.936949 0.00115477
3 │ 0.90156 0.461149 0.968727
データフレーム df の要素は df[row, col] で求めることができます。また、行数・列数は関数 nrow(df), ncol(df), size(df) で取得することができます。
julia> df = DataFrame(
Name = ["Alice", "Bob", "Charlie"],
Age = [25, 30, 35],
Score = [85.5, 92.0, 78.0]
)
3×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼───────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
3 │ Charlie 35 78.0
julia> df[1, :Name]
"Alice"
julia> df[2, :Score]
92.0
julia> nrow(df)
3
julia> ncol(df)
3
julia> size(df)
(3, 3)
列名を取得するには関数 names(df), propertynames(df) を使います。
julia> names(df)
3-element Vector{String}:
"Name"
"Age"
"Score"
julia> propertynames(df)
3-element Vector{Symbol}:
:Name
:Age
:Score
関数 describe(df) は各列の統計量 (平均、最小、最大など) を表示します。
julia> describe(df)
3×7 DataFrame
Row │ variable mean min median max nmissing eltype
│ Symbol Union… Any Union… Any Int64 DataType
───┼─────────────────────────────────────
1 │ Name Alice Charlie 0 String
2 │ Age 30.0 25 30.0 35 0 Int64
3 │ Score 85.1667 78.0 85.5 92.0 0 Float64
CSV.jl を使うと、CSV 形式のファイルを簡単に読み書きすることができます。
読み込み: df = CSV.read("data.csv", DataFrame)
書き出し: CSV.write("output.csv", df)
簡単な使用例を示します。
julia> df
3×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼────────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
3 │ Charlie 35 78.0
julia> using CSV
julia> CSV.write("testdat.csv", df)
"testdat.csv"
julia> df1 = CSV.read("testdat.csv", DataFrame)
3×3 DataFrame
Row │ Name Age Score
│ String7 Int64 Float64
───┼───────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
3 │ Charlie 35 78.0
関数 first(df, N) はデータフレームの先頭から N 行、関数 last(df, N) は末尾から N 行取り出します。df[M:N, :] は M 〜 N 行目を取り出します。M : N だけではなく、配列のような Indexing も可能です。
julia> first(df, 2)
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
julia> last(df, 2)
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Bob 30 92.0
2 │ Charlie 35 78.0
julia> df[1:2, :]
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
julia> df[[3, 1], :]
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Charlie 35 78.0
2 │ Alice 25 85.5
列を取得するには df.Name, df[:, :Name] を使います。複数の列を指定する場合は、ベクタに列名を格納して渡します。
julia> df.Name
3-element Vector{String}:
"Alice"
"Bob"
"Charlie"
julia> df[:, :Age]
3-element Vector{Int64}:
25
30
35
julia> df[:, [:Name, :Age]]
3×2 DataFrame
Row │ Name Age
│ String Int64
───┼─────────────
1 │ Alice 25
2 │ Bob 30
3 │ Charlie 35
行や列を取得するとき、データはコピーされます。ただし、列を取得するとき、: のかわりに ! を用いると、データをコピーせずにビュー (view) を生成して返します。
julia> df1 = df[:, :Name]
3-element Vector{String}:
"Alice"
"Bob"
"Charlie"
julia> df1[1] = "ALICE"
"ALICE"
julia> df1
3-element Vector{String}:
"ALICE"
"Bob"
"Charlie"
julia> df
3×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼───────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
3 │ Charlie 35 78.0
julia> df2 = df[!, :Name]
3-element Vector{String}:
"Alice"
"Bob"
"Charlie"
julia> df2[1] = "ALICE"
"ALICE"
julia> df2
3-element Vector{String}:
"ALICE"
"Bob"
"Charlie"
julia> df
3×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼───────────────
1 │ ALICE 25 85.5
2 │ Bob 30 92.0
3 │ Charlie 35 78.0
列の取得は関数 select(), select!() を使って行うこともできます。
select() は引数のデータフレーム df をコピーし、select!() は df を破壊的に修正します。
julia> select(df, :Name)
3×1 DataFrame
Row │ Name
│ String
───┼─────
1 │ Alice
2 │ Bob
3 │ Charlie
julia> select(df, [:Name, :Age])
3×2 DataFrame
Row │ Name Age
│ String Int64
───┼──────────
1 │ Alice 25
2 │ Bob 30
3 │ Charlie 35
julia> select(df, [:Name, :Score])
3×2 DataFrame
Row │ Name Score
│ String Float64
───┼───────────
1 │ Alice 85.5
2 │ Bob 92.0
3 │ Charlie 78.0
filter(), filter!() を使うと、条件を満たす行を取り出すことができます。
filter(row -> condition, df) : 行データ row が condition を満たせば、その行を取得する filter(:col => pred, df) : 行から取り出す列名と真偽を判定する述語 pred をペアで渡す
filter!() は引数の df を破壊的に修正します。
julia> filter(row -> row.Age > 25, df)
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼───────────────
1 │ Bob 30 92.0
2 │ Charlie 35 78.0
julia> filter(:Age => >(25), df)
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼───────────────
1 │ Bob 30 92.0
2 │ Charlie 35 78.0
julia> filter(row -> row.Score > 90, df)
1×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Bob 30 92.0
julia> filter(:Score => >(90), df)
1×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Bob 30 92.0
同様のことは、データフレームの Indexing でも行うことができます。
julia> df[df.Age .> 25, :]
2×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Bob 30 92.0
2 │ Charlie 35 78.0
julia> df[df.Score .> 90, :]
1×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼──────────────
1 │ Bob 30 92.0
select() は列を選択するだけではなく、列名を変更したり、列のデータに処理を加えて新しい列を作成することもできます。
:OldCol => :NewCol : OldCol を NewCol に変更 :OldCol => proc => :NewCol : 列 OldCol に関数 proc に適用し、その結果を新しい列 NewCol として追加する :OldCol => ByRow(proc) => :NewCol : 列 OldCol の要素に関数 proc に適用し、その結果を新しい列 NewCol に追加する
列の要素に関数 proc を適用する場合は ByRow(proc) を使うと簡単です。なお、新しい列を追加するだけでよければ、df.NewCol = data で行うことができます。
julia> d = DataFrame(x = [1, 2], y = [3, 4])
2×2 DataFrame
Row │ x y
│ Int64 Int64
───┼────────
1 │ 1 3
2 │ 2 4
julia> select(d, :x => :x1, :y => :y1)
2×2 DataFrame
Row │ x1 y1
│ Int64 Int64
───┼────────
1 │ 1 3
2 │ 2 4
julia> select(d, :x, :x => (x -> sqrt.(x)) => :z)
2×2 DataFrame
Row │ x z
│ Int64 Float64
───┼──────────
1 │ 1 1.0
2 │ 2 1.41421
julia> select(d, :x, :x => ByRow(sqrt) => :z)
2×2 DataFrame
Row │ x z
│ Int64 Float64
───┼──────────
1 │ 1 1.0
2 │ 2 1.41421
julia> select(d, :x, :y, [:x, :y] => ((x, y) -> (x .+ y) ./ 2) => :z)
2×3 DataFrame
Row │ x y z
│ Int64 Int64 Float64
───┼──────────────
1 │ 1 3 2.0
2 │ 2 4 3.0
最後の例のように、元のデータはそのままで新しい列を追加したい場合は、関数 transform(), transform!() を使うと簡単です。
julia> transform(d, :x => ByRow(sqrt))
2×3 DataFrame
Row │ x y x_sqrt
│ Int64 Int64 Float64
───┼──────────────
1 │ 1 3 1.0
2 │ 2 4 1.41421
julia> transform(d, :y => ByRow(sqrt))
2×3 DataFrame
Row │ x y y_sqrt
│ Int64 Int64 Float64
───┼──────────────
1 │ 1 3 1.73205
2 │ 2 4 2.0
julia> transform(d, [:x, :y] => ((x, y) -> (x .+ y) ./ 2) => :z)
2×3 DataFrame
Row │ x y z
│ Int64 Int64 Float64
───┼──────────────
1 │ 1 3 2.0
2 │ 2 4 3.0
All() を使うと、すべての列を選択することができます。
All() .=> function : ブロードキャストを使うと列ごとに関数 function を適用する All() => function : 各列の要素を取り出し、それらを function に渡して呼び出す
julia> transform(d, All() .=> ByRow(sqrt))
2×4 DataFrame
Row │ x y x_sqrt y_sqrt
│ Int64 Int64 Float64 Float64
───┼────────────────────
1 │ 1 3 1.0 1.73205
2 │ 2 4 1.41421 2.0
julia> transform(d, All() => +)
2×3 DataFrame
Row │ x y x_y_+
│ Int64 Int64 Int64
───┼─────────────
1 │ 1 3 4
2 │ 2 4 6
julia> transform(d, All() => ((x, y) -> (x + y) / 2) => :z)
2×3 DataFrame
Row │ x y z
│ Int64 Int64 Float64
───┼─────────────
1 │ 1 3 2.0
2 │ 2 4 3.0
AsTable は、select() や transform() で「複数の列を一つのまとまり (名前付きタプルなど) にして関数に渡す」、または「関数の戻り値 (名前付きタプルなど) を複数の列として展開する」ために使用します。
julia> d1 = select(d, AsTable(:) => ByRow(identity))
2×1 DataFrame
Row │ x_y_identity
│ NamedTuple…
───┼───────────
1 │ (x = 1, y = 3)
2 │ (x = 2, y = 4)
julia> select(d1, :x_y_identity => AsTable)
2×2 DataFrame
Row │ x y
│ Int64 Int64
───┼───────────
1 │ 1 3
2 │ 2 4
julia> transform(d, AsTable([:x, :y]) => ByRow(row -> (row.x + row.y) / 2) => :z)
2×3 DataFrame
Row │ x y z
│ Int64 Int64 Float64
───┼──────────────
1 │ 1 3 2.0
2 │ 2 4 3.0
関数 combine() は、データフレーム (またはグループ化されたデータフレーム) に対して集計や変換を行い、結果を新しいデータフレームに格納して返します。具体的には、特定の列 col に対して関数 f を適用し、その結果を格納したデータフレームを返します。col と f はペア col => f で指定します。
julia> using Statistics # mean(), 平均値を求める
julia> df
3×3 DataFrame
Row │ Name Age Score
│ String Int64 Float64
───┼───────────────
1 │ Alice 25 85.5
2 │ Bob 30 92.0
3 │ Charlie 35 78.0
julia> combine(df, :Age => mean)
1×1 DataFrame
Row │ Age_mean
│ Float64
───┼──────
1 │ 30.0
julia> combine(df, :Age => mean, :Age => sum)
1×2 DataFrame
Row │ Age_mean Age_sum
│ Float64 Int64
───┼────────────
1 │ 30.0 90
julia> combine(df, [:Age, :Score] .=> mean)
1×2 DataFrame
Row │ Age_mean Score_mean
│ Float64 Float64
───┼─────────────
1 │ 30.0 85.1667
関数 groupby() を使うと、データをグループに分けて、グループごとに集計することができます。
gd = groupby(df, 列名) combine(gd, 列名 => 関数 => 出力先列名)
groupby() で列を配列で指定すると、複数列でグループ化をすることができます。
リスト : 生徒の伸長
df_height = DataFrame(
Name = ["Ada", "Alice", "Carey", "Ellen", "Hanna", "Janet",
"Linda", "Maria", "Miranda", "Sara", "Tracy", "Violet"],
Height = [148.7, 149.5, 133.7, 157.9, 154.2, 147.8,
154.6, 159.1, 148.2, 153.1, 138.2, 138.7],
Class = [:A,:B,:C,:D,:A,:B,:C,:D,:A,:B,:C,:D]
)
julia> df_height = DataFrame(
Name = ["Ada", "Alice", "Carey", "Ellen", "Hanna", "Janet",
"Linda", "Maria", "Miranda", "Sara", "Tracy", "Violet"],
Height = [148.7, 149.5, 133.7, 157.9, 154.2, 147.8,
154.6, 159.1, 148.2, 153.1, 138.2, 138.7],
Class = [:A,:B,:C,:D,:A,:B,:C,:D,:A,:B,:C,:D]
)
12×3 DataFrame
Row │ Name Height Class
│ String Float64 Symbol
───┼───────────────
1 │ Ada 148.7 A
2 │ Alice 149.5 B
3 │ Carey 133.7 C
4 │ Ellen 157.9 D
5 │ Hanna 154.2 A
6 │ Janet 147.8 B
7 │ Linda 154.6 C
8 │ Maria 159.1 D
9 │ Miranda 148.2 A
10 │ Sara 153.1 B
11 │ Tracy 138.2 C
12 │ Violet 138.7 D
julia> df_class = groupby(df_height, :Class)
GroupedDataFrame with 4 groups based on key: Class
First Group (3 rows): Class = :A
Row │ Name Height Class
│ String Float64 Symbol
───┼───────────────
1 │ Ada 148.7 A
2 │ Hanna 154.2 A
3 │ Miranda 148.2 A
⋮
Last Group (3 rows): Class = :D
Row │ Name Height Class
│ String Float64 Symbol
───┼───────────────
1 │ Ellen 157.9 D
2 │ Maria 159.1 D
3 │ Violet 138.7 D
julia> combine(df_class, :Height => mean)
4×2 DataFrame
Row │ Class Height_mean
│ Symbol Float64
───┼───────────────
1 │ A 150.367
2 │ B 150.133
3 │ C 142.167
4 │ D 151.9
julia> combine(df_class, :Height => maximum)
4×2 DataFrame
Row │ Class Height_maximum
│ Symbol Float64
───┼───────────────
1 │ A 154.2
2 │ B 153.1
3 │ C 154.6
4 │ D 159.1
julia> combine(df_class, :Height => minimum)
4×2 DataFrame
Row │ Class Height_minimum
│ Symbol Float64
───┼───────────────
1 │ A 148.2
2 │ B 147.8
3 │ C 133.7
4 │ D 138.7
データフレーム df を 1 行ずつ処理する場合、関数 eachrow(df) を使用します。各行は DataFrameRow という型で返され、列名でアクセスすることができます。
julia> d = DataFrame(rand(3, 3), [:x, :y, :z])
3×3 DataFrame
Row │ x y z
│ Float64 Float64 Float64
───┼───────────────────
1 │ 0.346425 0.364283 0.599289
2 │ 0.104331 0.438215 0.0422631
3 │ 0.365164 0.0548274 0.495545
julia> for row in eachrow(d) println(row) end
DataFrameRow
Row │ x y z
│ Float64 Float64 Float64
───┼───────────────────
1 │ 0.346425 0.364283 0.599289
DataFrameRow
Row │ x y z
│ Float64 Float64 Float64
───┼───────────────────
2 │ 0.104331 0.438215 0.0422631
DataFrameRow
Row │ x y z
│ Float64 Float64 Float64
───┼───────────────────
3 │ 0.365164 0.0548274 0.495545
julia> for row in eachrow(d) println(row.x, row.y, row.z) end
0.34642496259045620.36428313121846680.5992887456834662
0.104330706549074390.438215036620621450.04226314830872879
0.365163828384003960.054827361797255890.4955448988219937
julia> map(row -> [row.x, row.y, row.z], eachrow(d))
3-element Vector{Vector{Float64}}:
[0.3464249625904562, 0.3642831312184668, 0.5992887456834662]
[0.10433070654907439, 0.43821503662062145, 0.04226314830872879]
[0.36516382838400396, 0.05482736179725589, 0.4955448988219937]
各列を 1 つのベクトルとして処理する場合は関数 eachcol(df) を使用します。pairs(eachcol(df)) を使うと、列名 (Symbol) とデータのペアを取得できます。
julia> for col in eachcol(d) println(col) end
[0.3464249625904562, 0.10433070654907439, 0.36516382838400396]
[0.3642831312184668, 0.43821503662062145, 0.05482736179725589]
[0.5992887456834662, 0.04226314830872879, 0.4955448988219937]
julia> for col in pairs(eachcol(d)) println(col) end
Pair{Symbol, AbstractVector}(:x, [0.3464249625904562, 0.10433070654907439, 0.36516382838400396])
Pair{Symbol, AbstractVector}(:y, [0.3642831312184668, 0.43821503662062145, 0.05482736179725589])
Pair{Symbol, AbstractVector}(:z, [0.5992887456834662, 0.04226314830872879, 0.4955448988219937])
Julia の for ループは高速なので、eachrow(df) などを使ってループを回しても、問題になることは少ないと思います。eachrow() よりも高速に動作させたい場合、各行を名前付きタプルとして取り出す方法があります。型推論が効きやすいためループ処理のパフォーマンスが向上する、といわれています。この方法も簡単で、ライブラリ Tables.jl の関数 namedtupleiterator() を呼び出すだけです。
Tables.jl は、データフレームや CSV ファイル、データベース、名前付きタプルの配列など、さまざまな形式の「表形式データ」を共通の形式で相互変換するためのインターフェースを提供するパッケージです。Julia のパッケージモード (REPL で ] キーを押す) で次のコマンドを実行するとインストールすることができます。
(@v1.12) pkg> add Tables
(@v1.12) pkg> st Status `~/.julia/environments/v1.12/Project.toml` [336ed68f] CSV v0.10.16 [a93c6f00] DataFrames v1.8.2 [91a5bcdd] Plots v1.41.6 [f3b207a7] StatsPlots v0.15.8 [bd369af6] Tables v1.12.1
M.Hiroi の環境では Tables v1.12.1 がインストールされました。
あとは、eachrow() のかわりに Tables.namedtupleiterator() を呼び出すだけです。
julia> using Tables julia> for row in Tables.namedtupleiterator(d) println(row) end (x = 0.3464249625904562, y = 0.3642831312184668, z = 0.5992887456834662) (x = 0.10433070654907439, y = 0.43821503662062145, z = 0.04226314830872879) (x = 0.36516382838400396, y = 0.05482736179725589, z = 0.4955448988219937)
Tables.jl はデータ形式の変換や、自作のデータ構造を「テーブル」として扱いたい場合、とても役に立ちます。
DataFrame.jl の基本的な使い方を簡単に説明しました。DataFrames.jl は高機能で多機能なライブラリなので、使いこなすのはちょっと大変だと思いますが、基本的な操作はそれほど難しくはありません。興味のある方はいろいろ試してみてください。