◯◯◯ 使用するライブラリ
ggpplot2ライブラリ(グラフ描画パッケージ)およびdplyr(データ操作)を使用するために、tidyverseパッケージを、また、複数グラフ配置のためにpatchworkライブラリを
install.packages("tidyverse")
install.packages("patchwork")
library(ggplot2)
library(dplyr)
library(patchwork)
◯◯◯ データフレームをインポート
今回の演習では、集団フリーシャッター実験のデータを扱います。
実験の内容については以下の映像を参考にしてください。 - [YOUTUBE] 集団フリーシャッター課題
まずは以下のCSVを読み込みましょう。
# csvは改訂版
url = "https://lab.kenrikodaka.com/_download/csv/freeshutter_230601m.csv"
dat = read.csv(url)
最初の6行を確認します。
head(dat)
## seat a1 b1 b2 b3 seat_ix seat_iy ## 1 192 2.2 1.5 2.7 5.6 12 16 ## 2 168 0.8 4.4 10.0 14.7 12 14 ## 3 59 0.6 0.6 0.8 0.9 11 5 ## 4 60 2.9 3.4 5.3 9.9 12 5 ## 5 89 6.2 0.3 10.2 18.1 5 8 ## 6 180 0.1 0.1 0.5 0.8 12 15
個々の列に対応する変数の内容は以下の通りです。
なおこのデータは、以下の二種類の集団フリーシャッター実験を含んでいます。
# 全部で119人分のデータがあることが確認できます。
dim(dat)
## [1] 119 7
◯◯◯ pivot_longerによるデータの整形
現在のデータフレームの仕様は、一行に一人の実験参加者が対応し、それぞれの行が、4つのシャッター時間の計測値(a1,b1,b2,b3)を持ちます。
これを、
pivot_longerを用いて、一行に一つの計測時間を対応させるかたちに変形させます。この場合、列の変数(a1,b1,b2,b3)を、新たな変数「group」の値として、もとの計測値を「time」に対応させます。
library(tidyr)
dat.fs = dat
dat.fs = dat.fs %>%
pivot_longer(
cols = c(a1, b1, b2, b3), # 値変換する変数を指定
names_to = "group", # colsを扱う新たな変数
values_to = "time" # もともとの値を収納する変数
)
dat.fs
## # A tibble: 476 × 5 ## seat seat_ix seat_iy group time ## <int> <int> <int> <chr> <dbl> ## 1 192 12 16 a1 2.2 ## 2 192 12 16 b1 1.5 ## 3 192 12 16 b2 2.7 ## 4 192 12 16 b3 5.6 ## 5 168 12 14 a1 0.8 ## 6 168 12 14 b1 4.4 ## 7 168 12 14 b2 10 ## 8 168 12 14 b3 14.7 ## 9 59 11 5 a1 0.6 ## 10 59 11 5 b1 0.6 ## # ℹ 466 more rows
最後にgroupを、実験の種類「exp」と撮影順序「order」に分解します。
dat.fs = dat.fs %>%
mutate(exp = if_else(group=="a1","Single","Triple")) %>%
mutate(order = case_when(group=="a1" ~ "1st",
group=="b1" ~ "1st",
group=="b2" ~ "2nd",
group=="b3" ~ "3rd")) %>%
select(-group) #groupを削除
dat.fs
## # A tibble: 476 × 6 ## seat seat_ix seat_iy time exp order ## <int> <int> <int> <dbl> <chr> <chr> ## 1 192 12 16 2.2 Single 1st ## 2 192 12 16 1.5 Triple 1st ## 3 192 12 16 2.7 Triple 2nd ## 4 192 12 16 5.6 Triple 3rd ## 5 168 12 14 0.8 Single 1st ## 6 168 12 14 4.4 Triple 1st ## 7 168 12 14 10 Triple 2nd ## 8 168 12 14 14.7 Triple 3rd ## 9 59 11 5 0.6 Single 1st ## 10 59 11 5 0.6 Triple 1st ## # ℹ 466 more rows
◯◯◯ Graph1A(Single実験)
# Single実験のデータフレームを取得
dat_si = dat.fs %>%
filter(exp == "Single")
# [ggplot]
## x軸をtimeに設定
gp_a = ggplot(dat_si,aes(x=time))
# [geom_histogram] ヒストグラムの作成
## 一つのビンの幅を0.1sec、塗りつぶしを白、枠線を黒
gp_a = gp_a + geom_histogram(binwidth=0.1, fill="white",colour="black")
# Y軸(Count)の最大を10とする
gp_a = gp_a + scale_y_continuous(limits=c(0,10),
breaks=2*(0:5))
# X軸の範囲と目盛の位置を決める
gp_a = gp_a + scale_x_continuous(limits=c(0,20),
breaks=5*(0:4))
# [labs]
## グラフのタイトル、XY軸のタイトル
gp_a = gp_a + labs(x = "Time[s]", y = "Count")
gp_a + theme_minimal() +
ggtitle("Graph1A_ShutterHistogram")
◯◯◯ Graph1B(Triple実験)
# Triple実験のデータフレームを取得
dat_tri = dat.fs %>%
filter(exp == "Triple")
# [ggplot]
## x軸をtimeに設定
gp_b = ggplot(dat_tri,aes(x=time))
gp_b = gp_b + geom_histogram(binwidth=0.1, fill="white",colour="black")
gp_b = gp_b + scale_y_continuous(limits=c(0,10),breaks=2*(0:5))
gp_b = gp_b + scale_x_continuous(limits=c(0,20),breaks=5*(0:4))
gp_b = gp_b + labs(x = "Time[s]", y = "Count")
gp_b + theme_minimal() +
ggtitle("Graph1B_ShutterHistogram")
◯◯◯ Graph1C(Triple実験、順序別、積み上げ方式)
# Triple実験のデータフレームを取得
dat_tri = dat.fs %>%
filter(exp == "Triple")
# [ggplot]
## x軸をtimeに設定, 塗りつぶしをorderに設定
gp_c = ggplot(dat_tri,aes(x=time,fill=order))
# 積み上げ順序の反転(factor順序:下から上へ)
## 1->2->3の順に積み上げるようにする
gp_c = gp_c + geom_histogram(binwidth=0.1,colour="black",
position = position_stack(reverse = TRUE))
gp_c = gp_c + scale_y_continuous(limits=c(0,10),breaks=2*(0:5))
gp_c = gp_c + scale_x_continuous(limits=c(0,20),breaks=5*(0:4))
# labs()で各種のタイトルをまとめて変更(fillは凡例)
gp_c = gp_c + labs(x = "Time[s]", y = "Count", fill="Order")
# 凡例の文字列(変更なし:実行しなくても良い)
gp_c = gp_c + scale_fill_discrete(
limits=c("1st","2nd","3rd"),labels=c("1st","2nd","3rd"))
gp_c + theme_minimal() +
ggtitle("Graph1C_ShutterHistogram")
◯◯◯ Graph1D(Triple実験、順序別)
# Triple実験のデータフレームを取得
dat_tri = dat.fs %>%
filter(exp == "Triple")
gp_d = ggplot(dat_tri,aes(x=time,fill=order))
gp_d = gp_d + geom_histogram(binwidth=0.1,colour="black")
gp_d = gp_d + scale_x_continuous(limits=c(0,20),breaks=2*(0:10))
gp_d = gp_d + scale_y_continuous(limits=c(0,10),breaks=5*(0:4))
# 凡例の文字列(変更なし:実行しなくても良い)
gp_d = gp_d + scale_fill_discrete(
limits=c("1st","2nd","3rd"),labels=c("1st","2nd","3rd"))
# labs()で各種のタイトルをまとめて変更(fillは凡例)
gp_d = gp_d + labs(x="Time[s]", y="Count", fill="Order")
# facetをgroupで分割、factの背景なども整える
gp_d = gp_d + facet_grid(order ~ .)
gp_d + theme_minimal() +
ggtitle("Graph1D_ShutterHistogram")
シャッター時間をボックスプロットで描画します。
まずは、一つのグラフで出力する方法から。
# [ggplot]
## x軸をexp、y軸にtime, 塗りつぶしをtroupに設定
gp_a = ggplot(dat.fs,aes(x=exp,y=time,fill=order))
# [geom_boxplot]ボックスプロットによる描画
gp_a = gp_a + geom_boxplot()
gp_a = gp_a + scale_x_discrete(limits=c("Single","Triple"),
labels=c("Type Single", "Type Triple"))
gp_a = gp_a + scale_y_continuous(limits=c(0,20),breaks=5*(0:4))
# Bluesパレットを使って、fillを描画。
gp_a = gp_a + scale_fill_brewer(palette = "Blues")
# RColorBrewer::display.brewer.all()
gp_a = gp_a + labs(x = "Experiment", y = "Time[s]",fill="Order")
gp_a + theme_minimal() +
ggtitle("Graph2_ShutterBox")
別の方法として、facetを使ってグラフをExpのタイプごとに切り分けます。
# [ggplot]
## x軸をorder、y軸にtime, 塗りつぶしをorderに設定
gp_b = ggplot(dat.fs,aes(x=order,y=time,fill=order))
# [geom_boxplot]ボックスプロットによる描画
gp_b = gp_b + geom_boxplot()
gp_b = gp_b + scale_x_discrete()
gp_b = gp_b + scale_y_continuous(limits=c(0,20),breaks=5*(0:4))
# Bluesパレットを使って、fillを描画。
gp_b = gp_b + scale_fill_brewer(palette = "Blues")
gp_b = gp_b + labs(x = "Order", y = "Time[s]",fill="Order")
# facetをgroupで分割、factの背景なども整える
gp_b = gp_b + facet_grid(. ~ exp)
gp_b + theme_minimal() +
ggtitle("Graph2B_ShutterBox")
あらためて、dat.fsの構造は以下の通り。
head(dat.fs); dim(dat.fs)
## # A tibble: 6 × 6 ## seat seat_ix seat_iy time exp order ## <int> <int> <int> <dbl> <chr> <chr> ## 1 192 12 16 2.2 Single 1st ## 2 192 12 16 1.5 Triple 1st ## 3 192 12 16 2.7 Triple 2nd ## 4 192 12 16 5.6 Triple 3rd ## 5 168 12 14 0.8 Single 1st ## 6 168 12 14 4.4 Triple 1st
## [1] 476 6
◯◯◯ 欠損値の削除
is.na()を使って、計測値が欠損している行を取り出してみます。 その結果、476行の中には42の欠損データがあり、 その多くが、triple実験の2~3回目であることがわかります。
dat.fs %>% filter(is.na(time))
## # A tibble: 42 × 6 ## seat seat_ix seat_iy time exp order ## <int> <int> <int> <dbl> <chr> <chr> ## 1 156 12 13 NA Triple 3rd ## 2 204 12 17 NA Triple 2nd ## 3 204 12 17 NA Triple 3rd ## 4 227 11 19 NA Triple 2nd ## 5 227 11 19 NA Triple 3rd ## 6 37 1 4 NA Triple 3rd ## 7 38 2 4 NA Triple 2nd ## 8 38 2 4 NA Triple 3rd ## 9 94 10 8 NA Triple 3rd ## 10 236 8 20 NA Triple 2nd ## # ℹ 32 more rows
sum(is.na(dat.fs$time))
## [1] 42
今後のために、dat.fsから欠損値を除いておきます(434行)。
dat.fs = dat.fs %>%
filter(!is.na(time))
dim(dat.fs)
## [1] 434 6
◯◯◯ コンマN秒ごとに集計
dat.fs2 = dat.fs %>%
mutate(time2 = as.integer(floor(time*10)%%10)) %>%
select(time,time2,exp,order)
dat.fs2
## # A tibble: 434 × 4 ## time time2 exp order ## <dbl> <int> <chr> <chr> ## 1 2.2 2 Single 1st ## 2 1.5 5 Triple 1st ## 3 2.7 7 Triple 2nd ## 4 5.6 6 Triple 3rd ## 5 0.8 8 Single 1st ## 6 4.4 4 Triple 1st ## 7 10 0 Triple 2nd ## 8 14.7 7 Triple 3rd ## 9 0.6 6 Single 1st ## 10 0.6 6 Triple 1st ## # ℹ 424 more rows
dat_comma = dat.fs2 %>%
group_by(exp,time2) %>% #実験の種類、コンマn秒でグループ分け
summarize(count=n()) #グループに基づき総数を数え上げる
dat_comma
## # A tibble: 20 × 3 ## # Groups: exp [2] ## exp time2 count ## <chr> <int> <int> ## 1 Single 0 17 ## 2 Single 1 5 ## 3 Single 2 16 ## 4 Single 3 13 ## 5 Single 4 10 ## 6 Single 5 11 ## 7 Single 6 11 ## 8 Single 7 12 ## 9 Single 8 14 ## 10 Single 9 10 ## 11 Triple 0 42 ## 12 Triple 1 25 ## 13 Triple 2 32 ## 14 Triple 3 32 ## 15 Triple 4 35 ## 16 Triple 5 28 ## 17 Triple 6 20 ## 18 Triple 7 31 ## 19 Triple 8 33 ## 20 Triple 9 37
◯◯◯ Graph3A(X軸を0:9の順で)
gp_a = ggplot(dat_comma,aes(x=time2,y=count,colour=exp))
gp_a = gp_a + geom_line(size=0.5) + geom_point(size=3)
gp_a = gp_a + scale_colour_brewer(palette ="Set2")
gp_a = gp_a + scale_x_continuous(limits=c(-0.5,9.5),breaks=0:9)
gp_a = gp_a + scale_y_continuous(limits=c(0,50),breaks=seq(0,50,by=10))
gp_a = gp_a + labs(x="Comma.N",y="Count",colour="Exp Type")
gp_a + theme_minimal() +
ggtitle("Graph3A_ShutterComma")
◯◯◯ Graph3B(X軸を6,7,8,9,0,1,2,3,4,5の順で)
# time2を指定した順序でfactor化
order_n = c(6,7,8,9,0,1,2,3,4,5)
dat_comma = dat_comma %>%
mutate(time2 = factor(time2, levels = order_n))
# groupで線を引くグループを指定します。
gp_b = ggplot(dat_comma,
aes(x=time2,y=count,group=exp,colour=exp))
## これは、後続するgeom_line()が
##「同じグループ内の点を順に線でつなぐ」構造になっているためです
# x=5(time2=0に相当)で縦の点線を引きます。
gp_b = gp_b + geom_vline(xintercept = 5, linetype = "dotted",
size=0.5,colour="black")
gp_b = gp_b + scale_colour_brewer(palette ="Set2")
gp_b = gp_b + geom_line(size=0.5) + geom_point(size=3)
# xはfactorのため、離散値(discrete)として扱われることに注意。
## paste0は、文字列の接合をします。
gp_b = gp_b + scale_x_discrete(labels=paste0(".",c(6:9,0:5)))
gp_b = gp_b + scale_y_continuous(limits=c(0,50),breaks=seq(0,50,by=10))
gp_b = gp_b + labs(x="Comma.N",y="Count",colour="Exp Type");
gp_b + theme_minimal() +
ggtitle("Graph3B_ShutterComma")
まずは、
pivot_longerをする前のデータを確認します。
head(dat,10)
## seat a1 b1 b2 b3 seat_ix seat_iy ## 1 192 2.2 1.5 2.7 5.6 12 16 ## 2 168 0.8 4.4 10.0 14.7 12 14 ## 3 59 0.6 0.6 0.8 0.9 11 5 ## 4 60 2.9 3.4 5.3 9.9 12 5 ## 5 89 6.2 0.3 10.2 18.1 5 8 ## 6 180 0.1 0.1 0.5 0.8 12 15 ## 7 223 14.8 0.8 9.8 19.5 7 19 ## 8 213 0.7 1.3 2.4 3.9 9 18 ## 9 214 1.9 1.2 2.2 3.1 10 18 ## 10 191 0.3 18.2 18.5 18.9 11 16
オリジナルの構造を保ったまま、 Triple実験の時間差のみを抽出するデータフレーム(
dat_dt)を作成します。
# datをdat_dtにコピーします。
dat_dt = dat
dat_dt = dat_dt %>%
mutate(time1 = b1, #変数名をtime1にコピー
time2 = b2, #同様
time3 = b3, #同様
time21 = time2-time1, #シャッター1とシャッター2の時間差
time32 = time3-time2 #シャッター2とシャッター3の時間差
) %>%
select(time1,time21,time32) #最初のシャッター時間、および時間差のみに注目
head(dat_dt)
## time1 time21 time32 ## 1 1.5 1.2 2.9 ## 2 4.4 5.6 4.7 ## 3 0.6 0.2 0.1 ## 4 3.4 1.9 4.6 ## 5 0.3 9.9 7.9 ## 6 0.1 0.4 0.3
◯◯◯ Graph4A(time1 と time21の相関図)
まずは、time1とtime21の相関をグラフ化してみます。
gp_a = ggplot(dat_dt, aes(x = time1, y = time21))
# [geom_point] 散布図(同一行のxとyの関係)
gp_a = gp_a + geom_point(size=2,shape=21)
# [geom_smooth] 線形回帰直線
gp_a = gp_a + geom_smooth(method="lm",fomula='y~x')
gp_a = gp_a + scale_x_continuous(limits=c(0,10),breaks=c(0,5,10))
gp_a = gp_a + scale_y_continuous(limits=c(0,10),breaks=c(0,5,10))
# [geom_segment] 任意の直線
# (x,y) to (xend,yend)の座標間に線を引く
gp_a = gp_a + geom_segment(x=0,y=0,xend=10,yend=10,
size=0.5,linetype="dotted")
gp_a = gp_a + labs(x = "Time1", y = "Time1 to Time2")
最初にシャッターを押すまでの待機時間は、次にシャッターを押すまでの時間と まるで相関していないことがわかります。
gp_a = gp_a + theme_minimal() +
ggtitle("Graph4A_Correlation1")
gp_a
◯◯◯ Graph4B(time21 と time32の相関図)
time21とtime32の相関も確認してみましょう。
gp_b = ggplot(dat_dt, aes(x = time21, y = time32))
gp_b = gp_b + geom_point(size=2,shape=21)
gp_b = gp_b + geom_smooth(method="lm",fomula='y~x')
gp_b = gp_b + scale_x_continuous(limits=c(0,10),breaks=c(0,5,10))
gp_b = gp_b + scale_y_continuous(limits=c(0,10),breaks=c(0,5,10))
gp_b = gp_b + geom_segment(x=0,y=0,xend=10,yend=10,
size=0.5,linetype="dotted")
gp_b = gp_b + labs(x = "Time1 to Time2", y = "Time2 to Time3")
シャッター間の時間差にはかなり強い相関が見られることがわかります。
gp_b = gp_b + theme_minimal() +
ggtitle("Graph4B_Correlation2")
gp_a | gp_b
(発展として)、順位相関係数を以下で求めることができます。
cor.test(dat_dt$time1, dat_dt$time21, method = "spearman")
## ## Spearman's rank correlation rho ## ## data: dat_dt$time1 and dat_dt$time21 ## S = 208358, p-value = 0.6126 ## alternative hypothesis: true rho is not equal to 0 ## sample estimates: ## rho ## -0.04974421
cor.test(dat_dt$time21, dat_dt$time32, method = "spearman")
## ## Spearman's rank correlation rho ## ## data: dat_dt$time21 and dat_dt$time32 ## S = 71330, p-value = 1.905e-05 ## alternative hypothesis: true rho is not equal to 0 ## sample estimates: ## rho ## 0.4319928
今回は、single実験のみを扱います。
まずは解析対象となる、座席(seatx,seaty)と計測値(time)のみのデータフレームを作ります。元となる
dat.fsの形式をまずは確認してください。
head(dat.fs)
## # A tibble: 6 × 6 ## seat seat_ix seat_iy time exp order ## <int> <int> <int> <dbl> <chr> <chr> ## 1 192 12 16 2.2 Single 1st ## 2 192 12 16 1.5 Triple 1st ## 3 192 12 16 2.7 Triple 2nd ## 4 192 12 16 5.6 Triple 3rd ## 5 168 12 14 0.8 Single 1st ## 6 168 12 14 4.4 Triple 1st
filter→mutate→selectで、single実験の座席と時間のみを取り出し、これを新たに
dat_seatとします。
dat_seat = dat.fs %>%
filter(exp=="Single") %>%
filter(!is.na(seat)) %>% #seatが不明なものを取り除く。
mutate(seatx = seat_ix, #変数名を変えます。
seaty = seat_iy) %>%
select(seatx,seaty,time)
dat_seat
## # A tibble: 117 × 3 ## seatx seaty time ## <int> <int> <dbl> ## 1 12 16 2.2 ## 2 12 14 0.8 ## 3 11 5 0.6 ## 4 12 5 2.9 ## 5 5 8 6.2 ## 6 12 15 0.1 ## 7 7 19 14.8 ## 8 9 18 0.7 ## 9 10 18 1.9 ## 10 11 16 0.3 ## # ℹ 107 more rows
まずは隣接関係にある者たちのシャッター時間を算出する関数を作成します。 引数は、自分の座席のxy座標です。
timeForward = function(sx,sy){
dat_seat %>%
filter(seatx>=sx-1 & seatx<=sx+1 & seaty==sy-1) %>%
summarize(avg = mean(time)) %>%
pull(avg)
}
timeNeighbor = function(sx,sy){
dat_seat %>%
filter((seatx==sx-1 | seatx==sx+1) & seaty==sy) %>%
summarize(avg = mean(time)) %>%
pull(avg)
}
timeBackward = function(sx,sy){
dat_seat %>%
filter(seatx>=sx-1 & seatx<=sx+1 & seaty==sy+1) %>%
summarize(avg = mean(time)) %>%
pull(avg)
}
これらの関数を使って、各参加者に隣接する3領域のシャッター時間を取得します。この場合、以下のように計算できたらいいのですが、実際にはうまくいきません。それは、上で定義した関数の引数が、長さ1の値しか想定できていないからです。
dat_seat %>%
mutate(
time.f = timeForward(seatx,seaty),
time.n = timeNeighbor(seatx,seaty),
time.b = timeBackward(seatx,seaty)
)
## # A tibble: 117 × 6 ## seatx seaty time time.f time.n time.b ## <int> <int> <dbl> <dbl> <dbl> <dbl> ## 1 12 16 2.2 NaN NaN NaN ## 2 12 14 0.8 NaN NaN NaN ## 3 11 5 0.6 NaN NaN NaN ## 4 12 5 2.9 NaN NaN NaN ## 5 5 8 6.2 NaN NaN NaN ## 6 12 15 0.1 NaN NaN NaN ## 7 7 19 14.8 NaN NaN NaN ## 8 9 18 0.7 NaN NaN NaN ## 9 10 18 1.9 NaN NaN NaN ## 10 11 16 0.3 NaN NaN NaN ## # ℹ 107 more rows
結論から言うと、この問題に対処するためには、
mapply関数を使う必要があります。以下が、その解決方法です。
dat_seat %>%
mutate(
time.f = mapply(timeForward,seatx,seaty),
time.n = mapply(timeNeighbor,seatx,seaty),
time.b = mapply(timeBackward,seatx,seaty)
)
## # A tibble: 117 × 6 ## seatx seaty time time.f time.n time.b ## <int> <int> <dbl> <dbl> <dbl> <dbl> ## 1 12 16 2.2 0.1 0.3 2.2 ## 2 12 14 0.8 19.1 20 0.1 ## 3 11 5 0.6 2 2.9 NaN ## 4 12 5 2.9 2 0.6 NaN ## 5 5 8 6.2 11.6 10.3 10.7 ## 6 12 15 0.1 10.4 NaN 1.25 ## 7 7 19 14.8 5.4 16 0.4 ## 8 9 18 0.7 7.4 1.9 NaN ## 9 10 18 1.9 7.4 0.7 11 ## 10 11 16 0.3 5.2 2.2 2.2 ## # ℹ 107 more rows
ここで、一旦、
mapply関数について解説します。
mapply関数
mapply() は、複数のベクトルを並行して関数に適用し、それぞれの要素ペアに対して処理を繰り返すベースRのベクトル化関数です。ここでは典型的な使用例を確認します。
ifを含むようなある種の関数は、引数に(長さ1より大きな)ベクトルを持てません。 そのため、以下の計算はエラーとなります。
plus = function(x, y){
if(x+y<25) "A" else "B"
}
plus(1:10,11:20)
## Error in if (x + y < 25) "A" else "B": the condition has length > 1
mapply関数を使うと、長さ1の引数を前提とした関数に対しても、 繰り返し同じ関数を適用し、計算結果をベクトルとしてまとめられます。
# 以下の計算結果は
## c(plus(1,11),plus(2,12),plus(3,13),...)と同じ。
mapply(plus, 1:10, 11:20)
## [1] "A" "A" "A" "A" "A" "A" "A" "B" "B" "B"
以上の結果は、
plus(x,y)をデータフレームの各行に対して適用した場合も同様です。 ここでは、seatxとseatyを加算したseatxyを新たに作成したいとします。mutateの中で、そのままplus関数を適用するとエラーになりますが、mapplyを使うことで、ベクトル化することができます。
dat_seat %>%
mutate(seatxy = plus(seatx,seaty))
## Error in `mutate()`:
## ℹ In argument: `seatxy = plus(seatx, seaty)`.
## Caused by error in `if (x + y < 25) ...`:
## ! the condition has length > 1
dat_seat %>%
mutate(seatxy = mapply(plus,seatx,seaty))
## # A tibble: 117 × 4 ## seatx seaty time seatxy ## <int> <int> <dbl> <chr> ## 1 12 16 2.2 B ## 2 12 14 0.8 B ## 3 11 5 0.6 A ## 4 12 5 2.9 A ## 5 5 8 6.2 A ## 6 12 15 0.1 B ## 7 7 19 14.8 B ## 8 9 18 0.7 B ## 9 10 18 1.9 B ## 10 11 16 0.3 B ## # ℹ 107 more rows
mapply関数と同じことは、 rowwise関数で行単位でグループ化しても実現できます。
dat_seat %>%
rowwise() %>%
mutate(seatxy = plus(seatx,seaty))
## # A tibble: 117 × 4 ## # Rowwise: ## seatx seaty time seatxy ## <int> <int> <dbl> <chr> ## 1 12 16 2.2 B ## 2 12 14 0.8 B ## 3 11 5 0.6 A ## 4 12 5 2.9 A ## 5 5 8 6.2 A ## 6 12 15 0.1 B ## 7 7 19 14.8 B ## 8 9 18 0.7 B ## 9 10 18 1.9 B ## 10 11 16 0.3 B ## # ℹ 107 more rows
このmapply関数を使うことで、簡単に既存のデータフレームを拡張して、 各席に対する、前方・近傍・後方の平均値を算出することができます。
dat_seat = dat_seat %>%
mutate(
time.f = mapply(timeForward,seatx,seaty),
time.n = mapply(timeNeighbor,seatx,seaty),
time.b = mapply(timeBackward,seatx,seaty)
)
dat_seat
## # A tibble: 117 × 6 ## seatx seaty time time.f time.n time.b ## <int> <int> <dbl> <dbl> <dbl> <dbl> ## 1 12 16 2.2 0.1 0.3 2.2 ## 2 12 14 0.8 19.1 20 0.1 ## 3 11 5 0.6 2 2.9 NaN ## 4 12 5 2.9 2 0.6 NaN ## 5 5 8 6.2 11.6 10.3 10.7 ## 6 12 15 0.1 10.4 NaN 1.25 ## 7 7 19 14.8 5.4 16 0.4 ## 8 9 18 0.7 7.4 1.9 NaN ## 9 10 18 1.9 7.4 0.7 11 ## 10 11 16 0.3 5.2 2.2 2.2 ## # ℹ 107 more rows
◯◯◯ Graph5A(棒グラフ:LATE vs EARLY)
最後に3区間(前方・隣接・後方)の撮影時間と比べて 早くシャッターを押したか(EARLY)か遅く押したか(LATE)について、 mutate関数で、新たに属性を追加し並べていきます。
dat_seat = dat_seat %>%
mutate(order.f = if_else(time - time.f>=0,"LATE","EARLY"),
order.n = if_else(time - time.n>=0,"LATE","EARLY"),
order.b = if_else(time - time.b>=0,"LATE","EARLY"))
dat_seat[1:20,]
## # A tibble: 20 × 9 ## seatx seaty time time.f time.n time.b order.f order.n order.b ## <int> <int> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr> ## 1 12 16 2.2 0.1 0.3 2.2 LATE LATE LATE ## 2 12 14 0.8 19.1 20 0.1 EARLY EARLY LATE ## 3 11 5 0.6 2 2.9 NaN EARLY EARLY <NA> ## 4 12 5 2.9 2 0.6 NaN LATE LATE <NA> ## 5 5 8 6.2 11.6 10.3 10.7 EARLY EARLY EARLY ## 6 12 15 0.1 10.4 NaN 1.25 EARLY <NA> EARLY ## 7 7 19 14.8 5.4 16 0.4 LATE EARLY LATE ## 8 9 18 0.7 7.4 1.9 NaN EARLY EARLY <NA> ## 9 10 18 1.9 7.4 0.7 11 EARLY LATE EARLY ## 10 11 16 0.3 5.2 2.2 2.2 EARLY EARLY EARLY ## 11 9 17 7.4 5.2 NaN 1.3 LATE <NA> LATE ## 12 9 16 9.8 5.75 0.6 7.4 LATE LATE LATE ## 13 7 6 2.5 10 6.1 0.7 EARLY EARLY LATE ## 14 8 6 6.1 10 2.5 2.2 EARLY LATE LATE ## 15 10 7 3 NaN 5.1 0.8 <NA> EARLY LATE ## 16 9 7 3.7 6.1 1.85 1.73 EARLY LATE LATE ## 17 12 13 19.1 1.9 NaN 10.4 LATE <NA> LATE ## 18 12 17 2.2 1.25 NaN NaN LATE <NA> <NA> ## 19 9 11 9.7 3.2 16.8 9.3 LATE EARLY LATE ## 20 10 11 16.8 5.8 5.6 5.6 LATE LATE LATE
以下のように解析することができます。
# 前方との比較:ほぼ変わらない
table(dat_seat$order.f)
## ## EARLY LATE ## 49 46
# 両隣との比較:ほぼ変わらない
table(dat_seat$order.n)
## ## EARLY LATE ## 41 42
# 後方との比較:後方よりも早く押す傾向
table(dat_seat$order.b)
## ## EARLY LATE ## 46 51
dat_dir = dat_seat %>%
pivot_longer(
cols = c(order.f,order.n,order.b),
names_to = "direction",
values_to = "dorder"
) %>%
filter(!is.na(dorder)) %>%
mutate(
direction = case_when(
direction=="order.f" ~ "FORWARD",
direction=="order.n" ~ "NEIGHBOR",
direction=="order.b" ~ "BACKWARD"
)
)
dat_dir
## # A tibble: 275 × 8 ## seatx seaty time time.f time.n time.b direction dorder ## <int> <int> <dbl> <dbl> <dbl> <dbl> <chr> <chr> ## 1 12 16 2.2 0.1 0.3 2.2 FORWARD LATE ## 2 12 16 2.2 0.1 0.3 2.2 NEIGHBOR LATE ## 3 12 16 2.2 0.1 0.3 2.2 BACKWARD LATE ## 4 12 14 0.8 19.1 20 0.1 FORWARD EARLY ## 5 12 14 0.8 19.1 20 0.1 NEIGHBOR EARLY ## 6 12 14 0.8 19.1 20 0.1 BACKWARD LATE ## 7 11 5 0.6 2 2.9 NaN FORWARD EARLY ## 8 11 5 0.6 2 2.9 NaN NEIGHBOR EARLY ## 9 12 5 2.9 2 0.6 NaN FORWARD LATE ## 10 12 5 2.9 2 0.6 NaN NEIGHBOR LATE ## # ℹ 265 more rows
## x軸をdirectionに、塗りつぶしをdorderに設定
gp_a = ggplot(dat_dir,aes(x=direction, fill=dorder))
gp_a = gp_a + geom_bar(position=position_dodge(width=0.9), colour="black")
gp_a = gp_a + scale_x_discrete(limits=c("FORWARD","NEIGHBOR","BACKWARD"))
gp_a = gp_a + scale_y_continuous(limits=c(0,60))
gp_a = gp_a + scale_fill_brewer(palette="Set2", limits=c("EARLY","LATE"))
gp_a = gp_a + labs(x = "DIRECTION", y = "COUNT",fill="ORDER")
gp_a + theme_minimal() +
ggtitle("Graph5A_DirectionEffect")
カイ二乗分布
統計で頻度の偏りの検定を行うのには、カイ二乗分布を使うことができます。 関数
chisq.testを使うと、以下のように一気に計算できます。
chisq.test(dat_dir$direction, dat_dir$dorder, correct=F)
## ## Pearson's Chi-squared test ## ## data: dat_dir$direction and dat_dir$dorder ## X-squared = 0.33183, df = 2, p-value = 0.8471
結果、
p>0.05で、方向による影響は統計的には認められないという結論となります。
◯◯◯ Graph5B(ボックスプロット)
新たに、隣接領域のシャッター時間と自分自身のシャッター時間の差分を
dif変数として列に追加します。difが正のとき「自分より遅いシャッター」、difが負のとき「自分より早くシャッター」を押していることになります。
dat_dir = dat_dir %>%
mutate(dif = case_when(
direction=="FORWARD" ~ time.f - time,
direction=="NEIGHBOR" ~ time.n - time,
direction=="BACKWARD" ~ time.b - time)
)
# グラフには使いませんが、平均値と中央値を比較します。
dat_dir %>%
group_by(direction) %>%
summarize(
avg = mean(dif), #平均値
med = median(dif) #中央値
)
## # A tibble: 3 × 3 ## direction avg med ## <chr> <dbl> <dbl> ## 1 BACKWARD -0.124 0 ## 2 FORWARD 0.210 0.2 ## 3 NEIGHBOR 0.0711 -0.4
# [ggplot]
## x軸をdirection、y軸にdif, 塗りつぶしをdirectionに設定
gp_b = ggplot(dat_dir,aes(x=direction,y=dif,fill=direction))
# [geom_boxplot]ボックスプロットによる描画
gp_b = gp_b + geom_boxplot()
gp_b = gp_b + scale_x_discrete(limits=c("FORWARD","NEIGHBOR","BACKWARD"))
gp_b = gp_b + scale_y_continuous(limits=c(-6,6),breaks=2*(-3:3))
# Bluesパレットを使って、fillを描画。
gp_b = gp_b + scale_fill_brewer(palette = "Blues",
limits=c("FORWARD","NEIGHBOR","BACKWARD"))
gp_b = gp_b + labs(x = "DIRECTION", y = "Time[s]",fill="DIRECTION")
gp_b + theme_minimal() +
ggtitle("Graph5B_DirectionEffect")
◯◯◯ Graph5C(ヒストグラム)
# 最初に、directionの並べ順を決めます(facet_gridの並べ順をFNBの順にします)
dat_dir$direction =
factor(dat_dir$direction,
levels = c("FORWARD","NEIGHBOR","BACKWARD"))
# [ggplot]
## x軸をdif、 塗りつぶしをdirectionに設定
gp_c = ggplot(dat_dir,aes(x=dif,fill=direction))
# x=0で縦の点線を引きます。
gp_c = gp_c + geom_vline(xintercept = 0, linetype = "solid",
size=0.4,colour="black")
# [geom_histogram]ヒストグラムによる描画
gp_c = gp_c + geom_histogram(binwidth=0.3,colour="black")
gp_c = gp_c + scale_x_continuous()
gp_c = gp_c + scale_y_continuous()
# Bluesパレットを使って、fillを描画。
gp_c = gp_c + scale_fill_brewer(palette = "Blues")
gp_c = gp_c + labs(x = "TIME DIFFERENCE", y = "COUNT",fill="DIRECTION")
gp_c = gp_c + facet_grid(direction ~ .)
gp_c + theme_minimal() +
ggtitle("Graph5C_DirectionEffect")
Graph6では、参加者間の近さ(近接性)が、シャッターを押すタイミングの近さ(同期性)に与える影響を検証します。素朴に考えると、参加者間の距離が近いほど、シャッタータイミングが近いと考えられます(以下ではこれを「近傍同期効果」と呼びます)。今回は、その仮説を検証してみましょう。
まずは、single実験の
seatxとseaty、timeを取り出します(seatが不明なものは除外)
dat_seat = dat.fs %>%
filter(exp=="Single") %>%
filter(!is.na(seat)) %>% #seatが不明なものを取り除く。
mutate(seatx = seat_ix, #変数名を変えます。
seaty = seat_iy) %>%
select(seatx,seaty,time)
dat_seat
## # A tibble: 117 × 3 ## seatx seaty time ## <int> <int> <dbl> ## 1 12 16 2.2 ## 2 12 14 0.8 ## 3 11 5 0.6 ## 4 12 5 2.9 ## 5 5 8 6.2 ## 6 12 15 0.1 ## 7 7 19 14.8 ## 8 9 18 0.7 ## 9 10 18 1.9 ## 10 11 16 0.3 ## # ℹ 107 more rows
◯◯◯ 特定の参加者周辺の近傍同期効果の検証
以下では、特定の座席[6,16]の参加者に関して、近傍同期効果を検証します。 データフレームより、座席[6,16]の参加者のシャッター時間は1.2秒であることがわかります。
dat616 = dat_seat %>% filter(seatx==6 & seaty==16)
dat616
## # A tibble: 1 × 3 ## seatx seaty time ## <int> <int> <dbl> ## 1 6 16 1.2
近傍同期効果を具体的に検証するまえに、簡単に近傍同期に関する指標を以下のように定義します。
以上を踏まえて、座席[6,16]の参加者に関して、NEIGHBORとREMOTEの集団の同期率を比較します。統計的には、NEIGHBORの同期率が有意に高ければ、近傍同期効果は支持されることになります。
# 座席の変数の代入
sx = dat616$seatx;
sy = dat616$seaty;
t = dat616$time
# シャッター時間の差が1秒未満のペアを「SYNC」とし、
# NEIGHBORグループ(周囲2席以内)とREMOTEグループ(それ以外)でSYNCの割合を比較
dat_sync = dat_seat %>%
filter(!(seatx==sx & seaty ==sy)) %>% #同じ席の行(自分自身)を除く
mutate(
# 2席分以内の距離を"NEIGHBOR"、それ以外を"REMOTE"
neighbor = if_else(
(seatx>=sx-2 & seatx<=sx+2) &
(seaty>=sy-2 & seaty<=sy+2),"NEIGHBOR","REMOTE"),
# 1秒未満の距離を"SYNC"、それ以外を"ASYNC"
sync = if_else(
abs(time-t)<1.0, "SYNC", "ASYNC")
)
# 71〜80行目を確認。
dat_sync[71:80,]
## # A tibble: 10 × 5 ## seatx seaty time neighbor sync ## <int> <int> <dbl> <chr> <chr> ## 1 5 15 0.3 NEIGHBOR SYNC ## 2 6 14 2.6 NEIGHBOR ASYNC ## 3 4 6 9.8 REMOTE ASYNC ## 4 4 19 9.5 REMOTE ASYNC ## 5 4 5 1.2 REMOTE SYNC ## 6 7 5 10 REMOTE ASYNC ## 7 5 14 10.5 NEIGHBOR ASYNC ## 8 11 12 1.9 REMOTE SYNC ## 9 10 12 9.3 REMOTE ASYNC ## 10 8 10 0.6 REMOTE SYNC
neighborとsyncをグループ化して、2x2の条件ごとに、生起数をカウントします。
dat_sync %>%
group_by(neighbor,sync) %>% #neighbor、syncでグループ分け
count(neighbor, sync, name = "n") #summarizeでn()するのと同様
## # A tibble: 4 × 3 ## # Groups: neighbor, sync [4] ## neighbor sync n ## <chr> <chr> <int> ## 1 NEIGHBOR ASYNC 6 ## 2 NEIGHBOR SYNC 7 ## 3 REMOTE ASYNC 67 ## 4 REMOTE SYNC 36
この結果は、座席[6,16]の参加者の近傍グループ(13人)、非近傍グループ(103人)のうち、シャッターのタイミングがSYNCした人は、7人(近傍)および36人 (非近傍)であり、近傍グループの方がシャッターが同調した人が多い(53.8% v.s. 40.0%)ことを示しています。
この結果は、近傍同期効果を支持するものです。
◯◯◯ 全ての参加者に関する近傍同期効果の検証
座席[6,16]に関して検討した解析を、全ての座席について調べてみましょう。 まずは先に座席[6,16]に対して行った処理を、一般の座席に適用させるかたちで関数化します。
compareSyncrate = function(sx,sy,t){
dat_sync = dat_seat %>%
filter(!(seatx==sx & seaty ==sy)) %>% #同じ席の行(自分自身)を除く
mutate(
# 2席分以内の距離を"NEIGHBOR"、それ以外を"REMOTE"
neighbor = if_else(
(seatx>=sx-2 & seatx<=sx+2) &
(seaty>=sy-2 & seaty<=sy+2),"NEIGHBOR","REMOTE"),
# シャッター時間が1秒以内を"SYNC"、それ以外を"ASYNC"
sync = if_else(
abs(time-t)<1.0, "SYNC", "ASYNC")
) %>%
group_by(neighbor,sync) %>%
count(neighbor, sync, name = "n") %>%
ungroup() %>% #以下は、いずれかの条件でn=0のときのための対処
complete( #存在しない組み合わせについても、完全にリストを作成する
neighbor = c("NEIGHBOR","REMOTE"),
sync = c("SYNC","ASYNC"),
fill = list(n = 0) #存在しないcountを0で埋める
)
# いずれかの総数が0の場合、NAを返す。(rateの計算で分母が0になるため)
if((dat_sync$n[1] + dat_sync$n[2] == 0) |
(dat_sync$n[3] + dat_sync$n[4] == 0)){
return(NA)
}
ns = dat_sync$n #総数ベクトル(2x2)
ratio1 = ns[2] / (ns[1] + ns[2]) #NEIGHBOER集団のSYNC率
ratio2 = ns[4] / (ns[3] + ns[4]) #REMOTE集団のSYNC率
return(c(ratio1,ratio2)) #長さ2のベクトルとして返します。
}
それでは、実際に座席[6,16]に対する関数の挙動を確認しましょう(正しく計算できていることがわかります)。
dat616 = dat_seat %>% filter(seatx==6 & seaty==16)
compareSyncrate(dat616$seatx,dat616$seaty,dat616$time)
## [1] 0.5384615 0.3495146
ひきつづき、
mutateでの使用を想定して、長さ2のベクトルを返すcompareSyncrateを、長さ1の値が返ってくるように関数を少し拡張します。具体的には、新たに引数MODEを立てて、近傍グループの同期率と非近傍グループの同期率を個別に算出できるようにします。
compareSyncrateMode = function(sx,sy,t,MODE){
if(MODE=="NEIGHBOR"){
return(compareSyncrate(sx,sy,t)[1])
}
if(MODE=="REMOTE"){
return(compareSyncrate(sx,sy,t)[2])
}
}
mutateを使って、新たな変数にNEIGHBOR集団とREMOTE集団の同期率を計算していきます。この中で、mapplyを使って、引数をベクトル対応にしている点に注意してください。
dat_seat = dat_seat %>% mutate(
ratioNeighbor = mapply(compareSyncrateMode, seatx,seaty,time,"NEIGHBOR"),
ratioRemote = mapply(compareSyncrateMode,seatx,seaty,time,"REMOTE")
) %>%
filter(!is.na(ratioNeighbor)) #同期率がNAのものを削除
dat_seat
## # A tibble: 116 × 5 ## seatx seaty time ratioNeighbor ratioRemote ## <int> <int> <dbl> <dbl> <dbl> ## 1 12 16 2.2 0.25 0.176 ## 2 12 14 0.8 0.333 0.364 ## 3 11 5 0.6 0 0.373 ## 4 12 5 2.9 0.6 0.144 ## 5 5 8 6.2 0.273 0.0571 ## 6 12 15 0.1 0.375 0.259 ## 7 7 19 14.8 0 0.00952 ## 8 9 18 0.7 0.3 0.358 ## 9 10 18 1.9 0.222 0.224 ## 10 11 16 0.3 0.364 0.314 ## # ℹ 106 more rows
グラフにする前に、NEIGHBORとREMOTEのそれぞれの同期率の平均を計算してみましょう。 平均値では、REMOTE集団の19.6%に対して、NEIGHBOR集団は22.5%で、近傍集団では、3%ほど同期率が高いことがわかります。
dat_seat %>%
summarize(
avgNeighbor = mean(ratioNeighbor),
avgRemote = mean(ratioRemote),
)
## # A tibble: 1 × 2 ## avgNeighbor avgRemote ## <dbl> <dbl> ## 1 0.225 0.196
(発展)「対応のあるt検定」を行うことで、この差が意味のあるものかどうかを検定することができます。検定の結果、
p<0.05で、有意に、近傍集団の同期率が高いことが示されました。この結果もまた、近傍同期効果を強く支持するものです。
t.test(dat_seat$ratioNeighbor,dat_seat$ratioRemote, paired = TRUE)
## ## Paired t-test ## ## data: dat_seat$ratioNeighbor and dat_seat$ratioRemote ## t = 2.109, df = 115, p-value = 0.03712 ## alternative hypothesis: true mean difference is not equal to 0 ## 95 percent confidence interval: ## 0.001791265 0.057152802 ## sample estimates: ## mean difference ## 0.02947203
◯◯◯ Graph6A(NEIGHBOR vs REMOTEの同期率の比較)
# 二つの列変数を行に展開
dat_seat2 = dat_seat %>%
pivot_longer(
c(ratioNeighbor,ratioRemote),
names_to = "group",
values_to = "ratio"
)
# [ggplot]
## x軸をdirection、y軸にdif, 塗りつぶしをdirectionに設定
gp_a = ggplot(dat_seat2,aes(x=group,y=ratio,fill=group))
# [geom_boxplot]ボックスプロットによる描画
gp_a = gp_a + geom_boxplot()
# X軸のラベルの書き換え
gp_a = gp_a + scale_x_discrete(
labels = c("ratioNeighbor"="NEIGHBOR", "ratioRemote"="REMOTE")
)
gp_a = gp_a + scale_y_continuous()
# Bluesパレットを使って、fillを描画。
gp_a = gp_a + scale_fill_brewer(
palette = "Blues",
labels = c("ratioNeighbor"="NEIGHBOR", "ratioRemote"="REMOTE")
)
gp_a = gp_a + labs(x = "TYPE", y = "Ratio",fill="TYPE")
gp_a + theme_minimal() +
ggtitle("Graph6A_NeighborSyncEffect")
◯◯◯ Graph6B(NEIGHBOR vs REMOTEの同期率の比較)
Graph6Aは、SYNC率を1秒未満と定義しましたが、これをt秒未満と一般化します。このSYNC率の窓を操作することで、距離による同期効果が、どのような時間スケールで生じるかどうかを検証することができます。
#第4引数に時間窓を追加
compareSyncrateWindow = function(sx,sy,t,t_window){
dat_sync = dat_seat %>%
filter(!(seatx==sx & seaty ==sy)) %>% #同じ席の行(自分自身)を除く
mutate(
# 2席分以内の距離を"NEIGHBOR"、それ以外を"REMOTE"
neighbor = if_else(
(seatx>=sx-2 & seatx<=sx+2) &
(seaty>=sy-2 & seaty<=sy+2),"NEIGHBOR","REMOTE"),
# シャッター時間が1秒以内を"SYNC"、それ以外を"ASYNC"
sync = if_else(
abs(time-t)<t_window, "SYNC", "ASYNC")
) %>%
group_by(neighbor,sync) %>%
count(neighbor, sync, name = "n") %>%
ungroup() %>% #以下は、いずれかの条件でn=0のときのための対処
complete( #存在しない組み合わせについても、完全にリストを作成する
neighbor = c("NEIGHBOR","REMOTE"),
sync = c("SYNC","ASYNC"),
fill = list(n = 0) #存在しないcountを0で埋める
)
# いずれかの総数が0の場合、NAを返す。(rateの計算で分母が0になるため)
if((dat_sync$n[1] + dat_sync$n[2] == 0) |
(dat_sync$n[3] + dat_sync$n[4] == 0)){
return(NA)
}
ns = dat_sync$n #総数ベクトル(2x2)
ratio1 = ns[2] / (ns[1] + ns[2]) #NEIGHBOER集団のSYNC率
ratio2 = ns[4] / (ns[3] + ns[4]) #REMOTE集団のSYNC率
return(c(ratio1,ratio2)) #長さ2のベクトルとして返します。
}
# 長さ1の値を返すように関数を分解
compareSyncrateWindowMode = function(sx,sy,t,t_window,MODE){
if(MODE=="NEIGHBOR"){
return(compareSyncrateWindow(sx,sy,t,t_window)[1])
}
if(MODE=="REMOTE"){
return(compareSyncrateWindow(sx,sy,t,t_window)[2])
}
}
dat_seatを初期化します。
dat_seat = dat.fs %>%
filter(exp=="Single") %>%
filter(!is.na(seat)) %>% #seatが不明なものを取り除く。
mutate(seatx = seat_ix, #変数名を変えます。
seaty = seat_iy) %>%
select(seatx,seaty,time)
dat_seat
## # A tibble: 117 × 3 ## seatx seaty time ## <int> <int> <dbl> ## 1 12 16 2.2 ## 2 12 14 0.8 ## 3 11 5 0.6 ## 4 12 5 2.9 ## 5 5 8 6.2 ## 6 12 15 0.1 ## 7 7 19 14.8 ## 8 9 18 0.7 ## 9 10 18 1.9 ## 10 11 16 0.3 ## # ℹ 107 more rows
getSyncWindow = function(t_window){
dat_seat %>%
mutate(
ratioNeighbor = mapply(
compareSyncrateWindowMode, seatx,seaty,time,t_window,"NEIGHBOR"),
ratioRemote = mapply(
compareSyncrateWindowMode,seatx,seaty,time,t_window,"REMOTE")
) %>%
filter(!is.na(ratioNeighbor)) %>%
summarize(
avgNeighbor = mean(ratioNeighbor),
avgRemote = mean(ratioRemote)
)
}
getSyncWindowMode = function(t_window,MODE){
avgs = getSyncWindow(t_window)
if(MODE=="NEIGHBOR"){
return(avgs$avgNeighbor)
}else{
return(avgs$avgRemote)
}
}
# t_window=1.0のときの結果を確認(正しいですね)
getSyncWindowMode(1.0,"NEIGHBOR"); getSyncWindowMode(1.0,"REMOTE")
## [1] 0.2253262
## [1] 0.1958542
#mapply(getSyncWindow,0.2 * (1:25))
dat_wi = tibble(
#t_window = 0.1 * (0:15)
t_window = 0.2 * (0:10)
#t_window = 1.0
)
dat_wi = dat_wi %>%
mutate(ratioNeighbor =
mapply(getSyncWindowMode,t_window,"NEIGHBOR"),
ratioRemote =
mapply(getSyncWindowMode,t_window,"REMOTE")
) %>%
pivot_longer(
cols = c(ratioNeighbor,ratioRemote),
names_to = "group",
values_to = "ratio"
)
dat_wi
## # A tibble: 22 × 3 ## t_window group ratio ## <dbl> <chr> <dbl> ## 1 0 ratioNeighbor 0 ## 2 0 ratioRemote 0 ## 3 0.2 ratioNeighbor 0.0491 ## 4 0.2 ratioRemote 0.0562 ## 5 0.4 ratioNeighbor 0.102 ## 6 0.4 ratioRemote 0.103 ## 7 0.6 ratioNeighbor 0.160 ## 8 0.6 ratioRemote 0.143 ## 9 0.8 ratioNeighbor 0.204 ## 10 0.8 ratioRemote 0.174 ## # ℹ 12 more rows
gp_b = ggplot(dat_wi,aes(x=t_window,y=ratio,colour=group))
gp_b = gp_b + geom_line(size=0.5) + geom_point(size=3)
gp_b = gp_b + scale_colour_brewer(
palette ="Set2",
labels = c("ratioNeighbor"="NEIGHBOR", "ratioRemote"="REMOTE")
)
gp_b = gp_b + scale_x_continuous(limits=c(0,2.0))
gp_b = gp_b + scale_y_continuous(limits=c(0,0.4))
gp_b = gp_b + labs(x="TIME WINDOW[s]",y="SYNC RATE",colour="GROUP TYPE")
gp_b + theme_minimal() +
ggtitle("Graph6B_NeighborSyncEffect")
グラフから、近傍同期効果が現れるのは、およそ0.5秒以降であることがわかります。ちなみに、実際に検定をしてみるとNEIGHBORとREMOTEとの間に有意な差があるのは、TIME WINDOWが1.0秒付近の限られた区間のみとなります。つまり、距離的に近いもの同士が影響を与え合う時間窓は、およそ1秒程度であることが示唆されます。
ApMedia06_Work
[10times フリーシャッター実験]
20秒の間にシャッターを10回押してください実験の結果(24年7月11日)が以下から入手できます。
url = "https://lab.kenrikodaka.com/_download/csv/freeshutter_240711.csv"
dat_240711 = read.csv(url)
参加者は109人
dim(dat_240711)
## [1] 109 12
最初と最後の6行
dat_240711[c(1:6,104:109),]
## seatx seaty t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 ## 1 9 2 0.3 1.1 1.4 1.6 1.7 2.1 2.6 2.7 3.6 5.1 ## 2 12 9 0.4 1.2 1.6 2.3 3.0 13.3 14.0 15.0 16.2 19.0 ## 3 4 4 1.2 2.9 4.9 6.8 9.0 10.7 12.5 15.0 17.2 19.2 ## 4 10 2 6.0 6.5 6.6 6.9 7.2 7.5 7.7 7.9 8.2 8.4 ## 5 6 14 1.3 2.7 3.2 8.2 8.9 11.2 13.3 17.3 17.7 18.0 ## 6 9 6 1.1 1.5 2.1 2.4 3.5 4.0 5.4 7.7 9.8 11.1 ## 104 9 1 1.1 2.4 3.8 5.5 6.9 8.3 9.2 10.8 12.1 NA ## 105 12 4 1.2 2.4 4.2 5.2 6.2 7.4 8.2 9.4 10.5 11.5 ## 106 12 15 2.3 3.4 4.5 6.7 10.3 11.8 16.9 18.9 20.1 21.0 ## 107 10 15 0.9 1.6 2.2 3.5 4.6 5.2 5.9 6.5 8.1 9.9 ## 108 12 15 2.0 3.6 4.6 5.7 10.2 11.5 16.7 17.9 18.7 19.9 ## 109 11 2 2.9 3.1 3.6 4.0 5.2 14.2 15.7 16.0 16.7 18.8