最初に入れておく(RstudioでRmdファイルを新規作成したらついてくる)
knitr::opts_chunk$set(echo = TRUE)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
kokuA.norm <-data.frame(as.integer(rnorm(1200, mean = 10, sd = 2)))
colnames(kokuA.norm) <- c("kokuA")
kokuA.norm$kokuA <- as.numeric(kokuA.norm$kokuA)
kokuA.norm.0.14 <- kokuA.norm %>%
dplyr::filter(kokuA >= 1 & kokuA <= 14)
kokuA.uni <- data.frame(
as.integer(runif(2000 - nrow(kokuA.norm.0.14), min = 0, max = 14))
)
colnames(kokuA.uni) <- c("kokuA")
kokuA.moto <- rbind(kokuA.norm.0.14, kokuA.uni)
kokuA.spl <- sample(kokuA.moto, length(kokuA.moto))
kokuB.norm <-data.frame(as.integer(rnorm(1200, mean = 7, sd = 2)))
colnames(kokuB.norm) <- c("kokuB")
kokuB.norm$kokuB <- as.numeric(kokuB.norm$kokuB)
kokuB.norm.0.9 <- kokuB.norm %>%
dplyr::filter(kokuB >= 1 & kokuB <= 9)
kokuB.uni <- data.frame(
as.integer(runif(2000 - nrow(kokuB.norm.0.9), min = 0, max = 9))
)
colnames(kokuB.uni) <- c("kokuB")
kokuB.moto <- rbind(kokuB.norm.0.9, kokuB.uni)
kokuB.spl <- sample(kokuB.moto, length(kokuB.moto))
sansA.norm <-data.frame(as.integer(rnorm(1200, mean = 12, sd = 2)))
colnames(sansA.norm) <- c("sansA")
sansA.norm$sansA <- as.numeric(sansA.norm$sansA)
sansA.norm.0.16 <- sansA.norm %>%
dplyr::filter(sansA >= 1 & sansA <= 16)
sansA.uni <- data.frame(
as.integer(runif(2000 - nrow(sansA.norm.0.16), min = 0, max = 16))
)
colnames(sansA.uni) <- c("sansA")
sansA.moto <- rbind(sansA.norm.0.16, sansA.uni)
sansA.spl <- sample(sansA.moto, length(sansA.moto))
sansB.norm <-data.frame(as.integer(rnorm(1200, mean = 9, sd = 2)))
colnames(sansB.norm) <- c("sansB")
sansB.norm$sansB <- as.numeric(sansB.norm$sansB)
sansB.norm.0.13 <- sansB.norm %>%
dplyr::filter(sansB >= 1 & sansB <= 13)
sansB.uni <- data.frame(
as.integer(runif(2000 - nrow(sansB.norm.0.13), min = 0, max = 13))
)
colnames(sansB.uni) <- c("sansB")
sansB.moto <- rbind(sansB.norm.0.13, sansB.uni)
sansB.spl <- sample(sansB.moto, length(sansB.moto))
id <- data.frame(c(1:2000)); colnames(id) <- c("id")
ach.spl <- cbind(id, kokuA.spl, kokuB.spl, sansA.spl, sansB.spl)
setwd("/Users/Shared/R")
#write.csv(ach.spl, "ach.csv", row.names = F)
id <- data.frame(c(1:300))
schl <- data.frame(c(rep(c(10001:10015), each = 10),
rep(c(20001:20015), each = 10)))
age <- data.frame(as.integer(runif(300 , min = 22, max = 54)))
gender <- data.frame(as.integer(runif(300 , min = 1, max = 3)))
# 整数の場合1以上3未満として範囲を1から2にする
q01 <- data.frame(as.integer(runif(300 , min = 1, max = 4)))
q02 <- data.frame(as.integer(runif(300 , min = 1, max = 4)))
q03 <- data.frame(as.integer(runif(300 , min = 1, max = 4)))
q04 <- data.frame(as.integer(runif(300 , min = 1, max = 4)))
ques <- cbind(id, schl, age, gender, q01, q02, q03, q04)
colnames(ques) <- c("id", "schl", "age", "gender",
"q01", "q02", "q03", "q04")
setwd("/Users/Shared/R")
#write.csv(ques, "ques.csv", row.names = F)
csvファイルのあるディレクトリを指定し,R上での任意のデータ名を付けて,csvファイルを読み込む。
setwd("/Users/Shared/R")
# Windowsの場合には,おそらくsetwd("c:\Users\Shared\R")のような感じと思われる
ach <- read.csv("ach.csv")
# データ名,変数名は数字では始めない ""で囲み,拡張子も付ける
ques <- read.csv("ques.csv")
日本語が入っているデータの場合,文字化けが生じることがある。その場合には文字コードを指定する。データに日本語は入れない方が無難
ach <- read.csv("ach.csv", encoding="Shift_JIS")
このようにデータが読み込まれている。head
で最初の数行を出力する。
head(ach)
## id kokuA kokuB sansA sansB
## 1 1 10 6 8 7
## 2 2 7 9 11 13
## 3 3 8 7 12 12
## 4 4 9 3 14 10
## 5 5 9 9 14 10
## 6 6 11 5 12 8
head(ques)
## id schl age gender q01 q02 q03 q04
## 1 1 10001 24 1 1 3 2 1
## 2 2 10001 44 1 1 3 2 1
## 3 3 10001 25 2 3 3 1 1
## 4 4 10001 27 1 3 1 1 2
## 5 5 10001 34 1 1 3 2 1
## 6 6 10001 48 1 3 1 2 3
素点ではなく通過率を扱いたいことがよくある(と思われる)。そうしたければ,変数を計算して追加する。たとえば,以下ではach
というデータのkokuA_prop
に,ach
というデータのkokuA
を14
(テスト冊子の全項目数)で割ったものを代入している。
ach$kokuA_prop <- ach$kokuA / 14
ach$kokuB_prop <- ach$kokuB / 9
ach$sansA_prop <- ach$sansA / 16
ach$sansB_prop <- ach$sansB / 13
以下のように列が追加されている
head(ach)
## id kokuA kokuB sansA sansB kokuA_prop kokuB_prop sansA_prop sansB_prop
## 1 1 10 6 8 7 0.7142857 0.6666667 0.5000 0.5384615
## 2 2 7 9 11 13 0.5000000 1.0000000 0.6875 1.0000000
## 3 3 8 7 12 12 0.5714286 0.7777778 0.7500 0.9230769
## 4 4 9 3 14 10 0.6428571 0.3333333 0.8750 0.7692308
## 5 5 9 9 14 10 0.6428571 1.0000000 0.8750 0.7692308
## 6 6 11 5 12 8 0.7857143 0.5555556 0.7500 0.6153846
データ全体に対する要約(summary)統計量は以下のようにして求められる
summary(ach)
## id kokuA kokuB sansA
## Min. : 1.0 Min. : 0.000 Min. :0.000 Min. : 0.000
## 1st Qu.: 500.8 1st Qu.: 7.000 1st Qu.:4.000 1st Qu.: 8.000
## Median :1000.5 Median : 9.000 Median :5.500 Median :11.000
## Mean :1000.5 Mean : 8.329 Mean :5.212 Mean : 9.822
## 3rd Qu.:1500.2 3rd Qu.:11.000 3rd Qu.:7.000 3rd Qu.:12.000
## Max. :2000.0 Max. :14.000 Max. :9.000 Max. :16.000
## sansB kokuA_prop kokuB_prop sansA_prop
## Min. : 0.000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.: 6.000 1st Qu.:0.5000 1st Qu.:0.4444 1st Qu.:0.5000
## Median : 8.000 Median :0.6429 Median :0.6111 Median :0.6875
## Mean : 7.436 Mean :0.5950 Mean :0.5791 Mean :0.6139
## 3rd Qu.:10.000 3rd Qu.:0.7857 3rd Qu.:0.7778 3rd Qu.:0.7500
## Max. :13.000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## sansB_prop
## Min. :0.0000
## 1st Qu.:0.4615
## Median :0.6154
## Mean :0.5720
## 3rd Qu.:0.7692
## Max. :1.0000
標準偏差などの記述(descirptive statistics)を知りたい場合には,psychパッケージを使う。ない場合にはinstall.packaes("psych")
としてインストールする。
library(psych)
describe(ach)
## vars n mean sd median trimmed mad min max range skew
## id 1 2000 1000.50 577.49 1000.50 1000.50 741.30 1 2000 1999 0.00
## kokuA 2 2000 8.33 3.29 9.00 8.63 2.97 0 14 14 -0.73
## kokuB 3 2000 5.21 2.41 5.50 5.38 2.22 0 9 9 -0.49
## sansA 4 2000 9.82 3.79 11.00 10.25 2.97 0 16 16 -0.94
## sansB 5 2000 7.44 3.11 8.00 7.71 2.97 0 13 13 -0.68
## kokuA_prop 6 2000 0.59 0.24 0.64 0.62 0.21 0 1 1 -0.73
## kokuB_prop 7 2000 0.58 0.27 0.61 0.60 0.25 0 1 1 -0.49
## sansA_prop 8 2000 0.61 0.24 0.69 0.64 0.19 0 1 1 -0.94
## sansB_prop 9 2000 0.57 0.24 0.62 0.59 0.23 0 1 1 -0.68
## kurtosis se
## id -1.20 12.91
## kokuA -0.04 0.07
## kokuB -0.55 0.05
## sansA 0.23 0.08
## sansB -0.15 0.07
## kokuA_prop -0.04 0.01
## kokuB_prop -0.55 0.01
## sansA_prop 0.23 0.01
## sansB_prop -0.15 0.01
上のように表示されたデータを,いわゆるcut and pasteすれば表を作ることは出来る。しかし,不注意極まりない自分は信用できないので,機械に作業をしていただく。
まず,表の元となるデータを作る。
exam <- c("国語A", "国語B", "算数A", "算数B")
m <- c(mean(ach$kokuA_prop), mean(ach$kokuB_prop),
mean(ach$sansA_prop), mean(ach$sansB_prop))
sd <- c(sd(ach$kokuA_prop), sd(ach$kokuB_prop),
sd(ach$sansA_prop), sd(ach$sansB_prop))
ach.table <- data.frame(cbind(exam, m, sd))
表をきれいに作るパッケージを読み込み,とりあえず出力させてみる。
ach.table$m <- as.numeric(ach.table$m)
ach.table$sd <- as.numeric(ach.table$sd)
library(tidyverse)
library(gt)
ach.table %>% gt()
exam | m | sd |
---|---|---|
国語A | 0.5949643 | 0.2350786 |
国語B | 0.5790556 | 0.2680215 |
算数A | 0.6139062 | 0.2366693 |
算数B | 0.5720385 | 0.2393384 |
わりときれいな表になっている。これを,APA styleに合わせてみる。まず,有効数字を揃える。
tab.1 <- ach.table %>% gt()
tab.2 <- tab.1 %>%
fmt_number(
columns = c("m", "sd"),
decimals = 2)
列名を振り直して,統計量を示すアルファベットを斜体にする
tab.3 <- tab.2 %>%
cols_label(
exam = md("教科"), # md は markdown記法
m = md("*M*"),
sd = md("*SD*")
)
tab.3
教科 | M | SD |
---|---|---|
国語A | 0.59 | 0.24 |
国語B | 0.58 | 0.27 |
算数A | 0.61 | 0.24 |
算数B | 0.57 | 0.24 |
さらにAPAスタイルに合わせる
tab.4 <- tab.3 %>%
tab_options(table.width = pct(25), # 表全体の幅をやや拡げる
table_body.hlines.width = 0, # tableの中の水平線は引かない
column_labels.border.top.width = 2,
column_labels.border.top.color = "black",
column_labels.border.bottom.width = 2,
column_labels.border.bottom.color = "black",
# 最上列上下の水平線は細く黒く
table_body.border.bottom.color = "black" #最下線は黒
) %>%
cols_align(align = "center", columns = c("m", "sd"))
# 2列目移行は中央寄せ(APA 7thから)
tab.4
教科 | M | SD |
---|---|---|
国語A | 0.59 | 0.24 |
国語B | 0.58 | 0.27 |
算数A | 0.61 | 0.24 |
算数B | 0.57 | 0.24 |
もっと工夫すると,次のようなことができる。
# 4教科のテストについて
exam <- c("国語A", "国語B", "算数A", "算数B")
# 素点の平均と標準偏差を求め
m <- c(mean(ach$kokuA), mean(ach$kokuB),
mean(ach$sansA), mean(ach$sansB))
sd <- c(sd(ach$kokuA), sd(ach$kokuB),
sd(ach$sansA), sd(ach$sansB))
# 通過率の平均と標準偏差を求め
m_prop <- c(mean(ach$kokuA_prop), mean(ach$kokuB_prop),
mean(ach$sansA_prop), mean(ach$sansB_prop))
sd_prop <- c(sd(ach$kokuA_prop), sd(ach$kokuB_prop),
sd(ach$sansA_prop), sd(ach$sansB_prop))
# これらをまとめた表にして
ach.tab <- data.frame(cbind(exam, m, sd, m_prop, sd_prop))
# MとSDを数値型にして
ach.tab$m <- as.numeric(ach.tab$m)
ach.tab$sd <- as.numeric(ach.tab$sd)
ach.tab$m_prop <- as.numeric(ach.tab$m_prop)
ach.tab$sd_prop <- as.numeric(ach.tab$sd_prop)
# APAスタイルにあわせた出力を得て
gt.tab <- ach.tab %>%
gt() %>%# gtパッケージで表
fmt_number(columns = c("m", "sd", "m_prop", "sd_prop"), decimals = 2) %>%
# 有効数字揃え
cols_label(exam = md("教科"), m = md("*M*"), sd = md("*SD*"),
m_prop = md("*M*"), sd_prop = md("*SD*")) %>%
# 列名を振り直してMとSDは斜体
tab_spanner(label = "素点", columns = c("m", "sd")) %>%
tab_spanner(label = "通過率", columns = c("m_prop", "sd_prop")) %>%
tab_options(table.width = pct(50), # 表全体の幅をやや拡げる
table_body.hlines.width = 0, # tableの中の水平線は引かない
# spannerの下線を適切にするための設定
column_labels.border.top.width = 2,
column_labels.border.top.color = "black",
column_labels.border.bottom.width = 1,
column_labels.border.bottom.color = "black",
table.border.top.width = 2,
table_body.border.top.color = "black", #最上線は黒
table.border.bottom.width = 2,
table_body.border.bottom.color = "black" #最下線は黒
) %>%
# 2列目以降は中央寄せ(APA 7thから)
cols_align(align = "center",
columns = c("m", "sd", "m_prop", "sd_prop"))
gt.tab
教科 | 素点 | 通過率 | ||
---|---|---|---|---|
M | SD | M | SD | |
国語A | 8.33 | 3.29 | 0.59 | 0.24 |
国語B | 5.21 | 2.41 | 0.58 | 0.27 |
算数A | 9.82 | 3.79 | 0.61 | 0.24 |
算数B | 7.44 | 3.11 | 0.57 | 0.24 |
タイトルも付けてみる
gt.title <- gt.tab %>%
tab_header(title = md("**Table 1**<br>教科別の平均と標準偏差")) %>%
tab_style(
style=cell_text(align="left"),
locations = cells_title("title")) %>%
tab_options(
heading.title.font.size = px(16),
table.border.top.width = 0, #タイトルの上の線はなし
column_labels.border.top.width = 3, # タイトル下の線はあり 調整のため3
column_labels.border.top.color = "black"
)
gt.title
Table 1 教科別の平均と標準偏差 |
||||
---|---|---|---|---|
教科 | 素点 | 通過率 | ||
M | SD | M | SD | |
国語A | 8.33 | 3.29 | 0.59 | 0.24 |
国語B | 5.21 | 2.41 | 0.58 | 0.27 |
算数A | 9.82 | 3.79 | 0.61 | 0.24 |
算数B | 7.44 | 3.11 | 0.57 | 0.24 |
注釈も付けてみる
gt.rem <- gt.title %>%
# ここは表の内容の脚注
tab_footnote(
footnote = "正答数",
location = cells_column_spanners(spanners = "素点")) %>%
tab_footnote(footnote = "平均",
locations = cells_column_labels(columns = c(m, m_prop))) %>%
tab_footnote(footnote = "標準偏差",
locations = cells_column_labels(columns = c(sd, sd_prop))) %>%
tab_options(table.border.bottom.width = 0) %>% #注の下の線は無し
tab_source_note(source_note = md("このページの「疑似データの作成」セクションで作ったデータから作表したもので*実際のデータではなく*__疑似データを集計したものである。__"))
gt.rem
Table 1 教科別の平均と標準偏差 |
||||
---|---|---|---|---|
教科 | 素点1 | 通過率 | ||
M2 | SD3 | M2 | SD3 | |
国語A | 8.33 | 3.29 | 0.59 | 0.24 |
国語B | 5.21 | 2.41 | 0.58 | 0.27 |
算数A | 9.82 | 3.79 | 0.61 | 0.24 |
算数B | 7.44 | 3.11 | 0.57 | 0.24 |
このページの「疑似データの作成」セクションで作ったデータから作表したもので実際のデータではなく疑似データを集計したものである。 | ||||
1 正答数 | ||||
2 平均 | ||||
3 標準偏差 |
あとから微修正ができなくなるが,信頼を得るために必要なことである。コードの下に出力されるのは生成されたpngファイルである。任意のディレクトリに保存されるので,あとは貼り付けるだけでよい。
setwd("/Users/Shared/R/Fig") # 保存先のディレクトリ
gtsave(gt.rem, "表の例_01.png")
setwd("/Users/Shared/R") # ディレクトリを元に戻しておく
今一度,学力検査の疑似データの内容を見てみる。
head(ach)
## id kokuA kokuB sansA sansB kokuA_prop kokuB_prop sansA_prop sansB_prop
## 1 1 10 6 8 7 0.7142857 0.6666667 0.5000 0.5384615
## 2 2 7 9 11 13 0.5000000 1.0000000 0.6875 1.0000000
## 3 3 8 7 12 12 0.5714286 0.7777778 0.7500 0.9230769
## 4 4 9 3 14 10 0.6428571 0.3333333 0.8750 0.7692308
## 5 5 9 9 14 10 0.6428571 1.0000000 0.8750 0.7692308
## 6 6 11 5 12 8 0.7857143 0.5555556 0.7500 0.6153846
例えば,国語Aの正答数について,単純にヒストグラムを出力することは,たいしたことはない。
hist(ach$kokuA)
データの様子を眺める程度なら,これで問題ない。しかし,国語Aの場合の得点の範囲は0から14(14段階)なのに,棒の数は13しかない。
hist(ach$kokuA, breaks=20)
とかすれば,それなりの出力は得られるが,0点の人が1点の人と隣り合わせになってしまう。これでは困るというご指導が入りそうである。仕方ないので,棒グラフを愚直に描くしかない。
まずは,国語Aについて,度数分布表を作ってみる。
# 度数分布表をfreq.kokuAというデータにする
freq.kokuA <- table(ach$kokuA)
freq.kokuA
##
## 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
## 48 58 55 51 69 97 104 166 246 276 283 220 180 129 18
単純にbarplotを作ると,以下のようになる。
barplot(freq.kokuA)
このままだと,あまり見た目がよくないので,調整する。
barplot(freq.kokuA,
xlab = "正答数", ylab = "人数", # 縦,横軸に名前を付ける
ylim = c(0, 300), # 縦軸を正答数ごとの人数の最大値よりも大きく
cex.names = 0.8, # 軸ラベルをやや小さくする
cex.axis = 0.8, # 縦軸のラベル(数字)をやや小さめにする
axis.lty = 1, # 横軸に線を引く
space = c(0, 0) # 棒の間を詰めてヒストグラム風にする
)
ただし,本来,barplotは,barの間が空いているのが普通なので,本当に大丈夫かというエラーメッセージが出る。
通過率のテーブルを作る
freq.kokuA <- table(ach$kokuA) # これは先ほどやったことと同じ
prop.kokuA <- freq.kokuA / sum(freq.kokuA) # 各セルの数を全体の数で割った表
prop.kokuA
##
## 0 1 2 3 4 5 6 7 8 9 10
## 0.0240 0.0290 0.0275 0.0255 0.0345 0.0485 0.0520 0.0830 0.1230 0.1380 0.1415
## 11 12 13 14
## 0.1100 0.0900 0.0645 0.0090
最大値が0.15あたりなので,これに合わせたbarplotを,先ほどと同様の方法で作る。
barplot(prop.kokuA,
xlab = "正答数", ylab = "割合", # 縦,横軸に名前を付ける
ylim = c(0, 0.2), # 縦軸を正答数ごとの人数の最大値よりも大きく
cex.names = 0.8, # 軸ラベルをやや小さくする
cex.axis = 0.8, # 縦軸のラベル(数字)をやや小さめにする
axis.lty = 1, # 横軸に線を引く
space = c(0, 0) # 棒の間を詰めてヒストグラム風にする
)
数十項目あるテストの場合,1点刻みだとグラフが見づらい。上記のデータは0点から14点の範囲なので,3点刻みにしてみる。
# データフレームにして集計できるようにする
df.fq.kokuA <- data.frame(freq.kokuA)
colnames(df.fq.kokuA) <- c("score", "freq")
# 得点カテゴリの変数を追加する
cat.fq.kokuA <- df.fq.kokuA %>%
dplyr::mutate(
cat = case_when(
score == "0" | score == "1" | score == "2" ~ 1,
score == "3" | score == "4" | score == "5" ~ 2,
score == "6" | score == "7" | score == "8" ~ 3,
score == "9" | score == "10" | score == "11" ~ 4,
score == "12" | score == "13" | score == "14" ~ 5,
TRUE ~ 0)
)
cat.fq.kokuA
## score freq cat
## 1 0 48 1
## 2 1 58 1
## 3 2 55 1
## 4 3 51 2
## 5 4 69 2
## 6 5 97 2
## 7 6 104 3
## 8 7 166 3
## 9 8 246 3
## 10 9 276 4
## 11 10 283 4
## 12 11 220 4
## 13 12 180 5
## 14 13 129 5
## 15 14 18 5
こういうデータになる。さらに続ける。
# 得点カテゴリごとの人数の和をデータ化する
cat.kokuA <- data.frame(
cat.fq.kokuA %>%
group_by(cat) %>%
dplyr::summarise(sum(freq))
)
cat.kokuA
## cat sum.freq.
## 1 1 161
## 2 2 217
## 3 3 516
## 4 4 779
## 5 5 327
このデータのsum.freq.
をヒストグラムにする
barplot(cat.kokuA$sum.freq.,
xlab = "正答数", ylab = "人数", # 縦,横軸に名前を付ける
ylim = c(0, 1000), # 縦軸を正答数ごとの人数の最大値よりも大きく
cex.names = 1.0, # 軸ラベルをやや小さくする
cex.axis = 0.8, # 縦軸のラベル(数字)をやや小さめにする
axis.lty = 1, # 横軸に線を引く
names.arg=c("0-2", "3-5", "6-8", "9-11", "12-14"))
画質がいいのはpdfである
setwd("/Users/Shared/R/Fig") # 保存先のディレクトリ
#pdf("barplot.pdf", width=6, height=6) # Windowsの場合
#par(family="Japan1GothicBBB")# Windowsの場合
quartz(type="pdf", file="barplot.pdf", width=6, height=6) #Macintoshの場合
par(family="HiraKakuProN-W3")#Macintoshの場合
barplot(cat.kokuA$sum.freq.,
xlab = "正答数", ylab = "人数", # 縦,横軸に名前を付ける
ylim = c(0, 1000), # 縦軸を正答数ごとの人数の最大値よりも大きく
cex.names = 1.0, # 軸ラベルをやや小さくする
cex.axis = 0.8, # 縦軸のラベル(数字)をやや小さめにする
axis.lty = 1, # 横軸に線を引く
names.arg=c("0-2", "3-5", "6-8", "9-11", "12-14"))
dev.off()
## quartz_off_screen
## 2
setwd("/Users/Shared/R")
データはこのような形
head(ques)
## id schl age gender q01 q02 q03 q04
## 1 1 10001 24 1 1 3 2 1
## 2 2 10001 44 1 1 3 2 1
## 3 3 10001 25 2 3 3 1 1
## 4 4 10001 27 1 3 1 1 2
## 5 5 10001 34 1 1 3 2 1
## 6 6 10001 48 1 3 1 2 3
性別
table(ques$gender)
##
## 1 2
## 162 138
年齢
table(ques$age)
##
## 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
## 13 9 5 11 10 10 11 4 9 17 8 5 10 8 12 5 12 10 11 9 7 5 8 9 12 10
## 48 49 50 51 52 53
## 5 7 12 13 11 12
年齢の平均や標準偏差
library(psych)
describe(ques$age)
## vars n mean sd median trimmed mad min max range skew kurtosis se
## X1 1 300 37.65 9.53 38 37.67 11.86 22 53 31 0.01 -1.25 0.55
割合を積み上げ棒グラフで表現するとよい。
barplot(matrix(prop.table(table(ques$gender))), horiz =TRUE)
これだと見栄えが悪いので,工夫する。#
以降改行までは処理されない。 グラフには数値は載せない(役所は違うようだが)
#setwd("/Users/Shared/R/Fig")
#bmp(filename="g01.bmp", width = 1000, height = 300)
par(mar=c(4,4,4,12)) # 凡例が入るように
barplot(matrix(prop.table(table(ques$gender))), # グラフ用に行列を渡す
col = c("gray1","gray50"),
horiz = TRUE)
##凡例をプロットエリア外に描けるようにする
par(xpd = TRUE)
# 凡例
legend(x = par()$usr[2],y = par()$usr[4], # 凡例がグラフエリア右下
legend = c("女性", "男性"),
xjust = -.05, #プロットエリアから少し離して凡例を載せる
fill = c("gray1","gray50") # 色をグラフと揃える
)
#dev.off()
#setwd("/Users/Shared/R")
回答の年齢層は,層別するとよさそうである。 層別にする方法は,学力検査データの例と同じである。
# 年齢カテゴリの変数を追加する
cat.age <- ques %>%
dplyr::mutate(
cat = case_when(
age < 30 ~ 1,
age < 40 ~ 2,
age < 50 ~ 3,
TRUE ~ 4)
)
# 年齢カテゴリごとの人数の和をデータ化する
cat.age.sum <- data.frame(table(cat.age$cat))
cat.age.sum
## Var1 Freq
## 1 1 73
## 2 2 96
## 3 3 83
## 4 4 48
これをグラフにする
# 上記のデータを因子型にする
cat.age.sum$fac <- factor(cat.age.sum$Var1,
levels = c(1,2,3,4),
labels = c("20代","30代","40代","50代以上"),
ordered=TRUE)
# 使う色を指定しておく
color=c("gray1","gray25","gray49","gray73")
# 凡例も
par(mar=c(4,4,4,12)) # 凡例が入るように
barplot(matrix(prop.table(cat.age.sum$Freq)), # グラフ用に行列を渡す
col = color,
horiz = TRUE)
##凡例をプロットエリア外に描けるようにする
par(xpd = TRUE)
# 凡例
legend(x = par()$usr[2], y = par()$usr[4], # 凡例がグラフエリア右下
legend = cat.age.sum$fac,
xjust = -.05, #プロットエリアから少し離して凡例を載せる
fill = color # 色をグラフと揃える
)
psych
パッケージのdescribe
関数で分かる。
library(psych) # 一度読み込めばよい
describe(ques)
## vars n mean sd median trimmed mad min max range
## id 1 300 150.50 86.75 150.5 150.50 111.19 1 300 299
## schl 2 300 15008.00 5008.36 15008.0 15008.00 7413.00 10001 20015 10014
## age 3 300 37.65 9.53 38.0 37.67 11.86 22 53 31
## gender 4 300 1.46 0.50 1.0 1.45 0.00 1 2 1
## q01 5 300 2.04 0.80 2.0 2.05 1.48 1 3 2
## q02 6 300 1.95 0.82 2.0 1.93 1.48 1 3 2
## q03 7 300 2.09 0.79 2.0 2.11 1.48 1 3 2
## q04 8 300 2.03 0.83 2.0 2.04 1.48 1 3 2
## skew kurtosis se
## id 0.00 -1.21 5.01
## schl 0.00 -2.01 289.16
## age 0.01 -1.25 0.55
## gender 0.16 -1.98 0.03
## q01 -0.07 -1.45 0.05
## q02 0.10 -1.53 0.05
## q03 -0.16 -1.40 0.05
## q04 -0.06 -1.56 0.05
例えば,年齢とq01とのクロス集計をしてみる。
# 年齢して層別化してデータに追加する
ques2 <- ques # 元のデータを保持しておく
ques2 <- ques2 %>%
dplyr::mutate(
cat = case_when(
age < 30 ~ 1,
age < 40 ~ 2,
age < 50 ~ 3,
TRUE ~ 4)
)
# カテゴリを因子型にする
## 年齢
ques2$cat <- factor(ques2$cat,
levels = c(1,2,3,4),
labels = c("20代","30代","40代","50代以上"),
ordered=TRUE)
## q01
ques2$q01 <- factor(ques2$q01,
levels = c(1,2,3),
labels = c("全く","時々","頻繁"),
ordered=TRUE)
head(ques2)
## id schl age gender q01 q02 q03 q04 cat
## 1 1 10001 24 1 全く 3 2 1 20代
## 2 2 10001 44 1 全く 3 2 1 40代
## 3 3 10001 25 2 頻繁 3 1 1 20代
## 4 4 10001 27 1 頻繁 1 1 2 20代
## 5 5 10001 34 1 全く 3 2 1 30代
## 6 6 10001 48 1 頻繁 1 2 3 40代
データが変わっていることが分かる。
クロス集計自体は,簡単である。
tab01 <- table(ques2$cat, ques2$q01)
tab01
##
## 全く 時々 頻繁
## 20代 24 19 30
## 30代 31 39 26
## 40代 23 31 29
## 50代以上 13 18 17
用途によって様々な形式がある。
# 行と列の合計欄を付ける
tab02 <- addmargins(tab01)
tab02
##
## 全く 時々 頻繁 Sum
## 20代 24 19 30 73
## 30代 31 39 26 96
## 40代 23 31 29 83
## 50代以上 13 18 17 48
## Sum 91 107 102 300
# 行方向の割合
tab03 <- addmargins(prop.table(tab01, margin = 1))
tab03
##
## 全く 時々 頻繁 Sum
## 20代 0.3287671 0.2602740 0.4109589 1.0000000
## 30代 0.3229167 0.4062500 0.2708333 1.0000000
## 40代 0.2771084 0.3734940 0.3493976 1.0000000
## 50代以上 0.2708333 0.3750000 0.3541667 1.0000000
## Sum 1.1996256 1.4150179 1.3853565 4.0000000
# 有効数字を少なくする
round(tab03, 2)
##
## 全く 時々 頻繁 Sum
## 20代 0.33 0.26 0.41 1.00
## 30代 0.32 0.41 0.27 1.00
## 40代 0.28 0.37 0.35 1.00
## 50代以上 0.27 0.38 0.35 1.00
## Sum 1.20 1.42 1.39 4.00
# 列方向の割合
tab04 <- addmargins(prop.table(tab01, margin = 2))
round(tab04,2) # 和が100にならない場合がある
##
## 全く 時々 頻繁 Sum
## 20代 0.26 0.18 0.29 0.74
## 30代 0.34 0.36 0.25 0.96
## 40代 0.25 0.29 0.28 0.83
## 50代以上 0.14 0.17 0.17 0.48
## Sum 1.00 1.00 1.00 3.00
ここまでのデータが揃うと,以下のような表を作ることができる。
# 人数の行列を分割する
tab.n.20 <- tab02[1, -4] # 1行目 合計列なし
tab.n.30 <- tab02[2, -4] # 2行目 合計列なし
tab.n.40 <- tab02[3, -4] # 3行目 合計列なし
tab.n.50 <- tab02[4, -4] # 4行目 合計列なし
# 行方向の割合を分割
tab.r.20 <- tab03[1, -4] # 1行目 合計列なし
tab.r.30 <- tab03[2, -4] # 2行目 合計列なし
tab.r.40 <- tab03[3, -4] # 3行目 合計列なし
tab.r.50 <- tab03[4, -4] # 4行目 合計列なし
# 列方向の割合を分割
tab.c.20 <- tab04[1, -4] # 1行目 合計列なし
tab.c.30 <- tab04[2, -4] # 2行目 合計列なし
tab.c.40 <- tab04[3, -4] # 3行目 合計列なし
tab.c.50 <- tab04[4, -4] # 4行目 合計列なし
# 分割したデータを積み上げ直す
tab.40 <- rbind(tab.n.20, tab.r.20, tab.c.20,
tab.n.30, tab.r.30, tab.c.30,
tab.n.40, tab.r.40, tab.c.40,
tab.n.50, tab.r.50, tab.c.50)
tab.40
## 全く 時々 頻繁
## tab.n.20 24.0000000 19.0000000 30.0000000
## tab.r.20 0.3287671 0.2602740 0.4109589
## tab.c.20 0.2637363 0.1775701 0.2941176
## tab.n.30 31.0000000 39.0000000 26.0000000
## tab.r.30 0.3229167 0.4062500 0.2708333
## tab.c.30 0.3406593 0.3644860 0.2549020
## tab.n.40 23.0000000 31.0000000 29.0000000
## tab.r.40 0.2771084 0.3734940 0.3493976
## tab.c.40 0.2527473 0.2897196 0.2843137
## tab.n.50 13.0000000 18.0000000 17.0000000
## tab.r.50 0.2708333 0.3750000 0.3541667
## tab.c.50 0.1428571 0.1682243 0.1666667
これを,報告できるような表に変換する
library(tidyverse) # 必要に応じて読み込む
library(gt) # 必要に応じて読み込む
tab.40.df <- data.frame(tab.40) # データの形を変換する
# 年齢のグループを付ける
tab.40.df$agegroup <- c(1,1,1,2,2,2,3,3,3,4,4,4)
tab.40.df$agegroup <- factor(tab.40.df$agegroup,
levels = c(1,2,3,4),
labels = c("20代","30代","40代","50代以上"),
ordered = TRUE)
# 出力の内容の名前を付ける
tab.40.df$out <- c(1,2,3,1,2,3,1,2,3,1,2,3)
tab.40.df$out <- factor(tab.40.df$out,
levels = c(1,2,3),
labels = c("度数","行の割合","列の割合"),
ordered = FALSE)
# 列を並び替える
tab.40.df.2 <- tab.40.df[c(4,5,1:3)]
tab.40.df.2
## agegroup out 全く 時々 頻繁
## tab.n.20 20代 度数 24.0000000 19.0000000 30.0000000
## tab.r.20 20代 行の割合 0.3287671 0.2602740 0.4109589
## tab.c.20 20代 列の割合 0.2637363 0.1775701 0.2941176
## tab.n.30 30代 度数 31.0000000 39.0000000 26.0000000
## tab.r.30 30代 行の割合 0.3229167 0.4062500 0.2708333
## tab.c.30 30代 列の割合 0.3406593 0.3644860 0.2549020
## tab.n.40 40代 度数 23.0000000 31.0000000 29.0000000
## tab.r.40 40代 行の割合 0.2771084 0.3734940 0.3493976
## tab.c.40 40代 列の割合 0.2527473 0.2897196 0.2843137
## tab.n.50 50代以上 度数 13.0000000 18.0000000 17.0000000
## tab.r.50 50代以上 行の割合 0.2708333 0.3750000 0.3541667
## tab.c.50 50代以上 列の割合 0.1428571 0.1682243 0.1666667
cross.tab <- tab.40.df.2 %>% gt()
cross.tab
agegroup | out | 全く | 時々 | 頻繁 |
---|---|---|---|---|
20代 | 度数 | 24.0000000 | 19.0000000 | 30.0000000 |
20代 | 行の割合 | 0.3287671 | 0.2602740 | 0.4109589 |
20代 | 列の割合 | 0.2637363 | 0.1775701 | 0.2941176 |
30代 | 度数 | 31.0000000 | 39.0000000 | 26.0000000 |
30代 | 行の割合 | 0.3229167 | 0.4062500 | 0.2708333 |
30代 | 列の割合 | 0.3406593 | 0.3644860 | 0.2549020 |
40代 | 度数 | 23.0000000 | 31.0000000 | 29.0000000 |
40代 | 行の割合 | 0.2771084 | 0.3734940 | 0.3493976 |
40代 | 列の割合 | 0.2527473 | 0.2897196 | 0.2843137 |
50代以上 | 度数 | 13.0000000 | 18.0000000 | 17.0000000 |
50代以上 | 行の割合 | 0.2708333 | 0.3750000 | 0.3541667 |
50代以上 | 列の割合 | 0.1428571 | 0.1682243 | 0.1666667 |
さらに,加工する。
cross.tab <- tab.40.df.2 %>%
gt(groupname_col = "agegroup") %>%
fmt_number(
columns = c(3:5),
rows = c(1, 4, 7, 10), # 1,4,7,10行目は実数
decimals = 0) %>%
fmt_number(
columns = c(3:5),
rows = c(2:3, 5:6, 8:9, 11:12), # 1,4,7,10行目以外は割合
decimals = 2) %>%
cols_label(
agegroup = md("年齢"),
out = ("")) %>%
tab_spanner(label = "頻度", columns = c(3:5)) %>%
cols_align(align = "left", columns = c(2)) %>%
tab_options(table_body.hlines.width = 0, # tableの中の水平線は引かない
# table.border.top.width = 0, #タイトルの上の線を消す,
# table.border.bottom.width = 0, #注の下の線を消す
heading.title.font.size = px(16), #タイトルのフォントサイズ
# row_group.border.bottom.width = 0, #セクション名の下の線を消す
# stub.border.width = 0,
column_labels.border.top.width = 3, # 変数名の行の線
column_labels.border.top.color = "black",
column_labels.border.bottom.width = 2, # 変数名の行の線
column_labels.border.bottom.color = "black",
table_body.border.bottom.color = "black", #テーブルの下線を黒く
row_group.border.top.color = "black", #セクション名の上の線を黒
row_group.border.top.width = 1, #セクション名の上の線を細く
row_group.border.bottom.color = "white", #セクション名の上の線を消す
row_group.border.bottom.width = 1, #セクション名の上の線を細く
table.border.top.width = 0, #タイトルの上の線はなし
table.width = pct(30)) %>%
tab_header(title = md("**Table 2**<br>年齢となんとかの頻度の関係")) %>%
tab_style(
style=cell_text(align="left"),
locations = cells_title("title")
)
cross.tab
Table 2 年齢となんとかの頻度の関係 |
|||
---|---|---|---|
頻度 | |||
全く | 時々 | 頻繁 | |
20代 | |||
度数 | 24 | 19 | 30 |
行の割合 | 0.33 | 0.26 | 0.41 |
列の割合 | 0.26 | 0.18 | 0.29 |
30代 | |||
度数 | 31 | 39 | 26 |
行の割合 | 0.32 | 0.41 | 0.27 |
列の割合 | 0.34 | 0.36 | 0.25 |
40代 | |||
度数 | 23 | 31 | 29 |
行の割合 | 0.28 | 0.37 | 0.35 |
列の割合 | 0.25 | 0.29 | 0.28 |
50代以上 | |||
度数 | 13 | 18 | 17 |
行の割合 | 0.27 | 0.38 | 0.35 |
列の割合 | 0.14 | 0.17 | 0.17 |
集計表を作る際に作ったデータを流用すればよい。
# グラフ用データ
closs.graph.tab <- t(prop.table(tab01, margin = 1))
closs.graph.tab
##
## 20代 30代 40代 50代以上
## 全く 0.3287671 0.3229167 0.2771084 0.2708333
## 時々 0.2602740 0.4062500 0.3734940 0.3750000
## 頻繁 0.4109589 0.2708333 0.3493976 0.3541667
# 使う色を指定しておく
color=c("gray25","gray49","gray73")
# 凡例も
par(mar=c(4,4,4,12)) # 凡例が入るように
barplot(closs.graph.tab, # グラフ用に行列を渡す
col = color,
horiz = TRUE,
main = "年齢となんとかの頻度の関係",
xlab = "割合",
ylab = "年齢")
##凡例をプロットエリア外に描けるようにする
par(xpd = TRUE)
# 凡例
legend(x = par()$usr[2], y = par()$usr[4], # 凡例がグラフエリア右下
legend = c("全く","時々","頻繁"),
xjust = -.05, #プロットエリアから少し離して凡例を載せる
fill = color # 色をグラフと揃える
)