完成序列列并填写行。

5

我开始处理一个包含一列数字的数据集(在我的情况下是时间,但以数字格式表示),该数据集还包含一个时间戳事件和一个ID。我希望运行一些代码来扩展数据框以填充数字序列,同时复制事件列(以及任何其他相关列)。我希望按ID来运行此操作,因此我不会填充ID之间的间隙。

这里是我开始处理的数据的简化示例。我想单独为每个ID填充“value”列的序列,每个新行都要用第一行的数据来完成“event”。

 a<-c("162", "164", "169", "171", "174", "188", "191", "198","200")
b<-c("start1","end1","start2", "event", "end2", "start1", "end1", "start2", "end2")
c<-c("A","A","A", "A", "A", "B", "B", "B", "B")

original<-data.table(value = a, event = b, ID = c)

以下是我最终目标的重建:

agoal<-c(seq(from = 162, to = 174), seq(from = 188, to = 200))
bgoal<-c("start1","start1","end1","end1", "end1", "end1", "end1",
     "start2", "start2",  "event", "end2","end2", "end2", 
     "start1", "start1", "start1", "end1", "end1", "end1", "end1", "end1", "end1", "end1",
     "start2", "start2","end2")
cgoal<-c(rep("A",13), rep("B",13))

goal<-data.table(value = agoal, event = bgoal, ID = cgoal)

如果这不太清楚,很抱歉!


感谢您提供的出色答案!它们很有效,但是我在将它们应用于最终大小的实际数据集时遇到了问题,出现了“错误:向量内存耗尽(达到限制?)”的提示。 - celow
3个回答

3
我们可以使用tidyr包中的complete和fill函数: 首先使用type.convert(as.is=TRUE)将数值分配给value。
library(dplyr)
library(tidyr)

original %>% 
  type.convert(as.is=TRUE) %>% 
  group_by(ID) %>% 
  complete(value = first(value):max(value)) %>% 
  fill(event) 

输出:

    value  event ID
 1:   162 start1  A
 2:   163 start1  A
 3:   164   end1  A
 4:   165   end1  A
 5:   166   end1  A
 6:   167   end1  A
 7:   168   end1  A
 8:   169 start2  A
 9:   170 start2  A
10:   171  event  A
11:   172   end2  A
12:   173   end2  A
13:   174   end2  A
14:   188 start1  B
15:   189 start1  B
16:   190 start1  B
17:   191   end1  B
18:   192   end1  B
19:   193   end1  B
20:   194   end1  B
21:   195   end1  B
22:   196   end1  B
23:   197   end1  B
24:   198 start2  B
25:   199 start2  B
26:   200   end2  B

1
另一种选项是使用 dplyrtidyr:
library(dplyr)
library(tidyr)

original %>% 
  split(.$ID) %>% 
  lapply(function(x) data.frame(value = as.character(seq(min(x$value), max(x$value)))) %>% 
           left_join(x, by="value") %>% 
           fill(c("event", "ID"))) %>% 
  do.call(rbind.data.frame, .)

返回
     value  event ID
A.1    162 start1  A
A.2    163 start1  A
A.3    164   end1  A
A.4    165   end1  A
A.5    166   end1  A
A.6    167   end1  A
A.7    168   end1  A
A.8    169 start2  A
A.9    170 start2  A
A.10   171  event  A
A.11   172  event  A
A.12   173  event  A
A.13   174   end2  A
B.1    188 start1  B
B.2    189 start1  B
B.3    190 start1  B
B.4    191   end1  B
B.5    192   end1  B
B.6    193   end1  B
B.7    194   end1  B
B.8    195   end1  B
B.9    196   end1  B
B.10   197   end1  B
B.11   198 start2  B
B.12   199 start2  B
B.13   200   end2  B

基本上一样,只是没有 splitdo.call 部分:

original %>% 
  group_by(ID) %>% 
  group_map(function(x, ...) data.frame(value = as.character(seq(min(x$value), max(x$value)))) %>% 
           left_join(original, by="value") %>% 
           fill(c("event", "ID"))) %>% 
  bind_rows()

1
有两个部分需要处理:a)如何在value列中添加缺失数字的行,b)如何用前面的内容填充event。(b)很容易(只需使用fill)。 (a)可以通过创建一个包含所有所需值并与原始数据框连接的新数据框来完成:
library(tidyverse)

original %>%
  group_by(ID) %>%
  summarize(a = as.integer(min(value)),
            b = as.integer(max(value))) %>%
  transpose() %>%
  map(~ data.frame(ID = .x$ID, value = .x$a:.x$b)) %>%
  reduce(bind_rows) %>%
  left_join(original, by = c("value", "ID")) %>%
  group_by(ID) %>%
  fill(event, .direction = "down")

步骤:

  1. 获取每个组中value列的最小值和最大值。
  2. 使用transpose将数据框拆分为列表(这将生成一个嵌套列表,其中第一级由行编号索引,第二级由列名索引)。
  3. 使用map为每个ID创建一个数据框列表,其中value列具有原始值的最小值和最大值之间的所有值。
  4. 使用reducebind_rows将所有数据框合并在一起。
  5. 使用valueID与原始数据框进行连接;请注意,left_join将确保所有新的valueID组合都存在,即使它们在原始数据框中没有任何对应的组合(这将导致任何缺少的event值为NA,这是下一步所需的)。
  6. 向下填充所有NA值,使用同一组中前一个事件的值。

结果:

   ID value  event
1   A   162 start1
2   A   163 start1
3   A   164   end1
4   A   165   end1
5   A   166   end1
6   A   167   end1
7   A   168   end1
8   A   169 start2
9   A   170 start2
10  A   171  event
11  A   172  event
12  A   173  event
13  A   174   end2
14  B   188 start1
15  B   189 start1
16  B   190 start1
17  B   191   end1
18  B   192   end1
19  B   193   end1
20  B   194   end1
21  B   195   end1
22  B   196   end1
23  B   197   end1
24  B   198 start2
25  B   199 start2
26  B   200   end2

请注意,如果您在original中拥有数字值作为实际数字,则实际上不需要两个as.integer调用。另外,我认为在OP中,您想要连续三次使用event而不是一次(否则模式会被打破)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接