タグ:summarize

第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パッケージがあり次回紹介します。










第2章ではデータハンドリングの基礎について紹介してきました。

【2-1】Rのfor関数、apply関数を使ってまとめて標準偏差などの統計量を求める方法

【2-2】Rのmutate関数を使って列の追加や修正を行う



今回は上記の復習として実際にデータを前処理し、集計をかけるところまで行います。

スクリーンショット 2019-02-08 10.33.40


1.データの準備

前回同様FIMのデータを使います。

FIM.xlsx 

今回は「入院時, 1ヶ月後, 退院時」全てのタブの使います。

スクリーンショット 2019-02-19 22.25.41


ダウンロードした後、プロジェクトの指定フォルダにファイルを移動させます。

プロジェクトの使い方がわからない場合は【1-6】Rstudioのプロジェクトについて解説しますをご参照ください。


まだ複数のデータを結合する方法を紹介していません。以下のコードを実行してください。

#必要なパッケージを読み込む
library(tidyverse)
library(readxl)

#それぞれのタブのデータフレームを作る
fim_in <- read_excel("FIM.xlsx", sheet = "入院時")
fim_1 <- read_excel("FIM.xlsx", sheet = "1ヶ月")
fim_out <- read_excel("FIM.xlsx", sheet = "退院時")

#bind_rows関数で3つのデータフレームを縦につなげる
fim <- bind_rows(fim_in, fim_1, fim_out)

bind_rows関数は複数のデータフレームを縦につなげる関数です。
fimという変数に入院時、1ヶ月、退院時全てのデータを縦につなげました。

これで準備完了です。


2.今回の目標

データにはFIM(18項目 + 運動合計 + 認知合計 + 全体の合計)のデータがあります。

更に今回同じ患者に3回反復測定を行っています。


<目標>
  • FIM各項目の平均点が入院時→1ヶ月→退院時でどう変化しているのかを表にする



課題①

まずデータの確認を行います。
  1. head関数、str関数を使ってデータを確認します。

回答は下にスクロールするとあります↓















head(fim)
スクリーンショット 2019-02-25 3.26.39

str(fim)
スクリーンショット 2019-02-25 3.27.15




課題②

データをlongデータに変えます。

まず列名と列番号を取得します。

t関数(またはdata.frame関数)とnames関数を組み合わせて列名と列番号を取得します。














t(names(fim))
スクリーンショット 2019-02-25 3.27.40

または
data.frame(names(fim))
スクリーンショット 2019-02-25 3.28.03



課題③

次はlongデータに変更します

  • パイプ演算子(%>%)を使います
  • 時期はbind_rows関数を使って縦に結合したので既にlongデータになっています
  • fimの「食事」〜「FIM全体」の列をlongデータに変えます
  • 今回はkeyの列名を「項目」、valueの項目を「点数」とします
  • 「項目」のfactorの要素を五十音順にせず、列で並んだ順で表示するようにします
















fim %>% 
  gather(5:25, key = 項目, value = 点数, factor_key = TRUE)
スクリーンショット 2019-02-25 3.55.14







課題④

次はsummarize関数を使い集計します。

  • fim_summarizeという変数名に作ります
  • 各項目が時期によってどう変化するのかが見たいのでした
  • 結果が項目→時期→点数と並ぶようにします。
  • 出た結果を見ると1箇所望まない結果になっている箇所があります。どこでしょう。














fim_summarize <- fim %>% 
  gather(5:25, key = 項目, value = 点数, factor_key = TRUE) %>% 
  group_by(項目,時期) %>% 
  summarize(平均 = mean(点数), 標準偏差 = sd(点数))
fim_summarize
スクリーンショット 2019-02-25 3.35.13



課題⑤
時期を見ると1ヶ月→退院時→入院時となっています。

時期のclassを確認します。















class(fim_summarize$時期)




課題④のコードに1行足して時期をfactor型に変え、入院時→1ヶ月→退院時の順に並ぶようにしてください。



















fim_summarize <- fim %>% 
  mutate(時期 = factor(時期, levels = c("入院時", "1ヶ月", "退院時"))) %>% 
  gather(5:25, key = 項目, value = 点数, factor_key = TRUE) %>% 
  group_by(項目,時期) %>% 
  summarize(平均 = mean(点数),
  標準偏差 = sd(点数))
fim_summarize
スクリーンショット 2019-02-25 3.29.54




課題⑥

今回の結果から「運動合計」「認知合計」「FIM合計」を取り除くにはどうすればいいでしょう。
fim_summarizeをつかって求めてください。
  • fim_summarize2 という変数名に作ります。
  • filter関数を使います
  • もし困ったら下の図を参考にしてください。
スクリーンショット 2019-02-11 2.04.40




















fim_summarize2 <- fim_summarize %>% 
  filter(!項目 %in% c("運動合計", "認知合計", "FIM合計"))
スクリーンショット 2019-02-25 3.31.41


まとめ

