在 R 中使用组重塑多列

问题描述

我有一个数据集 my.data,包含多列数字数据(x.1x.3x.5)和两个组 (CC) .

my.data <- read.table(text = '
    AA  BB  CC   x.1   x.3   x.5
   aaa   3   1     5    10    15
   aaa   3   2    20    25    30
   aaa   4   1    50   100   150
   aaa   4   2   200   250   300
   aaa   5   1   500  1000  1500
   aaa   5   2  2000  2500  3000
',header = TRUE,stringsAsFactors = FALSE)

我想通过 x.1 重塑 x.3x.5CC。以下两个数据集中的任何一个都是理想的。我意识到这可能是重复的,但到目前为止我已经查看了大约两打 reshape 问题,但没有找到类似的问题。

desired.result.1 <- read.table(text = '
      CC   x.1   x.3   x.5
       1     5    50   500
       1    10   100  1000
       1    15   150  1500
       2    20   200  2000
       2    25   250  2500
       2    30   300  3000 
',stringsAsFactors = FALSE)

desired.result.2 <- read.table(text = '
      CC   x.1   x.3   x.5
       1     5    50   500
       2    20   200  2000
       1    10   100  1000
       2    25   250  2500
       1    15   150  1500
       2    30   300  3000 
',stringsAsFactors = FALSE)

到目前为止,我实际上已经提出了两种不同的解决方案(如下所示)。但是这两种解决方案似乎都过于复杂。我一直想知道是否可能有一个更好的单线 R

这是第一个解决方案。它结合了 sapplyreshape

new.data <- do.call(cbind,sapply(unique(my.data$BB),function(x) {reshape(my.data[my.data$BB == x,3:6],idvar = "CC",varying = list(2:4),v.names = "x",direction = "long")[3]}))
new.data <- data.frame(CC = rep(c(1,2),(nrow(new.data)/2)),new.data)
colnames(new.data) <- c('CC',paste0('x.',seq(1,5,by=2)))

all.equal(new.data,desired.result.2)
#[1] TRUE

这是第二种解决方案。它不使用 sapply。但是,它仍然需要大量的后期处理:

new.data2 <- t(reshape(my.data,idvar = c('AA','BB'),timevar='CC',direction = 'wide'))[3:(nrow(new.data)+2),]
new.data2 <- data.frame(CC = rep(c(1,each=(nrow(my.data)/2)),new.data2)
colnames(new.data2) <- c('CC',by=2)))
new.data2 <- apply(new.data2,2,as.numeric)
new.data2 <- data.frame(new.data2)

all.equal(new.data2,desired.result.1)
#[1] TRUE

解决方法

试试下面的 data.table 选项

setDT(my.data)[,setNames(transpose(.SD),names(.SD)),CC,.SDcols = patterns("x\\.\\d+")
]

给出

   CC x.1 x.3  x.5
1:  1   5  50  500
2:  1  10 100 1000
3:  1  15 150 1500
4:  2  20 200 2000
5:  2  25 250 2500
6:  2  30 300 3000
,

这是一个 tidyverse 选项,用于在每个组内内部转置数据。

library(dplyr)
library(tidyr)

my.data %>%
  pivot_longer(cols = starts_with('X')) %>%
  arrange(CC,name) %>%
  group_by(CC,name) %>%
  mutate(row = row_number()) %>%
  group_by(CC) %>%
  mutate(value = value[order(row)]) %>%
  ungroup %>%
  select(-row) %>%
  pivot_wider() %>%
  select(-AA,-BB)

#     CC   x.1   x.3   x.5
#  <int> <int> <int> <int>
#1     1     5    50   500
#2     1    10   100  1000
#3     1    15   150  1500
#4     2    20   200  2000
#5     2    25   250  2500
#6     2    30   300  3000