Lesson 2-2: データフレーム・tibble

2018/10/15

前のステップ

Lesson 2-1: R のベクトル では次のことを勉強しました。

このセッションの目標

データフレーム

「データフレーム」というのは複数のベクトルを並べたものです。

データフレームのイメージ

Figure 1: データフレームのイメージ

少し書き換えると,表のような形式を取っていることがわかります。データフレームは表形式データあるいは矩形データをRで実現する方法の1つです。

Name Econ Math
Alice 90 100
Bob 80 90
Charlie 100 60

データフレームをイメージするときに,表のイメージから入るとどうしても上から下に読んでいく感じがしてしまいます。個票を上から下に並べたものに見えるからでしょう。しかし,縦方向に同じ形式(型)のデータが集まっているので,データの保存という観点から言えば,各変数を集めたベクトルを横に並べる方が自然です。

上から下にデータを積み上げるという風に覚えてしまうと,データフレームに対するインデックスを使う際に失敗しやすくなりますので注意してください。

データフレームの生成

データフレームの生成には data.frame() を使います。

score <- data.frame(
  Name = c("Alice", "Bob", "Charlie"),
  Econ = c(90,      80,    100),
  Math = c(100,     90,    60)
)
> score
##      Name Econ Math
## 1   Alice   90  100
## 2     Bob   80   90
## 3 Charlie  100   60

代入記号 <- ではなく = を使っていることに注意してください。

同じデータフレームは次のようにしても作ることができます。オブジェクト名が列名になっていることを確認してください。

Name <- c("Alice", "Bob", "Charlie")
Econ <- c(90,      80,    100)
Math <- c(100,     90,    60)
score <- data.frame(Name, Econ, Math)
> score
##      Name Econ Math
## 1   Alice   90  100
## 2     Bob   80   90
## 3 Charlie  100   60

データフレームの切り出し

データフレームから値を切り出すにはベクトルと同様に [] を使うのが基本です。ただし,ベクトルと違って縦横2次元の広がりがあるという点に注意しましょう。

列の切り出し

列の番号を使うか,

> score[3]
##   Math
## 1  100
## 2   90
## 3   60

列の名前を使って切り出します。結果はデータフレームになります。

> score["Math"]
##   Math
## 1  100
## 2   90
## 3   60

特別な演算子 $ を使うこともできます。しかし,これは結果がベクトルになります。

> score$Econ
## [1]  90  80 100

次の方法でも切り出せます。[行インデックス, 列インデックス] と考えます。行インデックスが省略されて,前行の情報を取り出します。結果がベクトルになってしまうことに注意してください。

> score[, 3]
## [1] 100  90  60

行の切り出し

行全体を切り出すには次のような構文を使います。結果はデータフレームになります。

> score[1, ]  # 1行目
##    Name Econ Math
## 1 Alice   90  100
> score[3, ]  # 3行目
##      Name Econ Math
## 3 Charlie  100   60

複数行を切り出すには次のようにします。

> score[c(1, 2), ]
##    Name Econ Math
## 1 Alice   90  100
## 2   Bob   80   90

条件付きで行を切り出すこともできます。例えば,Name が Alice である行を取り出したいという場合があります。 その場合は,次のようにします。

> score[score$Name == "Alice", ]
##    Name Econ Math
## 1 Alice   90  100

値の切り出し

Alice の Math のスコアを取り出したい場合にはどうすればよいでしょうか?もし,Alice が 1行目であることを知っていれば,次のいずれかの方法を使うことができます。

> score[1, "Math"]
## [1] 100
> score$Math[1]
## [1] 100

また,Math が3列目であることもわかっていれば

> score[1, 3]
## [1] 100

とできます。

“Alice” が 1行目であることを知らない場合には,Name が Alice である行を取り出す操作と,列名が Math の行を取り出す操作を組み合わせます。

> score[score$Name == "Alice", "Math"]
## [1] 100

データフレームに対する演算

データフレームの列はベクトルなので,ベクトル演算が自然に実行できます。

> Math_mean <- mean(score$Math)
> Math_mean
## [1] 83.33333

複数の列の情報を元に新しい計算をすることもよくあるでしょう。例えば,学生ごとに経済学のスコアと数学のスコアの平均を計算するような場合です。

> (avg_score <- rowMeans(score[c("Econ", "Math")]))
## [1] 95 85 80

列の追加

次のコードによって平均点を保持する新しい列が追加されたことを確認してください。

> score$Avg <- avg_score
> score
##      Name Econ Math Avg
## 1   Alice   90  100  95
## 2     Bob   80   90  85
## 3 Charlie  100   60  80

問題

問題 2.2.1: rowSum() という関数を使うと行方向の合計を計算することができます。score データフレームに経済学と数学の合計点を保持する Total 列を追加してください。

tibble

