繁体   English   中英

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

[英]expand data.frame to long format and increment value

我想将我的数据从短格式转换为长格式,我想有一种简单的方法(可能使用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可能已经做过的事情,但我只是不知道在哪里看。 无论如何,任何避免循环的解决方案都是有帮助的。

您可以使用长格式的正确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

类似的合并适用于第一次扩展。 使用与上面相同的创建数据框合并foo而不使用time列:

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 ),因为每个匹配的id值有多个time值,并且这些值被扩展。 在先前的情况下, time值从两个数据帧匹配,并且没有指定all (或all.y )您将获得原始数据。

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

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
  }
)

如果您愿意使用“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][]

使用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())

希望能帮助到你

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM