使用指示向量从宽变长

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

有些列是对同一主题的重复观察,例如 v11v12 是来自同一主题的两次观察。 v21v32 只是单个观察值。 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。 一定有更简单的方法。

注意:解决方案不必依赖于原始列名v11v41等,而只依赖于向量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")

您也可以使用 meltdcast 来自 reshape2transform:

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在这个答案是来自用户的答案。