在data.table中配对行

时间:2013-04-22 21:10:40

标签: 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)。时间列包含请求到达或离开某个部分的时间戳。如果实例类型为A,则时间戳表示到达时间(输入),如果类型为B,则时间戳表示离开时间(退出)。

   time part type      data
1:    1    1    A 0.5842668
2:    2    1    B 0.0000000
3:    3    2    A 0.5842668
4:    4    2    B 0.0000000
5:    5    1    A 0.5842668
6:    6    1    B 0.0000000
7:    7    2    A 0.5842668
8:    8    2    B 0.0000000

我想配对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)]

但是,这给了我以下,这不是我想要的:

   time part type      data enter.time exit.time
1:    1    1    A 0.3441592          1         2
2:    2    1    B 0.3441592          5         6
3:    3    2    A 0.3441592          3         4
4:    4    2    B 0.3441592          7         8
5:    5    1    A 0.3441592          1         2
6:    6    1    B 0.3441592          5         6
7:    7    2    A 0.3441592          3         4
8:    8    2    B 0.3441592          7         8

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

2 个答案:

答案 0 :(得分:3)

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

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组(即by部分),即每组将是一个A和一个B,然后为每个组首先采用part和第一个data然后输入和退出时间分别只是第一个和第二个time。如果您遵循副手逻辑,这很可能就是这样做的,这使得它易于阅读(一旦您对data.table的工作方式有所了解)。

答案 1 :(得分:2)

另一种方式:

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

如果您愿意,现在可以setkey(dt.out, "part")获得相同的订单。


这个想法:你的问题对我来说似乎是一个简单的“重塑”问题。我接近它的方法是首先创建一个关键列type。现在,我们可以通过以下方式为关键列中的特定值分组data.table:dt[J("A")]。这将返回整个data.table。由于您希望重命名列time,因此我明确提到要使用子集的哪些列:

dt[J("A"), list(part, data, entry.time = time)]

当然,这也将返回我们要移除的type列(= A)。因此,我添加了[, type := NULL]以通过引用删除列type

现在我们是第一部分。我们所需要的只是exit.time。这可以类似地获得:

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)

希望这有帮助。

相关问题