最近Juliaでデータフレームを操作するパッケージDataFrames.jlのバージョンが少しbump upしました。ただし,この執筆時点での最新バージョンは0.22.2です。
そもそもデータフレームとは,基本的に二次元配列の構造で,行列と同様に行と列を持ちます。
多言語だとRのdplyrやPythonのpandasなどがありますが,JuliaではDataFrames.jlがデータフレームの操作を実行するためのパッケージとなります。連携するパッケージとしてはDataFramesMeta.jl
やQuery
などがありますが,それらを使わなくても,たいていのことはできる印象です。
ここ最近0.21系から0.22にバージョンアップしました。
Juliaの公式パッケージはGutHub上で管理されているので,GutHubで変更履歴や,おもな変更点を確認することができます。
どうやら今回のおもな変更点はデータフレームの列操作に関する関数select
/transform
の改善や,データの持ち方を縦から横に変更する関数unstack
に関する挙動のようです。
これに加えて,いくつかの補助関数(isapprox
, empty
)が新たに追加されました。
# using Pkg; Pkg.add(name = "DataFrames", version = "0.22.0");
using DataFrames;
mat = randn(3, 3);
A = DataFrame(mat, [:a, :b, :c])
a | b | c | |
---|---|---|---|
Float64 | Float64 | Float64 | |
1 | 0.800516 | -2.26194 | 0.277444 |
2 | -0.567185 | 1.5837 | -0.36308 |
3 | -1.33798 | 0.694831 | -0.375632 |
DataFrame(:a => 1:2, :b => 2:4) # 長さが違うので,エラーになる。
DimensionMismatch("column :a has length 2 and column :b has length 3") Stacktrace: [1] DataFrame(::Array{Any,1}, ::DataFrames.Index; copycols::Bool) at /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframe/dataframe.jl:181 [2] DataFrame(::Pair{Symbol,UnitRange{Int64}}, ::Vararg{Pair{Symbol,UnitRange{Int64}},N} where N; makeunique::Bool, copycols::Bool) at /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframe/dataframe.jl:218 [3] DataFrame(::Pair{Symbol,UnitRange{Int64}}, ::Vararg{Pair{Symbol,UnitRange{Int64}},N} where N) at /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframe/dataframe.jl:216 [4] top-level scope at In[4]:1 [5] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
作ったデータフレームの列要素のアクセスは,データフレームをA
,取り出したい列名をa
とすると,
A.a
A."a"
A[!, :A]
A[!, "A"]
getproperty(A, :a)
getproperty(A, "a")
で取り出すことができます。[]
やgetproperty
を使用する場合,シンボルでも文字列でもどちらでもアクセス可能です(シンボルのほうがわずかに効率がいいらしい)。
これらの方法で取り出された配列はコピーされません。コピーされないということは取り出した要素を変化させると,もとのデータフレームも変化するということです。コピーを取り出したい場合はA[:, :a]
のように,!
の代わりに:
を使用します。
複数の列にアクセスすることもできます。
A[:, [:a, :b]]
a | b | |
---|---|---|
Float64 | Float64 | |
1 | 0.800516 | -2.26194 |
2 | -0.567185 | 1.5837 |
3 | -1.33798 | 0.694831 |
正規表現を使ってマッチする列を取り出すこともできます。正規表現を使う場合にはr""
をつかってRegex
を作り出します。
正規表現による強力なマッチング機能が利用できるのは,DataFrames.jlの強みだと思います。
A[:, r"[ab]"]
a | b | |
---|---|---|
Float64 | Float64 | |
1 | 0.800516 | -2.26194 |
2 | -0.567185 | 1.5837 |
3 | -1.33798 | 0.694831 |
サポート関数を使っても,列方向に対する操作ができます。利用できるサポート関数は
Not()
選択したシンボルかRegex
にマッチした列以外を取り出す。Cols()
選択したシンボルかRegex
にマッチした列だけを取り出す。All()
選んでくる条件はCols()
と同じ。Between(first, last)
first
~last
間の列をすべて取り出す。です。
Cols
とAll()
は機能的には同一ですが,Cols()
は何も列を選択しないことができるので,こちらのほうができることがひとつ多い印象。
並び替える
これらのサポート関数は,列方向に関する並び替えを行いたいときに役に立ちます。並び替えにはRegex
とシンボル,文字列が使えます。つまり,列選択のときと全く同じです。
A[!, Cols(r"a|c", "b")]
a | c | b | |
---|---|---|---|
Float64 | Float64 | Float64 | |
1 | 0.800516 | 0.277444 | -2.26194 |
2 | -0.567185 | -0.36308 | 1.5837 |
3 | -1.33798 | -0.375632 | 0.694831 |
列の要素を指定する方法は他にもあります。それがselect()
とtransform()
です。しかし,この2種類の関数は,ただ取り出すだけでなく,指定した要素に対して関数を作用させるための関数です。
select()
, select!()
選択した列だけを取り出すtransform()
, transform!()
選択した列以外も取り出す。!
がついている方はin-place関数であり,作用させた変数の内容そのものを破壊的に変更します。
transform
は選択列以外も取り出すので「なんのこっちゃ」となるかもしれませんが,この2つの関数は,ただ列を取り出すだけでなく,取り出した列に対して関数を作用させるための関数であるため,transform
が重要になります。
これらはRegex
やシンボル,文字列と組み合わせて使うことができるだけでなく,先述したサポート関数と一緒に使うこともできます。
select()
マッチした列だけを取り出して,新しいデータフレームを作成します。
さらに,単純に取り出すだけでなく,列名を変更したり,取り出した列に関数を作用させて新しい列にすることもできます。select
はデフォルトでは自動的にコピーを作成する仕様になっていますが,..., copycols = false
でコピーの作成をしないようにすることもできます。
B = select(A, :a, copycols = true)
C = select(A, :a, copycols = false)
a | |
---|---|
Float64 | |
1 | 0.800516 |
2 | -0.567185 |
3 | -1.33798 |
B.a === A.a
false
C.a === A.a
true
select()
は単に列を取り出すだけでなく,選択した列に対して関数を適用し,それを新たな列として返すという操作もできます。
新しい列を作るための書き方は,
select(A, :列名 => 関数 => :新しい列名)
です。関数には()
でくくることで無名関数も使うことができます。
select(A, :a => (x -> 2x) => :d)
d | |
---|---|
Float64 | |
1 | 1.60103 |
2 | -1.13437 |
3 | -2.67596 |
関数は列の要素に対してまとめててきようされます。そのため行の各要素に関数を作用させたいときはbroadcastを利用します.
例えば各要素をround
したいときは,
select(A, :a => round => :d)
ではなく
select(A, :a => (x -> round.(x)) => :d)
d | |
---|---|
Float64 | |
1 | 1.0 |
2 | -1.0 |
3 | -1.0 |
と実行する必要があります。
transform()
select()
と同じ記述方法で列を指定しますが,select()
とは異なり,選択していない列も残して,値を返します。
transform(A, :a => (x -> 2x) => :d)
a | b | c | d | |
---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | |
1 | 0.800516 | -2.26194 | 0.277444 | 1.60103 |
2 | -0.567185 | 1.5837 | -0.36308 | -1.13437 |
3 | -1.33798 | 0.694831 | -0.375632 | -2.67596 |
select
やtransform
では基本的に列方向に対して関数を作用させました。しかしデータフレームには行ごとに意味をもたせたデータを入力したいこともあります(たとえばあるクラスのテスト得点の表とか)。
そんな操作をするためにはByRow
を使うと良いです。
B = DataFrame("名前" => ["Kato", "Noguchi", "Yamada"], "国語" => [20, 31, 55], "数学" => [100, 23, 78], "英語" => [10, 30, 89])
名前 | 国語 | 数学 | 英語 | |
---|---|---|---|---|
String | Int64 | Int64 | Int64 | |
1 | Kato | 20 | 100 | 10 |
2 | Noguchi | 31 | 23 | 30 |
3 | Yamada | 55 | 78 | 89 |
transform(B, Not(:名前) => ByRow(+) => :合計)
名前 | 国語 | 数学 | 英語 | 合計 | |
---|---|---|---|---|---|
String | Int64 | Int64 | Int64 | Int64 | |
1 | Kato | 20 | 100 | 10 | 130 |
2 | Noguchi | 31 | 23 | 30 | 84 |
3 | Yamada | 55 | 78 | 89 | 222 |
行方向の取り出し
列方向のデータフレーム操作をみたところで,単純な取り出しの例についても確認しておきましょう。
列方向だけでなく,行方向に対しても一部の要素を取り出すことができます。[,]
のカンマの左側に,ベクトルなどで位置を指定してやることで,取り出せます。
A[1:2, :]
a | b | c | |
---|---|---|---|
Float64 | Float64 | Float64 | |
1 | 0.800516 | -2.26194 | 0.277444 |
2 | -0.567185 | 1.5837 | -0.36308 |
特定の条件にマッチする行だけを取り出したい場合には,2通りの書き方ができます。
A[条件, :]
filter(:ID -> 関数, A)
filter()
は結構便利な関数です。次のように派生した書き方ができます。
filter(:a => i -> i < 0, A)
a | b | c | |
---|---|---|---|
Float64 | Float64 | Float64 | |
1 | -0.567185 | 1.5837 | -0.36308 |
2 | -1.33798 | 0.694831 | -0.375632 |
DataFramesRow
には要注意¶しかし,1行だけ取り出す場合,DataFrame
からDataFramesRow
と呼ばれる型に変化します。DataFrame
の型を維持したままで1行だけ取り出す場合は,A[[1], :a]
のように,ベクトルで行の要素を指定すればよいです。
DataFramesRow
に対してはbroadcastをすることができません。
A[1, [:a, :b]] # DataFramesRow
DataFrameRow (2 columns)
a | b | |
---|---|---|
Float64 | Float64 | |
1 | 0.800516 | -2.26194 |
A[[1], [:a, :b]] # DataFrame
a | b | |
---|---|---|
Float64 | Float64 | |
1 | 0.800516 | -2.26194 |
string.(A[1, [:a, :b]])
ArgumentError: broadcasting over `DataFrameRow`s is reserved Stacktrace: [1] broadcastable(::DataFrameRow{DataFrame,DataFrames.SubIndex{DataFrames.Index,Array{Int64,1},Array{Int64,1}}}) at /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframerow/dataframerow.jl:415 [2] broadcasted(::Function, ::DataFrameRow{DataFrame,DataFrames.SubIndex{DataFrames.Index,Array{Int64,1},Array{Int64,1}}}) at ./broadcast.jl:1255 [3] top-level scope at In[20]:1 [4] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
このようにDataFramesRow
に対してはbroadcast演算をすることはできません。
データフレーム操作で頻繁に行われるのが,データフレームの結合です。単純に行や列の大きさが同じものをくっつけるだけでなく,特定列の要素をキーにマッチさせたりすることができます。
こうした操作は**join(A, B)
系の関数で実行できます。
**join()
の基本的な書き方¶基本的な使用方法は
**join(A, B, on = :列名)
です。AとBで列名が違っている場合,
**join(A, B, on = :Aの列名 => :Bの列名)
というように,Pair
を使用します。複数のキーがある場合はシンボルやペアをベクトルとして渡してあげます。
**join(A, B, on = [:列名1, :Aの列名2 => :Bの列名2])
leftjoin
, rightjoin
左右どちらかのデータフレーム(A)を基準としてマッチさせる。基準のデータフレームAにないBの行は無視され,Bの列のうちAに含まれていないものは欠測missing
として追加される。innerjoin
両方のデータフレームの要素を行に関してすべて残す形でマッチさせる。片方のデータフレームにしかない行も完全に保存される。outerjoin
両方のデータフレームの要素のうち,キーがマッチした行だけ残し,それ以外は無視する。この形では,マッチによる欠測が生じない。semijoin
マッチした行だけを保存するが,さらに列に関しては基準となるAの列だけを保存し,Bに関する要素は完全に無視される。antijoin
マッチした行以外を保存するが,列に関しては基準となるAの列だけが保存される。semijoin
の逆バージョン。crossjoin
与えたデータフレームらの直積を返す。マッチと言うよりも,全パタンの網羅するための関数?実際の実行例はパッケージドキュメントに詳しいです。
people = DataFrame(ID = [20, 40], Name = ["John Doe", "Jane Doe"]);
jobs = DataFrame(ID = [20, 40], Job = ["Lawyer", "Doctor"]);
crossjoin(people, jobs; makeunique = true)
ID | Name | ID_1 | Job | |
---|---|---|---|---|
Int64 | String | Int64 | String | |
1 | 20 | John Doe | 20 | Lawyer |
2 | 20 | John Doe | 40 | Doctor |
3 | 40 | Jane Doe | 20 | Lawyer |
4 | 40 | Jane Doe | 40 | Doctor |
列名が同じ要素の2つのデータフレームを結合したい場合はvcat
が便利です。
vcat((people, jobs)...; cols = :union)
ID | Name | Job | |
---|---|---|---|
Int64 | String? | String? | |
1 | 20 | John Doe | missing |
2 | 40 | Jane Doe | missing |
3 | 20 | missing | Lawyer |
4 | 40 | missing | Doctor |
データフレームをピボットしたり,変形したりする操作もDataFrames.jlの標準的な関数として提供されています。それがstack
とunstack
です。
データフレームを縦方向に伸ばす(要素を縦に積み上げる)のがstack
であり,逆に横方向に伸ばす(積み上がっている要素を横に展開する)のがunstack
です。
もっともかんたんな使用方法は,
stack(A, [縦に伸ばしたい複数列]; variable_name = :列名を要素とする新たな列の名前, value_name = :列の要素を立てに伸ばした新たな列の名前)
です。
B
名前 | 国語 | 数学 | 英語 | |
---|---|---|---|---|
String | Int64 | Int64 | Int64 | |
1 | Kato | 20 | 100 | 10 |
2 | Noguchi | 31 | 23 | 30 |
3 | Yamada | 55 | 78 | 89 |
long_B = stack(B, Between(:国語, :英語), variable_name = :教科, value_name = :得点)
名前 | 教科 | 得点 | |
---|---|---|---|
String | String | Int64 | |
1 | Kato | 国語 | 20 |
2 | Noguchi | 国語 | 31 |
3 | Yamada | 国語 | 55 |
4 | Kato | 数学 | 100 |
5 | Noguchi | 数学 | 23 |
6 | Yamada | 数学 | 78 |
7 | Kato | 英語 | 10 |
8 | Noguchi | 英語 | 30 |
9 | Yamada | 英語 | 89 |
unstack(A, :列名にしたい要素を持つ列名, :横に伸ばしたい要素をもつ列名))
で元のデータフレームに戻せます。
※unstackした列は型が勝手に変わってしまうことに注意。
wide_B = unstack(long_B, :教科, :得点)
名前 | 国語 | 数学 | 英語 | |
---|---|---|---|---|
String | Int64? | Int64? | Int64? | |
1 | Kato | 20 | 100 | 10 |
2 | Noguchi | 31 | 23 | 30 |
3 | Yamada | 55 | 78 | 89 |
typeof(wide_B.国語)
Array{Union{Missing, Int64},1}
データフレームで扱う変数には様々な理由により観測されない値が入っています。JuliaのDataFrameではMissing
という型で,この欠測値を扱います。
先程のunstackで変化した型はこのMissing
を含んだものでした。
このMissing
の型に関連して,次のような関数が準備されています。
allowmissing
allowmissing!
列の型を欠測値を認める型Union{hoge, Missing}
に変える。disallowmissing
disallowmissing!
列の方を欠測値を認めない型に変える。completecases
すべての列,もしくは一部の列で欠測を含んでいない行を検索してtrue
or false
のベクトルを返す。dropmissing
dropmissing!
欠測を含んでいない行だけを残して,返す。append!(B, DataFrame(名前 = missing, 国語 = 0, 数学 = 0, 英語 = 0))
┌ Error: Error adding value to column :名前. └ @ DataFrames /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframe/dataframe.jl:1237
MethodError: Cannot `convert` an object of type Missing to an object of type String Closest candidates are: convert(::Type{T}, !Matched::T) where T<:AbstractString at strings/basic.jl:229 convert(::Type{T}, !Matched::AbstractString) where T<:AbstractString at strings/basic.jl:230 convert(::Type{S}, !Matched::CategoricalArrays.CategoricalValue) where S<:Union{AbstractChar, AbstractString, Number} at /Users/takuizum/.julia/packages/CategoricalArrays/ZjBSI/src/value.jl:73 ... Stacktrace: [1] setindex!(::Array{String,1}, ::Missing, ::Int64) at ./array.jl:847 [2] _unsafe_copyto!(::Array{String,1}, ::Int64, ::Array{Missing,1}, ::Int64, ::Int64) at ./array.jl:257 [3] unsafe_copyto! at ./array.jl:311 [inlined] [4] _copyto_impl! at ./array.jl:335 [inlined] [5] copyto! at ./array.jl:321 [inlined] [6] append!(::Array{String,1}, ::Array{Missing,1}) at ./array.jl:977 [7] append!(::DataFrame, ::DataFrame; cols::Symbol, promote::Bool) at /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframe/dataframe.jl:1194 [8] append!(::DataFrame, ::DataFrame) at /Users/takuizum/.julia/packages/DataFrames/yqToF/src/dataframe/dataframe.jl:1131 [9] top-level scope at In[28]:1 [10] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
allowmissing!(B, :名前);
append!(B, DataFrame(名前 = missing, 国語 = 0, 数学 = 0, 英語 = 0))
名前 | 国語 | 数学 | 英語 | |
---|---|---|---|---|
String? | Int64 | Int64 | Int64 | |
1 | Kato | 20 | 100 | 10 |
2 | Noguchi | 31 | 23 | 30 |
3 | Yamada | 55 | 78 | 89 |
4 | missing | 0 | 0 | 0 |
completecases(B)
4-element BitArray{1}: 1 1 1 0
dropmissing!(B, :名前)
名前 | 国語 | 数学 | 英語 | |
---|---|---|---|---|
String | Int64 | Int64 | Int64 | |
1 | Kato | 20 | 100 | 10 |
2 | Noguchi | 31 | 23 | 30 |
3 | Yamada | 55 | 78 | 89 |
groupby
は特定の列の要素が同じもの同士でグループ化したデータフレームをひとまとめにしたGroupedDataFrame
を返します。
using Random, Statistics
D = DataFrame(クラス = [1, 1, 1, 2, 2 ,2, 3, 3, 3], ID = [randstring('A':'Z', 5) for i in 1:9], 英語 = rand(1:1:100, 9), 数学 = rand(1:1:100, 9), 国語 = rand(1:1:100, 9))
クラス | ID | 英語 | 数学 | 国語 | |
---|---|---|---|---|---|
Int64 | String | Int64 | Int64 | Int64 | |
1 | 1 | QNPLM | 22 | 45 | 18 |
2 | 1 | EKWFU | 13 | 91 | 75 |
3 | 1 | KMKBG | 47 | 84 | 82 |
4 | 2 | DIGUX | 6 | 89 | 35 |
5 | 2 | LNOXB | 40 | 36 | 32 |
6 | 2 | XGAVU | 10 | 62 | 81 |
7 | 3 | PZGSC | 53 | 38 | 71 |
8 | 3 | SSNOF | 6 | 40 | 27 |
9 | 3 | XIQKF | 84 | 91 | 28 |
E = groupby(D, :クラス)
GroupedDataFrame with 3 groups based on key: クラス
First Group (3 rows): クラス = 1
クラス | ID | 英語 | 数学 | 国語 | |
---|---|---|---|---|---|
Int64 | String | Int64 | Int64 | Int64 | |
1 | 1 | QNPLM | 22 | 45 | 18 |
2 | 1 | EKWFU | 13 | 91 | 75 |
3 | 1 | KMKBG | 47 | 84 | 82 |
⋮
Last Group (3 rows): クラス = 3
クラス | ID | 英語 | 数学 | 国語 | |
---|---|---|---|---|---|
Int64 | String | Int64 | Int64 | Int64 | |
1 | 3 | PZGSC | 53 | 38 | 71 |
2 | 3 | SSNOF | 6 | 40 | 27 |
3 | 3 | XIQKF | 84 | 91 | 28 |
グループ化するとSubDataFrame
と呼ばれる亜種を内包したGroupedDataFrame
に変わります。
グループ化したデータフレームに対しては,グループごとに処理を実行することができます。代表的なものがcombine
です。
combine
はSubDataFrame
の指定した列に対して関数を作用させ,最終的に全グループを同じデータフレームにまとめたものを返します。
combine(E, :英語 => mean => :クラス英語平均, :英語 => var => :クラス英語分散)
クラス | クラス英語平均 | クラス英語分散 | |
---|---|---|---|
Int64 | Float64 | Float64 | |
1 | 1 | 27.3333 | 310.333 |
2 | 2 | 18.6667 | 345.333 |
3 | 3 | 47.6667 | 1542.33 |
do
ブロックを使った書き方もできます。この書き方のほうが,一度に複数の処理を走らせたり,一時的に利用する変数が必要なときには便利です(が,速度的には遅いと言われているので,多用しないほうが吉)。
combine(E) do sdf
m = mean(sdf.英語)
s = std(sdf.英語)
t = @. 10*(sdf.英語 - m)/s + 50
println(t)
(英語平均 = m, 英語分散 = s)
end
[46.97249746956562, 41.863586949457584, 61.1639155809768] [43.18378796832533, 61.479936053346805, 45.33627597832786] [51.35803075539493, 39.39038472347713, 59.25158452112794]
クラス | 英語平均 | 英語分散 | |
---|---|---|---|
Int64 | Float64 | Float64 | |
1 | 1 | 27.3333 | 17.6163 |
2 | 2 | 18.6667 | 18.5831 |
3 | 3 | 47.6667 | 39.2726 |
combineは1グループあたり1行しか値を含むことができないので,もとのデータフレーム長に合わせる場合はselect
を使うと良いです。
transform(E) do sdf
m = mean(sdf.英語)
s = std(sdf.英語)
t = @. 10*(sdf.英語 - m)/s + 50
t
end
クラス | ID | 英語 | 数学 | 国語 | x1 | |
---|---|---|---|---|---|---|
Int64 | String | Int64 | Int64 | Int64 | Float64 | |
1 | 1 | QNPLM | 22 | 45 | 18 | 46.9725 |
2 | 1 | EKWFU | 13 | 91 | 75 | 41.8636 |
3 | 1 | KMKBG | 47 | 84 | 82 | 61.1639 |
4 | 2 | DIGUX | 6 | 89 | 35 | 43.1838 |
5 | 2 | LNOXB | 40 | 36 | 32 | 61.4799 |
6 | 2 | XGAVU | 10 | 62 | 81 | 45.3363 |
7 | 3 | PZGSC | 53 | 38 | 71 | 51.358 |
8 | 3 | SSNOF | 6 | 40 | 27 | 39.3904 |
9 | 3 | XIQKF | 84 | 91 | 28 | 59.2516 |
empty(A)
Aと同じ名前を持つ0行の新しいデータフレームを返すdescribe(A)
Aの各列に対して要約統計量を計算する(前はカテゴリカル変数に対応していたが...) 関数 => :新しい列名
で任意の関数を作用させることもできる。isapprox(A, B)
AとBが任意の誤差の範囲内で一致しているかどうかをたしかめる。broadcastすると列ごとに論理値を返す。列名が一緒でないとエラーを吐く。完全一致ならisqeual()
。sort(A)
, sort(A, :列名)
データフレーム全体か,列名に関してソートする。!
をつけるとin-place。names(A)
, propertynames(A)
Aの列名を取得する。names
で文字列のベクトルを,propertynames
でシンボルのベクトルを得る。empty(A)
a | b | c | |
---|---|---|---|
Float64 | Float64 | Float64 |
describe(D)
variable | mean | min | median | max | nmissing | eltype | |
---|---|---|---|---|---|---|---|
Symbol | Union… | Any | Union… | Any | Int64 | DataType | |
1 | クラス | 2.0 | 1 | 2.0 | 3 | 0 | Int64 |
2 | ID | DIGUX | XIQKF | 0 | String | ||
3 | 英語 | 31.2222 | 6 | 22.0 | 84 | 0 | Int64 |
4 | 数学 | 64.0 | 36 | 62.0 | 91 | 0 | Int64 |
5 | 国語 | 49.8889 | 18 | 35.0 | 82 | 0 | Int64 |
isequal.(A, A)
a | b | c | |
---|---|---|---|
Bool | Bool | Bool | |
1 | 1 | 1 | 1 |
2 | 1 | 1 | 1 |
3 | 1 | 1 | 1 |
sort(D, :国語)
クラス | ID | 英語 | 数学 | 国語 | |
---|---|---|---|---|---|
Int64 | String | Int64 | Int64 | Int64 | |
1 | 1 | QNPLM | 22 | 45 | 18 |
2 | 3 | SSNOF | 6 | 40 | 27 |
3 | 3 | XIQKF | 84 | 91 | 28 |
4 | 2 | LNOXB | 40 | 36 | 32 |
5 | 2 | DIGUX | 6 | 89 | 35 |
6 | 3 | PZGSC | 53 | 38 | 71 |
7 | 1 | EKWFU | 13 | 91 | 75 |
8 | 2 | XGAVU | 10 | 62 | 81 |
9 | 1 | KMKBG | 47 | 84 | 82 |
names(A)
3-element Array{String,1}: "a" "b" "c"
propertynames(A)
3-element Array{Symbol,1}: :a :b :c