データフレームの操作

tidy data (整然データ)

データフレームはリストの一種で,同じ長さのベクトルを束ねたオブジェクトである。1つの列は1つの変数であり,1つの行は1つの観測単位 (横断面データであれば観測固体,時系列データであれば観測時点)である。たとえば,5人の個人の氏名,性別,身長,体重が記録されたデータであれば,データフレームは10行4列となる。4つの列 (氏名,性別,身長,体重)はそれぞれ長さが10のベクトルであり,5つの行はそれぞれがひとりひとりの個人に対応する。

また,irisデータは,150行5列のデータフレームであるが,各列 (Sepal.Length, Sepal.Width, Petal.Length, Petal.Width, Species)はそれぞれ長さが150のベクトルである。そして,各行はひとつひとつの観測個体のデータを表している。

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
data(iris)
head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

このようなデータは,tidy data (整然データ)と呼ばれる。tidy dataとは,以下の4つの条件を満たすデータのことである。

  1. 1つの列が1つの変数,1つの変数が1つの列
  2. 1つの行が1つの観測単位,1つの観測単位が1つの行
  3. 1つの値が1つのセル,1つのセルに1つの値
  4. 観測単位が1つに統一されている

tidy dataの概念は,Hadley Wichhamによって提唱された。Wickham (2014)では,tidy dataの条件として上記の1,2,4があげられている。一方,tidyverseに含まれているtidyrパッケージの説明では,1,2,3があげられている。ここでは,4つの条件すべてを満たすデータをtidy dataと呼ぶことにする。

tidy dataは構造と意味が一致していて分析の際に扱いやすい形である (必ずしも見た目にわかりやすいというわけではない)。

tidy dataではないデータの典型例を1つ示そう。

商品 2月3日 2月4日
たこ焼き 4000円 5000円
お茶 1000円 1500円
チョコレート 2000円 2500円

このデータは,売上げという1つの変数が2列にまたがっており,1つの列に日付と売上げという2つの変数が記録されている。1つの列が1つの変数となっていないため,このデータはtidy dataではない。

このデータをtidy dataにするには,日付と売上げをそれぞれ1つの列にまとめ,1つの列が1つの変数になるようにする。

商品 日付 売上げ
たこ焼き 2月3日 4000円
たこ焼き 2月4日 5000円
お茶 2月3日 1000円
お茶 2月4日 1500円
チョコレート 2月3日 2000円
チョコレート 2月4日 2500円

当然,このデータに小計や合計の行を加えると,商品ごとの売上げと小計や合計という観測単位が統一されていない行が混在することになるので,tidy dataではなくなる。

tidyverse

tidyverseは,モダンなデータ分析を行うためのパッケージをひとまとめにしたパッケージである。tidyverseに含まれるパッケージは,設計の哲学,文法,データ構造を共有している。

tidyverseに含まれるパッケージは,以下の9つである。tidyverseを読み込めば,すべてのパッケージが自動的に読み込まれる。

  • ggplot2: グラフを描くためのパッケージ
  • dplyr: データフレームを操作するためのパッケージ
  • tidyr: tidy dataを作成するためのパッケージ
  • readr: データを読み込むためのパッケージ
  • purrr: 関数プログラミングを行うためのパッケージ
  • tibble: tibble型データフレームを使うためのパッケージ
  • stringr: 文字列を操作するためのパッケージ
  • forcats: 因子を操作するためのパッケージ
  • lubridate: 日付を操作するためのパッケージ

それぞれのパッケージのページには,関数の使い方を簡潔にまとめたチートシートが用意されている。印刷しておくと良いだろう。

tidyverseによるデータフレームの操作

readrパッケージによるデータの読込み

分析したいデータをRのデータフレームに読み込むためには,readrパッケージを利用する。readrパッケージには,csvファイルを読み込むread_csv(),固定長ファイルを読み込むread_fwf()などの関数が含まれ,さまざまな形式のデータを読み込むことができる。

通常はcsvファイルを読み込むことができれば十分だろう (Excelのデータはcsvに保存することができる)。base Rのread.csv()関数でも十分だが,readrパッケージのread_csv()関数の方が,読込み速度が速い。

dplyrパッケージによるデータフレームの操作

dplyrパッケージは,データフレームを操作するためのパッケージである。すでにいくつかの関数については,データの要約 (記述統計量)回帰分析で利用した。ここでは,よく使うものをリストアップしておく。

新しい変数の追加 (mutate)

dplyr::mutate()関数を用いると,新しい変数を追加することができる。具体的な使い方は回帰分析を参照。

列 (変数)の抽出 (select)

dplyr::select()関数を用いると,データフレームから列 (変数)を抽出することができる。具体的な使い方はデータの要約 (記述統計量)を参照。

