将多字列从宽格式转换为长格式。

时间:2017-10-22 14:37:24

标签: r tidyr

首先 - 我不认为这是一个重复的帖子。我发现了几个很棒的帖子和网页,用于将多个列从宽格式转换为长格式,但是它们都没有与我的数据类似,因为它们处理的是几个相同尺寸的列(在我的例子中是A1,A2) ,A3,A4),它们还没有包含长格式变量(在我的情况下为框架)。

这是我的问题:

我正在使用包含由两个不同运动捕捉系统测量的许多变量的数据集。目前我的数据集是宽格式的,但我已经意识到ggplot在长格式下工作得更好,因此我希望转换我的数据。

以下是我的数据的简化版本:

id <- (rep(1:3, each = 3))
frame <- (rep(1:3, 3))
A1 <- runif(9, min =1, max =100)
B1 <- runif(9, min =1, max =10)
C1 <- runif(9, min =-10, max =10)
A2 <- rnorm(9, mean = A1, sd=1)
B2 <- rnorm(9, mean = B1, sd=1)
C2 <- rnorm(9, mean = C1, sd=1)
df_wide <- as.data.frame.matrix(cbind(id, frame, A1, B1, C1, A2, B2, C2))
rm(id, frame, A1, A2, B1, B2, C1, C2)

df_wide$id <- as.factor(df_wide$id)
df_wide$frame <- as.factor(df_wide$frame)

head(df_wide)

  id frame        A1       B1        C1        A2       B2         C2
1  1     1 50.940395 4.141713 -1.294736 51.324398 4.271260  0.6174782
2  1     2 33.117691 5.044080  1.820367 32.977860 5.506677  0.8811504
3  1     3 50.000625 8.584148 -1.294245 50.603195 8.099262  0.6418580
4  2     1 61.675927 5.269216 -6.002856 61.996378 6.186417 -6.5428624
5  2     2  5.514353 6.570010  5.199728  4.798275 4.955662  5.1502535
6  2     3 51.580086 5.683788  9.831663 50.717459 5.430070 10.9601541
A1和A2,B1和B2是由系统1和系统2进行的相同类型的运动(A和B)的测量。 如框架变量所示,每位患者已经进行了多次测量。

我希望我的data.frame看起来像这样:

  id frame system        A        B         C
1  1     1      1 
2  1     1      2 
3  1     2      1 
4  1     2      2 
5  1     3      1 
6  1     3      2 

我有两个问题阻止我解决这个问题:

1)两个系统之间的测量不是彼此相邻的。因此我不能使用这样的代码:

library(tidyr)
df_long <- gather(df_wide, System, A, A1:A2, factor_key=TRUE)

2)我的数据集包含近120个变量,因此我想要一个不需要我为每个变量编写代码的解决方案。我正在考虑制作一个循环来解决这个问题,但是在这方面的任何帮助都将非常受欢迎。

2 个答案:

答案 0 :(得分:2)

tidyr方法是:1)收集度量列,2)使用movements将标题分隔为system(alpha)+ extract(数字) 正则表达式,3)将movements传播到标题:

library(tidyr)

df_wide %>% 
    gather(keys, values, -id, -frame) %>% 
    extract(keys, c("movements", "system"), "([a-zA-Z]+)([0-9]+)") %>% 
    spread(movements, values)

#   id frame system         A        B         C
#1   1     1      1 62.175823 9.661748 -9.120404
#2   1     1      2 62.957358 9.229938 -8.814429
#3   1     2      1 22.463641 3.904546  4.059267
#4   1     2      2 22.798492 3.045190  4.663611
#5   1     3      1 13.897632 6.675986 -9.528184
#6   1     3      2 15.036539 6.964412 -8.920507
#7   2     1      1 38.765030 7.735174  8.373283
#8   2     1      2 40.124285 4.947368 10.143035
#9   2     2      1  5.924254 9.358200  9.866305
#10  2     2      2  5.197255 9.859347 10.088928
#11  2     3      1 29.961107 7.451472 -3.143658
#12  2     3      2 31.322740 8.328626 -2.050261
#13  3     1      1 71.010782 6.909414  7.128306
#14  3     1      2 69.860047 7.675693  7.817473
#15  3     2      1 64.985282 1.596932 -3.422237
#16  3     2      2 64.839996 2.828168 -3.826748
#17  3     3      1 70.631159 1.238806  5.398818
#18  3     3      2 70.963814 1.255340  3.728302

答案 1 :(得分:2)

运行reshape,然后对结果进行排序。

前4行设置了reshape的参数。特别是,varyinglist(A = c("A1", "A2"), B = c("B1", "B2"), C = c("C1", "C2"))。最后一行代码对行进行排序,如果行顺序不重要,则可以省略。

这里的A ...列与B ...列和C ...列的类型相同,但即使不是这样,这个解决方案也会继续有效。

没有使用任何包裹。

这个问题/答案是相似的,但差异很小:Gather multiple date/value columns using tidyr

idvar <- 1:2
nms <- names(df_wide)[-idvar]   # names of non-id variables
varying <- split(nms, sub("\\d+$", "", nms))
v.names <- names(varying)
r <- reshape(df_wide, dir = "long", varying = varying, v.names = v.names, idvar = idvar)
r[order(r$id, r$frame), ]

,并提供:

      id frame time         A        B          C
1.1.1  1     1    1 50.940395 4.141713 -1.2947360
1.1.2  1     1    2 51.324398 4.271260  0.6174782
1.2.1  1     2    1 33.117691 5.044080  1.8203670
1.2.2  1     2    2 32.977860 5.506677  0.8811504
1.3.1  1     3    1 50.000625 8.584148 -1.2942450
1.3.2  1     3    2 50.603195 8.099262  0.6418580
2.1.1  2     1    1 61.675927 5.269216 -6.0028560
2.1.2  2     1    2 61.996378 6.186417 -6.5428624
2.2.1  2     2    1  5.514353 6.570010  5.1997280
2.2.2  2     2    2  4.798275 4.955662  5.1502535
2.3.1  2     3    1 51.580086 5.683788  9.8316630
2.3.2  2     3    2 50.717459 5.430070 10.9601541

注意:可重复形式的输入如下 - 在问题中生成输入的代码不可重现,因为没有set.seed使用随机数。

df_wide <- structure(list(id = c(1L, 1L, 1L, 2L, 2L, 2L), frame = c(1L, 
2L, 3L, 1L, 2L, 3L), A1 = c(50.940395, 33.117691, 50.000625, 
61.675927, 5.514353, 51.580086), B1 = c(4.141713, 5.04408, 8.584148, 
5.269216, 6.57001, 5.683788), C1 = c(-1.294736, 1.820367, -1.294245, 
-6.002856, 5.199728, 9.831663), A2 = c(51.324398, 32.97786, 50.603195, 
61.996378, 4.798275, 50.717459), B2 = c(4.27126, 5.506677, 8.099262, 
6.186417, 4.955662, 5.43007), C2 = c(0.6174782, 0.8811504, 0.641858, 
-6.5428624, 5.1502535, 10.9601541)), .Names = c("id", "frame", 
"A1", "B1", "C1", "A2", "B2", "C2"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6"))