tibble はデータフレームを改善するために提案されたデータ保持形式で,tibble というパッケージで定義されています。基本的にはR の基本のデータフレームと同様の使い方ができます。

tibble を使うためには,パッケージ tibble を読み込むか,tidyverse というパッケージ群を読み込みます。 パッケージの読み込みには library() という関数を使います。

パッケージを読み込む前には,パッケージをインストールする必要があります。読み込みでエラーが表示され場合はコンソールで次のコマンドを実行してください。

> install.packages("tidyverse")

まず tidyverse を読み込みます。

> library(tidyverse)
## ── Attaching packages ───────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
## ✔ ggplot2 3.0.0     ✔ purrr   0.2.5
## ✔ tibble  1.4.2     ✔ dplyr   0.7.6
## ✔ tidyr   0.8.1     ✔ stringr 1.3.1
## ✔ readr   1.1.1     ✔ forcats 0.3.0
## ── Conflicts ──────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

tidyverse は複数のパッケージを読み込みます。tidyverse に含まれる関数・オブジェクトが既存の関数の名前(filter, lag)を奪ってしまうので,警告メッセージがでます。

> score2 <- as_tibble(score)
> score2
## # A tibble: 3 x 4
##   Name     Econ  Math   Avg
##   <fct>   <dbl> <dbl> <dbl>
## 1 Alice      90   100    95
## 2 Bob        80    90    85
## 3 Charlie   100    60    80

おおよそデータフレームと同じように使うことができますが,データフレームの場合と違って次のコードは tibble を返します。

> score2[, 2]
## # A tibble: 3 x 1
##    Econ
##   <dbl>
## 1    90
## 2    80
## 3   100

tibble の作成

data.frame() を介さずに tibble を作成するには tibble() を使います。

tibble()

tibble(
  Name = c("Alice", "Bob", "Charlie"),
  Econ = c(90,      80,    100),
  Math = c(100,     90,    60)
)
## # A tibble: 3 x 3
##   Name     Econ  Math
##   <chr>   <dbl> <dbl>
## 1 Alice      90   100
## 2 Bob        80    90
## 3 Charlie   100    60

tribble()

tibble を作成するために tribble()を使う方法があります(tribble = transposed tibble)。下のコードのように表形式を表現しやすいので,小さいデータを作る際にはこちらも便利です。

tribble(
  ~Name,     ~Econ, ~Math,
  "Alice",      90,   100,
  "Bob",        80,    90,
  "Charlie",   100,    60
)
## # A tibble: 3 x 3
##   Name     Econ  Math
##   <chr>   <dbl> <dbl>
## 1 Alice      90   100
## 2 Bob        80    90
## 3 Charlie   100    60

一行目はName, Econ, Math ではなく,~Name, ~Econ, ~Math とチルダがついていることに注意してください。

問題

問題 2.2.2: ?tibbletibble() のヘルプを読みましょう。tibbledata.frame のどのような問題点を改善しているか,確認してください。(特に Example のセクションを見よ)

外部ファイルのインポート/エクスポート

次のコードはここまでに使った成績データをCSVファイル(コンマ区切りファイル)と Excel 形式で保存したものをダウンロードして “Data” フォルダに保存します。実行する前にプロジェクトフォルダに “Data” というサブフォルダがあることを確認してください!

download.file("https://opur.club/files/18d2/score.csv", "Data/score.csv")
download.file("https://opur.club/files/18d2/score.xlsx", "Data/score.xlsx")

readr による CSVファイルの読み込み

readr::read_csv() を使うと CSV ファイルを読み込むことができます。

library(readr)
(score <- read_csv("Data/score.csv"))
## Parsed with column specification:
## cols(
##   Name = col_character(),
##   Econ = col_integer(),
##   Math = col_integer()
## )
## # A tibble: 3 x 3
##   Name     Econ  Math
##   <chr>   <int> <int>
## 1 Alice      90   100
## 2 Bob        80    90
## 3 Charlie   100    60

R に組み込みの read.csv() という関数がありますが,readr::read_csv() の方が使いやすく改良されていて,高速に動作します。

readxl による Excel ファイルの読み込み

入力した “Data/score.xlsx” を MS Excel で開いてみてください。

Data/score.xlsx

Figure 2: Data/score.xlsx

これを R に読み込むには readxlread_xlsx() または read_xls() または read_excel() 関数を使います。

> (score <- readxl::read_xlsx("Data/score.xlsx"))
## # A tibble: 3 x 3
##   Name     Econ  Math
##   <chr>   <dbl> <dbl>
## 1 Alice      90   100
## 2 Bob        80    90
## 3 Charlie   100    60

:: オペレーターについて

> readxl::read_xlsx("Data/score.xlsx")

> library(readxl)
> read_xlsx("Data/score.xlsx")

としても構いません。 library() はパッケージに含まれる関数をすべて読み込むときに使います。1つの関数を1回だけ使うときには,:: を使った関数呼び出しを使うと便利です。