select()関数で取り出されたデータフレームは,変数の並び順がselect()関数の引数に指定した順になる。したがって,変数を並び替えたい場合にもselect()関数を用いることができる。なお,変数の列を移動させるには,dplyr::relocate()関数も用意されている。

行 (観測単位)の抽出 (filter)

dplyr::filter()関数を用いると,データフレームから行 (観測単位)を抽出することができる。具体的な使い方はデータの要約 (記述統計量)を参照。

要約統計表の作成 (summarize, group_by)

dplyr::summarize()関数を用いると,データフレームの要約統計量を計算することができる。具体的な使い方はデータの要約 (記述統計量)を参照。また,dplyr::group_by()関数を用いると,データフレームをグループに分けることができる。具体的な使い方はデータの要約 (記述統計量)を参照。

データの並替え (arrange)

dplyr::arrange()関数を用いると,データフレームの行を並べ替えることができる。第1引数にはデータフレーム,第二引数以降には並替えに用いる変数を優先する順に指定する。逆順に並替えたい場合は,その変数をdesc()関数でラップする。

tidyrパッケージによるデータのtidy data化

tidyrパッケージは,データフレームをtidy dataに整理するためのパッケージである。とくに,pivot_longer()関数とpivot_wider()関数は,wide型データとlong型データを相互に変換するための関数で,使う機会が多い。

long型データとwide型データ

1つの行が観測単位を表し,1つ1つの列に変数が記録されたデータをwide型データという。それに対して,1つの行が観測値を表し,変数名と値がそれぞれの列に記録されたデータをlong型データという。言語で理解するよりは,具体例を見た方が理解しやすいだろう。

次のデータは,1つの行が観測単位を表し,1つ1つの列に変数が記録されているため,wide型データである。

wide <-tribble(
  ~氏名,      ~`労働所得 (万円)`, ~`不労所得 (万円)`,
  "大阪太郎",             300,            100,
  "山田花子",             800,            200, 
  "神戸次郎",             500,            150
)

wide
# A tibble: 3 × 3
  氏名     `労働所得 (万円)` `不労所得 (万円)`
  <chr>                <dbl>             <dbl>
1 大阪太郎               300               100
2 山田花子               800               200
3 神戸次郎               500               150

このデータをlong型データに変換すると,次のようになる。

long <- wide |>
  pivot_longer(
    cols = c(`労働所得 (万円)`, `不労所得 (万円)`), 
    names_to = "income_source",
    values_to = "income")

long
# A tibble: 6 × 3
  氏名     income_source   income
  <chr>    <chr>            <dbl>
1 大阪太郎 労働所得 (万円)    300
2 大阪太郎 不労所得 (万円)    100
3 山田花子 労働所得 (万円)    800
4 山田花子 不労所得 (万円)    200
5 神戸次郎 労働所得 (万円)    500
6 神戸次郎 不労所得 (万円)    150

通常,人間が見た目で理解しやすいのはwide型であるが,Rで分析を行う際には,long型データの方が扱いやすいことも多い (ggplot2では,long型データが前提になっている)。また,tidy dataになっていないwide型データは,long型に変換することでtidy dataにすることができる場合がある。

long型データ=tidy dataというわけではないし,すべてのデータをlong型に変換するような必要はないことに注意。分析内容によってはlong型データよりもwide型データの方が扱いやすい場合もある。たとえば,要約統計表を作成したり回帰分析を行ったりする場合は,wide型データの方が扱いやすい。実際には,データをwide型とlong型との間で相互に変換しながら分析を行う。

pivot_longer()関数

tidyr::pivot_longer()関数を用いると,すでに上の例で示したとおり,wide型データをlong型データに変換することができる。第1引数はデータフレーム,第2引数以降は以下の通り。

  • cols引数には,変数名の列と値の列との2列に変換する変数名を指定する。
  • names_to引数には,変換後のデータフレームで,変数名を格納するための列の名前を指定する。
  • values_to引数には,変換後のデータフレームで,値を格納するための列の名前を指定する。

この場合は,cols引数には,労働所得と不労所得の列を指定している。これにより,氏名の列はそのままで,労働所得と不労所得の列が,変数名と値の2列に変換される。names_tovalues_toは省略可能で,省略した場合,変数名を格納する列はname,値を格納する列はvalueという名前になる。

pivot_wider()関数

tidyr::pivot_wider()関数を用いると,long型データをwide型データに変換することができる。

wide <- long |>
  pivot_wider(
    names_from = income_source,
    values_from = income
  )

wide
# A tibble: 3 × 3
  氏名     `労働所得 (万円)` `不労所得 (万円)`
  <chr>                <dbl>             <dbl>
1 大阪太郎               300               100
2 山田花子               800               200
3 神戸次郎               500               150

第1引数はデータフレーム,第2引数以降は以下の通り。

  • names_from引数には,変数名が格納されている列を指定する。
  • values_from引数には,値が格納されている列の名前を指定する。