今回は第2章の一部の復習を行いました。

第2章では他にもifelse関数やcase_when関数を使って新たな変数名を作ったり、select関数で列を抽出しています。

今回のデータでも年代を作ったり、年代ごとにグループ分けすることも可能です。



fim %>% 
  mutate(年代 = cut(.$年齢, 
                  breaks = c(50, 60, 70, 80, 90),
                  right = FALSE, 
                  include.lowest = TRUE,
                  labels = c("50代", "60代", "70代", "80代")),
         年代 = as_factor(年代),
         時期 = factor(時期, levels = c("入院時", "1ヶ月", "退院時"))) %>% 
  gather(食事:FIM合計, key = 項目, value = 点数, factor_key = TRUE) %>% 
  group_by(時期, 項目, 年代) %>% 
  summarize(平均 = mean(点数), 
            標準偏差 = sd(点数),
            人数 = n())

スクリーンショット 2019-02-25 3.44.47


n関数はまだ紹介していませんでしたが、グループの数を表示することができます。
それ以外は過去の記事で紹介したものなので、まだしっくりこない方はサイトマップから過去の記事を探してみてください。



また色々な切り口があると思いますので、ぜひ色々試してみてください。


今までRでデータの変数名を変更したり条件でグループ化したりしました。







そして前回の記事では集計して平均や標準偏差などの要約を出したり、グラフを作るためにgather関数を使ってlongデータを作りました。

【2-5】Rでデータを集計するのに便利なtidyデータとgather関数



今回は棒グラフや折れ線グラフ作成に必要な平均や標準偏差などの統計量を求めます。



データは前回と同じデータを使います。

FIM.xlsx

スクリーンショット 2019-02-19 22.25.41


ダウンロードした後、プロジェクトの指定フォルダにファイルを移動させておけば、以下のコマンドで前回最後の場面まで進みます。

library(tidyverse)
library(readxl)
fim <- read_excel("FIM.xlsx", sheet = "入院時")
fim_long <- fim %>% 
  gather(食事:FIM合計, key = 項目, value = 点数, factor_key = TRUE) 
head(fim_long)


1,groop_by関数でグループ化したい項目を指定する

ますグループ化するためにはgroop_by関数を使います。

group_by(データ,列名)

%>%を使うとデータの部分は省略できます。

fim_long %>% 
  group_by(項目) 

スクリーンショット 2019-02-21 21.42.39



何も変わってないように見えますが、薄い文字のところに# Groups:項目 [21]とあります。


ちなみにグループの種類が複数でも可能です。

fim_long %>% 
  group_by(項目,性別) 

スクリーンショット 2019-02-21 21.45.43



2.列名の順番について

下の2つのコードはどういった違いがあるのでしょうか?

スクリーンショット 2019-02-22 0.15.27

表の並び順が違うだけでその後グラフを作るときには影響はないのですがイメージとしては上記のようになります。





3.統計量を出すsummarize関数


平均などの統計量などを出すにはsummarize関数を使います。

summarize(名前1 = 関数1, 名前2 = 関数2)

summarize関数の前にgroup_by関数を使っていると、グループごとの集計が出てきます。

fim_summarize <-  fim_long %>% 
  group_by(項目,性別) %>% 
  summarize(平均 = mean(点数), 
            標準偏差 = sd(点数), 
            最小値 = min(点数), 
            最大値 = max(点数))
fim_summarize

スクリーンショット 2019-02-22 0.49.09




もし保存したい場合はwrite.csv関数を使います。

wite.csv(保存する変数名, "ファイル名.csv")
ファイル名には" "と.csvを入れます

write.csv(fim_summarize,"FIM集計")
スクリーンショット 2019-02-22 0.57.54
右のfilesビューにFIM集計.csvができました。

csvファイルをExcelで読み込む時は【1-11】Rで医療統計で必要なtable1を作るtableoneパッケージについて紹介しますをご参照ください。

ポイントとしてはファイルの出力先を$A$1ではなく$B$1にしてください。
$A$1だとなぜかエラーが出ます。
スクリーンショット 2019-02-22 1.04.57

スクリーンショット 2019-02-22 1.07.22

Excelにするとわかるのですが、小数点10桁まで表示されます。
もし小数点第一位までの表示にしたい時はround関数を使います。

round(数値, x)
たとえばxが1だと小数点第二位を四捨五入して小数点第一位まで表示します。

fim_summarize <- fim_long %>%
group_by(項目,性別) %>%
summarize(平均 = round(mean(点数), 1),
   標準偏差 = round(sd(点数), 1),
   最小値 = min(点数),
   最大値 = max(点数))
fim_summarize
スクリーンショット 2019-02-22 1.18.28



4.summarize関数を使うときの注意点。

group_by関数の順番に影響する

2でも紹介しましたが、groop_by関数で指定したグループが複数の場合、summarize関数で表示される順番はgroop_by関数の影響を受けます。

スクリーンショット 2019-02-22 0.15.27

