目的 データフレーム上の複数の変数を指定して,クロス集計を行い,必要ならば独立性の検定,フィッシャーの正確検定,コルモゴロフ・スミルノフ検定の何れかを行う。 使用法 cross(i, j, df, row=TRUE, latex=TRUE, captions=NULL, labels=NULL, test=c("none", "chisq", "fisher", "kruskal"), output="") 引数 i, j データフレーム上で,クロス集計をする二変数が入っている,列の番号または変数名ベクトル i は表側に来る変数,j は表頭に来る変数 i, j は,それぞれベクトルでもかまわない。i の要素と j の要素のすべての組み合わせでクロス集計を行う df 読み込んだデータフレームの名前 row デフォルトは行方向の % を取る 列方向なら row=FALSE にする latex 集計結果を LaTeX ソースとして出力する。タブ区切りで出すだけなら(Word 用など),latex=FALSE にする captions latex=TRUE の場合,各表の表題を文字列ベクトルとして指定することができる(デフォルトではあり合わせの表題を付ける)。 labels latex=TRUE の場合,各表の label を文字列ベクトルとして指定することができる(デフォルトでは付けない)。 test 検定法を指定する。"chisq", "fisher", "kruskal" の何れか デフォルトは検定をしない "kruskal" の場合は,i(つまり表側)の変数を群と見なし,j(つまり表頭)の変数を順序のあるカテゴリーとして検定を行う output 出力コネクション デフルトはコンソールに出力 ソース インストールは,以下の 1 行をコピーし,R コンソールにペーストする source("http://aoki2.si.gunma-u.ac.jp/R/src/cross.R", encoding="euc-jp") ##### # # クロス集計表を作成し,独立性の検定または代表値の差の検定を行う # ##### cross <- function(i, # 表側に来る変数が入っているデータフレーム上の列番号または変数名ベクトル j, # 表側に来る変数が入っているデータフレーム上の列番号または変数名ベクトル df, # データフレーム row=TRUE, # 行ごとに 100% となるようにパーセントを取る latex=TRUE, # LaTeX 形式で度数分布表を出力する(デフォルトは LaTeX 形式) captions=NULL, # latex=TRUE のときに,各表の表題を表す文字列ベクトルを指定できる(NULL のときはデフォルトの表題) labels=NULL, # latex=TRUE のときに,各表の label を表す文字列ベクトルを指定できる(NULL のときは付けない) test=c("none", "chisq", "fisher", "kruskal"), # デフォルト none では検定を行わない。検定を行うときはその種類を指定する output="", # ファイルに出力するときはファイル名(デフォルトはコンソールに出力) encoding=getOption("encoding")) # ファイルに出力するときのエンコーディング(デフォルトは OS による) { # 下請け関数 cross.sub <- function(ii, jj) # ii, jj はスカラー。ii, jj で指定されたクロス集計を 1 つだけ行う { tbl <- table(df[,ii], df[,jj]) # 表の本体は table 関数で作る tbl <- tbl[rowSums(tbl) > 0,, drop=FALSE] # 行和が 0 になる行を除く(factor 関数の使い方によってはこのような集計表ができる) tbl <- tbl[,colSums(tbl) > 0, drop=FALSE] # 列和が 0 になる列を除く(同上) ans <- addmargins(tbl) # 周辺和を付け加える nr <- nrow(ans) # 集計表の行数 nc <- ncol(ans) # 集計表の列数 colnames(ans)[nc] <- rownames(ans)[nr] <- "合計" # 表頭,表側の該当箇所を「合計」とする pct <- ans*100 / if (row) ans[,nc] else rep(ans[nr,], each=nr) # row の指示により,行 % か列 % のいずれかを取る if (latex) { # LaTeX 形式で集計結果を出力する cat("\n\\begin{table}[htbp]\n", file=output) # \begin{table}[htbp] if (is.null(captions)) { cat(sprintf("\\caption{%s : %s}\n", colnames(df)[ii], colnames(df)[jj]), file=output) # \caption{変数名 : 変数名} } else { cat(sprintf("\\caption{%s}\n", captions[index]), file=output) # \caption{○○○○} } if (!is.null(labels)) { cat(sprintf("\\label{%s}\n", labels[index]), file=output) # \labels{○○○○} } cat("\\centering\n", file=output) # \centering cat("\\begin{tabular}{l", rep("c", nc), "} \\hline\n", sep="", file=output) # \begin{tabular}{cc…c} \hline cat(sprintf("& \\multicolumn{%i}{c}{%s}\\\\ \\cline{2-%i}\n", nc-1, colnames(df)[jj], nc), file=output) # 表頭の変数名 cat(colnames(df)[ii], colnames(ans), sep=" & ", file=output) # 表側の変数名 & 表頭1 & 表頭2 & … & 合計 cat("\\\\ \\hline\n", file=output) # \\ \hline for (i in 1:nr) { # 各行について, cat(rownames(ans)[i], ans[i,], sep=" & ", file=output) # 表側i & 観察数i1 & 観察数i2 & … & 合計 cat("\\\\\n", file=output) # \\ cat("\\%", sprintf("{\\small \\textit{%.1f}}", pct[i,]), sep=" & ", file=output)# % & パーセントi1 & パーセントi2 & … & パーセント cat("\\\\", file=output) # \\ if (i >= nr-1) { cat("\\hline\n", file=output) # \hline \n } else { cat("\n", file=output) # そのまま改行 \n } } cat("\\end{tabular}\n", file=output) # \end{tabular} } else { # tab で区切って出力する cat("\n表 ", colnames(df)[ii], ":", colnames(df)[jj], sep="", file=output) # 表 変数名:変数名 cat("\n", colnames(df)[jj], sep="\t", file=output, fill=TRUE) # 表頭の変数名 cat(colnames(df)[ii], colnames(ans), sep="\t", file=output, fill=TRUE) # 表側の変数名 表頭1 表頭2 … 合計 for (i in 1:nr) { # 各行について cat(rownames(ans)[i], ans[i,], sep="\t", file=output, fill=TRUE) # 表側i 観察数i1 観察数i2 … 合計 cat("%", sprintf("%.1f", pct[i,]), sep="\t", file=output, fill=TRUE) # % パーセントi1 パーセントi2 … パーセント } } if (nr > 2 && nc > 2 && test != "none") { # 2 行× 2 列以上の集計表については,検定オプションあり if (latex) { # LaTeX 形式の出力なら表の後に追加 cat("\\\\ \\noindent\n", file=output) } if (test == "chisq") { # 独立性の検定 chisq を選んだ場合 res <- chisq.test(tbl) # chisq.test を使う cat(sprintf(if (latex) "$\\chi^2$値 = %.3f, 自由度 = %i, $P$値 = %.3f\n" else "カイ二乗値 = %.3f, 自由度 = %i, P 値 = %.3f\n", res$statistic, res$parameter, res$p.value), file=output) } else if (test == "fisher") { # 独立性の検定(Fisher の正確検定) fisher を選んだ場合 cat(sprintf(if (latex) "$P_{Fisher}$値 = %.3f\n" else "P 値(Fisher)= %.3f\n", fisher.test(tbl)$p.value), file=output) } else if (test == "kruskal") { # クラスカル・ウォリスの検定 kruskal を選んだ場合 if (row) { # 行ごとの % が 100% となるようにした row=TRUE の場合 if (nc > 3 && (!is.ordered(df[,jj]) && !is.numeric(df[,jj]))) { warning(paste("「", colnames(df)[jj], "」は,順序尺度・間隔尺度・比尺度変数でなくてはなりません。", sep="")) } res <- kruskal.test(rep(col(tbl), tbl), rep(row(tbl), tbl)) } else { # 列ごとの % が 100% となるようにした row=FALSE の場合 if (nr > 3 && (!is.ordered(df[,ii]) && !is.numeric(df[,ii]))) { warning(paste("「", colnames(df)[ii], "」は,順序尺度・間隔尺度・比尺度変数でなくてはなりません。", sep="")) } res <- kruskal.test(rep(row(tbl), tbl), rep(col(tbl), tbl)) } cat(sprintf(if (latex) "$\\chi^2_{kw}$値 = %.3f, 自由度 = %i, $P$値 = %.3f\n" else "カイ二乗値(kw) = %.3f, 自由度 = %i, P 値 = %.3f\n", res$statistic, res$parameter, res$p.value), file=output) } } if (latex) { # LaTeX 形式で集計結果を出力する場合は, cat("\\end{table}\n", file=output) # \end{table} } } getNum <- function(str, df) { # 変数名から列番号を得る names <- colnames(df) seq_along(names)[names %in% str] } # cross 関数の本体 if (output != "") { # 結果をファイルに出力する場合の処理 output <- file(output, open="w", encoding=encoding) } test <- match.arg(test) # test 引数から,完全な検定手法名を得る if (is.character(i[1])) { i <- getNum(i, df) } if (is.character(j[1])) { j <- getNum(j, df) } index <- 0 for (ii in i) { # i はベクトルまたはスカラー for (jj in j) { # j はベクトルまたはスカラー if (ii != jj) { # i, j の全ての組み合わせについて(ii と jj が違うときのみ), index <- index+1 cross.sub(ii, jj) # クロス集計のための下請け関数 cross.sub を呼ぶ } } } if (output != "") { # 結果をファイルに出力した場合の後始末 close(output) } } 使用例 性別 血液型 職業 1 1 3 2 2 2 1 3 1 1 4 2 1 1 1 2 2 1 2 3 3 1 3 2 2 2 1 2 1 1 のようなファイル test.dat があるとする 入力と変数の定義 df <- read.table("test.dat", header=TRUE) df[,1] <- factor(df[,1], levels=1:2, labels=c("男", "女")) df[,2] <- factor(df[,2], levels=1:4, labels=c("A", "B", "O", "AB")) df[,3] <- factor(df[,3], levels=1:3, labels=c("自由業", "会社員", "無職")) コンソールに出力するとき cross(1, 2, df) ファイルに出力するとき cross(1, 2, df, output="ファイル名", encoding="EUC-JP") 出力結果例 latex=FALSE の場合 > cross(1, 2:3, df, latex=FALSE) 血液型 性別 A B O AB 合計 男 2 0 2 1 5 % 40.0 0.0 40.0 20.0 100.0 女 1 3 1 0 5 % 20.0 60.0 20.0 0.0 100.0 合計 3 3 3 1 10 % 30.0 30.0 30.0 10.0 100.0 表 性別:職業 職業 性別 自由業 会社員 無職 合計 男 2 2 1 5 % 40.0 40.0 20.0 100.0 女 3 1 1 5 % 60.0 20.0 20.0 100.0 合計 5 3 2 10 % 50.0 30.0 20.0 100.0 latex=TRUE(デフォルト)の場合 > cross(1, 2:3, df) \begin{table}[htbp] \caption{性別 : 血液型} \begin{center} \begin{tabular}{lccccc} \hline & \multicolumn{4}{c}{血液型}\\ \cline{2-5} 性別 & A & B & O & AB & 合計\\ \hline 男 & 2 & 0 & 2 & 1 & 5\\ \% & {\small \textit{40.0}} & {\small \textit{0.0}} & {\small \textit{40.0}} & {\small \textit{20.0}} & {\small \textit{100.0}}\\ 女 & 1 & 3 & 1 & 0 & 5\\ \% & {\small \textit{20.0}} & {\small \textit{60.0}} & {\small \textit{20.0}} & {\small \textit{0.0}} & {\small \textit{100.0}}\\\hline 合計 & 3 & 3 & 3 & 1 & 10\\ \% & {\small \textit{30.0}} & {\small \textit{30.0}} & {\small \textit{30.0}} & {\small \textit{10.0}} & {\small \textit{100.0}}\\\hline \end{tabular} \\ \noindent \end{center} \end{table} \begin{table}[htbp] \caption{性別 : 職業} \begin{center} \begin{tabular}{lcccc} \hline & \multicolumn{3}{c}{職業}\\ \cline{2-4} 性別 & 自由業 & 会社員 & 無職 & 合計\\ \hline 男 & 2 & 2 & 1 & 5\\ \% & {\small \textit{40.0}} & {\small \textit{40.0}} & {\small \textit{20.0}} & {\small \textit{100.0}}\\ 女 & 3 & 1 & 1 & 5\\ \% & {\small \textit{60.0}} & {\small \textit{20.0}} & {\small \textit{20.0}} & {\small \textit{100.0}}\\\hline 合計 & 5 & 3 & 2 & 10\\ \% & {\small \textit{50.0}} & {\small \textit{30.0}} & {\small \textit{20.0}} & {\small \textit{100.0}}\\\hline \end{tabular} \\ \noindent \end{center} \end{table} これを LaTeX でタイプセットすると以下のようになる。