使用指示向量从宽变长
reshape wide to long with indicator vector
我有这样一个数据框:
df <- data.frame(v11=rnorm(1), v12=rnorm(1), v21=rnorm(1), v31=rnorm(1), v41=rnorm(1), v42=rnorm(1), v43=rnorm(1))
这个数据框只有一行
df
# v11 v12 v21 v31 v41 v42 v43
# 1 -0.4425873 0.6062714 -0.3816921 0.2477926 0.1292103 0.2801346 0.4290997
有些列是对同一主题的重复观察,例如 v11
和 v12
是来自同一主题的两次观察。
v21
和 v32
只是单个观察值。 v4x
有三个观察结果。
我想将数据框重塑为如下所示:
v11 v21 v31 v41
v12 NA NA v42
NA NA NA v43
请注意,它应该包含变量的值而不是它们的名称。
我有一个指示向量,用于标记哪个观察是什么:
v <- c(1, 2, 1, 1, 1, 2, 3)
每个“1”表示新列的开始。
我的第一个想法是单独构建每一行:
row1 <- df[, v == 1]
row2 <- df[, v == 2]
row3 <- df[, v == 3]
但这不包含 NA。
一定有更简单的方法。
注意:解决方案不必依赖于原始列名v11
、v41
等,而只依赖于向量v
.
这是 dplyr
+ tidyr
的解决方案:
library(dplyr)
library(tidyr)
v <- c(1,2,1,1,1,2,3)
df %>%
gather(var, value) %>%
mutate(row_num = v,
col_num = paste0("v", cumsum(v==1))) %>%
select(-var) %>%
spread(col_num, value)
结果:
row_num v1 v2 v3 v4
1 1 -0.5604756 1.558708 0.07050839 0.1292877
2 2 -0.2301775 NA NA 1.7150650
3 3 NA NA NA 0.4609162
数据:
df = structure(list(v11 = -0.560475646552213, v12 = -0.23017748948328,
v21 = 1.55870831414912, v32 = 0.070508391424576, v41 = 0.129287735160946,
v42 = 1.71506498688328, v43 = 0.460916205989202), .Names = c("v11",
"v12", "v21", "v32", "v41", "v42", "v43"), row.names = c(NA,
-1L), class = "data.frame")
您也可以使用 melt
和 dcast
来自 reshape2
和 transform
:
library(reshape2)
dcast(transform(melt(df),
rownum = substr(variable,3,3),
cols = substr(variable,1,2)),
rownum ~ cols, value.var = 'value')
结果:
rownum v1 v2 v3 v4
1 1 1.43420148 0.7391372 -1.758605 -0.06982523
2 2 -0.07729196 NA NA 0.45190553
3 3 NA NA NA -1.95836646
数据:
set.seed(2017)
df <- data.frame(v11=rnorm(1), v12=rnorm(1), v21=rnorm(1), v31=rnorm(1), v41=rnorm(1), v42=rnorm(1), v43=rnorm(1))
这是基础 R
的解决方案,仅取决于对测量数据进行分组的向量 v
:
首先,示例数据:
set.seed(0)
df <- data.frame(v11=rnorm(1), v12=rnorm(1), v21=rnorm(1), v31=rnorm(1), v41=rnorm(1), v42=rnorm(1), v43=rnorm(1))
# v11 v12 v21 v31 v41 v42 v43
#1 1.262954 -0.3262334 1.329799 1.272429 0.4146414 -1.53995 -0.928567
v <- c(1,2,1,1,1,2,3)
然后做:
v_grp <- cumsum(v == 1) # create index that groups the measurement data
out <- split(unlist(df), v_grp) # split vector of measurements by group
## following required to pad split vectors with `NA`.
## There are packages that give functions that do this
max_in_group <- max(summary(factor(v_grp)))
out <- lapply(out, function(v) {
num_in_vec <- length(v)
if (num_in_vec < max_in_group) {
v <- c(v, rep(NA, max_in_group - num_in_vec))
}
return(unname(v))
})
out <- do.call(cbind, out)
out
# 1 2 3 4
# v11 1.2629543 1.329799 1.272429 0.4146414
# v12 -0.3262334 NA NA -1.5399500
# NA NA NA -0.9285670
这是使用矩阵索引的完美案例,因为您已经有了行索引。
操作方法如下:
M <- matrix(NA, nrow = max(v), ncol = sum(v == 1))
M[cbind(v, cumsum(v == 1))] <- unlist(df, use.names = FALSE)
M
# [,1] [,2] [,3] [,4]
# [1,] -0.5604756 1.558708 0.07050839 0.1292877
# [2,] -0.2301775 NA NA 1.7150650
# [3,] NA NA NA 0.4609162
本质上,您首先创建一个 matrix
填充 NA
值,然后您使用 "v" (您的行索引)创建您的列索引,子集该矩阵,并将值替换为 "df".
中未列出的值
df
在这个答案是来自用户的答案。
我有这样一个数据框:
df <- data.frame(v11=rnorm(1), v12=rnorm(1), v21=rnorm(1), v31=rnorm(1), v41=rnorm(1), v42=rnorm(1), v43=rnorm(1))
这个数据框只有一行
df
# v11 v12 v21 v31 v41 v42 v43
# 1 -0.4425873 0.6062714 -0.3816921 0.2477926 0.1292103 0.2801346 0.4290997
有些列是对同一主题的重复观察,例如 v11
和 v12
是来自同一主题的两次观察。
v21
和 v32
只是单个观察值。 v4x
有三个观察结果。
我想将数据框重塑为如下所示:
v11 v21 v31 v41
v12 NA NA v42
NA NA NA v43
请注意,它应该包含变量的值而不是它们的名称。
我有一个指示向量,用于标记哪个观察是什么:
v <- c(1, 2, 1, 1, 1, 2, 3)
每个“1”表示新列的开始。
我的第一个想法是单独构建每一行:
row1 <- df[, v == 1]
row2 <- df[, v == 2]
row3 <- df[, v == 3]
但这不包含 NA。 一定有更简单的方法。
注意:解决方案不必依赖于原始列名v11
、v41
等,而只依赖于向量v
.
这是 dplyr
+ tidyr
的解决方案:
library(dplyr)
library(tidyr)
v <- c(1,2,1,1,1,2,3)
df %>%
gather(var, value) %>%
mutate(row_num = v,
col_num = paste0("v", cumsum(v==1))) %>%
select(-var) %>%
spread(col_num, value)
结果:
row_num v1 v2 v3 v4
1 1 -0.5604756 1.558708 0.07050839 0.1292877
2 2 -0.2301775 NA NA 1.7150650
3 3 NA NA NA 0.4609162
数据:
df = structure(list(v11 = -0.560475646552213, v12 = -0.23017748948328,
v21 = 1.55870831414912, v32 = 0.070508391424576, v41 = 0.129287735160946,
v42 = 1.71506498688328, v43 = 0.460916205989202), .Names = c("v11",
"v12", "v21", "v32", "v41", "v42", "v43"), row.names = c(NA,
-1L), class = "data.frame")
您也可以使用 melt
和 dcast
来自 reshape2
和 transform
:
library(reshape2)
dcast(transform(melt(df),
rownum = substr(variable,3,3),
cols = substr(variable,1,2)),
rownum ~ cols, value.var = 'value')
结果:
rownum v1 v2 v3 v4
1 1 1.43420148 0.7391372 -1.758605 -0.06982523
2 2 -0.07729196 NA NA 0.45190553
3 3 NA NA NA -1.95836646
数据:
set.seed(2017)
df <- data.frame(v11=rnorm(1), v12=rnorm(1), v21=rnorm(1), v31=rnorm(1), v41=rnorm(1), v42=rnorm(1), v43=rnorm(1))
这是基础 R
的解决方案,仅取决于对测量数据进行分组的向量 v
:
首先,示例数据:
set.seed(0)
df <- data.frame(v11=rnorm(1), v12=rnorm(1), v21=rnorm(1), v31=rnorm(1), v41=rnorm(1), v42=rnorm(1), v43=rnorm(1))
# v11 v12 v21 v31 v41 v42 v43
#1 1.262954 -0.3262334 1.329799 1.272429 0.4146414 -1.53995 -0.928567
v <- c(1,2,1,1,1,2,3)
然后做:
v_grp <- cumsum(v == 1) # create index that groups the measurement data
out <- split(unlist(df), v_grp) # split vector of measurements by group
## following required to pad split vectors with `NA`.
## There are packages that give functions that do this
max_in_group <- max(summary(factor(v_grp)))
out <- lapply(out, function(v) {
num_in_vec <- length(v)
if (num_in_vec < max_in_group) {
v <- c(v, rep(NA, max_in_group - num_in_vec))
}
return(unname(v))
})
out <- do.call(cbind, out)
out
# 1 2 3 4
# v11 1.2629543 1.329799 1.272429 0.4146414
# v12 -0.3262334 NA NA -1.5399500
# NA NA NA -0.9285670
这是使用矩阵索引的完美案例,因为您已经有了行索引。
操作方法如下:
M <- matrix(NA, nrow = max(v), ncol = sum(v == 1))
M[cbind(v, cumsum(v == 1))] <- unlist(df, use.names = FALSE)
M
# [,1] [,2] [,3] [,4]
# [1,] -0.5604756 1.558708 0.07050839 0.1292877
# [2,] -0.2301775 NA NA 1.7150650
# [3,] NA NA NA 0.4609162
本质上,您首先创建一个 matrix
填充 NA
值,然后您使用 "v" (您的行索引)创建您的列索引,子集该矩阵,并将值替换为 "df".
df
在这个答案是来自用户的答案。