Julia 入門03 -- R と比較して（データフレーム）

以下は，R 入門のために書かれた

https://ai-trend.jp/programming/r-beginner/r-beginner/

を，Julia ではこんな風になるよということで書いたものである。

R の入門というページは多く存在する。

R に入門しようとしている人 ... Julia にしませんか？

# Julia でのデータフレームの作成

よくあるのは，いきなり「データフレームを読み込んで...」とうのですが，ここではまず，読み込むべきデータフレームを作り，ファイルに保存することから始めましょう。

R や Python を既に経験済みの人はデータフレームについての説明は今更不要とは思いますが，イメージ的には Excel などのスプレッドシートのデータ部分と考えてもよいかも知れません。

実際の分析に使うデータでも，比較的小さなデータの場合は，Julia のプログラムで直接データフレームを作ることができます。

まず必要なのは以下のおまじない。`DataFrames` は データフレーム を使う場合には，セッションごとに最初に一度必要です。R で言うところの library() です。`CSV` はデータフレームの入出力を行うときに必要です（入出力をしない場合には不要です）。

```Julia
using DataFrames, CSV
```

なお，そもそもまだ一度も DataFrame を使ったことがないという場合は上のおまじないを唱えるとエラーがでるので，そのエラーメッセージ中のサジェスチョンに従って，以下を行います（REPL の場合は，`]` でパッケージモードに入りますので，続けて `add DataFrames` と入力します。`delete` キーで通常モードに戻ります）。
これは，R での install.packages() なので，一回だけ行えばよいものです

```Julia
import Pkg
Pkg.add("DataFrames")
Pkg.add("CSV")
```

さて，架空のデータをデータフレームにしてみましょう。

```Julia
df = DataFrame(
  id=[101, 106, 132, 145, 173],
  height=[175.3, 167.2, 173.1, 178.5, 168.7],
  weight=[73.2, 79.1, 65.6, 90.4, 66.2],
  bloodtype=["A", "B", "O", "A", "AB"]
  )

println(df)
```

これを実行するとデータフレームが表示されます。

```
5×4 typename(DataFrame)
│ Row │ id    │ height  │ weight  │ bloodtype │
│     │ Int64 │ Float64 │ Float64 │ String    │
├─────┼───────┼─────────┼─────────┼───────────┤
│ 1   │ 101   │ 175.3   │ 73.2    │ A         │
│ 2   │ 106   │ 167.2   │ 79.1    │ B         │
│ 3   │ 132   │ 173.1   │ 65.6    │ O         │
│ 4   │ 145   │ 178.5   │ 90.4    │ A         │
│ 5   │ 173   │ 168.7   │ 66.2    │ AB        │
```

5 人分のデータ，IDNO，身長，体重，血液型 が Excel の表のように並んでいるのがわかります。

# データフレームをファイルに保存する

新しく作ったデータフレームや，前に作ったデータフレームに編集を加えたデータフレームを，後でまた使いたいような場合にはファイルに保存しておきます。

その前に，保存される場所（ディレクトリ）がどこなのか確認しておきます。

Julia を立ち上げた後，プロンプトの後に `pwd()` と入力したときに表示されるディレクトリが，現在の入出力の対象（作業ディレクトリ）になります。[^1]
[^1]: R では getwd() です。

例示するのは Mac OS の場合で，`foo` の部分にはユーザ名が入ります。

```Julia
pwd() # "/Users/foo"
```

上のような場合，`/Users/foo` は Mac OS におけるユーザーズディレクトリ直下なので，特定の目的を持つファイルなどを保存するのは不適当でしょう。
前もって，適切なディレクトリを作成しておき，Julia 関連のファイルはそこへ保存するのがよいでしょう。
ディレクトリの作成方法はいろいろありますが，Julia では以下のようにすれば作成できます。

```Julia
mkdir("/Users/foo/Desktop/Julia") # "/Users/foo/Desktop/Julia"
```
一度 `mkdir` でディレクトリを作ると，その下にさらにディレクトリを作ることができます。
同じ Julia 関連のファイルであっても目的別のサブディレクトリにまとめたほうが管理しやすいのです。

```Julia
mkdir("/Users/foo/Desktop/Julia/project1") # "/Users/foo/Desktop/Julia/project1"
```

さて，目的とするディレクトリができた（できている）とすれば，入出力を行う前に以下のように　`cd()` でこのディレクトリを作業ディレクトリにします。[^2]
[^2]: R では setwd() です。

作業ディレクトリが変更されたかどうかは `pwd()` で確認できます。

```Julia
cd("/Users/foo/Desktop/Julia/project1")
pwd() # "/Users/foo/Desktop/Julia/project1"
```

以上で入出力の準備ができました。保存するファイル名を "example1.dat" とすれば，以下のようにします。

```Julia
using DataFrames, CSV # 必要ならば
cd("/Users/aoki/Desktop/Julia/project1")
CSV.write("example1.dat", df) # "example1.dat"
# CSV.write("/Users/foo/Desktop/Julia/project1/example1.dat", df) 上の 2 行と同じ
```

