Rの{psych}
パッケージに逆転項目を逆転させる処理するrevese.code()
という関数があるのでそのメモ。
Rで逆転処理をする話がたまに見かけるので。
ここでの逆転項目というのは1~7の結果を1は7、2は6、……と結果を逆にしなければならない項目のこと。心理尺度なんかだとたまにある。(今どきは殆どないが、たまに日本語が不自然な逆転項目の質問項目(ないがあるみたいな)がある質問紙があって混乱することがある。)
ということで心理学系御用達(だと思う)の{psych}
パッケージに逆転項目を逆転させる処理(1~7の場合に1を7、2を6……)するrevese.code()
という関数があるがちょっと使いにくいのでまとめておく。
逆転項目を逆転させる処理をするのならこの関数よりも、項目の最大値と最小値を足した値から逆転項目の値を引く処理をしたほうが簡単だと思う(最後に書いた)。
あと蛇足で{car}
パッケージのrecode()
関数を使って逆転処理する方法も最後に書いた。{psych}
のrevese.code()
よりも用途によっては簡便だと思う。
revese.code()
の基本的挙動
サンプルデータを作る。5項目(Item_1
~Item_5
)分、すべてが1~7での回答結果が10人分あるようなデータをイメージしている。
set.seed(123) df <- data.frame(Item_1 = sample(1:7, 10, replace = TRUE), Item_2 = sample(1:7, 10, replace = TRUE), Item_3 = sample(1:7, 10, replace = TRUE), Item_4 = sample(1:7, 10, replace = TRUE), Item_5 = sample(1:7, 10, replace = TRUE))
このデータは次のようになっている。head()
をつかって最初6行のみを表示する。
head(df)
## Item_1 Item_2 Item_3 Item_4 Item_5
## 1 7 4 4 3 5
## 2 7 6 1 4 7
## 3 3 6 1 6 1
## 4 6 1 5 1 1
## 5 3 2 3 3 2
## 6 2 3 2 7 7
ここで、Item_3
とItem_4
が逆転項目のときrevese.code()
をつかって処理するときには、keys
の引数にその列名を指定するか、そのままの列を1とし逆転項目の列を-1としたベクトルを与えると変換できる。後者がちょっとわかりにくいが次に具体例を上げる。
その他の引数は、items
で変換対象のデータセットを指定し、mini
で項目の最小値(質問紙などでもともと想定している最小の値)を指定(指定しないとデータセット中の観測値の最小値で処理)、maxi
で項目の最大値(質問紙などでもともと想定している最大値)を指定(指定しないとデータセット中の観測値の最大値で処理)する。
まずは列名を指定する方法はkey
に列名をベクトルで与えてやる次のようになる。
library(psych) df.revese <- reverse.code(keys = c("Item_3", "Item_4"), items = df, mini = 1, maxi = 7) head(df.revese)
## Item_1 Item_2 Item_3- Item_4- Item_5
## [1,] 7 4 4 5 5
## [2,] 7 6 7 4 7
## [3,] 3 6 7 2 1
## [4,] 6 1 3 7 1
## [5,] 3 2 5 5 2
## [6,] 2 3 6 1 7
これで、Item_3
とItem_4
が逆転される。このとき、逆転された列は列名の末尾に-
がつく。
次に、keys
にそのままの列を1とし逆転する列を-1としたベクトルを与えるパターンは、列分だけ1と-1で構成されたベクトルを作る。今回の例だとc(1, 1, -1, -1, 1)
というベクトルを与える。
library(psych) df.revese <- reverse.code(keys = c(1, 1, -1, -1, 1), items = df, mini = 1, maxi = 7) head(df.revese)
## Item_1 Item_2 Item_3- Item_4- Item_5
## [1,] 7 4 4 5 5
## [2,] 7 6 7 4 7
## [3,] 3 6 7 2 1
## [4,] 6 1 3 7 1
## [5,] 3 2 5 5 2
## [6,] 2 3 6 1 7
これで同様にItem_3
とItem_4
が逆転される。このときも、逆転された列はもともとの列名の末尾に-
がつく。
これが基本的挙動。
revese.code()
の注意を要する点
入力される元データは数値のみのデータでなければならない
次のようなサンプルデータを作る。Item_1
~Item_5
は先のサンプルデータを同じで1~7で回答された結果。それに加えてSex
という列にMやFで回答者の性別が含まれているデータを想定している。
アンケート回答の集計結果ではありがちな感じだと思う。
set.seed(123) df <- data.frame(Sex = sample(c("M", "F"), 10, replace = TRUE), Item_1 = sample(1:7, 10, replace = TRUE), Item_2 = sample(1:7, 10, replace = TRUE), Item_3 = sample(1:7, 10, replace = TRUE), Item_4 = sample(1:7, 10, replace = TRUE), Item_5 = sample(1:7, 10, replace = TRUE)) head(df)
## Sex Item_1 Item_2 Item_3 Item_4 Item_5
## 1 M 4 4 3 5 5
## 2 M 6 1 4 7 3
## 3 M 6 1 6 1 6
## 4 F 1 5 1 1 1
## 5 M 2 3 3 2 2
## 6 F 3 2 7 7 5
これを先程と同様にItem_3
をItem_4
が逆転項目として処理しようとすると次のようにエラーとなり処理できない
。
df.revese <- reverse.code(keys = c("Item_3", "Item_4"), items = df, mini = 1, maxi = 7)
## Error in items %*% keys.d: 数値/複素数/行列またはベクトルが要求されます
これは1と-1でのベクトルで指定された場合でも同様にエラーがでる。
df.revese <- reverse.code(keys = c(1, 1, 1, -1, -1, 1), items = df, mini = 1, maxi = 7)
## Error in items %*% keys.d: 数値/複素数/行列またはベクトルが要求されます
文字列等の列が含まれないようにするとちゃんと処理できる。
df.revese <- reverse.code(keys = c("Item_3", "Item_4"), items = df[2:6], mini = 1, maxi = 7) head(df.revese)
## Item_1 Item_2 Item_3- Item_4- Item_5
## [1,] 4 4 5 3 5
## [2,] 6 1 4 1 3
## [3,] 6 1 2 7 6
## [4,] 1 5 7 7 1
## [5,] 2 3 5 6 2
## [6,] 3 2 1 1 5
これはrevese.code()
が処理の過程で与えられたデータセットを逆転させる項目をマイナスに変換するために対角ベクトルとの内積をとっているためで文字列は内積をとれないためにエラーになっている。
ということで、revese.code()
に入力するデータは数値のみのデータはとなるようにしなければならない。
revese.code()
の出力結果は行列になる
これまでの出力結果をみてもらえると分かる通り、revese.code()
の出力結果は行列になる。
is(df.revese)
## [1] "matrix" "array" "structure" "vector"
別にわかっていればよいのだけれど、データフレームやデータテーブルであることが前提になっている関数だとそのままだと処理できない。
999を超える値はNA
になってしまう
心理尺度など処理しているとそういうことはほとんどないが、値が999を超えているとコード上NA
にに置換されるのでなきものになってしまう。
df.over <- data.frame(V1 = 998:1002, V2 = 1002:998) reverse.code(keys = rep(-1, 2), items = df.over)
## V1- V2-
## [1,] NA 998
## [2,] NA 999
## [3,] NA NA
## [4,] 999 NA
## [5,] 998 NA
マイナスの値が含まれていると逆転する処理ができないことがある
7件法のときになにか特殊な意図があって、-3~3(0を含む)として配点することがあるかもしれない。
これは逆転化できる。
df.minus <- data.frame(V1 = -3:3, V2 = 3:-3) reverse.code(keys = rep(-1, 2), items = df.minus, mini = -3, maxi = 3)
## V1- V2-
## [1,] 3 -3
## [2,] 2 -2
## [3,] 1 -1
## [4,] 0 0
## [5,] -1 1
## [6,] -2 2
## [7,] -3 3
ただし、全てマイナスか0の値だったり、0をまたいで左右対称ではない場合は逆転する処理がまくできない。
df.minus <- data.frame(V1 = -3:0, V2 = 0:-3) reverse.code(keys = rep(-1, 2), items = df.minus, mini = -3, maxi = 0)
## V1- V2-
## [1,] 6 3
## [2,] 5 4
## [3,] 4 5
## [4,] 3 6
0を挟んで非対称の場合は次の通り。
df.minus <- data.frame(V1 = -3:2, V2 = 2:-3) reverse.code(keys = rep(-1, 2), items = df.minus, mini = -3, maxi = 2)
## V1- V2-
## [1,] 4 -1
## [2,] 3 0
## [3,] 2 1
## [4,] 1 2
## [5,] 0 3
## [6,] -1 4
意図した通りに逆転できていない。
revese.code()
は逆転項目を処理する以外にもつかえ……る?
revese.code()
のドキュメントを読むと
Reverse the coding of selected items prior to scale analysis
と大きく書かれていて、逆転項目を処理する専用関数に見えるが、実際の処理を追いかけてみると
- 元のデータセットが行列データではない場合は行列化
- 逆転項目の
keys
に文字列で列名が指定された場合は、指定された列が-1になるように、逆転しない列が1、逆転する列が-1になるように列数分のベクトルを作る keys
のベクトルを用いて対角行列をつくる- 元のデータの行列と対角行列との内積をとる、つまり逆転項目の列の値は-1がかけられた値になる
- 逆転項目は-1がかけられているのでここに指定された最大値と最小値を足したものを足す
という処理をしている。
この処理をコード上で追いかけると、別にkeys
に入力される値が1か-1かに限定している部分はないのでどんな値でも受け入れる仕様になっている。
さらに、最小値や最大値は未入力だと観測値から持ってくることになっているが、これも任意で入力した場合は特に制限はないため、実際に観測されている最小値や最大値よりも狭い幅でもできる。しかも最大値や最小値は列ごとに任意で指定できる。
これが何を意味するかというとrevese.code()
は逆転する関数というより、値を任意の倍数だったり、任意の範囲の値に変換したりすることができる関数ということ。その一端として逆転処理できているという感じ。
例えば、
set.seed(123) df <- data.frame(Item_1 = sample(1:7, 10, replace = TRUE), Item_2 = sample(1:7, 10, replace = TRUE), Item_3 = sample(1:7, 10, replace = TRUE), Item_4 = sample(1:7, 10, replace = TRUE), Item_5 = sample(1:7, 10, replace = TRUE)) head(df)
## Item_1 Item_2 Item_3 Item_4 Item_5
## 1 7 4 4 3 5
## 2 7 6 1 4 7
## 3 3 6 1 6 1
## 4 6 1 5 1 1
## 5 3 2 3 3 2
## 6 2 3 2 7 7
このデータに対して、Item_1
を2倍、Item_2
を0.5倍、Item_3
を逆転処理、Item_4
を逆転させて0をまたぐ-3~3の範囲の値に変換、Item_5
をマイナスに変換するには次のようにするとできる。
reverse.code(keys = c(2, 0.5, -1, -1, -1), items = df, mini = c(1, 1, 1, -4, -3), maxi = c(7, 7, 7, -0, 3))
## Item_1 Item_2 Item_3- Item_4- Item_5-
## [1,] 14 2.0 4 1 -5
## [2,] 14 3.0 7 0 -7
## [3,] 6 3.0 7 -2 -1
## [4,] 12 0.5 3 3 -1
## [5,] 6 1.0 5 1 -2
## [6,] 4 1.5 6 -3 -7
## [7,] 4 2.5 1 -1 -3
## [8,] 12 1.5 6 0 -4
## [9,] 6 1.5 7 -3 -5
## [10,] 10 0.5 2 2 -7
とまぁいろいろ工夫すれば値の変換ができる。ただし、そもそも関数側の意図した挙動ではないと思う。
ということで逆転処理以外もできる……のか?
そもそも逆転処理するなら普通に足し引きをしたらよい気がする
冒頭にも書いたが、逆転処理するなら最大値と最小値を足した値から元の値をひいいたら良い。
set.seed(123) df <- data.frame(Item_1 = sample(1:7, 10, replace = TRUE), Item_2 = sample(1:7, 10, replace = TRUE), Item_3 = sample(1:7, 10, replace = TRUE), Item_4 = sample(1:7, 10, replace = TRUE), Item_5 = sample(1:7, 10, replace = TRUE)) head(df)
## Item_1 Item_2 Item_3 Item_4 Item_5
## 1 7 4 4 3 5
## 2 7 6 1 4 7
## 3 3 6 1 6 1
## 4 6 1 5 1 1
## 5 3 2 3 3 2
## 6 2 3 2 7 7
これのItem_3
を逆転する(逆転項目をItem_3R
とする)なら、{dplyr}
を使えば次のようにもかける。
library(dplyr)
##
## 次のパッケージを付け加えます: 'dplyr'
## 以下のオブジェクトは 'package:stats' からマスクされています:
##
## filter, lag
## 以下のオブジェクトは 'package:base' からマスクされています:
##
## intersect, setdiff, setequal, union
df.revese <- df %>% mutate(Item_3 = 7 + 1 - Item_3) %>% rename(Item_3R = Item_3) head(df.revese)
## Item_1 Item_2 Item_3R Item_4 Item_5
## 1 7 4 4 3 5
## 2 7 6 7 4 7
## 3 3 6 7 6 1
## 4 6 1 3 1 1
## 5 3 2 5 3 2
## 6 2 3 6 7 7
で逆転できる。しかも名付けは自由。今回は列名を変えたが、別に名前を変えなくてもいいし、別の列として併存させることもできる。
その他の0をまたいで……や、マイナスにするなどの簡単にできる。
蛇足:car::recode()
をつかって逆転処理をする
{car}
パッケージのにも値変換系のrecode()
関数もあり、これを使って逆転処理する方法もある。
df.revese <- df %>% mutate(Item_3 = car::recode(Item_3, "7 = 1; 6 = 2; 5 = 3; 3 = 5; 2 = 6; 1 = 7")) %>% rename(Item_3R = Item_3) head(df.revese)
## Item_1 Item_2 Item_3R Item_4 Item_5
## 1 7 4 4 3 5
## 2 7 6 7 4 7
## 3 3 6 7 6 1
## 4 6 1 3 1 1
## 5 3 2 5 3 2
## 6 2 3 6 7 7
一見簡単にみえるが、recodes
引数の中の書き方がちょっと特殊。あと書くことが多い。
というかこんなに書くなら、{dplyr}
のcase_when()
とかでいい。