每 n 列重塑从宽到长

Reshaping wide to long with every n columns

假设我有一个数据框:

dw <- read.table(header=T, text='
 ID     q1    q2   q3     q4     q5    ...q10  
   A   10    6     50     10      bA   
   B   12    5     70     11      bB
   C   20    7     20     8       bC
   D   22    8     22     9       bD
 ')

我想将 'ID' 之后的每 2 列移动到新行,因此它看起来像:

   ID  q1   q2 
   A   10    6     
   B   12    5     
   C   20    7     
   D   22    8 
   A   50    10
   B   70   11
   C   20   8
   D   22   9
   ....

pivot_longer 似乎移动了每一列而不是多列?

您似乎并不关心列名(ID 除外),它们都是相同的 class。为此,我们可以手动“旋转”,也许没有 pivot_lower 的保护措施或功能,但也没有要求。

第一步是确保 class 不会成为问题;因为那里有一些字符串,我们需要将所有字符串转换为 character:

dw[-1] <- lapply(dw[-1], as.character)

之后,我们可以手动提取每两列(非ID)并与ID组合:

cols <- seq_along(dw)[-1]
list_of_frames <- lapply(split(cols, cols %/% 2), function(ind) setNames(dw[,c(1, ind)], c("ID", "q1", "q2")))
list_of_frames
# $`1`
#   ID q1 q2
# 1  A 10  6
# 2  B 12  5
# 3  C 20  7
# 4  D 22  8
# $`2`
#   ID q1 q2
# 1  A 50 10
# 2  B 70 11
# 3  C 20  8
# 4  D 22  9
# $`3`
#   ID q1 q2
# 1  A bA zA
# 2  B bB zB
# 3  C bC zC
# 4  D bD zD

这可以很容易地与多种方法结合使用,选择其中一种:

data.table::rbindlist(list_of_frames)
dplyr::bind_rows(list_of_frames)
do.call(rbind, list_of_frames)
#    ID q1 q2
# 1   A 10  6
# 2   B 12  5
# 3   C 20  7
# 4   D 22  8
# 5   A 50 10
# 6   B 70 11
# 7   C 20  8
# 8   D 22  9
# 9   A bA zA
# 10  B bB zB
# 11  C bC zC
# 12  D bD zD

数据

dw <- structure(list(ID = c("A", "B", "C", "D"), q1 = c("10", "12", "20", "22"), q2 = c("6", "5", "7", "8"), q3 = c("50", "70", "20", "22"), q4 = c("10", "11", "8", "9"), q5 = c("bA", "bB", "bC", "bD"), q6 = c("zA", "zB", "zC", "zD")), row.names = c(NA, -4L), class = "data.frame")

另一个选项:

data.frame(ID = dw$ID,
           q1 = unlist(dw[,seq(2, ncol(dw), 2)], use.names = FALSE),
           q2 = unlist(dw[,seq(3, ncol(dw), 2)], use.names = FALSE))

有数据:

dw <- structure(list(ID = c("A", "B", "C", "D"),
                     q1 = c(10L, 12L, 20L, 22L),
                     q2 = c(6L, 5L, 7L, 8L),
                     q3 = c(50L, 70L, 20L, 22L),
                     q4 = c(10L, 11L, 8L, 9L),
                     q5 = c("bA", "bB", "bC", "bD"),
                     q6 = c("cc", "dd", "ee", "ff"))
                , class = "data.frame", row.names = c(NA, -4L))

data.frame(ID = dw$ID,
           q1 = unlist(dw[,seq(2, ncol(dw), 2)], use.names = FALSE),
           q2 = unlist(dw[,seq(3, ncol(dw), 2)], use.names = FALSE))
#>    ID q1 q2
#> 1   A 10  6
#> 2   B 12  5
#> 3   C 20  7
#> 4   D 22  8
#> 5   A 50 10
#> 6   B 70 11
#> 7   C 20  8
#> 8   D 22  9
#> 9   A bA cc
#> 10  B bB dd
#> 11  C bC ee
#> 12  D bD ff

或更一般地说:

n <- 3L # operate on every 3 columns
data.frame(
  setNames(
    c(
      list(dw[,1]),
      lapply(
        2:(n + 1L),
        function(i) unlist(dw[,seq(i, ncol(dw), n)], TRUE, FALSE)
      )
    ),
    names(dw)[1:(n + 1L)]
  )
)

#>   ID q1 q2 q3
#> 1  A 10  6 50
#> 2  B 12  5 70
#> 3  C 20  7 20
#> 4  D 22  8 22
#> 5  A 10 bA cc
#> 6  B 11 bB dd
#> 7  C  8 bC ee
#> 8  D  9 bD ff

data.tablemelt(...) 方法允许熔化列组。使用来自@r2evans 的 dw 回答:

library(data.table)
setDT(dw)
result <- melt(dw, measure.vars = list(seq(2, ncol(dw), 2), seq(3, ncol(dw), 2)))
result[, variable:=NULL]
result
##     ID value1 value2
##  1:  A     10      6
##  2:  B     12      5
##  3:  C     20      7
##  4:  D     22      8
##  5:  A     50     10
##  6:  B     70     11
##  7:  C     20      8
##  8:  D     22      9
##  9:  A     bA     zA
## 10:  B     bB     zB
## 11:  C     bC     zC
## 12:  D     bD     zD

melt(...) 引入了一个列 variable,它跟踪原始列在宽数据集中的位置。你似乎并不关心这一点,所以它被删除了。如果确实存在不同的 类(整数,字符)melt(...) 将通过警告处理。