R 对data.table中的行进行配对

R 对data.table中的行进行配对,r,data.table,R,Data.table,我有一个数据表,类似于通过以下命令获得的数据表: dt <- data.table( time = 1:8, part = rep(c(1, 1, 2, 2), 2), type = rep(c('A', 'B'), 4), data = rep(c(runif(1), 0), 4)) 我希望将A和B实例配对,并获得以下数据表: part data enter.time exit.time 1: 1 0.4658239 1

我有一个数据表,类似于通过以下命令获得的数据表:

dt <- data.table(
  time = 1:8,
  part = rep(c(1, 1, 2, 2), 2),
  type = rep(c('A', 'B'), 4),
  data = rep(c(runif(1), 0), 4))
我希望将A和B实例配对,并获得以下数据表:

   part data        enter.time exit.time
1:    1 0.4658239   1          2
2:    1 0.4658239   5          6
3:    2 0.4658239   3          4
4:    2 0.4658239   7          8
我尝试了以下方法:

pair.types <- function(x) {
  a.type <- x[type == 'A']
  b.type <- x[type == 'B']
  return(data.table(
      enter.time = a.type$time,
      exit.time = b.type$time,
      data = a.type$data))
}

dt[, c('enter.time', 'exit.time', 'data') := pair.types(.SD), by = list(part)]

这有点接近,但由于列“type”被保留,所以有些行是重复的。也许,我可以尝试删除列“time”和“type”,然后删除后半行。但是,我不确定这是否适用于所有情况,我想学习一种更好的方法来执行此操作。

假设您的数据看起来像示例数据:

dt[, list(part = part[1],
          data = data[1],
          enter.time = time[1],
          exit.time = time[2]),
     by = as.integer((seq_len(nrow(dt)) + 1)/2)]
#    by = rep(seq(1, nrow(dt), 2), each = 2)]
#    ^^^ a slightly shorter and a little more readable alternative
想法非常简单-将行分组为2组(即按
部分),即每个组将有一个A和一个B,然后对于每个组,先取
部分
和第一个
数据
,然后进入和退出时间分别为第一次和第二次
时间
。如果您遵循手动逻辑,使其易于阅读(只要您对
data.table
的工作原理有一点点了解),您可能会这样做。

另一种方法:

setkey(dt, "type")
dt.out <- cbind(dt[J("A"), list(part, data, entry.time = time)][, type := NULL], 
      exit.time = dt[J("B"), list(time)]$time)
#    part      data entry.time exit.time
# 1:    1 0.1294204          1         2
# 2:    2 0.1294204          3         4
# 3:    1 0.1294204          5         6
# 4:    2 0.1294204          7         8
当然,这也将返回我们要删除的
type
列(=A)。因此,我添加了一个
[,type:=NULL]
以通过引用删除列
type

现在我们来看第一部分。我们所需要的就是退出时间。这可以通过以下类似方式获得:

dt[J("B"), list(time)] # I don't name the column here
但当您只需要时间列时,这将提供一个
data.table
,可通过以下方式访问:

dt[J("B"), list(time)]$time
因此,在使用
cbind
时,我将此列命名为
exit.time
,以获得最终结果:

cbind(dt[J("A"), list(part, data, entry.time = time)][, type := NULL], 
      exit.time = dt[J("B"), list(time)]$time)

希望这有帮助。

您的成对行总是一行接一行吗?也就是说,首先是
A
,然后是相应的
B
@JuliánUrbano是的,相应的A-B对总是连续的。至少,到目前为止,我观察到的数据就是这样。如果不是太难的话,我会对一个通用的解决方案感兴趣。你能解释一下这个解决方案是如何工作的吗?我想投你一票,因为这看起来真的很优雅,而且是正确的答案,但如果没有一些解释,我觉得它对更广泛的观众没有用处!谢谢:-)我还一直怀疑这个部分可以简化很多。(+1)埃迪,怎么样
dt[,as.list(c(data=data[1],entry=time[1],exit=time[2])),by=list(part,rep(1:4,each=2))
+1谢谢,您的解释帮助我完全理解了您实现的功能。伟大的解决方案!非常感谢你的回答。看到data.table运行起来真的很有趣。不幸的是,在创建一个玩具示例并将其发布到这里时,我将问题过分简化了。我应该更新这个问题,还是创建一个新问题更好?阿伦和你一样(见eddi解决方案下的评论)-请你为我们解释一下
数据。表
新手这是如何工作的?!我通常写
想法:
并解释。但我仍在努力寻找答案:)。哈哈!!!是的,好的,我也经常这样做-你必须在这里快!尤其是像
data.table
:-)@SimonO101这样的性感话题,不,你完全正确。有时我忘了解释。这样做更有意义,这样对更广泛的受众更有用。我会记住这一点,以便将来回答。啊,是的,data.table语法!如此简单、紧凑、直观且易于阅读!;)
dt[J("B"), list(time)] # I don't name the column here
dt[J("B"), list(time)]$time
cbind(dt[J("A"), list(part, data, entry.time = time)][, type := NULL], 
      exit.time = dt[J("B"), list(time)]$time)