使用多个值列重新定义宽到长

Vas*_*y A 27 r reshape melt reshape2

我需要将我的宽表重新整形为长格式,但为每条记录保留多个字段,例如:

dw <- read.table(header=T, text='
 sbj f1.avg f1.sd f2.avg f2.sd  blabla
   A   10    6     50     10      bA
   B   12    5     70     11      bB
   C   20    7     20     8       bC
   D   22    8     22     9       bD
 ')

# Now I want to melt this table, keeping both AVG and SD as separate fields for each measurement, to get something like this:

 #    sbj var avg  sd  blabla
 #     A   f1  10  6     bA
 #     A   f2  50  10    bA
 #     B   f1  12  5     bB
 #     B   f2  70  11    bB
 #     C   f1  20  7     bC
 #     C   f2  20  8     bC
 #     D   f1  22  8     bD
 #     D   f2  22  9     bD
Run Code Online (Sandbox Code Playgroud)

我有使用melt和的基本知识reshape,但对我来说,如何在我的案例中应用这种重塑并不明显.如果已经提出类似的问题,我会感激任何提示或指向另一个SO帖子.

Mai*_*ura 27

使用Hadley新tidyr包装的另一种选择.

library(tidyr)
library(dplyr)

dw <- read.table(header=T, text='
 sbj f1.avg f1.sd f2.avg f2.sd  blabla
   A   10    6     50     10      bA
   B   12    5     70     11      bB
   C   20    7     20     8       bC
   D   22    8     22     9       bD
 ')

dw %>% 
  gather(v, value, f1.avg:f2.sd) %>% 
  separate(v, c("var", "col")) %>% 
  arrange(sbj) %>% 
  spread(col, value)
Run Code Online (Sandbox Code Playgroud)

  • 有关 tidyr 的有用信息以及上述代码的工作原理:https://blog.rstudio.org/2014/07/22/introducing-tidyr/ (3认同)

Mat*_*erg 26

reshape 用适当的参数做到这一点.

varying列出以宽格式存在的列,但以长格式拆分为多行. v.names是长格式的等价物.在两者之间,创建映射.

来自?reshape:

此外,如果明确给出v.names,则不会尝试猜测.请注意,变量的顺序类似于x.1,y.1,x.2,y.2.

给定这些varyingv.names参数,reshape足够聪明,我已经指明索引在此处的点之前(即,顺序1.x,1.y,2.x,2.y).请注意,原始数据按此顺序具有列,因此我们可以varying=2:5为此示例数据指定数据,但这通常不安全.

鉴于值timesv.names,reshape拆分varying在列.字符(默认sep参数)在输出中创建的列.

times指定要在创建的var列中使用的值,并v.names粘贴到这些值上以获取宽格式的列名称以映射到结果.

最后,idvar指定为sbj列,以宽格式标识各个记录(感谢@thelatemail).

reshape(dw, direction='long', 
        varying=c('f1.avg', 'f1.sd', 'f2.avg', 'f2.sd'), 
        timevar='var',
        times=c('f1', 'f2'),
        v.names=c('avg', 'sd'),
        idvar='sbj')

##      sbj blabla var avg sd
## A.f1   A     bA  f1  10  6
## B.f1   B     bB  f1  12  5
## C.f1   C     bC  f1  20  7
## D.f1   D     bD  f1  22  8
## A.f2   A     bA  f2  50 10
## B.f2   B     bB  f2  70 11
## C.f2   C     bC  f2  20  8
## D.f2   D     bD  f2  22  9
Run Code Online (Sandbox Code Playgroud)

  • 为了使这个答案在处理多对“avg”和“sd”列时更可靠,用 `variing=list(grep("avg", colnames(dw), value=T), grep(" sd", colnames(dw), value=T))` (5认同)

Mar*_*ler 8

这似乎做你想要的,除了f从元素中删除time.

reshape(dw, idvar = "sbj", varying = list(c(2,4),c(3,5)), v.names = c("ave", "sd"), direction = "long")

    sbj blabla time ave sd
A.1   A     bA    1  10  6
B.1   B     bB    1  12  5
C.1   C     bC    1  20  7
D.1   D     bD    1  22  8
A.2   A     bA    2  50 10
B.2   B     bB    2  70 11
C.2   C     bC    2  20  8
D.2   D     bD    2  22  9
Run Code Online (Sandbox Code Playgroud)

  • @VasilyA"变化"的结构在答案之间也是非常不同的.实际上,他们似乎唯一共同的东西是他们都使用相同的`v.names`并且都使用`direction ="long"`. (2认同)
  • 比较两个正确答案的有趣之处在于,这个使用`list`参数的结构为'vary',而Lundberg答案使用'times'和'v.names'来传达一些结构.我从来没有把这些选择理顺在我脑海中并且通常使用反复试验. (2认同)

akr*_*run 8

melt从> = 1.9.6版本开始data.table,通过将列索引指定measure.vars为a来实现list.

 melt(setDT(dw), measure.vars=list(c(2,4), c(3,5)), 
     variable.name='var', value.name=c('avg', 'sd'))[, 
      var:= paste0('f',var)][order(sbj)]
#   sbj blabla var avg sd
#1:   A     bA  f1  10  6
#2:   A     bA  f2  50 10
#3:   B     bB  f1  12  5
#4:   B     bB  f2  70 11
#5:   C     bC  f1  20  7
#6:   C     bC  f2  20  8
#7:   D     bD  f1  22  8
#8:   D     bD  f2  22  9
Run Code Online (Sandbox Code Playgroud)

或者您可以使用新patterns功能:

melt(setDT(dw), 
     measure = patterns("avg", "sd"),
     variable.name = 'var', value.name = c('avg', 'sd'))
#    sbj blabla var avg sd
# 1:   A     bA   1  10  6
# 2:   B     bB   1  12  5
# 3:   C     bC   1  20  7
# 4:   D     bD   1  22  8
# 5:   A     bA   2  50 10
# 6:   B     bB   2  70 11
# 7:   C     bC   2  20  8
# 8:   D     bD   2  22  9
Run Code Online (Sandbox Code Playgroud)


A5C*_*2T1 6

要添加到此处提供的选项,您还可以merged.stack从我的"splitstackshape"包中考虑:

library(splitstackshape)
merged.stack(dw, var.stubs = c("avg", "sd"), sep = "var.stubs", atStart = FALSE)
#    sbj blabla .time_1 avg sd
# 1:   A     bA     f1.  10  6
# 2:   A     bA     f2.  50 10
# 3:   B     bB     f1.  12  5
# 4:   B     bB     f2.  70 11
# 5:   C     bC     f1.  20  7
# 6:   C     bC     f2.  20  8
# 7:   D     bD     f1.  22  8
# 8:   D     bD     f2.  22  9
Run Code Online (Sandbox Code Playgroud)

您还可以对".time_1"变量进行更多清理,如下所示.

merged.stack(dw, var.stubs = c("avg", "sd"), 
             sep = "var.stubs", atStart = FALSE)[, .time_1 := sub(
               ".", "", .time_1, fixed = TRUE)][]
#    sbj blabla .time_1 avg sd
# 1:   A     bA      f1  10  6
# 2:   A     bA      f2  50 10
# 3:   B     bB      f1  12  5
# 4:   B     bB      f2  70 11
# 5:   C     bC      f1  20  7
# 6:   C     bC      f2  20  8
# 7:   D     bD      f1  22  8
# 8:   D     bD      f2  22  9
Run Code Online (Sandbox Code Playgroud)

你会注意到atStart = FALSE参数的使用.这是因为你的名字与重塑相关的函数似乎有点不同.一般来说,"存根"预计将首先出现,然后是"时代",如下所示:

dw2 <- dw
setnames(dw2, gsub("(.*)\\.(.*)", "\\2.\\1", names(dw2)))
names(dw2)
# [1] "sbj"    "avg.f1" "sd.f1"  "avg.f2" "sd.f2"  "blabla"
Run Code Online (Sandbox Code Playgroud)

如果名称采用该格式,则两者都基于R reshapemerged.stack从更直接的语法中受益:

merged.stack(dw2, var.stubs = c("avg", "sd"), sep = ".")
reshape(dw2, idvar = c("sbj", "blabla"), varying = 2:5, 
        sep = ".", direction = "long")
Run Code Online (Sandbox Code Playgroud)