カテゴリ:統計 > R

(2019.09.17 更新)

前回はmutate関数を使って列の追加や修正を行いました。
そして最後に以下のコメントで終了しました。
mutate関数を使うと「FIMの合計点」とかだけでなく、年齢を「年代ごとに分類する」だったり、ある評価の「カットオフ値以上とそれ以下」に分けた列をExcelを使わずに作成することができます。
【2-2】Rのmutate関数を使って列の追加や修正を行うより)

今回は連続変数を条件に応じて複数のカテゴリーに変換していきます。

Rではいくつか方法があるのですが、今回はifelse関数、case_when関数、cut関数を紹介します。


今回使う架空のデータは年齢、性別、病名が入っています。
下のコードをまとめてスクリプトにコピペして実行してください。
library(tidyverse)
set.seed(1) 年齢 <- floor(rnorm(100,60,20)) 性別 <- sample(c("男性","女性"),100,replace = TRUE) 病名 <- sample(c("脳梗塞", "脳出血", "骨折", "靭帯損傷","心筋梗塞"), 100, replace = TRUE) data <- data.frame(年齢,性別,病名) head(data)
スクリーンショット 2019-02-10 22.48.49



今回は%>%やmutate関数を使って作業します。
これらの使い方に不安がある場合は前回の記事をご参照ください



1.ifelse関数をつかって分類する

条件から2つに分類する場合

ifelse関数は条件からTRUEとFALSEの2つに分けることができます。

ifelse(条件TRUEの場合FALSEの場合)



まず例としてdata_20という変数名に以下のように分類していきます。
・20歳未満を「未成年」、20歳以上を「成人」
・性別の男性を「male」、女性を「female」

data_20 <- data %>% 
  mutate(成人 = ifelse(.$年齢 < 20 , "未成年", "成人")) %>% 
  mutate(性別 = ifelse(.$性別 == "男性", "male", "female"))
head(data_成人)

スクリーンショット 2019-02-11 0.44.03

スクリーンショット 2019-02-11 0.46.48



条件から3つ以上分類する場合

3つ以上分類するはifelse関数のFALSEにifelseを入れ込みます。

ifelse(条件, TRUE, ifelse(条件2, TRUE, ifelse(条件3, TRUE, FALSE)))

このような形を「入れ子」と呼ぶそうです。繰り返せばいくらでも条件を増やすことができます。


今度は「年代」という変数名で20歳ごとに区切ってみます。
20歳未満なら「0〜」、
そうでなければ40歳未満なら「20〜」
そうでなければ60歳未満なら「40〜」・・・

上のイメージをコードにするとこうなります。

data_age <- data %>% 
  mutate(年代 = ifelse(.$年齢 < 20,"0〜", 
                      		ifelse(.$年齢 <40 ,"20〜", 
                        		ifelse(.$年齢 < 60,"40〜",
                        			ifelse(.$年齢 < 80, "60〜",
                        				ifelse(.$年齢 < 100, "80〜", "100〜")))))) %>% 
  mutate(年代 = factor(.$年代, levels = c("0〜", "20〜", "40〜", "60〜", "80〜", "100〜"), ordered = TRUE))
head(data_age)
スクリーンショット 2019-02-12 21.22.24

 mutate(年代 = factor(.$年代, levels = c("0〜", "20〜", "40〜", "60〜", "80〜", "100〜"), ordered = TRUE)の部分はfactorの順番を並べ替えています。

factorは50音順に並ぶので、もしこれをしなかったらグラフなど作る時に「0〜, 100〜, 20〜・・・」と100歳以上の場所がずれてしまいます。それを修正するためにlevels = でfactorの並べ替えをしています。
参照:【1-10】Rでよく使われる型について説明します。


個人的に感じる「ifelse関数+入れ子」のメリットは「他の関数を覚えなくていい」で、デメリットは終わりの))))))))の数が多すぎて間違えやすく、エラーに悩まされることです(笑)



2.case_when関数を使って分類する

3つ以上の分類で入れ子構造対策で慣れるとわかりやすいのがcase_when関数かもしれません。

case_when(条件A ~ 結果A, 条件B ~ 結果B, 条件C ~ 結果C)

今回は0歳以上 かつ 20歳未満といったように「かつ」が入る時は & を使います

年齢が0歳以上   かつ 年齢が20歳未満 を「0〜」
年齢が20歳以上 かつ 年齢が40歳未満 を「20〜」
年齢が40歳以上 かつ 年齢が60歳未満 を「60〜」・・・

上のイメージをコードにするとこうなります。

data_age <- data %>% 
  mutate(年代 = case_when(
    .$年齢 >= 0  & .$年齢 <20 ~ "0〜",
    .$年齢 >= 20 & .$年齢 <40 ~ "20〜",
    .$年齢 >= 40 & .$年齢 <60 ~ "40〜",
    .$年齢 >= 60 & .$年齢 <80 ~ "60〜",
    .$年齢 >= 80 & .$年齢 <100 ~ "80〜",
    .$年齢 >= 100 ~ "100〜")) %>% 
  mutate(年代 = factor(.$年代, levels = c("0〜", "20〜", "40〜", "60〜", "80〜", "100〜"), ordered = TRUE))

head(data_age)
スクリーンショット 2019-02-12 23.58.42


入れ子よりも( )の数が少ないのがありがたいところです。


数値でなくカテゴリーを条件で更に分類する場合

上記の場合は数値でしたが次のような場合はどうでしょう?

最初に行ったdata_20という変数名に以下のように分類していきます。
・成人という変数名で20歳未満を「未成年」、20歳以上を「成人」とする
・性別の男性を「male」、女性を「female」に変更
・疾患分類という変数名で、骨折・靭帯損傷を「運動器疾患」、脳梗塞・脳出血を「脳血管疾患」、心筋梗塞を「心疾患」とする