あとでExcelで並べ替えるのはただ面倒です。もしcsvで保存をする時は何を示したいかをあらかじめイメージしておくことが重要になります。



2.欠損地があるとNAとなる

平均を求めるmean関数などはどこか1つでも欠損値(空欄)があると結果はNAとなります。

氏名 <- c("A", "B", "C", "D")
年齢 <- c(55, 63, 67, 71)
test_1回目 <- c(1,2,3,NA)
test_2回目 <- c(5:8) test_3回目 <- c(9:12)
data <- data_frame(氏名, 年齢, test_1回目, test_2回目, test_3回目)
data data %>%
gather(3:5, key = 回数, value = 点数) %>%
group_by(回数) %>%
summarize(平均 = mean(点数))
スクリーンショット 2019-02-22 1.53.38


もし結果にNAが出た時はまずはそもそものデータの入れ損ねがないか確認をし対応します。

それでも欠損値がある時はNAを省いて計算する欠損値を統計の技術を使って代入するといった方法があります。

もし欠損値を省いて平均を出す場合はna.rm = TRUEを付け加えます。

data %>%
gather(3:5, key = 回数, value = 点数) %>%
group_by(回数) %>%
summarize(平均 = mean(点数, na.rm = TRUE))
スクリーンショット 2019-02-22 2.06.51


ただ実は欠損値を省いた方がいいのかどうかという問題があります。ただ業務で傾向を確認したいなどであれば欠損値を省いてもいいと思いますが、きちんと出さないと行けない場面ではこれはこれできちんと勉強する必要があります。欠測データに関しては医療統計の本では紹介されていない事が多く専門書が必要かもしれません。


欠測データ処理: Rによる単一代入法と多重代入法 (統計学One Point)



summarize関数に入れられる関数は単一の値が出るものに限る。

summarize関数で使える統計量はmean関数,sd関数,median関数など単一の値になります。

ただ、関数の中には最小値と最大値を一度に出してくれるrange関数など複数の値を出すものがあります。

test_2回目 <- c(5:8)
range(test_2回目)
スクリーンショット 2019-02-22 2.27.00


range関数をsummarize関数に入れようとするとエラーが出ます。

data %>% gather(3:5, key = 回数, value = 点数) %>% group_by(回数) %>% summarize(平均 = mean(点数, na.rm = TRUE), 範囲 = range(点数))

スクリーンショット 2019-02-22 2.36.38

「1つの値しか入れられないのに2つ入ってるよ!」と怒られています。

スクリーンショット 2019-02-22 2.41.49

特にあるのが四分位範囲です。
四分位範囲はquantile関数を使って以下のように一度に値を出すことができます。
quantile(fim$年齢, c(0.1, 0.25, 0.5, 0.75, 0.9))
スクリーンショット 2019-02-22 2.45.59


しかしsummarize関数ではまとめて入れられないので1つずつ入れる必要があります。
fim_summarize <- fim_long %>% group_by(項目,性別) %>% summarize(平均 = mean(点数), 標準偏差 = sd(点数), 最小値 = min(点数), percent_25 = quantile(点数, 0.25), 中央値 = median(点数), percent_75 = quantile(点数, 0.75), 最大値 = max(点数)) fim_summarize
スクリーンショット 2019-02-22 3.01.32



そもそもの列のfactorの順番があってるのか?

gather関数のkey列に関してはfactor_key=TRUEで五十音順ではなく、元の順番に戻すことができます。

しかし他の列でfactorの順番が合っていない可能性もあります。

もしほかの列でfactorの順番を合わせるにはmutate関数とfactor関数を組み合わせて使うことができます。

今回は女性と男性を入れ替えてみます。
fim_summarize <- fim_long %>%
mutate(性別 = factor(性別, levels = c("男性","女性"))) %>%
group_by(項目,性別) %>%
summarize(平均 = mean(点数),
     標準偏差 = sd(点数),
      最小値 = min(点数),
   percent_25 = quantile(点数, 0.25),
   中央値 = median(点数),
   percent_75 = quantile(点数, 0.75),
      最大値 = max(点数))
fim_summarize
スクリーンショット 2019-02-22 3.01.32


factor関数の使い方は【1-10】Rでよく使われる型について説明しますをご参照ください


まとめ

今回はgroup_by関数とsummarize関数と、実際に集計を出し保存するところまで紹介しました。

これでひとまず第2章で行う予定だった「データを集計しやすいように形を整え集計する」が終わりです。

スクリーンショット 2019-02-08 10.33.40


ただ今回は「できるだけExcelの時点でデータは編集しやすい形式で保存している」ことを前提に話を進めています。

もっと形が整っていないデータの扱いやここでは説明できなかった項目も多くあります。

もし「もっと知りたい」「ここの情報では足りない!」ということであれば下記のサイトなどもご参照ください。


データハンドリング入門
https://kazutan.github.io/kazutanR/hands_on_170730/index.html




↑このページのトップヘ