使用 R 根据逻辑条件及其发生的时间接近程度来识别和分组相关观测值

  • 本文关键字:识别 程度 接近 关观 时间 条件 使用 r
  • 更新时间 :
  • 英文 :


我有一个数据集("出价"),它由一系列观察值(~500万)组成,每个观察值代表购买产品的出价(在下面的简化示例中,是一本书或一个游戏)。对于每个观察结果,我都有(请参阅下面的示例数据):

  1. 提交投标的日期
  2. 提交投标的时间
  3. 投标人投标的产品名称
  4. 投标人姓名

    observation date        time        product bidder
    1           1/1/2016    9:00:00 AM  book    AB
    2           1/1/2016    9:01:00 AM  book    CD
    3           1/1/2016    9:02:00 AM  book    EF
    4           1/1/2016    9:03:00 AM  book    CD
    5           1/1/2016    9:00:00 AM  game    AB
    6           1/1/2016    9:01:00 AM  game    CD
    7           1/1/2016    9:02:00 AM  game    CD
    8           1/1/2016    9:07:00 AM  game    CD
    9           1/2/2016    9:00:00 AM  book    AB
    10          1/2/2016    9:06:00 AM  book    CD
    11          1/2/2016    9:02:00 AM  book    EF
    12          1/2/2016    9:03:00 AM  book    EF
    13          1/2/2016    9:00:00 AM  game    EF
    14          1/2/2016    8:59:00 AM  game    CD
    15          1/2/2016    9:00:00 AM  game    GH
    16          1/2/2016    9:01:00 AM  game    AB
    17          1/2/2016    10:00:00 AM game    AB
    18          1/2/2016    10:06:00 AM game    CD
    19          1/2/2016    10:06:00 AM game    EF
    20          1/2/2016    10:06:00 AM game    GH
    21          1/2/2016    3:00:00 PM  game    AB
    