data_age <- data %>% 
  mutate(年代 = case_when(
    .$年齢 < 20 ~ "未成年",
    .$年齢 <= 20 ~ "成人")) %>% 
  mutate(性別 = case_when(
    .$性別 == "男性" ~ "male",
    .$性別 == "女性" ~ "female")) %>%
  mutate(疾患分類 = case_when(
    .$病名 %in% c("骨折", "靭帯損傷") ~ "運動器疾患",
    .$病名 %in% c("脳梗塞", "脳出血") ~ "脳血管疾患",
    .$病名 %in% "心筋梗塞" ~ "心疾患"))
head(data_age)
スクリーンショット 2019-02-13 0.58.43


ここで新たな ==%in% が出てきました。

スクリーンショット 2019-02-11 2.04.40
A == Bと A %in% Bは似ていますが、Bにあたる部分で==であれば1つしか入りません。
%in%は1つでも複数でも大丈夫です。

上記のコードで言うと .$性別 == "男性" ~ "male" の==は %in%に変えても大丈夫ですし、.$病名 %in% "心筋梗塞" ~ "心疾患" の %in% は == に変えても大丈夫です。

しかし.$病名 %in% c("骨折", "靭帯損傷") ~ "運動器疾患" に関しては ==に変えるとエラーが出ます。

上記図は今回の記事だけでなく条件式を作る時に必要になる考え方です。


case_when関数のメリットは入れ子構造にならないことで、デメリットとしては2つに分類するならifelseの方がコードが短くなります。



その他を作る(2019.09.17追記)

Exploratoryというソフトを開発しているKan Nishidaさんのツイートでcase_whenの解説を見つけました。ExploratoryはRをベースにしたデータ分析のソフトです。Rはプログラムを勉強してからでしか使えないところがありますが、ExploratoryはRコマンダーのようにプログラムを使わずにデータ分析ができます。

すべての人にデータサイエンスを

最新のデータサイエンスでは、さまざまな種類のデータを扱うことができ、機械学習や統計アルゴリズムを使用して、深い統計の知識がなくてもデータに隠れているパターンや傾向を見つけることができます。しかし、それはデータサイエンティストとプログラマーに限られています。

