LoginSignup
2
3

More than 5 years have passed since last update.

複数選択データが1カラムで来たときにRで分割する方法

Last updated at Posted at 2018-03-29

いわゆるマルチアンサー(以下、MA)、ようするに複数選択のデータなんですけれども、一般的にはこんな感じのCSVでやってくるはずですよね。

sampl.csv
q1,q2_1,q2_2,q2_3
"1","1","0","0"
"2","1","1","0"
"1","0","1","0"
"2","1","1","1"
"1","0","1","1"
"2","0","0","1"
"1","1","0","1"
"2","1","0","0"

ですが、たまにこんな時があって非常に困ります。

sampl.csv
q1,q2
"1","1"
"2","1,2"
"1","2"
"2","1,2,3"
"1","2,3"
"2","3"
"1","1,3"
"2","1"

べつにRubyとかPythonとかで変換しても良いのですけれども、Rの中でやっちゃいたいとき用のメモです。

Rのバージョンはこちら

> R.version.string
[1] "R version 3.4.4 (2018-03-15)"

比較的最新版です。

まず、サンプルデータ

を用意してみました。

sample
> test <- data.frame(q1=c(1,2,1,2,1,2,1,2), q2=c("1","1,2","2","1,2,3","2,3","3","1,3","1"))

> test
  q1    q2
1  1     1
2  2   1,2
3  1     2
4  2 1,2,3
5  1   2,3
6  2     3
7  1   1,3
8  2     1

> class(test$q1)
[1] "numeric"

> class(test$q2)
[1] "factor"

> test$q2
[1] 1     1,2   2     1,2,3 2,3   3     1,3   1    
Levels: 1 1,2 1,2,3 1,3 2 2,3 3

いろいろ方法はあるのだと思いますが、まだまだRをはじめたばかりでして、基本的な構文しか知りません…… なので非常にベタな方法でいきたいと思います。

とりあえず、カンマ区切りを分割

したいところですが、型がfactorですので、そのままでは分割できません。

> strsplit(test$q2, ",")
Error in strsplit(test$q2, ",") : non-character argument

なので、まずは型を文字列にしてから分割します。

> strsplit(as.character(test$q2), ",")
[[1]]
[1] "1"

[[2]]
[1] "1" "2"

[[3]]
[1] "2"

[[4]]
[1] "1" "2" "3"

[[5]]
[1] "2" "3"

[[6]]
[1] "3"

[[7]]
[1] "1" "3"

[[8]]
[1] "1"

こんな感じ。ちなみにstrsplitの出力はlistです。Rは型が複雑でなかなか大変ですね。このままでは使いにくいので、これを一旦代入しておきます。

> q2A <- strsplit(as.character(test$q2), ",")

listをループでまわしてみる

listについては後半でまとめているので割愛。こんな感じでループで回せます。

> for (y in 1:length(q2A)) {
+     for (x in 1:length(q2A[[y]])) {
+         print(q2A[[y]][x])
+     }
+ }
[1] "1"
[1] "1"
[1] "2"
[1] "2"
[1] "1"
[1] "2"
[1] "3"
[1] "2"
[1] "3"
[1] "3"
[1] "1"
[1] "3"
[1] "1"

元のデータフレームに追加する

さて、データフレームですが、1行追加や1列追加はbind系のコマンドで意外と簡単です。
今回のMAは1,2,3の3選択肢で、本来でしたら1行ずつ、3つ分のデータを8回追加していきたいところですが、そういう方法は探してみたけど見つからなかったので、全8レコード分の1列を3回追加する方針で行きます。

> for (i in 1:3) {
+     tmp <- numeric(8)
+     for (y in 1:length(q2A)) {
+         for (x in 1:length(q2A[[y]])) {
+             n <- as.integer(q2A[[y]][x])
+             if (n == i) {
+                 tmp[y] = 1
+             }
+         }
+     }
+     test <- cbind(test, tmp)
+ }

> test
  q1    q2 tmp tmp tmp
1  1     1   1   0   0
2  2   1,2   1   1   0
3  1     2   0   1   0
4  2 1,2,3   1   1   1
5  1   2,3   0   1   1
6  2     3   0   0   1
7  1   1,3   1   0   1
8  2     1   1   0   0

やっていることは簡単なので、ここも割愛です。Rでの実装方法を把握するまでが面倒なんですよね……

名前を付ける

このままでは全て「tmp」なので、名前を付けます。これも一発です。

> names(test)[3:5] <- paste("q2", 1:3, sep="_")
> test
  q1    q2 q2_1 q2_2 q2_3
1  1     1    1    0    0
2  2   1,2    1    1    0
3  1     2    0    1    0
4  2 1,2,3    1    1    1
5  1   2,3    0    1    1
6  2     3    0    0    1
7  1   1,3    1    0    1
8  2     1    1    0    0

集計してみる

> summarise_all(data.frame(test[3:5]), funs(sum))
  q2_1 q2_2 q2_3
1    5    4    4

> reshape2::melt(summarise_all(data.frame(test[3:5]), funs(sum)))
No id variables; using all as measure variables
  variable value
1     q2_1     5
2     q2_2     4
3     q2_3     4

> transform(reshape2::melt(summarise_all(data.frame(test[3:5]), funs(sum))), prop=value/nrow(test))
No id variables; using all as measure variables
  variable value  prop
1     q2_1     5 0.625
2     q2_2     4 0.500
3     q2_3     4 0.500

大丈夫そうですね。

listについてのメモ

以下、Appendix的な。
listってのが複雑でして、以下の通りとなります。

> q2A[2]
[[1]]
[1] "1" "2"

> q2A[[2]]
[1] "1" "2"

> q2A[2][2]
[[1]]
NULL

> q2A[[2]][2]
[1] "2"

まず、[]では返ってくるのがlistでして、[[]]だと中身が返ってくる感じです。以下、確認です。

> str(q2A[2])
List of 1
 $ : chr [1:2] "1" "2"

> str(q2A[[2]])
 chr [1:2] "1" "2"

なので、以下の2つが同じことになります。

> q2A[[2]][2]
[1] "2"

> unlist(q2A[2])[2]
[1] "2"
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3