收集多组列

我有一个在线调查的数据,受访者经历了1-3次的问题循环。 调查软件(Qualtrics)将这些数据记录在多个栏目中,也就是说,调查中的Q3.2.1.将具有Q3.2.1.Q3.2.2.Q3.2.3.

 df <- data.frame( id = 1:10, time = as.Date('2009-01-01') + 0:9, Q3.2.1. = rnorm(10, 0, 1), Q3.2.2. = rnorm(10, 0, 1), Q3.2.3. = rnorm(10, 0, 1), Q3.3.1. = rnorm(10, 0, 1), Q3.3.2. = rnorm(10, 0, 1), Q3.3.3. = rnorm(10, 0, 1) ) # Sample data id time Q3.2.1. Q3.2.2. Q3.2.3. Q3.3.1. Q3.3.2. Q3.3.3. 1 1 2009-01-01 -0.2059165 -0.29177677 -0.7107192 1.52718069 -0.4484351 -1.21550600 2 2 2009-01-02 -0.1981136 -1.19813815 1.1750200 -0.40380049 -1.8376094 1.03588482 3 3 2009-01-03 0.3514795 -0.27425539 1.1171712 -1.02641801 -2.0646661 -0.35353058 ... 

我想把所有的QN.N *列整合到单独的QN.N列中,最终得到如下的结果:

  id time loop_number Q3.2 Q3.3 1 1 2009-01-01 1 -0.20591649 1.52718069 2 2 2009-01-02 1 -0.19811357 -0.40380049 3 3 2009-01-03 1 0.35147949 -1.02641801 ... 11 1 2009-01-01 2 -0.29177677 -0.4484351 12 2 2009-01-02 2 -1.19813815 -1.8376094 13 3 2009-01-03 2 -0.27425539 -2.0646661 ... 21 1 2009-01-01 3 -0.71071921 -1.21550600 22 2 2009-01-02 3 1.17501999 1.03588482 23 3 2009-01-03 3 1.11717121 -0.35353058 ... 

tidyr库具有gather()函数,该函数非常适合组合组列:

 library(dplyr) library(tidyr) library(stringr) df %>% gather(loop_number, Q3.2, starts_with("Q3.2")) %>% mutate(loop_number = str_sub(loop_number,-2,-2)) %>% select(id, time, loop_number, Q3.2) id time loop_number Q3.2 1 1 2009-01-01 1 -0.20591649 2 2 2009-01-02 1 -0.19811357 3 3 2009-01-03 1 0.35147949 ... 29 9 2009-01-09 3 -0.58581232 30 10 2009-01-10 3 -2.33393981 

结果数据帧有30行,如预期的那样(10个人,每个3个循环)。 但是,收集第二组列并不能正确工作 – 它成功地创建了两个组合列Q3.2Q3.3 ,但最终以90行而不是30行(所有10个人的组合,Q3.2的3个循环,Q3.3的3个循环;对于实际数据中的每组列,组合将大大增加):

 df %>% gather(loop_number, Q3.2, starts_with("Q3.2")) %>% gather(loop_number, Q3.3, starts_with("Q3.3")) %>% mutate(loop_number = str_sub(loop_number,-2,-2)) id time loop_number Q3.2 Q3.3 1 1 2009-01-01 1 -0.20591649 1.52718069 2 2 2009-01-02 1 -0.19811357 -0.40380049 3 3 2009-01-03 1 0.35147949 -1.02641801 ... 89 9 2009-01-09 3 -0.58581232 -0.13187024 90 10 2009-01-10 3 -2.33393981 -0.48502131 

有没有办法使用多个调用gather()像这样,结合这样的列的小子集,同时保持正确的行数?

这个方法对我来说很自然:

 df %>% gather(key, value, -id, -time) %>% extract(key, c("question", "loop_number"), "(Q.\\..)\\.(.)") %>% spread(question, value) 

首先收集所有的问题列,使用extract()分解成questionloop_number ,然后spread()问题回到列。

 #> id time loop_number Q3.2 Q3.3 #> 1 1 2009-01-01 1 0.142259203 -0.35842736 #> 2 1 2009-01-01 2 0.061034802 0.79354061 #> 3 1 2009-01-01 3 -0.525686204 -0.67456611 #> 4 2 2009-01-02 1 -1.044461185 -1.19662936 #> 5 2 2009-01-02 2 0.393808163 0.42384717 