Exploratoryは誰もが、プログラミングを必要とせずに、モダンで最先端のデータサイエンスのアルゴリズムに簡単にアクセスできるようにします。私たちは誰もが最新のテクノロジーにアクセスし、データを通じて世界をより深く理解していくべきだと強く信じています。
(https://exploratory.io/より)





Exploratoryのサイトでもcase_when紹介がありました。

 


もし運動器疾患脳血管疾患以外をその他にしたければ以下のようになります。
TRUEはここでは残り全部といった意味になります。
data_age <- data %>% 
  mutate(年代 = case_when(
    .$年齢 < 20 ~ "未成年",
    .$年齢 <= 20 ~ "成人")) %>% 
  mutate(性別 = case_when(
    .$性別 == "男性" ~ "male",
    .$性別 == "女性" ~ "female")) %>%
  mutate(疾患分類 = case_when(
    .$病名 %in% c("骨折", "靭帯損傷") ~ "運動器疾患",
    .$病名 %in% c("脳梗塞", "脳出血") ~ "脳血管疾患",
    TRUE ~ "その他"

勉強になりました!


3.cut関数を使って分類する


数値を分類するに限って言うとcut関数も使えます。
cut関数はコードはスッキリしますが、少しクセがあり、おまじないが必要です。

cut(目的の列,
      breaks = (下限, カットする数値, 上限),
      right = FALSE,
      include.lowest = TRUE,
      labels =c("カテゴリー毎の名前"))

スクリーンショット 2019-02-13 1.49.04


「10代・20代」などでは0〜19のような分け方をするので、right = FALSEが必要になります。
加えてinclude.lowest = TRUEを加えないと端の値を読み込まず<NA>とデータなしとみなされてしまいます。


これらをふまえると以下のコードになります。

 data_age <- data %>% 
  mutate(年代 = cut(.$年齢, 
                  breaks = c(0, 20, 40, 60, 80, 100, 120),
                  right = FALSE, 
                  include.lowest = TRUE,
                  labels = c("0〜", "20〜", "40〜", "60〜", "80〜", "100〜")))
head(data_age)
スクリーンショット 2019-02-13 1.51.09


cut関数を使えば細かい条件が必要にならない分、right = や include.lowest = を忘れないように注意が必要です。


補足.mutate関数の組み合わせについて

mutate関数を複数回行うにはいくつかの方法があります。

スクリーンショット 2019-02-16 19.31.02
(追記)2019/02/16更新
すみません。mutate関数では②の方法は使えませんでした。
②の方法は次で紹介するfilter関数で使うことができます。


本来は,でつなぐと1つの行で済むのですが、初心者だと、()や,の数がうまくあわなかったりします。プログラミングの上級者からは意見があるかもしれませんが、プログラミング未経験・初心者はまず自分の覚えやすいものから始めていいと思っています。



まとめ

今回は条件によって分類する方法についてifelse関数、case_when関数、cut関数を紹介しました。

それぞれに特徴がありますので、必要に応じて使い分けてください。

また今回は演算子(<, ==, %in%など)の紹介も行いました。

演算子はデータを集計するときに必要になりますのでまた紹介していきたいと思います。

第3章ではggplot2を使ったグラフの作り方について説明してきました。

【3-1】ExcelにはないRでグラフを作るメリットと特徴

【3-2】ggplot2でグラフを作る流れを説明します

【3-3】Rのggplot2で散布図を作るgeom_point関数

【3-4】Rのggplot2でヒストグラムを作るgeom_histogram関数

【3-5】Rのggplot2で密度曲線を作るgeom_density関数

【3-6】Rのggplot2で箱ひげ図を作るgeom_boxplot関数

【3-7】棒グラフの基本とRのggplot2で棒グラフを作るgeom_bar関数

【3-8】ggplot2で折れ線グラフを作るgeom_line関数

【3-9】ggplot2でヒートマップを作るgeom_tile関数



【3-10】Rのggplot2でグループ毎にグラフを作りまとめて表示するfacet_wrap関数では3つのグラフを作成しました。今回はその3つのグラフを並べて1つのグラフにしてしまうgridExtraパッケージを紹介します。


1.使用するデータ

今回は【3-10】Rのggplot2でグループ毎にグラフを作りまとめて表示するfacet_wrap関数で取り込んだデータを使用します。データやコードについては以下をご参照ください。


今回はtidyverseパッケージに加えgridExtraパッケージを使用します。

上記パッケージを一度も使ったことがなければインストールします。
install.packages("tidyverse")
既にインストールしていればlibrary関数で呼び出します。
library(tidyverse) 


macの日本語文字化け対策にヒラギノ角ゴ Pro W3 ("HiraKakuPro-W3"を使っています。
windowの場合はエラーが出るかもしれませんのでtheme_gray(base_family = "HiraKakuPro-W3") +をの色をつけた箇所を消してください。

url <- "https://github.com/mitti1210/myblog/blob/master/fim.csv?raw=true" 
dat <- read.csv(url)

dat_stat <-
  dat %>% 
    gather(key = 項目, value = 点数, 食事:合計, factor_key = TRUE) %>% 
    group_by(項目, sheet) %>% 
    summarize(平均 = mean(点数)) %>% 
    mutate(平均 = round(平均, 1)) 

p_1 <-
dat_stat %>% 
  filter(項目 == "合計") %>% 
  ggplot(aes(x = sheet, y = 平均)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(aes(fill = sheet), stat = "identity") +
  geom_text(aes(label = 平均), position = position_stack(vjust = 0.5)) +
  facet_wrap( ~ 項目) +
  labs(x = "", y = "") +
  guides(fill = "none")

p_2 <-
dat_stat %>% 
  filter(項目 %in% c("運動合計", "認知合計")) %>% 
  ggplot(aes(x = sheet, y = 平均)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(aes(fill = sheet), stat = "identity") +
  geom_text(aes(label = 平均), position = position_stack(vjust = 0.5)) +
  facet_wrap( ~ 項目) +
  labs(x = "", y = "") +
  guides(fill = "none")

p_3 <-
dat_stat %>% 
  filter(!項目 %in% c("運動合計", "認知合計", "合計")) %>% 
  ggplot(aes(x = sheet, y = 平均)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(aes(fill = sheet), stat = "identity") +
  geom_text(aes(label = 平均), position = position_stack(vjust = 0.5)) +
  facet_wrap( ~ 項目) +
  labs(x = "", y = "") +
  guides(fill = "none")
スクリーンショット 2019-08-24 1.07.31
スクリーンショット 2019-08-24 1.07.41
スクリーンショット 2019-08-24 1.07.55


2.gridExtraパッケージについて


gridExtraパッケージのgrid.arrange関数は複数のグラフを並べて1つのグラフにしてくれます。



gridExtraパッケージを一度も使ったことがなければインストールします。
install.packages("gridExtra")
既にインストールしていればlibrary関数で呼び出します。
library(gridExtra) 



3.基本的な使い方

grid.arrange(グラフ1, グラフ2, ...)とつなげるだけです。そうすると縦に並びます。

grid.arrange(p_1, p_2)
スクリーンショット 2019-08-24 1.13.21



4.行数、列数の指定

行数と列数を指定することもできます。

grid.arrange(p_1, p_3, ncol = 1)
ncol = 1は横には1つ並べるという意味になります(何もつけないとこれになる)。
スクリーンショット 2019-08-27 7.17.26



grid.arrange(p_1, p_3, ncol = 2)
ncol = 2は横に2つ並べるとなります。
縦には制限なく並びます。

スクリーンショット 2019-08-27 7.17.34




grid.arrange(p_1, p_2, p_3, ncol = 2, nrow = 2)
ncol = 2は横に2つ並べる、nrow = 2は縦に2つ並べるという意味になります。
スクリーンショット 2019-08-27 7.17.45






5.大きさの比率を変える

縦と横の比率を変えることもできます。

横の比率を指定するwidths =と縦の比率を指定するheights =があります。
また1:2というように2つの数を指定する必要がありますので、width = c(1, 2)のように指定します。
もし横に3つ並べるならwidth = c(1, 3, 2)のように3つ数を指定します。

grid.arrange(p_1, p_2, p_3, ncol = 2, nrow = 2, widths = c(2, 1), heights = c(2,3))

スクリーンショット 2019-08-28 7.51.45





6.自分でレイアウトを作る

さらに細かいレイアウトも可能です。この方法は以下のサイトを参考にさせていただきました。




①自分でレイアウトを作る

3パターンレイアウトを作ってみました。
スクリーンショット 2019-08-28 21.55.43


ポイントはmatrix型にすることです。rbind関数c関数で縦につなぎます。
layout1 <- 
rbind(c(1, 2), c(1, 3)) layout2 <-
rbind(c(1, 3), c(2, 3)) layout3 <-
rbind(c(1, 2), c(3, 3))



②関数で作ったレイアウトを指定する

作ったレイアウトを指定するにはlayout_matrix = を指定します。
加えてwidths =heights =で微調整しています。


レイアウト1
grid.arrange(p_1, p_2, p_3, layout_matrix = layout1, widths = c(1, 3), heights = c(1, 2.5))
スクリーンショット 2019-08-28 22.00.35


レイアウト2
grid.arrange(p_1, p_2, p_3, layout_matrix = layout2, widths = c(1, 2))
スクリーンショット 2019-08-28 22.00.49



レイアウト3
grid.arrange(p_1, p_2, p_3, layout_matrix = layout3, heights = c(1, 2))
スクリーンショット 2019-08-28 22.01.01


7.まとめ

今回はパッケージを使って複数のグラフを1つにまとめる方法を紹介しました。

工夫次第でいろいろな見せ方ができます。gridExtraは便利なパッケージですのでぜひ試してみてください。










第3章ではggplot2を使ったグラフの作り方について説明してきました。

【3-1】ExcelにはないRでグラフを作るメリットと特徴

【3-2】ggplot2でグラフを作る流れを説明します

【3-3】Rのggplot2で散布図を作るgeom_point関数

【3-4】Rのggplot2でヒストグラムを作るgeom_histogram関数

【3-5】Rのggplot2で密度曲線を作るgeom_density関数

【3-6】Rのggplot2で箱ひげ図を作るgeom_boxplot関数

【3-7】棒グラフの基本とRのggplot2で棒グラフを作るgeom_bar関数

【3-8】ggplot2で折れ線グラフを作るgeom_line関数

【3-9】ggplot2でヒートマップを作るgeom_tile関数




今回は少し実用的な場面としてテストの結果を下位項目も含めてまとめてグラフに表示するを目指したいと思います。



以前仕事で1日で130個以上のグラフをExcelでコピペの手作業で作るという苦行をしたことがあるのですが、この方法で一気に作れるようになりました。

【3-4】Rのggplot2でヒストグラムを作るgeom_histogram関数でfacet_grid関数について説明しましたが、今回はfacet_wrap関数を紹介します。


また下位項目をまとめてグラフで表示するためにはデータハンドリングも必要になります。
データハンドリングについては2章で説明していますのでそちらもご参照ください。




1.使用するデータ

今回は【2-7】Excelの複数シートをRで一気に読み込むで取り込んだデータを使用します。
ただすぐに始められるように以下のコードを用意しました。
氏名、年齢、病院で使用するFIMという架空のデータです。
FIM18項目ありそれぞれ1〜7点で採点します。

またFIMには運動項目と認知項目というサブグループがあります。

食事〜階段までの13項目を運動項目(FIM_運動合計:13〜91点)
理解〜記憶までの5項目を認知項目(FIM_認知合計:5〜35点)
すべての合計をFIM_合計(18〜126点)

またFIMを3回計測し、sheetという列には1回目,2回目,3回目が入っています。

url <- "https://github.com/mitti1210/myblog/blob/master/fim.csv?raw=true" 
dat <- read.csv(url)
head(dat)

このデータからFIMの合計点と下位項目が1〜3回目でどのように変化したのかをグラフにしてみたいと思います。なお今回は棒グラフで平均を出します。


2.データハンドリング

【3-9】ggplot2でヒートマップを作るgeom_tile関数ではFIM_食事の項目だけを使いましたが、今回は全ての下位項目を使用します。

グラフ作成にあたり、以下をイメージしてデータハンドリングします。
・グラフを作る際、FIM_○○の"FIM_"はいらないので消したい
・sheetごと、各項目ごとの平均を出したい。そのためにはwideデータになっているdatをlongデータにする必要がある
・グラフに数値を表示するときに小数点が続くと見栄えが悪いので、小数点第1位まで表示したい

今回もtidyverseパッケージを使用します。

まだtidyverseパッケージを一度も使ったことがなければインストールします。
install.packages("tidyverse")
既にインストールしていればlibrary関数で呼び出します。
library(tidyverse) 

以下1例と解説です。

names(dat) <- str_remove(names(dat), "FIM_")

dat_stat <-
  dat %>% 
    gather(key = 項目, value = 点数, 食事:合計, factor_key = TRUE) %>% 
    group_by(項目, sheet) %>% 
    summarize(平均 = mean(点数), 標準偏差 = sd(点数)) %>% 
    mutate(平均 = round(平均, 1)) 



data.frameの名前を変更する方法

names関数を使うとdata.frameの列名をベクトルとして出してくれます。

names(dat)


そして直接変更するにはrename関数などありますが、同じ長さのベクトルを準備すると一括して入れ替えることができます。

names(dat) <- 同じ長さのベクトル


指定した文字列を消すstr_remove関数

str_remove関数は指定した文字を消す事ができます。

str_remove(ベクトル, "消したい文字列")

names(dat) <- str_remove(names(dat), "FIM_")


wideデータからlongデータに変える


wide, longデータについてはこちらの記事で紹介しています。




  dat %>% 
    gather(key = 項目, value = 点数, 食事:合計, factor_key = TRUE) %>% 
項目が五十音順にならないようにfactor_key = TRUEを指定します。



group_by関数とsummarize関数で集計する


項目とsheet毎に集計を行うにはgroup_by関数summarize関数を使います。



  dat %>% 
    gather(key = 項目, value = 点数, 食事:合計, factor_key = TRUE) %>% 
    group_by(項目, sheet) %>% 
    summarize(平均 = mean(点数), 標準偏差 = sd(点数)) 


四捨五入する

mutate関数round関数を使って平均の値を四捨五入します。




dat %>% 
    gather(key = 項目, value = 点数, 食事:合計, factor_key = TRUE) %>% 
    group_by(項目, sheet) %>% 
    summarize(平均 = mean(点数), 標準偏差 = sd(点数)) %>% 
    mutate(平均 = round(平均, 1)) 


まとめると最初のコードになります。

names(dat) <- str_remove(names(dat), "FIM_")

dat_stat <-
  dat %>% 
    gather(key = 項目, value = 点数, 食事:合計, factor_key = TRUE) %>% 
    group_by(項目, sheet) %>% 
    summarize(平均 = mean(点数), 標準偏差 = sd(点数)) %>% 
    mutate(平均 = round(平均, 1)) 


3.棒グラフを作成する

棒グラフを作るにはgeom_bar関数を使います。
棒グラフに数値を乗せる方法も紹介しています。





macの日本語文字化け対策にヒラギノ角ゴ Pro W3 ("HiraKakuPro-W3"を使っています。
windowの場合はエラーが出るかもしれませんのでtheme_gray(base_family = "HiraKakuPro-W3") +をの色をつけた箇所を消してください。



facet_wrapでグループ毎のグラフをまとめて作る

そしてグループ毎にグラフを作るにはfacet_wrap関数を使います。

facet_wrap( ~ グループ)

ggplot() +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(data = dat_stat, aes(x = sheet, y = 平均, fill = sheet), stat = "identity") +
  facet_wrap( ~ 項目)

スクリーンショット 2019-08-24 1.07.00


これで項目ごとのグラフができました。しかし気になる点があります。

facet_wrapはそのままだと軸が固定されています。
合計と各項目に点数差がありすぎて、各項目の点数の差がわかりません。


facet_wrapの軸をグループ毎に変更する

軸をグループ毎に帰るにはscales = "free"を追加します。

ggplot() +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(data = dat_stat, aes(x = sheet, y = 平均, fill = sheet), stat = "identity") +
  facet_wrap( ~ 項目, scales = "free")

スクリーンショット 2019-08-24 1.07.14


ただこれも1つ問題があります。
グラフ自体は変化しているように見えるのですが、それぞれの軸が違うのでグループ毎の比較ができません。


4.3種類のグラフに分けて保存する

全てのグラフをまとめてしまうとわかりにくいのでy軸のサイズを考慮して次の3種類に分けてグラフを作成してみます。

・合計
・運動項目合計, 認知項目合計
・下位項目

そのためにはdat_statからそれぞれを抽出してグラフを作成する必要があります。

・filter関数を使い抽出し、そのまま %>% でグラフ作成まで行ってみます。
・datから %>% でつないでggplot() + 〜と直接つなげるとdata = datを省略できます。
このようにするとdat_statのデータだけで完結します(dat_stat_合計みたいな変数を作らなくて済む)
下のコードの中にはdata = datが入っていないことを確認してみてください

・x軸の「sheet」とy軸の「平均」はいらないのでlabs関数で消します

・棒グラフはx軸の名前と凡例が同じなので凡例を消します
凡例を消すにはguides(fill = "none")を追加します

・facet_wrapでは軸を固定するためscales = "free"は追加しません

そしてfilter()の列以外は全部同じなことにも注目してください。
filter関数については【2-4】で紹介しています。



p_1:合計だけのグラフ
p_1 <-
dat_stat %>% 
  filter(項目 == "合計") %>% 
  ggplot(aes(x = sheet, y = 平均)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(aes(fill = sheet), stat = "identity") +
  geom_text(aes(label = 平均), position = position_stack(vjust = 0.5)) +
  facet_wrap( ~ 項目) +
  labs(x = "", y = "") +
  guides(fill = "none")
スクリーンショット 2019-08-24 1.07.31


p_2:運動項目合計と認知項目合計のグラフ
p_2 <-
dat_stat %>% 
  filter(項目 %in% c("運動合計", "認知合計")) %>% 
  ggplot(aes(x = sheet, y = 平均)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(aes(fill = sheet), stat = "identity") +
  geom_text(aes(label = 平均), position = position_stack(vjust = 0.5)) +
  facet_wrap( ~ 項目) +
  labs(x = "", y = "") +
  guides(fill = "none")
スクリーンショット 2019-08-24 1.07.41


p_3:下位項目のグラフ
p_3 <-
dat_stat %>% 
  filter(!項目 %in% c("運動合計", "認知合計", "合計")) %>% 
  ggplot(aes(x = sheet, y = 平均)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_bar(aes(fill = sheet), stat = "identity") +
  geom_text(aes(label = 平均), position = position_stack(vjust = 0.5)) +
  facet_wrap( ~ 項目) +
  labs(x = "", y = "") +
  guides(fill = "none")
スクリーンショット 2019-08-24 1.07.55



5.まとめ

今回はfacet_wrapでグループ毎のグラフを作る方法を紹介しました。
facet_wrapはグループの違いをみるのに非常に便利です。
ただ合計と下位項目が入っているデータだと軸の問題が出ますので注意が必要です。

今回作った3種類のグラフを組み合わせるためのgridExtraパッケージがあり次回紹介します。










第3章ではggplot2を使ったグラフの作り方を紹介しています。


【3-1】ExcelにはないRでグラフを作るメリットと特徴

【3-2】ggplot2でグラフを作る流れを説明します

【3-3】Rのggplot2で散布図を作るgeom_point関数

【3-4】Rのggplot2でヒストグラムを作るgeom_histogram関数

【3-5】Rのggplot2で密度曲線を作るgeom_density関数

【3-6】Rのggplot2で箱ひげ図を作るgeom_boxplot関数

【3-7】棒グラフの基本とRのggplot2で棒グラフを作るgeom_bar関数

【3-8】ggplot2で折れ線グラフを作るgeom_line関数



今回はヒートマップを紹介します。



ヒートマップはx軸,y軸だけでなくタイルの色で3次元的な表現ができます。
ヒートマップといえば天気アプリの雨雲予報などでも見られますが、今回は集計したデータを折れ線グラフとヒートマップで比べてみるとどうなるかをRのコードを書きながら紹介したいと思います。


1.使用するデータ

今回は【2-7】Excelの複数シートをRで一気に読み込むで取り込んだデータを使用します。
ただすぐに始められるように以下のコードを用意しました。
氏名、年齢、病院で使用するFIMという架空のデータです。
FIM18項目ありそれぞれ1〜7点で採点します(合計18〜126点)。
またFIMを3回計測し、sheetという列には1回目,2回目,3回目が入っています。

url <- "https://github.com/mitti1210/myblog/blob/master/fim.csv?raw=true"

dat <- read.csv(url)


2.データハンドリング

今回はFIMの中でもFIM_食事のデータだけを使います。
dat_eat:datから氏名,sheet,FIM_食事の列だけを抜き出す
dat_eat_summarize:dat_eatからsheet毎の平均を集計
dat_eat_stat:1〜7点がそれぞれ何人ずついるかカウント


今回はselect関数, rename関数, group_by関数, summarize関数, n関数, この後グラフ作成で使うggplot2関数は全てtidyverseパッケージに含まれます。

まだtidyverseパッケージを一度も使ったことがなければインストールします。
install.packages("tidyverse")
既にインストールしていればlibrary関数で呼び出します。
library(tidyverse) 

もしデータハンドリングを練習しているところであれば下のコードをみながら作成してみてください。

dat_eat <- 
dat %>%
select(氏名, sheet, FIM_食事) 

dat_eat_summarize <-
dat_eat %>%
group_by(sheet) %>%
summarize(平均 = mean(食事), 項目 = "食事")

dat_eat_stat <-
dat %>%
select(FIM_食事, sheet) %>%
group_by(sheet, FIM_食事) %>%
summarize(n = n())
 
スクリーンショット 2019-08-23 2.47.56
ポイントは以下2つです。
dat_eat_summarizeでは折れ線グラフを作成するのに項目という列を追加しました。
dat_eat_statで個数を数える場合はn()を使います。()の中は何も入れません。

まだなれない場合は下のリンクを参照してください。

【2-4】Rで指定した列や行だけを取り出すselect関数、slice関数、filter関数を紹介します

【2-6】Rでgroup_by関数とsummarize関数を使ってグラフ作成に必要な統計量(平均や標準偏差など)を求める

【演習2】データハンドリングの基礎を復習します


3.折れ線グラフを作る

まずは折れ線グラフを作ってみます。
折れ線グラフはgeom_line関数、数値を入れる場合はgeom_text関数です。

参照:【3-8】ggplot2で折れ線グラフを作るgeom_line関数


平均の折れ線グラフ

まずdat_eat_summarizeを使って平均の折れ線グラフを作ります。
ylim関数はy軸の最大値と最小値を設定します。

ggplot() +

theme_classic(base_family = "HiraKakuPro-W3") +
geom_line(data = dat_eat_summarize, aes(x = sheet, y = 平均, group = 項目), color = "red") +
geom_text(data = dat_eat_summarize, aes(x = sheet, y = 平均, label = 平均), vjust = -1, color = "red") +
ylim(0, 7)
スクリーンショット 2019-08-23 2.07.44



実際の生データと平均を組み合わせる

要約としてはこれでもいいですが、実際のデータも確認してみたいと思います。

実際のデータはdat_eatにあるので使います。group = 氏名 にすると患者毎の折れ線グラフを作ることができます。またcolor = "gray" として薄くしたほうが平均の線が目立ちます。

ggplot() +

theme_classic(base_family = "HiraKakuPro-W3") +
geom_line(data = dat_eat, aes(x = sheet, y = FIM_食事, group = 氏名), color = "gray") +
geom_line(data = dat_eat_summarize, aes(x = sheet, y = 平均, group = 項目), color = "red") +
geom_text(data = dat_eat_summarize, aes(x = sheet, y = 平均, label = 平均), vjust = -1, color = "red")
 
スクリーンショット 2019-08-23 2.07.51



4.ヒートマップを作る

上の折れ線グラフもいいのですが、線が重なっているので数字が多いのか少ないのかわかりません。
そのため次はヒートマップを作成してみます。

ヒートマップはgeom_tile関数、またはgeom_raster関数です。

geom_tile関数でヒートマップの色を指定するのにfillを指定します。
使うデータはdat_eat_stat, x軸にsheet(1〜3回目) , y軸にFIM_食事(1〜7点), 色をn(カウント数)としたいので以下になります。

geom_tile(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, fill = n)) 

ggplot() +
	
theme_gray(base_family = "HiraKakuPro-W3") +
geom_tile(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, fill = n))
 
スクリーンショット 2019-08-23 2.07.59



5.タイルの色を変える

タイルの色を変えるにはscale_fill_gradientn関数を使います。
折れ線グラフの線の色を変える時はscale_color_gradientn関数でした。
今回は中の色を指定するのでcolorではなくfillになっています。
colours =で2色指定するとグラデーションができます。
3色以上指定すると中間の色も指定できます。

ggplot(data = dat_eat_stat) +

theme_gray(base_family = "HiraKakuPro-W3") +
geom_tile(aes(x = sheet, y = FIM_食事, fill = n)) +
scale_fill_gradientn(colours = c("yellow", "red"))
スクリーンショット 2019-08-23 2.08.06

すると1→3回目になるにつれて7点が増えていることがわかります。


6.ヒートマップに数値を加える

ヒートマップで傾向はつかめますが数値も欲しいところです。
棒グラフや折れ線グラフの時と同様にgeom_text関数を使います。

【3-7】棒グラフの基本とRのggplot2で棒グラフを作るgeom_bar関数

【3-8】ggplot2で折れ線グラフを作るgeom_line関数


ggplot() +

theme_gray(base_family = "HiraKakuPro-W3") +
geom_tile(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, fill = n)) +
scale_fill_gradientn(colours = c("yellow", "red")) +
geom_text(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, label = n))
 

よく見るとgeom_tileもgeom_textも"data = dat_eat_stat, aes(x = sheet, y = FIM_食事" までは同じなので下のようにまとめることができます。
ggplot(data = dat_eat_stat, aes(x = sheet, y = FIM_食事)) +
  theme_gray(base_family = "HiraKakuPro-W3") +
  geom_tile(aes(fill = n)) +
  scale_fill_gradientn(colours = c("yellow", "red")) +
  geom_text(aes(label = n)) 
スクリーンショット 2019-08-23 2.08.14


数値が入ったことで見やすくなりました。



7.ヒートマップに折れ線グラフを組み合わせる

更にヒートマップと折れ線グラフを重ねてみます。
ポイントは下に書いたコードが上に配置されます。

1番上:数値
2番目:折れ線グラフ
3番目:ヒートマップ

順番が大事です。

ggplot() +

theme_gray(base_family = "HiraKakuPro-W3") +
geom_tile(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, fill = n)) +
scale_fill_gradientn(colours = c("yellow", "red")) +
geom_line(data = dat_eat, aes(x = sheet, y = FIM_食事, group = 氏名), color = "gray") +
geom_text(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, label = n))
 
スクリーンショット 2019-08-23 2.08.21


これで十分ではありませんが、数値の変化も少し追えるようになりました。


8.全部のグラフを重ねる

過剰かもしれませんが、上のグラフに平均の折れ線グラフも加えてみます。
順番に注意してください。

ggplot() +

theme_gray(base_family = "HiraKakuPro-W3") +
geom_tile(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, fill = n)) +
scale_fill_gradientn(colours = c("yellow", "red")) +
geom_line(data = dat_eat, aes(x = sheet, y = 食事, group = 氏名), color = "gray") +
geom_line(data = dat_eat_summarize, aes(x = sheet, y = 平均, group = 項目), color = "red", size = 1.5) +
geom_text(data = dat_eat_stat, aes(x = sheet, y = FIM_食事, label = n)) +
geom_text(data = dat_eat_summarize, aes(x = sheet, y = 平均, label = 平均), vjust = -1, color = "black")
 
スクリーンショット 2019-08-23 2.08.28




9.まとめ

今回はヒートマップを紹介しました。
難しそうに感じますが、x軸とy軸にfillを加えるだけでヒートマップができてしまいます。
いろいろアイデアを試してみるとggplot2やその前のデータハンドリングの理解にもつながりますので、3章の他のグラフも見ながらグラフ作成に挑戦してみてください。



職場ではExcelでデータ集計しているという職場も多いと思います。

その中でタブに1回目,2回目,,,や1月,2月,,,というように同じデータ形式を複数シートで管理する場合もあるかもしれません。

同じ形式で複数シートにまたがるデータをRで一度に読み込む方法を紹介します。

今回はfim.xlsx という架空のファイルを準備していますが、記事を見ながらご自身でExcelファイルを作ってみても大丈夫です。


(注意)
今回の記事はr-wakalang」でのアドバイスを参考にさせていただきました。
自分も別の方法でしていたのですが、あまりにもスマートな方法なので今回の方法に切り替えました。





1.前提

まずこの方法ができるためにはいくつか前提が必要です。

・全てのシートが同じ形式

シート毎に列名やいろいろなものがバラバラでは読み込めません。
フォーマットはちゃんと決めましょう。もしくはシートを複数選択しまとめて処理をしましょう。
cntr(macではcommand)を押しながらクリックするとシートを複数選択→一括処理ができます。

・セル結合が無い

Excelでセル結合をすると見栄えは良くなったように感じますが、集計を行う時はExcelでもRでも非常に困る場合が多いです!!!!!

・列のタイトルが複数行にまたいでいる

例えば病院で下位項目があるテストを記録すると列名が複数行にまたぐ場合があります。

そしてこのような場合、上の行はだいたいセル結合されています・・・


スクリーンショット 2019-08-20 22.47.19



2.Excelでの対策

ただ上司にセル結合+列名を2行にしないと減給だ!!!と脅される場合もあるかもしれません。

自分ならもう一列Rで列名読み込むための行を作るという回避策を用意します。
そのときに役に立つのがExcelの&です。

ポイントは以下の3つです。
①集計に使うシートを全て選択し一括処理の状態にする
② 実際のデータ入力する列の1つ上の行に挿入する(ここでは3行目)

スクリーンショット 2019-08-20 22.49.52



③ A,B,C列は実質1行なので を使う。
④ D列以降は複数行にまたがるため でつなげる
⑤ D1は絶対参照($)をつけるとコピーで失敗しない

(下の図ではわかりやすいように数式を表示しています)

スクリーンショット 2019-08-21 0.18.00


実際の見え方はこうなります。
スクリーンショット 2019-08-20 23.05.24


⑥D3をコピー→右の空いた所全てを選択し貼り付け

⑤誰かに邪魔されないように3行目を非表示にすれば完成!


スクリーンショット 2019-08-20 23.06.38

スクリーンショット 2019-08-20 23.07.25




3.Excelの&について

D1&D2とするとD1の値とD2の値を文字列としてくっつけてくれます

D1はFIMD2は食事なので、=D1&D2FIM食事となります。

今回はFIMと食事の間に_をはさみます。
(- * , . )などの記号や空白はRで読み込んだ際に不都合が出ることがあります。
そのため間に挟む記号は _ (アンダーバー)をおすすめします。

_のような文字列を&で繋げる場合は"(ダブルクォーテーション)ではさみます。

よって=D1&"_"&D2 は FIM_食事となります。


4.Excelの相対参照と絶対参照について

D3にある=D1&"_"&D2をコピーしてE3に貼り付けるとE1&"_"&E2となります。
他にもD3を選択→セル右下のちっちゃい■をクリックし右にビーーーーーーっと伸ばすと一気にコピーできます。
Excelはコピーした位置関係を覚えてくれるのでこういった処理は非常に便利です。これを相対参照と言います。

しかしこの方法を使うと不都合が出ます。本来E3セルにはD1&"_"&E2と1行目は動かずに2行目を動かしたいところです。
この動かさないを指定するのが$(絶対参照)です。

$D1:左右にコピペしてもDの値は変わらない。上下にコピペすると1の値は変わる
D$1:左右にコピペするとDの値が変わる。上下にコピペしても1の値は変わらない
$D$1:上下左右にコピペしてもD1の値は変わらない。

今回は横にコピペしてもずれてほしくないので、D3の計算式ではD1ではなく$D1もしくは$D$1とすれば上手くいきます。するとE3〜は一気にコピペ可能です。


5.Rへの読み込み

前置きが長くなりましたがいよいよRに取り込みます。

・プロジェクトを作成し、Excelデータも同じフォルダに置く

実際に分析を行う時は仕事1つ1つでプロジェクトを作ると管理が便利です。
プロジェクトについては【1-6】Rstudioのプロジェクトについて解説しますで紹介しています。


・パッケージを読み込む

パッケージに関しては【1-7】Rで使うパッケージのインストールについて紹介します。をご参照ください。

Excelを読み込むにはいくつかあるのですが今回はreadxlパッケージとpurrrパッケージを使います。
purrrパッケージは2章で使ってきたtidyverseパッケージに含まれています。


#tidyverseパッケージをインストールしていなければインストール。
install.packages("
readxl
")
install.packages("tidyverse")

#パッケージをインストールしている方は以下でもOK
library(
readxl)
library(tidyverse)


・実際に読み込む

file <- "fim.xlsx"
sheets <- excel_sheets(file)
dat <- 
  sheets %>% 
  map_dfr( ~ read_excel(file, skip = 2, sheet = .x) %>% 
             mutate(sheet = .x)) 
これで全てのタブを読み込むことができます。



(以下補足)
6.何をしているのか?

1つずつ説明していきます。

ファイル名を指定する

まずfileという変数(別に名前はfileでなくても良い)にExcelのファイルを指定します。
そうすれば後のread_excel関数で楽ができます。

シートの名前リストを作る

Excelから複数シートを読み込むにはシートのリストが必要です。

readxlパッケージのexcel_sheet関数がまさにそれです。

sheets <- excel_sheets(file)
スクリーンショット 2019-08-21 19.47.36



これでsheetsという変数にタブの名前が入りました。

今回はこのままで問題ありませんが、Excelの中には「基本情報」や「選択項目」など必要ないタブもあるかもしれません。
その時はsheetsから間引きます。間引く時は【1-12】Rで特定の条件にあう要素を抜き出す方法で紹介した [ ] が使えます。

シートは名前だけでなく、左から1,2,,,,と自動的に番号が振り分けられています。
いらないシート番号がわかっている時はいらない番号だけ指定すればいいです。
(もちろんいる番号だけ指定してもいいです)

sheets <- excel_sheets(file)

sheets <- sheets[-2]
スクリーンショット 2019-08-21 19.52.58


シートの数が多すぎてシート番号がもはや分からない時は  %in% でシート名を指定することができます。
sheets <- excel_sheets(file) 
sheets <- sheets[!sheets %in% "2回目"]
スクリーンショット 2019-08-21 19.53.16


1つだけExcelを読み込む時のread_excel

1つだけExcelを読み込む関数はいくつかあるのですが、今回はread_excel関数を使います。
read_excel関数はxls,xlsxどちらも読み込むことができます。

read_excel(file, skip = 2, sheet = "1回目") 
基本の形は read_excel("Excelファイル", sheet = "シート名") となります。
そしてskip=を使うと最初の数行を飛ばして読み込むことができ、今までの対策が活きてきます。
skip=2で1,2行目をskipし3行目を列名、4行目以降をデータとして読み込むことができます。

スクリーンショット 2019-08-22 0.27.55



繰り返し処理でdata.frameにデータを追加するmap_dfr関数

map_dfr関数はExcelの複数のシートやフォルダの中にあるcsvファイルをまとめて読み込むのに便利です。
ちなみにmap(繰り返し処理)、df(データフレーム)、r(縦につなげる)でmap_dfrです。
横につなげる時はmap_dfc関数ですがここでは目的に合致しません。


最初に.xにあたる繰り返すためのリストを用意して%>%でつなぎます。
ここでは"1回目"→"2回目"→"3回目"としたいわけです。そこで先程のsheets <- excel_sheets(file)でそれを作りました。


そして先程紹介したread_excel関数を使います。最初に ~(チルダ)を入れることがポイントです。


加えて足した後にこのデータがどのシートかわかる(集計する)ためにmutate関数をつかってsheet(別の名前でもいい)という列名を追加します。ここでも.xを使うことで"1回目", "2回目", "3回目"を追加してくれます。

スクリーンショット 2019-08-22 0.22.50



7.まとめ

今回はExcelで複数のシートを読み込む方法を紹介しました。
今までExcel使っていたけどRも使ってみたいなという方がいましたら練習と実用を兼ねている課題だと思いますのでぜひ挑戦してみてください。


8.参考にさせていただいたサイト

purrrを使ったExcelの読み込みはr-wakalangさんの質問を参考にさせていただきました。
それまではforを使っていたのですが、map_dfrはとても便利でした!
r-wakalangはRを使っている方は絶対におすすめです!





↑このページのトップヘ