複数のソフトウェアを使いこなす

調査データを手作業で入力する場合などには,MS Excel などで作業するのが標準的だと思います。 R スクリプトにデータを直接入力するのは現実的ではありません。

データが Excel にあるからといって,分析までExcel で行うという必要もありません。各ソフトウェアには得意な分野とそうでない分野がありますので,すべてを R で行うのも,すべてを Excel で行うのも,どちらも最適ではありません。

複数のソフトウェア間でデータをやり取りする方法を学ぶことが重要です。これには R で読み込む方法(readxl の使い方など)に加えて,R に渡しやすいように Excel ファイルを整理する方法を学ぶといったことも含まれます。

Excel ファイルの取り込みの実例

http://www.esri.cao.go.jp/jp/sna/data/data_list/kakuhou/files/h28/h28_kaku_top.html から平成28年度のGDP統計を取得できます。Excel 形式のファイルを見ていただけるとわかると思いますが,このレッスンの最初に提示したデータフレームの形とはまったく異なっています。

良くも悪くも Excel は自由度の高いソフトウェアなので,色々な形のファイルが存在しています。取り込みにくいファイルを取り込んで分析しようとすると,ひと手間かかります。こういう場合に少しのプログラミングが役に立ちます

> sna_xls <- readxl::read_excel("Data/28ffm1rn_jp.xls", sheet = 1, range = "A7:X66")
> (rgdp_untidy <- sna_xls[41, 2:length(sna_xls)])
## # A tibble: 1 x 23
##   `1994` `1995` `1996` `1997` `1998` `1999` `2000` `2001` `2002` `2003`
##    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
## 1 4.27e5 4.41e5 4.54e5 4.54e5 4.50e5 4.53e5 4.64e5 4.62e5 4.66e5 4.75e5
## # ... with 13 more variables: `2004` <dbl>, `2005` <dbl>, `2006` <dbl>,
## #   `2007` <dbl>, `2008` <dbl>, `2009` <dbl>, `2010` <dbl>, `2011` <dbl>,
## #   `2012` <dbl>, `2013` <dbl>, `2014` <dbl>, `2015` <dbl>, `2016` <dbl>
> (rgdp <- tidyr::gather(rgdp_untidy, year, RGDP))
## # A tibble: 23 x 2
##    year     RGDP
##    <chr>   <dbl>
##  1 1994  426792.
##  2 1995  441147.
##  3 1996  453589.
##  4 1997  453748.
##  5 1998  449794.
##  6 1999  452936.
##  7 2000  464240.
##  8 2001  461642.
##  9 2002  465763.
## 10 2003  475119.
## # ... with 13 more rows
> plot(rgdp, type = "l")

tidyr については次回勉強します。

問題

問題 2.2.3: 手元の環境で上のコードを再現してみてください。ファイルは次のコードでダウンロードできます。

> download.file("http://www.esri.cao.go.jp/jp/sna/data/data_list/kakuhou/files/h28/tables/28ffm1rn_jp.xls", "Data/28ffm1rn_jp.xls")

CSV ファイルに書き出す

ひとまずここでは基本を押さえておきましょう。

基本

再び使い古した例を使います。

library(readr)
score <- readr::read_csv("Data/score.csv")
> score$Avg <- rowMeans(score[c("Econ", "Math")])
> score
## # A tibble: 3 x 4
##   Name     Econ  Math   Avg
##   <chr>   <int> <int> <dbl>
## 1 Alice      90   100    95
## 2 Bob        80    90    85
## 3 Charlie   100    60    80

readr::write_csv() で CSV に書き出します。出力先のフォルダが Data であるか Results であるかは,一考の余地があるでしょう。

> write_csv(score, "Results/score_avg.csv")

MS Excel で読み込める CSV ファイルにする

日本語混じりのファイルを書き出すときには write_csv() ではうまくいかないかもしれません。試してみましょう。

> score$合計 <- rowSums(score[c("Econ", "Math")])
> score
## # A tibble: 3 x 5
##   Name     Econ  Math   Avg  合計
##   <chr>   <int> <int> <dbl> <dbl>
## 1 Alice      90   100    95   190
## 2 Bob        80    90    85   170
## 3 Charlie   100    60    80   160
> write_csv(score, "Results/score_sum.csv")      # 注意が必要!!

結果は次のようになります。「合計」が文字化けしてしまいます。

write_csv() の結果を Excel で開く

Figure 3: write_csv() の結果を Excel で開く

MS Excel で開くことを前提に CSV を作成する場合には, write_excel_csv() という関数を使う方が良い結果が得られます。試してみてください。

問題

問題 2.2.4: 「合計」列を含む scorereadr::write_excel_csv() 関数で書き出してください。出力される CSV ファイルを Excel で開いて,文字化けが解消されているかを確認してください。