在某些情况下,一个投标人对特定产品进行了一次出价,而没有针对该产品的其他投标在时间上接近(例如,观察#21)。然而,在大多数情况下,同一产品的几个投标人对同一产品的几个投标在时间上是接近的(例如,意见1-4组成一个组;意见14-16组成一个组)。为了研究这些组,我需要能够对它们进行分组,并使用唯一标识符识别每个组。最终,我还需要能够计算每个组中的出价总数以及每个组中唯一/不同出价者的数量。如果我弄清楚如何创建组,我可能会自己解决,但我提到它以防万一有更简单/更集成的方法。

我正在努力解决的是"时间接近"参数。如果"接近时间"意味着同一天,我很清楚我可以在 plyr 中使用 id 并创建一个新列("bidgrp")(或其他几种方法中的任何一种)。像这样:

bids$bidgrp <- id(bids[c("date", "product")], drop = TRUE)

但是,"时间接近"实际上意味着在 5 分钟内。换句话说,例如,观测值 9、11 和 12 是组的一部分,但 10 - 因为它比组的最早成员 (9) 晚 5 分钟以上,因此不属于组。部分挑战是弄清楚如何确定组的第一个(最早)成员是什么(我没有可靠的指标,所以 [this](基于第一行值对观察进行分组)解决方案不起作用,但这可以通过在尝试任何分组之前对数据进行排序来完成(尽管在这里,如果有更聪明的话, 更有效的方法,我欢迎他们)

通过查看SO和其他地方的其他条件分组问题,我的直觉是通过一系列类似ifelse循环的步骤来解决这个问题,如下所示:

  1. 首先按日期对数据集进行排序,然后按产品排序,然后按时间排序
  2. 将组 ID 号 i 分配给第一个观测值
  3. 在下一个观察中查看正在出价的产品;如果它是对与先前观察中的产品不同的产品的出价,请为其分配组 ID i+1;如果是对同一产品的出价,请查看日期。
  4. 如果日期与先前的观测值不同,请为其分配组 ID i+1;如果日期相同,请查看时间
  5. 如果时间比先前的观测值晚 5 分钟以上,则为其分配一个组 ID i+1;如果它在组中最早观测值的 5 分钟内(这就是使问题特别棘手的原因 - 不仅仅是查看最后一个观测值的问题,而是知道在确定时间距离时要关闭哪个观测值), 给它分配一个组号i,然后查看下一个观察结果

结果(对于上面的示例数据)将识别 9 个组,如下所示:

observation date    time        product bidder  grpid
1       1/1/2016    9:00:00 AM  book    AB      1
2       1/1/2016    9:01:00 AM  book    CD      1
3       1/1/2016    9:02:00 AM  book    EF      1
4       1/1/2016    9:03:00 AM  book    CD      1
5       1/1/2016    9:00:00 AM  game    AB      2
6       1/1/2016    9:01:00 AM  game    CD      2
7       1/1/2016    9:02:00 AM  game    CD      2
8       1/1/2016    9:07:00 AM  game    CD      3
9       1/2/2016    9:00:00 AM  book    AB      4
11      1/2/2016    9:02:00 AM  book    EF      4
12      1/2/2016    9:03:00 AM  book    EF      4
10      1/2/2016    9:06:00 AM  book    CD      5
14      1/2/2016    8:59:00 AM  game    CD      6
13      1/2/2016    9:00:00 AM  game    EF      6
15      1/2/2016    9:00:00 AM  game    GH      6
16      1/2/2016    9:01:00 AM  game    AB      6
17      1/2/2016    10:00:00 AM game    AB      7
18      1/2/2016    10:06:00 AM game    CD      8
19      1/2/2016    10:06:00 AM game    EF      8
20      1/2/2016    10:06:00 AM game    GH      8
21      1/2/2016    3:00:00 PM  game    AB      9

而且,我最终需要得到这样的事情:

grpid   bids    uniquebidders
1       4       3
2       3       2
3       1       1
4       3       2
5       1       1
6       4       4
7       1       1
8       3       3
9       1       1

对这个长问题表示歉意。我知道这里的几个子问题(处理时间;类似循环的操作)已经在 SO 上涵盖了(我已经审查了其中的许多),但正是这些问题的结合使这对我来说特别具有挑战性(并希望对其他人有用)。

提前感谢您提供的任何帮助。

您可以为日期时间比较创建一个特定的函数。我确实知道它是否适用于 5 百万行,但它适用于示例数据集。我跟着你的脚步走。它使用创建运行长度类型 id 列的rleid。使用它两次,您将获得所需的组 ID。它刻意一步一步地进行,但可以写得更简洁。

library(data.table)
setDT(DT)
# this function compare each datetime of the vector with the first one
# If > 5 mins then a new time reference is set for next group and group 
# is incremented
func_perso <- function(vec){
  time1 <- vec[1]
  grp <- 1
  res <- vector("integer", length(vec))
  for(i in 1:length(vec)){
    time <- vec[i]
    if(difftime(time, time1, units = "secs") > 5*60){
      grp <- grp + 1
      time1 <- time
    }
    res[i] <- grp
  }
  res
}
# Create a datetime object (POSIXct) for easier comparaison
DT[, dtime := as.POSIXct(strptime(paste(date, time), "%d/%m/%Y %I:%M:%S %p", tz = "UTC"))]
# order data as you mentionned
setorder(DT, date, product, dtime)
# Apply func on column dtime by data and product
DT[, grp1 := .SD[, func_perso(dtime)], by = .(date, product)]
# use rleid to count identify the group
DT[, grp2 := paste0(product, rleid(grp1)), by = .(date, product)]
# count the group
DT[, grpid := rleid(grp2)]
# delete non necessary column
DT[, `:=`(
  dtime = NULL, 
  grp1 = NULL, 
  grp2 = NULL
)]
# the result
DT
#>     Observation     date        time product bidder grpid
#>  1:           1 1/1/2016  9:00:00 AM    book     AB     1
#>  2:           2 1/1/2016  9:01:00 AM    book     CD     1
#>  3:           3 1/1/2016  9:02:00 AM    book     EF     1
#>  4:           4 1/1/2016  9:03:00 AM    book     CD     1
#>  5:           5 1/1/2016  9:00:00 AM    game     AB     2
#>  6:           6 1/1/2016  9:01:00 AM    game     CD     2
#>  7:           7 1/1/2016  9:02:00 AM    game     CD     2
#>  8:           8 1/1/2016  9:07:00 AM    game     CD     3
#>  9:           9 1/2/2016  9:00:00 AM    book     AB     4
#> 10:          11 1/2/2016  9:02:00 AM    book     EF     4
#> 11:          12 1/2/2016  9:03:00 AM    book     EF     4
#> 12:          10 1/2/2016  9:06:00 AM    book     CD     5
#> 13:          14 1/2/2016  8:59:00 AM    game     CD     6
#> 14:          13 1/2/2016  9:00:00 AM    game     EF     6
#> 15:          15 1/2/2016  9:00:00 AM    game     GH     6
#> 16:          16 1/2/2016  9:01:00 AM    game     AB     6
#> 17:          17 1/2/2016 10:00:00 AM    game     AB     7
#> 18:          18 1/2/2016 10:06:00 AM    game     CD     8
#> 19:          19 1/2/2016 10:06:00 AM    game     EF     8
#> 20:          20 1/2/2016 10:06:00 AM    game     GH     8
#> 21:          21 1/2/2016  3:00:00 PM    game     AB     9
#>     Observation     date        time product bidder grpid

最新更新