通过使用reshape()或melt()将数据帧从宽数据格式转换为长数据格式的更优雅方式

时间:2014-12-16 13:04:38

标签: r reshape

我正在寻找一种更优雅的方法,通过使用melt(reshape2)或reshape函数来重塑我的数据框。

假设我有一个像这样的简单数据框:

d<-data.frame("PID"=factor(c(1,1,1,2,2,2)),
              "Cue1"=factor(c(1,2,3,1,2,3)),
              "Cue2"=factor(c(5,5,5,5,5,5)))

我想将第二和第三列转换为一个长列。我的代码可以使用,但我正在寻找一种更优雅的方式:

d1<-data.frame("trigger"=as.vector(t(d[,c(2:3)])))
d1$PID<-factor(rep(c(1,2),each=6))

重要的是两个因子的水平数不同(Cue1有3,Cue2有1个水平)。我上面的代码给了我一个看起来像这样的新列(这实际上就是我想要的):

trigger
1
5
2
5
3
5
...

不幸的是,互联网上关于重塑的大多数例子讨论了以下(在我的例子中,非首选)示例:

trigger
1
2
3
1
2
3
...

但我需要前者。

提前感谢您的建议。

3 个答案:

答案 0 :(得分:3)

最简单的方法是使用melt。这与您的初始数据框(d1)相同,除非触发的确切顺序很重要。

library(reshape2)
d2 <- melt(d, id="PID", value.name="trigger")[,c(3,1)]
> d2
       trigger PID
1        1   1
2        2   1
3        3   1
4        1   2
5        2   2
6        3   2
7        5   1
8        5   1
9        5   1
10       5   2
11       5   2
12       5   2

如果您喜欢使用base功能,还可以使用reshape

d3 <- reshape(d, direction="long", 
              varying=list(names(d)[2:3]), 
              v.names="trigger",
              idvar="PID", 
              new.row.names=seq(12))[,c(3,1)]

通过按触发器排序

,您可以看到它们是相同的
> d2[order(d2$trigger),]
   trigger PID
1        1   1
4        1   2
2        2   1
5        2   2
3        3   1
6        3   2
7        5   1
8        5   1
9        5   1
10       5   2
11       5   2
12       5   2
> d1[order(d1$trigger),]
   trigger PID
1        1   1
7        1   2
3        2   1
9        2   2
5        3   1
11       3   2
2        5   1
4        5   1
6        5   1
8        5   2
10       5   2
12       5   2

答案 1 :(得分:2)

我认为“优雅”是主观的,但如果您正在寻找替代方案,您可以从我的“splitstackshape”包中考虑merged.stack。但是,为了使merged.stack正常工作,您的ID变量必须是唯一的。为此,您可以使用getanID(也来自“splitstackshape”):

library(splitstackshape)
packageVersion("splitstackshape")
# [1] ‘1.4.2’
merged.stack(getanID(d, "PID"), var.stubs = "Cue", 
             sep = "var.stubs")[, c("PID", "Cue"), with = FALSE]
#     PID Cue
#  1:   1   1
#  2:   1   5
#  3:   1   2
#  4:   1   5
#  5:   1   3
#  6:   1   5
#  7:   2   1
#  8:   2   5
#  9:   2   2
# 10:   2   5
# 11:   2   3
# 12:   2   5

## factor levels retained as desired
str(.Last.value)
# Classes ‘data.table’ and 'data.frame':  12 obs. of  2 variables:
#  $ PID: Factor w/ 2 levels "1","2": 1 1 1 1 1 1 2 2 2 2 ...
#  $ Cue: Factor w/ 4 levels "1","2","3","5": 1 4 2 4 3 4 1 4 2 4 ...
#  - attr(*, "sorted")= chr "PID"
#  - attr(*, ".internal.selfref")=<externalptr> 

默认情况下,如果只是这样做,这种方法会创建一些额外的列:

merged.stack(getanID(d, "PID"), var.stubs = "Cue", sep = "var.stubs")

两个额外的列将是:

  • .id,由getanID创建。此列与“PID”列组合时将创建唯一ID。
  • .time_1,这是“堆叠”步骤的结果,用于指示值来自哪个“Cue”列(在这种情况下,在1和2之间循环以表示“Cue1”和“Cue2”)

代码中标有[, c("PID", "Cue"), with = FALSE]的部分意味着只向我们展示这两列(因为您似乎只对它感兴趣)。

答案 2 :(得分:1)

如果您只是在寻找使用熔体的单线,下面是一种方法(保留所需的订单):

# assume DF is your data frame
DF_new = data.frame(trigger = melt(t(DF[,2:3]))[,3], PID = rep(DF[,1], each=2))
DF_new
#    trigger PID
# 1        1   1
# 2        5   1
# 3        2   1
# 4        5   1
# 5        3   1
# 6        5   1
# 7        1   2
# 8        5   2
# 9        2   2
# 10       5   2
# 11       3   2
# 12       5   2