すでに他の方々が紹介された内容の繰り返しばかりですが,勉強を兼ねた自分用のメモです。
自分用ではありますが,同じくらいのR歴の方のお役に立てばいいなと思います。
間違いやもっと良い方法など,お気づきの場合は私のtwitterアカウント@imuyaoyiにお知らせいただければとっても助かります。
library(ggplot2)
library(dplyr)
手順は以下通りです。
1. tableとas.data.frameを使ってデータフレーム型の度数分布表を作成する
2. 通常通り,ggplotで好きなようにプロットする
str(iris)
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
度数分布表を作成する関数はtableです。
dplyr::selectで変数を選択し,%>%でtableにつなげます。このときオブジェクトクラスはtable型です。
このままではデータフレーム型しか扱えないggplotに渡すことができません。
(ftab1 <- iris %>%
dplyr::select(Species) %>% # 手順1.変数を選択する
table) # table形式の度数分布表が作成される
## .
## setosa versicolor virginica
## 50 50 50
class(ftab1)
## [1] "table"
as.data.frame()を使ってデータフレーム形式に変換し,ggplotに渡せるようにします。
作成された度数分布表のデータフレームの列名は「.」=選択した変数,「Freq」=観測度数と設定されます。
(ftab1 <- ftab1 %>% as.data.frame())
## . Freq
## 1 setosa 50
## 2 versicolor 50
## 3 virginica 50
あとはggplotに渡すだけ。
(p1 <- p1 %>%
+geom_text(aes(x = ., y = Freq, label = Freq, vjust = -0.5), size = 5)+ # 数値ラベル
theme_classic(base_size = 18)+ # 全体のテーマ
labs(title = "Species" , y = "")) # グラフタイトルの設定とy軸ラベルの削除
BarPlot1 <- function(data, i){
Data <- data[!is.na(data[,i]),] # 選択した変数に欠損値がない行を使用する
VName1 = colnames(Data)[i]
P <- Data %>%
dplyr::select_(VName1) %>%
table %>% as.data.frame() %>%
ggplot(aes(x = ., y = Freq))+
geom_bar(stat = "identity")+
geom_text(aes(x = ., y = Freq, label = Freq, vjust = -0.5), size = 5)+
theme_classic(base_size = 18)+
labs(title = colnames(Data)[i] , y = "")
print(P) #
}
BarPlot1(iris, 5)
2変数では,集合グラフ( position = "dodge")と積み上げグラフ( position = "stack")の2通りを紹介します。
irisデータにカテゴリー変数をひとつ追加したデータ(iris2)を使ってやってみます。
iris2 <- iris
V1 <- rep(LETTERS[1:5], c(15,45,30,25,35))
V1 <- V1[order(rnorm(length(V1)))]
iris2$V1 <- V1
str(iris2)
## 'data.frame': 150 obs. of 6 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ V1 : chr "D" "C" "A" "B" ...
主な手順は,1変数の時に比べて 以下の2点が加わります。
dplyr::group_byとdplyr::selectで度数プロットをしたい変数を指定します。
このとき,2つの変数はどちらで指定しても同じ内容の度数分布になります。(当たり前ですが)
1変数の時と違って,観測度数の列名が「Freq」であることは同じですが,変数名は元の変数名が与えられます。
(ftab2 <- iris2 %>%
dplyr::group_by(Species) %>%
dplyr::select(V1) %>%
table %>% as.data.frame())
## Species V1 Freq
## 1 setosa A 3
## 2 versicolor A 6
## 3 virginica A 6
## 4 setosa B 18
## 5 versicolor B 11
## 6 virginica B 16
## 7 setosa C 6
## 8 versicolor C 12
## 9 virginica C 12
## 10 setosa D 15
## 11 versicolor D 6
## 12 virginica D 4
## 13 setosa E 8
## 14 versicolor E 15
## 15 virginica E 12
ほぼggplot2逆引きの記事の通りです。
集合グラフの場合は,1変数の手順に次のが手順が加わるだけです。
ftab2 %>%
ggplot(aes(x = Species, y = Freq, fill = V1))+
geom_bar(stat = "identity", position = "dodge")+ # プロット位置を"dodge"に指定
geom_text(aes(x = Species, y = Freq, label = Freq, vjust = -0.5,
group = V1), # 数値ラベルの位置をグループの水準ごとの位置に配置する
position = position_dodge(width = 0.9))# 指定しないとエラーが表示される
ftab2 %>%
ggplot(aes(x = Species, y = Freq, fill = V1))+
geom_bar(stat = "identity", position = "dodge")+
geom_text(aes(x = Species, y = Freq, label = Freq, vjust = -0.5,
group = V1),
position = position_dodge(width = 0.9), size = 5)+
theme_classic(base_size = 18)+
labs(title = "Species * V1" , x = "", y = "")
BarPlot2 <- function(data, i, j){
Data <- data[!is.na(data[,i]),]
VName1 = colnames(Data)[i]
VName2 = colnames(Data)[j]
P <- Data %>%
dplyr::group_by_(VName1) %>%
dplyr::select_(VName2) %>%
table %>% as.data.frame() %>%
ggplot(aes_string(x = VName1, y = "Freq", fill = VName2))+
geom_bar(stat = "identity", position = "dodge")+
geom_text(aes_string(x = VName1, y = "Freq",label = "Freq", vjust = -0.5,
group = VName2),
position = position_dodge(width = 0.9), size = 5)+
theme_classic(base_size = 18)+
labs(title = paste(VName1, " * ", VName2) , x = "", y = "")
print(P)
}
BarPlot2(iris2, 6, 5)
集合グラフで数値ラベルを入れるのは比較的簡単ですが,積み上げグラフの場合は
ちょうど良い位置に数値ラベルを配置しようとすると少し工夫が必要です。
kazutan先生のggplot2逆引きを参考にさせていただき,
度数が集計されていないデータフレームからでも作図できるようにやってみました。
(ftab3 <- iris2 %>%
dplyr::group_by(V1) %>%
dplyr::select(Species) %>%
table %>% as.data.frame() %>%
dplyr::group_by(V1) %>% # x軸にとりたい変数で再度グループ
dplyr::mutate(Pos = cumsum(Freq) - (Freq * 0.5))) # 積み上げの各部位の中央になる位置を計算し,列に追加
## Source: local data frame [15 x 4]
## Groups: V1 [5]
##
## V1 Species Freq Pos
## (fctr) (fctr) (int) (dbl)
## 1 A setosa 3 1.5
## 2 B setosa 18 9.0
## 3 C setosa 6 3.0
## 4 D setosa 15 7.5
## 5 E setosa 8 4.0
## 6 A versicolor 6 6.0
## 7 B versicolor 11 23.5
## 8 C versicolor 12 12.0
## 9 D versicolor 6 18.0
## 10 E versicolor 15 15.5
## 11 A virginica 6 12.0
## 12 B virginica 16 37.0
## 13 C virginica 12 24.0
## 14 D virginica 4 23.0
## 15 E virginica 12 29.0
(ftab3 %>%
ggplot(aes(x = V1, y = Freq, fill = Species))+
geom_bar(stat = "identity", position = "stack")+ # プロット位置のを"dodge"に指定
geom_text(aes(label = Freq, y = Pos))) # 数値ラベル位置をPosで指定
(ftab3 %>%
ggplot(aes(x = reorder(x = V1, X = Freq, FUN = sum), y = Freq, fill = Species))+ # 頻度の多い順に並べる
geom_bar(stat = "identity", position = "stack", alpha = 0.7)+ # グラフを透過させる
coord_flip()+ # 縦と横を入れ替える
guides(fill = guide_legend(reverse = TRUE))+ # 凡例の位置を積み上げ順と同じにする
geom_text(aes(label = Freq, y = Pos), size = 5)+
theme_classic(base_size = 18)+
theme(panel.grid.major = element_line(color = "lightgray"),
panel.grid.major.y = element_blank(), # y軸のグリッドを消す
plot.background = element_rect(color = "gray", size = 1))+
labs(title = "V1 * Species" , x = "", y = ""))
BarPlot3 <- function(data, i, j){
Data <- data[!is.na(data[,i]),]
VName1 = colnames(Data)[i]
VName2 = colnames(Data)[j]
P <- Data %>%
dplyr::group_by_(VName1) %>%
dplyr::select_(VName2) %>%
table %>% as.data.frame()%>%
dplyr::arrange_(VName1)%>%
dplyr::group_by_(VName1)%>%
dplyr::mutate(Pos = cumsum(Freq) - (Freq * 0.5))%>%
ggplot(aes_string(x = paste("reorder(x = ", VName1, ",X = Freq, FUN = sum)"), y = "Freq", fill = VName2))+
geom_bar(stat = "identity", position = "stack", alpha = 0.7)+
coord_flip()+
guides(fill = guide_legend(reverse = TRUE))+
geom_text(aes(label = Freq, y = Pos), size = 5)+
theme_classic(base_size = 18)+
theme(panel.grid.major = element_line(color = "lightgray"),
panel.grid.major.y = element_blank(),
plot.background = element_rect(color = "gray", size = 1))+
labs(title = paste(VName1, " * " ,VName2), x = "", y = "")
print(P)
}
BarPlot3(iris2, 5, 6)
どうせなら,集合も積み上げもオプションで選べる関数にしたい。
BarPlot4 <- function(data, i, j, type){
Data <- data[!is.na(data[,i]),]
VName1 = colnames(Data)[i]
VName2 = colnames(Data)[j]
switch (type,
"dodge" = BarPlot2(Data, i, j),
"stack" = BarPlot3(Data, i, j))
}
BarPlot4(iris2, 6, 5, type = "dodge")
BarPlot4(iris2, 6, 5, type = "stack")
現時点で,geom_ bar(stat = ""count))やstat_ summaryをうまく使えばもっと効率よくできるのでは?と思ってます。。
なので,中途半端なものをさらしてすみません。。 そもそもplotly使えば苦労して数値ラベル入れる必要もないのではと思ったりも。
さらには、そもそもなぜこんなことしてるんだろうと思ったりも・・・。 それでも,ひとまずggplot2で好みのグラフが描けて気持ちいいです。
とくに,2変数の積み上げグラフはお気に入り。
逆引きからとくに抜粋