第8章 Qualtricsデータの処理
Qualtricsはオンラインサーベイを便利に行うツールですが、政治学では特にサーベイ実験で使われていると思います。
サーベイ実験では被験者ごとに処理をランダムに与える必要があり、Qualtricsでは簡単にそれを行うことができるのですが、得られたデータを統計分析する際には一手間かかります。
ここでは、ロング・ワイドの変換で紹介したワイド形式からロング形式に変換する方法を応用してQualtricsのデータを統計分析用に整形する方法を説明します。
8.1 Qualtricsのデータ
ここではQualtricsから得られる架空のデータを使いたいと思います。 Qualtricsでは行は各被験者でユニークな識別番号の他に設問への回答が記載されています。
サーベイ実験ではグループごとに異なる設問がなされるため、例えば、グループが2つでグループ固有の設問が2種類、共通の設問が2種類の場合、以下のような形になっています。
data <- tibble(id = c(1,2,3,4),
y1_1 = c(2,3,NA,NA),
y2_1 = c(1,4,NA,NA),
y1_2 = c(NA,NA,5,3),
y2_2 = c(NA,NA,6,4),
x1 = c(2,3,3,6),
x2 = c(6,4,1,1))
data
- 1, 2番目の被験者がグループ1で、3, 4番目の被験者がグループ2である。
- 便宜上、グループ1を統制群、グループ2を処置群とする。
- グループ1の固有の設問の1つ目の回答が
y1_1
で2つ目の回答がy2_1
である。- よってグループ1の被験者の
y1_2
とy2_2
への回答は欠損値扱いになっている。 - グループ2についても同様である。
- よってグループ1の被験者の
- 全員共通の設問への回答は
x1
とx2
である。 - 5件法の場合、「回答しない」は6番目の回答であることが多いため、被験者1は
x2
への、被験者3はy2_2
への、被験者4はx1
への回答を拒否したものとする。
このデータを統計的に分析するには以下のような形式になっていることが望ましいです。
t
は処置群であれば1
、統制群であれば0
となるような変数である。
8.2 気合による方法
変数やグループの数が少ない場合は気合で書いてしまうという手もあります。 つまり、
- 統制群の応答変数
y1_1
とy2_1
および共変量x1
とx2
を選択する。 - 応答変数の名前を
y1
とy2
に共通化する。 - 応答変数が欠損しているサンプルは処置群なので除外する。
という方法です。
control <- data[c("id", "y1_1", "y2_1", "x1", "x2")]
names(control) <- c("id", "y1", "y2", "x1", "x2")
control <- control[!is.na(control$y1) & !is.na(control$y2),]
control$t <- 0
control
control[!is.na(control$y1) & !is.na(control$y2),]
は「第1の応答変数が欠損していない」かつ「第2の応答変数が欠損していない」サンプルを指定しています。
同様の処理を処置群についても行います。
treatment <- data[c("id", "y1_2", "y2_2", "x1", "x2")]
names(treatment) <- c("id", "y1", "y2", "x1", "x2")
treatment <- treatment[!is.na(treatment$y1) & !is.na(treatment$y2),]
treatment$t <- 1
treatment
最後に、両者を結合し、6
を欠損値に入れ替えます。
data_reshaped <- bind_rows(control, treatment)
data_reshaped[data_reshaped$y2 == 6,]$y2 <- NA
data_reshaped[data_reshaped$x1 == 6,]$x1 <- NA
data_reshaped[data_reshaped$x2 == 6,]$x2 <- NA
data_reshaped
8.3 ループによる方法
この方法はグループの数は多くなるとめんどくさくなるという欠点があります。
各グループについては同様の処理を行うので、for
ループによって簡略化することができます。
アイデアとしてはi
回目の処理においてid
共変量とy1_i
とy2_i
を抜き出します。
文字列はpaste0()
という関数で結合することができます。
## [1] "y1_1"
ちなみに、paste()
は文字列の間に特定の記号を含める関数です。
## [1] "a/b"
つまり、paste0()
はpaste(..., sep = ")
のラッパー関数です。
さて、ここではi
を1
として選択する変数の名前をsel_vars
というベクトルとして作成します。
## [1] "id" "y1_1" "y2_1" "x1" "x2"
無事、必要な変数名を抜き出せています。
その他の処理は先ほどと変わらないので、まとめると次のようになります。
data_reshaped <- NULL
for (i in 1:2) {
sel_vars <- c("id", paste0(c("y1_", "y2_"), i), "x1", "x2")
temp <- data[sel_vars]
names(temp) <- c("id", "y1", "y2", "x1", "x2")
temp <- temp[!is.na(temp$y1) & !is.na(temp$y2),]
temp$t <- i - 1
data_reshaped <- bind_rows(data_reshaped, temp)
}
data_reshaped[data_reshaped$y2 == 6,]$y2 <- NA
data_reshaped[data_reshaped$x1 == 6,]$x1 <- NA
data_reshaped[data_reshaped$x2 == 6,]$x2 <- NA
data_reshaped
- 処置変数はグループ番号から
1
を引いていますが本質的な問題ではありません。
8.4 tidyverseな方法
実はtidyverse
のtidyr
を応用するとこのような処理をすることができます。
まずは、応答変数に関してgather()
でロングにします。
data_reshaped <- data %>%
pivot_longer(-c(id, x1, x2), names_to = "t", values_to = "y")
data_reshaped
ポイントはこの段階で応答変数y
が欠損値になっているのは、グループが異なる回答ということです。
- グループ1の被験者にとっての
y1_2
とy2_2
- グループ2の被験者にとっての
y1_1
とy2_1
なので、指定した変数が欠損値であるサンプルを除外する関数drop_na()
でy
が欠損しているサンプルを除外します。
続いて、適当に名付けておいたt
からグループの情報と応答変数の種類の情報を抜き出します。
str_extraxt()
という関数は文字列からパターンに合致したものを抜き出します。
## [1] "y1"
パターンのマッチングには正規表現というやや難しい表記が必要になります。
y[0-9]
というのはy
という文字と数字が1文字続く文字列という意味です。
## [1] "_1"
同様にしてグループの情報も抜き出せますが、_
が邪魔です。
文字列から数値を抜き出す関数はparse_naumber()
になります。
## [1] 1
これでグループと応答変数の種類の情報を抜き出す準備が整いました。
以下ではt
にグループ番号をk
に応答変数の種類を入れる操作をします
data_reshaped <- data_reshaped %>%
mutate(k = str_extract(t, "y[0-9]"),
t = str_extract(t, "_[0-9]") %>%
parse_number())
data_reshaped
続いて、k
の中身y1
とy2
を変数名に戻すためにspread()
を使います。
最後に欠損値をna_if()
で代入しておきます。
data_reshaped <- data_reshaped %>%
mutate(y2 = na_if(y2, 6),
x1 = na_if(x1, 6),
x2 = na_if(x2, 6))
data_reshaped
以上をパイプでまとめると次のようになります。
data_reshaped <- data %>%
pivot_longer(-c(id, x1, x2), names_to = "t", values_to = "y") %>%
drop_na(y) %>%
mutate(k = str_extract(t, "y[0-9]"),
t = str_extract(t, "_[0-9]") %>%
parse_number()) %>%
pivot_wider(names_from = "k", values_from = "y") %>%
mutate(y2 = na_if(y2, 6),
x1 = na_if(x1, 6),
x2 = na_if(x2, 6))
data_reshaped
ループを用いるか、tidyverse
を用いるかにかかわらず重要なのは変数名の設定です。
- 応答変数と共変量をグループごとに定型的な変数名としておくことで処理が楽になります。