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

将data.frame展开为长格式并增加值,r,dplyr,R,Dplyr,我想将我的数据从短格式转换为长格式,我想有一种简单的方法可以做到这一点(可能使用Reforme2、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 作为奖励,我还想对变量“y”进行某种递增,其

我想将我的数据从短格式转换为长格式,我想有一种简单的方法可以做到这一点(可能使用Reforme2、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
作为奖励,我还想对变量“y”进行某种递增,其中,对于y=1的ID,y设置为0,直到“time”的最大值。也就是说,我想制作:

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
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可能已经做过的事情,但我只是不知道去哪里看。无论如何,任何避免循环的解决方案都是有帮助的。

您可以使用适当的
id
time
列为长格式创建一个新的数据帧,然后将其与原始数据帧合并。这将为不匹配的值留下
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
使用dplyr(和magritte,清晰易读):


希望对您有所帮助

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

@MatthewLundberg—
cumsum(foo$time)
将始终给出扩展数据中组的最后一行。对
y==1
进行子集设置,并将其反转,将每组中最后一行之前的所有行设置为0.+1。我没有做任何测量,但这看起来比合并更有效。@MatthewLundberg,现在选项中有“dplyr”和“data.table”解决方案,谁来做基准测试;-)实际上,通过一些快速测试,这项功能的性能非常好。您可以尝试
foo[rep(1:nrow(foo),foo$time),]%%>%groupby(id)%%>%mutate(time=1:n(),y=replace(y,!!y,c(rep(0,n()-1),1))
within(
  foo[rep(rownames(foo),foo$time),],
  {
    time <- sequence(foo$time)
    y[-cumsum(foo$time)] <- 0
  }
)
library(magrittr)
library(dplyr)

foo[rep(1:nrow(foo), foo$time), ] %>%
    group_by(id) %>%
    mutate(y = !duplicated(y, fromLast = TRUE),
                  time = 1:n())
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][]