将多列从宽格式转换为长格式。
Transforming multible columns from wide to long format.
首先 - 我不认为这是重复的 post。我发现了几个关于将多列从宽格式转换为长格式的很棒的 post 和网页,但其中 none 与我的数据相似,因为它们处理的是同一事物的多个列(在我的例子中是 A1、A2、A3、A4),而且它们还没有包含长格式变量(在我的例子中是 frame)。
这是我的问题:
我正在处理一个数据集,其中包含由两个不同的运动捕捉系统测量的许多变量。目前我的数据集是宽格式,但我意识到 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 个变量,因此我想要一个不需要我为每个变量编写代码的解决方案。我正在考虑制作一个循环来解决这个问题,但是在这方面的任何帮助也将不胜感激。
tidyr
方法是:1) 收集度量列,2) 将 headers 分成 movements
(字母)+ system
(数字)使用extract
与 regex, 3) 传播 movements
到 headers:
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
运行 reshape
然后对结果进行排序。
前 4 行设置 reshape
的参数。特别是, varying
是 list(A = c("A1", "A2"), B = c("B1", "B2"), C = c("C1", "C2"))
。最后一行代码对行进行排序,如果行顺序不重要,则可以省略。
这里的 A... 列与 B... 列和 C... 列具有相同的类型,但即使不是这种情况,此解决方案也将继续有效。
没有使用包。
这个 SO question/answer 很相似,但有细微的差别:
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"))
首先 - 我不认为这是重复的 post。我发现了几个关于将多列从宽格式转换为长格式的很棒的 post 和网页,但其中 none 与我的数据相似,因为它们处理的是同一事物的多个列(在我的例子中是 A1、A2、A3、A4),而且它们还没有包含长格式变量(在我的例子中是 frame)。
这是我的问题:
我正在处理一个数据集,其中包含由两个不同的运动捕捉系统测量的许多变量。目前我的数据集是宽格式,但我意识到 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 个变量,因此我想要一个不需要我为每个变量编写代码的解决方案。我正在考虑制作一个循环来解决这个问题,但是在这方面的任何帮助也将不胜感激。
tidyr
方法是:1) 收集度量列,2) 将 headers 分成 movements
(字母)+ system
(数字)使用extract
与 regex, 3) 传播 movements
到 headers:
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
运行 reshape
然后对结果进行排序。
前 4 行设置 reshape
的参数。特别是, varying
是 list(A = c("A1", "A2"), B = c("B1", "B2"), C = c("C1", "C2"))
。最后一行代码对行进行排序,如果行顺序不重要,则可以省略。
这里的 A... 列与 B... 列和 C... 列具有相同的类型,但即使不是这种情况,此解决方案也将继续有效。
没有使用包。
这个 SO question/answer 很相似,但有细微的差别:
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"))