这可以使用reshape来完成。 尽管dplyr是可能的。

  colnames(df) <- gsub("\\.(.{2})$", "_\\1", colnames(df)) colnames(df)[2] <- "Date" res <- reshape(df, idvar=c("id", "Date"), varying=3:8, direction="long", sep="_") row.names(res) <- 1:nrow(res) head(res) # id Date time Q3.2 Q3.3 #1 1 2009-01-01 1 1.3709584 0.4554501 #2 2 2009-01-02 1 -0.5646982 0.7048373 #3 3 2009-01-03 1 0.3631284 1.0351035 #4 4 2009-01-04 1 0.6328626 -0.6089264 #5 5 2009-01-05 1 0.4042683 0.5049551 #6 6 2009-01-06 1 -0.1061245 -1.7170087 

或者使用dplyr

  library(tidyr) library(dplyr) colnames(df) <- gsub("\\.(.{2})$", "_\\1", colnames(df)) df %>% gather(loop_number, "Q3", starts_with("Q3")) %>% separate(loop_number,c("L1", "L2"), sep="_") %>% spread(L1, Q3) %>% select(-L2) %>% head() # id time Q3.2 Q3.3 #1 1 2009-01-01 1.3709584 0.4554501 #2 1 2009-01-01 1.3048697 0.2059986 #3 1 2009-01-01 -0.3066386 0.3219253 #4 2 2009-01-02 -0.5646982 0.7048373 #5 2 2009-01-02 2.2866454 -0.3610573 #6 2 2009-01-02 -1.7813084 -0.7838389 

随着最近更新到melt.data.table ,我们现在可以融化多个列。 有了这个,我们可以做到:

 require(data.table) ## 1.9.5 melt(setDT(df), id=1:2, measure=patterns("^Q3.2", "^Q3.3"), value.name=c("Q3.2", "Q3.3"), variable.name="loop_number") # id time loop_number Q3.2 Q3.3 # 1: 1 2009-01-01 1 -0.433978480 0.41227209 # 2: 2 2009-01-02 1 -0.567995351 0.30701144 # 3: 3 2009-01-03 1 -0.092041353 -0.96024077 # 4: 4 2009-01-04 1 1.137433487 0.60603396 # 5: 5 2009-01-05 1 -1.071498263 -0.01655584 # 6: 6 2009-01-06 1 -0.048376809 0.55889996 # 7: 7 2009-01-07 1 -0.007312176 0.69872938 

你可以从这里得到开发版本。

这与“tidyr”和“dplyr”没有任何关系,但是这里有另外一种选择:从我的“splitstackshape”包 ,V1.4.0及以上的版本中合并.stack。

 library(splitstackshape) merged.stack(df, id.vars = c("id", "time"), var.stubs = c("Q3.2.", "Q3.3."), sep = "var.stubs") # id time .time_1 Q3.2. Q3.3. # 1: 1 2009-01-01 1. -0.62645381 1.35867955 # 2: 1 2009-01-01 2. 1.51178117 -0.16452360 # 3: 1 2009-01-01 3. 0.91897737 0.39810588 # 4: 2 2009-01-02 1. 0.18364332 -0.10278773 # 5: 2 2009-01-02 2. 0.38984324 -0.25336168 # 6: 2 2009-01-02 3. 0.78213630 -0.61202639 # 7: 3 2009-01-03 1. -0.83562861 0.38767161 # <<:::SNIP:::>> # 24: 8 2009-01-08 3. -1.47075238 -1.04413463 # 25: 9 2009-01-09 1. 0.57578135 1.10002537 # 26: 9 2009-01-09 2. 0.82122120 -0.11234621 # 27: 9 2009-01-09 3. -0.47815006 0.56971963 # 28: 10 2009-01-10 1. -0.30538839 0.76317575 # 29: 10 2009-01-10 2. 0.59390132 0.88110773 # 30: 10 2009-01-10 3. 0.41794156 -0.13505460 # id time .time_1 Q3.2. Q3.3. 

如果你像我一样,不能解决如何使用“捕获组的正则表达式” extract ,下面的代码复制Hadleys的答案extract(...)行:

 df %>% gather(question_number, value, starts_with("Q3.")) %>% mutate(loop_number = str_sub(question_number,-2,-2), question_number = str_sub(question_number,1,4)) %>% select(id, time, loop_number, question_number, value) %>% spread(key = question_number, value = value) 

这里的问题是最初的集合形成了一个实际上是两个键的组合的关键列。 我选择在我的原始解决方案中使用mutate来将此列拆分为两列,分别具有相同的信息,一个loop_number列和一个question_number列。 然后可以使用扩展将长格式数据(它是关键值对(question_number, value)为宽格式数据。

Interesting Posts