将data.frame扩展为长格式并增加值

时间:2014-10-02 01:01:33

标签: r dplyr

我想将数据从短格式转换为长格式,我想有一种简单的方法(可能使用reshape2,plyr,dplyr等?)。

例如,我有:

foo <- data.frame(id = 1:5, 
              y = c(0, 1, 0, 1, 0),
              time = c(2, 3, 4, 2, 3))

id y time
1  0  2
2  1  3
3  0  4
4  1  2
5  0  3

我想扩展/复制每一行n次,其中n是“时间”列中该行的值。但是,我还希望变量“time”从1增加到n。也就是说,我想制作:

id  y time
1   0   1
1   0   2
2   1   1
2   1   2
2   1   3
3   0   1
3   0   2
3   0   3
3   0   4
4   1   1
4   1   2
5   0   1
5   0   2
5   0   3

作为奖励,我还想对变量“y”进行一种递增,其中,对于y = 1的那些id,y被设置为0直到“time”的最大值。也就是说,我想制作:

id  y time
1   0   1
1   0   2
2   0   1
2   0   2
2   1   3
3   0   1
3   0   2
3   0   3
3   0   4
4   0   1
4   1   2
5   0   1
5   0   2
5   0   3

这似乎是dplyr可能已经做过的事情,但我只是不知道在哪里看。无论如何,任何避免循环的解决方案都是有帮助的。

4 个答案:

答案 0 :(得分:3)

您可以使用长格式的正确idtime列创建新数据框,然后将其与原始列合并。这会为NA留下不匹配的值,然后可以用0代替:

merge(foo, 
      with(foo, 
           data.frame(id=rep(id,time), time=sequence(time))
      ), 
      all.y=TRUE
)
##    id time  y
## 1   1    1 NA
## 2   1    2  0
## 3   2    1 NA
## 4   2    2 NA
## 5   2    3  1
## 6   3    1 NA
## 7   3    2 NA
## 8   3    3 NA
## 9   3    4  0
## 10  4    1 NA
## 11  4    2  1
## 12  5    1 NA
## 13  5    2 NA
## 14  5    3  0

类似的合并适用于第一次扩展。将footime列合并,并使用与上面相同的已创建数据框进行合并:

merge(foo[c('id','y')], 
      with(foo, 
           data.frame(id=rep(id,time), time=sequence(time))
      )
) 
##    id y time
## 1   1 0    1
## 2   1 0    2
## 3   2 1    1
## 4   2 1    2
## 5   2 1    3
## 6   3 0    1
## 7   3 0    2
## 8   3 0    3
## 9   3 0    4
## 10  4 1    1
## 11  4 1    2
## 12  5 0    1
## 13  5 0    2
## 14  5 0    3

没有必要在后一个表达式中指定all(或all.y),因为每个匹配的time值都有多个id值,并且这些都扩大了。在前一种情况下,time值来自两个数据框,并且未指定all(或all.y),您将获得原始数据。

答案 1 :(得分:3)

初始扩展可以通过以下方式实现:

newdat <- transform( 
  foo[rep(rownames(foo),foo$time),], 
  time = sequence(foo$time)
)

#    id y time
#1    1 0    1
#1.1  1 0    2
#2    2 1    1
#2.1  2 1    2
#2.2  2 1    3
# etc

要获得完整的解决方案,包括奖励部分,请执行以下操作:

newdat$y[-cumsum(foo$time)] <- 0

#    id y time
#1    1 0    1
#1.1  1 0    2
#2    2 0    1
#2.1  2 0    2
#2.2  2 1    3
#etc

如果您真的很兴奋,可以使用within一步一步完成所有操作:

within(
  foo[rep(rownames(foo),foo$time),],
  {
    time <- sequence(foo$time)
    y[-cumsum(foo$time)] <- 0
  }
)

答案 2 :(得分:3)

如果您愿意使用“data.table”,可以尝试:

library(data.table)
fooDT <- as.data.table(foo)
fooDT[, list(time = sequence(time)), by = list(id, y)]
#     id y time
#  1:  1 0    1
#  2:  1 0    2
#  3:  2 1    1
#  4:  2 1    2
#  5:  2 1    3
#  6:  3 0    1
#  7:  3 0    2
#  8:  3 0    3
#  9:  3 0    4
# 10:  4 1    1
# 11:  4 1    2
# 12:  5 0    1
# 13:  5 0    2
# 14:  5 0    3

并且,对于奖金问题:

fooDT[, list(time = sequence(time)), 
      by = list(id, y)][, y := {y[1:(.N-1)] <- 0; y}, 
                        by = id][]
#     id y time
#  1:  1 0    1
#  2:  1 0    2
#  3:  2 0    1
#  4:  2 0    2
#  5:  2 1    3
#  6:  3 0    1
#  7:  3 0    2
#  8:  3 0    3
#  9:  3 0    4
# 10:  4 0    1
# 11:  4 1    2
# 12:  5 0    1
# 13:  5 0    2
# 14:  5 0    3

对于奖金问题,或者:

fooDT[, list(time=seq_len(time)), by=list(id,y)][y == 1, 
                y := c(rep.int(0, .N-1L), 1), by=id][]

答案 3 :(得分:0)

使用dplyr(以及非常易读的magritte):

library(magrittr)
library(dplyr)

foo[rep(1:nrow(foo), foo$time), ] %>%
    group_by(id) %>%
    mutate(y = !duplicated(y, fromLast = TRUE),
                  time = 1:n())

希望有所帮助