◯◯◯ 使用するライブラリ
ggpplot2ライブラリ(グラフ描画パッケージ)およびdplyr(データ操作)を使用するために、tidyverseパッケージを、また、複数グラフ配置のためにpatchworkライブラリを
install.packages("tidyverse")
install.packages("patchwork")
library(ggplot2)
library(dplyr)
library(patchwork)
◯◯◯ データフレームをインポート
以下から、奇数偶数の好み実験結果の架空(N=2865)のデータをインポートしてください。
url = "https://lab.kenrikodaka.com/_download/csv/oddeven_2865.csv"
#url = "https://lab.kenrikodaka.com/_download/csv/oddeven_3544.csv" #3544人分版
source = read.csv(url)
誕生日と奇数偶数の好みに関するアンケートの架空のデータ(2865人分)です。
#最初の6行をちょっと出し
source[1:6,]
## month day preference gender age domhand ## 1 10 26 ODD MALE 19 RIGHT ## 2 4 6 ODD MALE NA <NA> ## 3 9 14 EVEN FEMALE 20 RIGHT ## 4 1 21 ODD FEMALE 17 RIGHT ## 5 4 7 EVEN FEMALE 20 RIGHT ## 6 7 27 EVEN FEMALE 18 RIGHT
NAは未定です。
翻訳すると以下の様になります。
# 全部で2865人分のデータがあることが確認できます。
dim(source)
## [1] 2865 6
◯◯◯ mutateの復習
mutate(要dplyr)を使うと データフレームの属性を簡単に追加/更新できます。
# sourceをdatにコピーします。
dat = source
head(dat,3)
## month day preference gender age domhand ## 1 10 26 ODD MALE 19 RIGHT ## 2 4 6 ODD MALE NA <NA> ## 3 9 14 EVEN FEMALE 20 RIGHT
datに月日の奇数/偶数のラベル(mtype)を新たに追加します。
# 2で割った余りを変数mtypeに登録
dat2 = dat %>%
mutate(mtype=month%%2)
head(dat2,4)
## month day preference gender age domhand mtype ## 1 10 26 ODD MALE 19 RIGHT 0 ## 2 4 6 ODD MALE NA <NA> 0 ## 3 9 14 EVEN FEMALE 20 RIGHT 1 ## 4 1 21 ODD FEMALE 17 RIGHT 1
# characterに変換
dat2 = dat2 %>%
mutate(mtype = if_else(mtype==0,"EVEN","ODD"))
head(dat2,4)
## month day preference gender age domhand mtype ## 1 10 26 ODD MALE 19 RIGHT EVEN ## 2 4 6 ODD MALE NA <NA> EVEN ## 3 9 14 EVEN FEMALE 20 RIGHT ODD ## 4 1 21 ODD FEMALE 17 RIGHT ODD
# 同様にdtypeも追加
dat2 = dat2 %>%
mutate(dtype = if_else(day%%2==0,"EVEN","ODD"))
head(dat2,4)
## month day preference gender age domhand mtype dtype ## 1 10 26 ODD MALE 19 RIGHT EVEN EVEN ## 2 4 6 ODD MALE NA <NA> EVEN EVEN ## 3 9 14 EVEN FEMALE 20 RIGHT ODD EVEN ## 4 1 21 ODD FEMALE 17 RIGHT ODD ODD
3つ以上の分岐はcase_whenを使いいます。
dat2 = dat2 %>%
mutate(combo =
case_when(mtype=="EVEN" & dtype=="EVEN" ~ "EE",
mtype=="ODD" & dtype=="ODD" ~ "OO",
mtype=="EVEN" & dtype=="ODD" ~ "EO",
TRUE ~ "OE"));
head(dat2,4)
## month day preference gender age domhand mtype dtype combo ## 1 10 26 ODD MALE 19 RIGHT EVEN EVEN EE ## 2 4 6 ODD MALE NA <NA> EVEN EVEN EE ## 3 9 14 EVEN FEMALE 20 RIGHT ODD EVEN OE ## 4 1 21 ODD FEMALE 17 RIGHT ODD ODD OO
グラフを作成します。
## 4つのcomboごとに、偶数好きと奇数好きの人数を数え上げます。
g = ggplot(dat2, aes(x=combo, fill=preference))
g = g + geom_bar(position = position_dodge()) #fillを横に並べる
g = g + scale_fill_brewer(palette = "Dark2")
g = g + ggtitle("Graph0_Mutate") #グラフのタイトル
g | plot_spacer()
◯◯◯ pivot_longer
今回の演習では、グラフ化に先立って、 特定の変数に関して、「偶数」「奇数」「奇素数」ごとの特性を比較集計する 場面が多くなります。 この場合、比較する条件に重複がない場合は(たとえば「奇数」と「偶数」)、集計は容易です。
例えば、月の属性ごとの偶数好き割合は、以下で一気に出せますね。
dat = source
dat %>%
mutate(month.type = if_else(month%%2 == 0,"EVEN","ODD")) %>%
group_by(month.type) %>%
summarize(
erate = mean(preference == 'EVEN')
)
## # A tibble: 2 × 2 ## month.type erate ## <chr> <dbl> ## 1 EVEN 0.635 ## 2 ODD 0.550
他方で、「偶数」「奇数」「奇素数」で分けたい場合、 「奇素数」は「奇数」に含まれるので、一つの変数で綺麗に分けることができなくなります。
このようなケースでは、
pivot_longerを使った集計が便利です。pivot_longerは、簡単にいうと、「列を減らした分を行で表現する」手続きとなります。
わかりやすく、最初の8行の「月」と「好み」を抽出したデータフレーム(
dat2)を使って説明します。
# select関数がconflictする場合は、以下を実行してください。
# detach("package:MASS", unload = TRUE)
dat = source
dat2 = dat %>%
filter(row_number() <=8) %>%
select(month, preference)
dat2
## month preference ## 1 10 ODD ## 2 4 ODD ## 3 9 EVEN ## 4 1 ODD ## 5 4 EVEN ## 6 7 EVEN ## 7 6 ODD ## 8 5 EVEN
dat2について、各行の月の属性を属性ごとの変数(EVEN,ODD,PRIME)を用意して、論理値で整形します。
dat2 = dat2 %>%
mutate(EVEN = month%%2==0,
ODD = month%%2==1,
PRIME = month %in% c(3,5,7,11)
)
dat2
## month preference EVEN ODD PRIME ## 1 10 ODD TRUE FALSE FALSE ## 2 4 ODD TRUE FALSE FALSE ## 3 9 EVEN FALSE TRUE FALSE ## 4 1 ODD FALSE TRUE FALSE ## 5 4 EVEN TRUE FALSE FALSE ## 6 7 EVEN FALSE TRUE TRUE ## 7 6 ODD TRUE FALSE FALSE ## 8 5 EVEN FALSE TRUE TRUE
ここで
pivot_longerを用いて 変数名を新たな変数「grouop」の値として、 元々の値を、新たな変数「belongs」の値にスライドします。
library(tidyr)
dat3 = dat2 %>%
pivot_longer(
cols = c(EVEN, ODD, PRIME),
names_to = "group",
values_to = "belongs"
)
dat3
## # A tibble: 24 × 4 ## month preference group belongs ## <int> <chr> <chr> <lgl> ## 1 10 ODD EVEN TRUE ## 2 10 ODD ODD FALSE ## 3 10 ODD PRIME FALSE ## 4 4 ODD EVEN TRUE ## 5 4 ODD ODD FALSE ## 6 4 ODD PRIME FALSE ## 7 9 EVEN EVEN FALSE ## 8 9 EVEN ODD TRUE ## 9 9 EVEN PRIME FALSE ## 10 1 ODD EVEN FALSE ## # ℹ 14 more rows
# ここからbelongsがTRUEのものだけを抽出する。
dat3 = dat3 %>%
filter(belongs) %>%
select(month,group,preference)
dat3
## # A tibble: 10 × 3 ## month group preference ## <int> <chr> <chr> ## 1 10 EVEN ODD ## 2 4 EVEN ODD ## 3 9 ODD EVEN ## 4 1 ODD ODD ## 5 4 EVEN EVEN ## 6 7 ODD EVEN ## 7 7 PRIME EVEN ## 8 6 EVEN ODD ## 9 5 ODD EVEN ## 10 5 PRIME EVEN
結果として、奇素数の数字のみ (上の例では、67と910行目 重複してエントリーされていることがわかります。
これを使って、最初の8行に、 3つの属性に適合する月の数字がいくつあるかを 以下で算出することができます。
dat3 %>%
group_by(group) %>%
summarize(
n=n()
)
## # A tibble: 3 × 2 ## group n ## <chr> <int> ## 1 EVEN 4 ## 2 ODD 4 ## 3 PRIME 2
以上を応用して、全てのデータについて、 月属性が「偶数」「奇数」「奇素数」ごとの 偶数好きの割合を一気に算出します。
dat = source
dat2 = dat %>%
# 月と好みに注目し、月の数字の3属性を変数に付加
select(month,preference) %>%
mutate(EVEN = month%%2==0,
ODD = month%%2==1,
PRIME = month %in% c(3,5,7,11)
) %>%
# 変数を行に展開し、
# もともとの値がTRUEのもののみをfilterで抽出します。
pivot_longer(
cols = c(EVEN, ODD, PRIME),
names_to = "group",
values_to = "belongs"
) %>%
filter(belongs == TRUE) %>%
group_by(group) %>%
# 各属性の数(n)と偶数好き割合を算出します。
summarize(
n = n(),
erate = sum(preference=="EVEN") / n
# erate = mean(preference=="EVEN") ##同じ
)
dat2
## # A tibble: 3 × 3 ## group n erate ## <chr> <int> <dbl> ## 1 EVEN 1387 0.635 ## 2 ODD 1478 0.550 ## 3 PRIME 976 0.536
# sourceをそのままdatに移します。
dat = source
# [ggplot]
## x軸をgenderに、枠線と塗りつぶしをpreferenceに設定
g = ggplot(dat,aes(x=gender, fill=preference))
# [geom_bar] genderごとにpreferenceを数え上げ
## position_dodge:横並び, width:グラフの幅, alpha:透明度
g = g + geom_bar(position=position_dodge(width=0.9),alpha=0.9)
g1 = g + ggtitle("g1") #出力用
# X軸は離散値、Y軸は連続値
g = g + scale_x_discrete(limits = c("FEMALE", "MALE"))
g = g + scale_y_continuous(limits = c(-100, 1100),breaks=c(0,250,500,750,1000))
# 塗りつぶしのパレットをDark2(brewer)
g = g + scale_fill_brewer(palette = "Dark2")
g2 = g + ggtitle("g2") #出力用
# 90度回転
g = g + coord_flip()
# [geom_text]
## geom_barの数え上げの量を表示
## just=0(左揃え), 0.5(中央揃え), 1(右揃え)
g = g + geom_text(stat = "count",
aes(label = paste("(",..count..,")",sep=""),
y=..count..*0.95, hjust=1),
position = position_dodge(width=0.9),
color="white", size=3)
# [labs]
## グラフのタイトル、XY軸のタイトル
g = g + labs(x = "Gender", y = "Population")
(g1 | g2) / g +ggtitle("Graph1_Gender")
グラフ化までは、
pivot_longerの解説の復習となります。
まずは準備として、奇素数(2を除く素数)を判定する関数を作成します(月と日にちの数字を想定としています。)。
# returnはあってもなくてもよい。
oddprime = function(n){
n %in% c(3,5,7,11,13,17,19,23,29,31)
}
# 使用例
oddprime(1:31)
## [1] FALSE FALSE TRUE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE FALSE ## [13] TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE FALSE ## [25] FALSE FALSE FALSE FALSE TRUE FALSE TRUE
月の数字の属性のみに注目し、重複を許すかたちで 各行で、月の属性(奇数・偶数・奇素数)と数字の好みが出力されるようなデータフレームに整形します。
dat = source
dat_ = dat %>%
# 女性のみの行、月と好みの列だけを取り出す。
filter(gender=="FEMALE") %>%
select(month,preference) %>%
# 偶数・奇数・奇素数の変数を追加(値は論理値)
mutate(EVEN = (month %% 2==0),
ODD = (month %% 2 == 1),
PRIME = oddprime(month)
) %>%
# 月の数字属性を行に展開
pivot_longer(
col = c(EVEN,ODD,PRIME),
names_to = "group", #数字属性の変数名
values_to = "belongs" #論理値の変数名
) %>%
# belongs=TRUEのみを抽出し、
# month,group,preference変数のみを取り出す
filter(belongs) %>%
select(month,group,preference)
#ここで一旦、dat_の内容を確認しましょう。
## 素数の数字のみ、2回のエントリーがあることを確認してください。
head(dat_,10)
## # A tibble: 10 × 3 ## month group preference ## <int> <chr> <chr> ## 1 9 ODD EVEN ## 2 1 ODD ODD ## 3 4 EVEN EVEN ## 4 7 ODD EVEN ## 5 7 PRIME EVEN ## 6 5 ODD EVEN ## 7 5 PRIME EVEN ## 8 5 ODD ODD ## 9 5 PRIME ODD ## 10 11 ODD ODD
最後に3つのグループのうち、 preferenceがEVENの割合を算出したデータフレームをxとして出力します。
x = dat_ %>%
group_by(group) %>%
summarize(
n = n(),
erate = sum(preference=="EVEN") / n
) %>%
select(group,n,erate)
# データフレームの内容を確認します。
## groupは月の数字の属性、
## nはサンプル数
## erateは偶数が好きな割合
x
## # A tibble: 3 × 3 ## group n erate ## <chr> <int> <dbl> ## 1 EVEN 780 0.701 ## 2 ODD 823 0.604 ## 3 PRIME 538 0.597
◯◯◯ 棒グラフの作成
# ベースラインからの偏り(100分率)をweightとします。
x = x %>%
mutate(weight = 100*(erate-0.5))
# [ggplot]
g = ggplot(x, aes(x = group, y = weight, fill = group, colour = group))
# [geom_bar]
## 個数(stat="count")でなく数値(stat="identity)を描画
g = g + geom_bar(stat = "identity",alpha=4/5)
# yは連続値
g = g + scale_y_continuous(limits=c(-10,30))
# 縦線(hline)と横線(vline)の描画
g = g + geom_hline(yintercept = 0, linetype = "solid", size=0.5,colour="black")
g = g + geom_vline(xintercept = 1.5, linetype = "dotted", size=0.5,colour="black")
g = g + geom_vline(xintercept = 2.5, linetype = "dotted", size=0.5,colour="black")
# 色の設定
col1 = rgb(174/255,69/255,50/255) #for even
col2 = rgb(58/255,128/255,77/255) #for odd
col3 = rgb(48/255,112/255,176/255) #for prime
g = g + scale_fill_manual(values = c(col1,col2,col3), name="number type")
g = g + scale_colour_manual(values = c(col1,col2,col3), name = "number type")
# 90度回転
g = g + coord_flip()
# [labs]
## グラフのタイトル、XY軸のタイトル
g = g + labs(x = "Month Number type", y = "Even number preference (%)")
g = g + theme_minimal()
g + ggtitle("Graph2_FemaleMonth")
女性に限って、月 x 日が同じ属性だった場合の好みの違いを検証します。 奇素数 x 奇素数は奇数 x 奇数を含むため、 Graph2と同様に、
pivot_longerをうまく使う必要があります。
dat = source
dat_ = dat %>%
# 女性のみの行、月と好みの列だけを取り出す。
filter(gender=="FEMALE") %>%
select(month,day,preference) %>%
# 偶数・奇数・奇素数の変数を追加(値は論理値)
mutate(EVEN2 = (month%%2==0) & (day%%2==0),
ODD2 = (month%%2==1) & (day%%2==1),
PRIME2 = oddprime(month) & oddprime(day)
) %>%
# 月の数字属性を行に展開
pivot_longer(
col = c(EVEN2,ODD2,PRIME2),
names_to = "group", #数字属性の変数名
values_to = "belongs" #論理値の変数名
) %>%
# belongs=TRUEのみを抽出し、
# month,daygroup,preference変数のみを取り出す
filter(belongs) %>%
select(month,day,group,preference)
#ここで一旦、dat_の内容を確認しましょう。
head(dat_,10)
## # A tibble: 10 × 4 ## month day group preference ## <int> <int> <chr> <chr> ## 1 1 21 ODD2 ODD ## 2 7 27 ODD2 EVEN ## 3 5 31 ODD2 ODD ## 4 5 31 PRIME2 ODD ## 5 6 16 EVEN2 EVEN ## 6 6 26 EVEN2 EVEN ## 7 7 9 ODD2 EVEN ## 8 3 1 ODD2 EVEN ## 9 3 21 ODD2 EVEN ## 10 9 3 ODD2 ODD
最後に3つのグループのうち、 preferenceがEVENの割合を算出したデータフレームをxとして出力します。
x = dat_ %>%
group_by(group) %>%
summarize(
n = n(),
erate = sum(preference=="EVEN") / n
) %>%
select(group,n,erate)
# データフレームの内容を確認します。
## groupは月の数字の属性、
## nはサンプル数
## erateは偶数が好きな割合
x
## # A tibble: 3 × 3 ## group n erate ## <chr> <int> <dbl> ## 1 EVEN2 373 0.751 ## 2 ODD2 434 0.571 ## 3 PRIME2 174 0.592
◯◯◯ 棒グラフの作成
# 準備としてベースラインからの偏り(100分率)をweightとします。
x = x %>%
mutate(weight = 100*(erate-0.5))
x
## # A tibble: 3 × 4 ## group n erate weight ## <chr> <int> <dbl> <dbl> ## 1 EVEN2 373 0.751 25.1 ## 2 ODD2 434 0.571 7.14 ## 3 PRIME2 174 0.592 9.20
g = ggplot(x, aes(x = group, y = weight, fill = group, colour = group))
g = g + geom_bar(stat = "identity",alpha=4/5)
g = g + scale_y_continuous(limits=c(-10,30))
# x軸(離散値)のラベルを変更
g = g + scale_x_discrete(limits=c("EVEN2","ODD2","PRIME2"),
label=c("EVENxEVEN","ODDxODD","PRIMExPRIME"))
g = g + geom_hline(yintercept = 0, linetype = "solid", size=0.5,colour="black")
g = g + geom_vline(xintercept = 1.5, linetype = "dotted", size=0.5,colour="black")
g = g + geom_vline(xintercept = 2.5, linetype = "dotted", size=0.5,colour="black")
g = g + scale_fill_manual(values = c(col1,col2,col3), name="number type")
g = g + scale_colour_manual(values = c(col1,col2,col3), name = "number type")
g = g + coord_flip()
g = g + labs(x = "Number type", y = "Even number preference (%)")
g = g + theme_minimal()
g + ggtitle("Graph3_FemaleMonthDay")
dat_ = source
head(dat_)
## month day preference gender age domhand ## 1 10 26 ODD MALE 19 RIGHT ## 2 4 6 ODD MALE NA <NA> ## 3 9 14 EVEN FEMALE 20 RIGHT ## 4 1 21 ODD FEMALE 17 RIGHT ## 5 4 7 EVEN FEMALE 20 RIGHT ## 6 7 27 EVEN FEMALE 18 RIGHT
dayPref = dat_ %>%
group_by(day) %>%
summarize(
even= sum(preference=="EVEN"),
odd = sum(preference=="ODD"),
erate = mean(preference=="EVEN"),
) %>%
arrange(desc(erate))
dayPref = dayPref %>%
mutate(type = case_when(
day%%2==0 ~ "EVEN",
oddprime(day) ~ "PRIME",
TRUE ~ "ODD"),
order = row_number(), #行番号を挿入
)
dayPref
## # A tibble: 31 × 6 ## day even odd erate type order ## <int> <int> <int> <dbl> <chr> <int> ## 1 22 57 23 0.712 EVEN 1 ## 2 2 64 27 0.703 EVEN 2 ## 3 21 72 39 0.649 ODD 3 ## 4 26 60 33 0.645 EVEN 4 ## 5 27 65 36 0.644 ODD 5 ## 6 8 54 30 0.643 EVEN 6 ## 7 30 50 28 0.641 EVEN 7 ## 8 12 65 37 0.637 EVEN 8 ## 9 19 61 35 0.635 PRIME 9 ## 10 3 55 33 0.625 PRIME 10 ## # ℹ 21 more rows
g = ggplot(dayPref,aes(x=order,y=100*erate,fill=type))
g = g + geom_bar(stat = "identity", colour="black")
#geom_bar(stat="identity")とgeom_colは同じなので、こちらでも同じ結果です。
#g = g + geom_col(colour ="black")
g = g + scale_x_continuous(limits=c(0.5,31.5),breaks=c(1,10,20,30))
g = g + scale_y_continuous(limits=c(0,87),breaks=c(0,25,50,75))
# 50%のところに線を引く
g = g + geom_hline(yintercept = 50, linetype = "dotted", size=1,colour="black");
# 各順位に対応する数字をバーの上端より1.5文字分下に描画する
g = g + geom_text(aes(label = day), colour = "white", size = 3, vjust = 1.5)
# 凡例の色をマニュアルで指定する
g = g + scale_fill_manual(values = c(col1,col2,col3),name="Number type")
g = g + scale_colour_manual(values = c(col1,col2,col3),name="Number type")
g = g + labs(x="ORDER",y="EVEN-PREF RATE")
g = g + theme_minimal() +
theme(legend.position = "bottom")
g + ggtitle("Graph4_DayRank")
dat_ = source;
# 年齢ごとのpreferenceを集計する
# 行番号があると混乱するので消します。
print(dat_ %>% count(age),row.names = FALSE)
## age n ## 12 2 ## 13 3 ## 14 1 ## 15 33 ## 16 57 ## 17 36 ## 18 964 ## 19 903 ## 20 256 ## 21 63 ## 22 18 ## 23 8 ## 24 7 ## 25 6 ## 26 3 ## 27 2 ## 28 1 ## 29 2 ## 33 1 ## 36 1 ## 38 1 ## 42 2 ## 44 1 ## 47 2 ## 48 1 ## 49 3 ## 51 1 ## 52 3 ## 53 1 ## 54 1 ## 56 1 ## 67 1 ## NA 480
# 比較的サンプルの多い、15〜22歳をとりあげます。
head(dat_)
## month day preference gender age domhand ## 1 10 26 ODD MALE 19 RIGHT ## 2 4 6 ODD MALE NA <NA> ## 3 9 14 EVEN FEMALE 20 RIGHT ## 4 1 21 ODD FEMALE 17 RIGHT ## 5 4 7 EVEN FEMALE 20 RIGHT ## 6 7 27 EVEN FEMALE 18 RIGHT
agePref = dat %>%
filter(age>14 & age<23) %>%
group_by(age) %>%
summarize(
even = sum(preference=="EVEN"),
odd = sum(preference=="ODD"),
erate = mean(preference=="EVEN") ## even / (even + odd)と同じ
) %>%
arrange(age)
agePref
## # A tibble: 8 × 4 ## age even odd erate ## <int> <int> <int> <dbl> ## 1 15 27 6 0.818 ## 2 16 37 20 0.649 ## 3 17 30 6 0.833 ## 4 18 597 367 0.619 ## 5 19 537 366 0.595 ## 6 20 141 115 0.551 ## 7 21 30 33 0.476 ## 8 22 7 11 0.389
◯◯◯ 棒グラフの作成
15歳から22歳までの偶数好きの割合の遷移を 折れ線グラフで視覚化します。
g = ggplot(agePref,aes(x=age,y=erate))
g = g + geom_line(colour = "black",size=1.5)
g = g + geom_point(size=5)
g = g + scale_x_continuous(limits=c(14.5,22.5),breaks=15:22)
g = g + scale_y_continuous(limits=c(0,1.0),breaks=c(0,0.25,0.50,0.75,1.0))
g = g + labs(x="AGE",y="EVEN-PREF RATE")
g = g + theme_minimal() +
theme(legend.position = "bottom")
g + ggtitle("Graph5_AgeEffect")
ApMedia05_Work