なお，ファイル名を絶対パス（相対パス）で指定するならば，上述のような `cd()` による作業ディレクトリの変更は不要です。
ただし，煩わしさを避けたり，間違いを防ぐためには作業ディレクトリを変更する手順を踏んでおいた方が無難でしょう。

書き出されたファイル `example1.dat` を見てみると以下のようになっています。
元のデータフレーム `df` と比べてみれば，どのようになっているか明らかです。
1行目に変数名，2行目以降にデータがそれぞれカンマで区切られて書き込まれています。CSV ファイルですね。

```csv
id,height,weight,bloodtype
101,175.3,73.2,A
106,167.2,79.1,B
132,173.1,65.6,O
145,178.5,90.4,A
173,168.7,66.2,AB
```

逆に言えば，Java 以外の何らかの方法でも，このような CSV ファイルを作れば，Julia でも，あるいは R や Python でも読み込むことができます。
既存の CSV ファイルも同じように，読み込むことができます。

#CSV ファイルをデータフレームに読み込む

CSV ファイルをデータフレームに読み込むには `CSV.read()` を使います。第1引数がファイル名，第2引数は `DataFrame` です。

```Julia
using DataFrames, CSV
cd("/Users/aoki/Desktop/Julia/project1")
pwd() # "/Users/aoki/Desktop/Julia/project1"
df2 = CSV.read("example1.dat", DataFrame)
println(df2)
```

表示してみると，最初に示したデータフレームと同じものであることがわかります。

```
5×4 typename(DataFrame)
│ Row │ id    │ height  │ weight  │ bloodtype │
│     │ Int64 │ Float64 │ Float64 │ String    │
├─────┼───────┼─────────┼─────────┼───────────┤
│ 1   │ 101   │ 175.3   │ 73.2    │ A         │
│ 2   │ 106   │ 167.2   │ 79.1    │ B         │
│ 3   │ 132   │ 173.1   │ 65.6    │ O         │
│ 4   │ 145   │ 178.5   │ 90.4    │ A         │
│ 5   │ 173   │ 168.7   │ 66.2    │ AB        │
```

#データフレームの参照方法

##列の参照

Julia のデータフレームで列を参照する方法は何通りかあります。
もっとも簡単なのは `データフレーム名.列名`，`データフレーム名[:列名]` という指定方法です。[^3]

[^3]: ほかには `データフレーム名[列番号]`，`データフレーム名[Symbol(列名)]` があります。

```Julia
height = df.height # [175.3, 167.2, 173.1, 178.5, 168.7]
BMI = df[:weight] ./ (height/100) .^ 2 # [23.820301209661277, ..., 23.260970165170463]
```

##複数列の抽出

複数の列を抽出して，新たなデータフレームを作るときは以下のようにします。[^4]
[^4]: df[[2, 3]] とするのと同じです。たくさんの列を抽出するときは，こちらの方が便利かも知れません。

```Julia
df2 = df[[:height, :weight]]
println(df2)
#=
5×2 typename(DataFrame)
│ Row │ height  │ weight  │
│     │ Float64 │ Float64 │
├─────┼─────────┼─────────┤
│ 1   │ 175.3   │ 73.2    │
│ 2   │ 167.2   │ 79.1    │
│ 3   │ 173.1   │ 65.6    │
│ 4   │ 178.5   │ 90.4    │
│ 5   │ 168.7   │ 66.2    │
=#
```

##行の参照

データフレームは見た目は行列に似ているので，行の参照も行列の参照に似ています。
たとえば，2 行目のデータを取り出すとき R では `df[2,]` のように書きますが，Julia では 2 番目の添え字は書略することはできません。必ず `：` を書かなければいけません。

```Julia
pringln(df[2, :])
#=
DataFrameRow
│ Row │ id    │ height  │ weight  │ bloodtype │
│     │ Int64 │ Float64 │ Float64 │ String    │
├─────┼───────┼─────────┼─────────┼───────────┤
│ 2   │ 106   │ 167.2   │ 79.1    │ B         │
=#
```

#行の抽出

血液型が A 型の人だけを抽出するときは，以下のようにします。

```Julia
df3 = df[df.bloodtype .== "A", :]
println(df3)
#=
2×4 typename(DataFrame)
│ Row │ id    │ height  │ weight  │ bloodtype │
│     │ Int64 │ Float64 │ Float64 │ String    │
├─────┼───────┼─────────┼─────────┼───────────┤
│ 1   │ 101   │ 175.3   │ 73.2    │ A         │
│ 2   │ 145   │ 178.5   │ 90.4    │ A         │
=#
```

または以下のようにします。[^5]

[^5]: ここにある `row` は引数なので，なんでもよいので，例えば `x` でも差し支えないです。

```Julia
df4 = filter(row -> row.bloodtype == "A", df)
```

なお，1 文字だけ違う関数 `filter!()` は元のデータファイルを書き換えるので，結果を代入する必要はありません。[^6]
元のデータファイルを書き換えては困る場合は上述のように `filter()` を使いましょう。

[^6]: Julia の慣例で，最後に `!` の付く関数は引数を書き換える副作用を持つので，便利な反面，注意が必要です。