R避免使用条件进行计数循环

时间:2019-07-08 07:29:42

标签: r performance count data.table

我将R和data.table包一起使用。 我有一个计算计数的循环,但是由于这是一个循环,因此非常慢。现在,我想以某种方式进行更改,以免计算几天。

我有一个数据集,我想计算这个人已经出现在数据集中的频率。当名字,姓氏和出生日期(生日,出生月份和出生年份)相同时,它是相同的“人”。但是,我的问题是日期也很重要。因此,如果我要看的那个人出现在数据集中,我必须检查“同一个人”的日期是否早于我要看的那个人的日期。因此,在我正在寻找的人之前,必须先认识同一个人。

然后,我还想计算这些条目之间的平均时间。 这是我当前的解决方案(可以运行,但是速度很慢):

library(data.table)
data <- data[order(-persondatetime)]
vec_countperson <- numeric(nrow(data))
vec_time <- numeric(nrow(data))

for (i in 1:nrow(data)){
  vec_countperson[i] <- data[firstname == data[i, firstname] &
                                   surname == data[i, surname] &
                                   birthdate == data[i, birthdate] &
                                   persondatetime < data[i, persondatetime], .N]
       vec_time[i] <- data[firstname == data[i, firstname] &
                                   surname == data[i, surname] &
                                   birthdate == data[i, birthdate] &
                                   persondatetime < data[i, persondatetime], 
                                   mean(abs(diff(c(persondatetime, data[i, persondatetime]))))]
}


data[, countperson := vec_countperson]
data[, timebetweenentries := vec_time]

示例数据表如下:

data <- data.table(
  firstname = c("Paul", "Jens", "Jens", "Jens","Paul", "Dieter"), 
  surname = c("Mueller", "Mustermann", "Mustermann", "Mustermann", "Mueller", "Brian"), 
  birthdate = as.Date(c("1960-05-08", "1960-05-08", "1960-05-08",
                        "1960-05-08", "1960-05-08", "1960-05-08")), 
  persondatetime = as.POSIXct(c("2018-05-01 23:18:38 CET", "2018-03-01 23:18:38 CET",
                                "2018-06-01 23:18:38 CET", "2018-04-01 23:18:38 CET", 
                                "2018-04-06 23:18:38 CET", "2018-04-08 23:18:38 CET")))

预期输出为:

   firstname    surname  birthdate      persondatetime countperson timebetweenentries
1:      Jens Mustermann 1960-05-08 2018-03-01 23:18:38           0                NaN
2:      Jens Mustermann 1960-05-08 2018-04-01 23:18:38           1           30.95833
3:      Paul    Mueller 1960-05-08 2018-04-06 23:18:38           0                NaN
4:    Dieter      Brian 1960-05-08 2018-04-08 23:18:38           0                NaN
5:      Paul    Mueller 1960-05-08 2018-05-01 23:18:38           1           25.00000
6:      Jens Mustermann 1960-05-08 2018-06-01 23:18:38           2           45.97917

您对我如何避免循环有任何想法吗?我想到了其他想法,但我的问题总是与日期有关!

1 个答案:

答案 0 :(得分:1)

您可以使用类似于@ chinsoon12在其评论中发布的代码来重新创建计数人员列。

data[data, 
     on=.(firstname, surname, birthdate=birthdate, persondatetime > persondatetime),
     countperson:=.N, 
     by=.EACHI]
data[, countperson := coalesce(countperson, 0L)]

此更新联接的data.table语法为X[I, on=.(conditions), var:=.N, by=.EACHI]。对于data.table I中的每一行,找到X中与conditions匹配的行。使用by=.EACHI参数对I中的行的联接结果进行分组。在data.table中,.N符号表示每个组的行数。在这种情况下,对于I中的每一行,.NX中基于conditions进行匹配的行数。如果I中的行在X中没有匹配的行,则.N为NA,我们在下一行中使用合并将其设置为0。

一种重新创建timebetweenentries变量的方法是对表示行属于同一个人的列进行分组,计算每个组的persondatetime平均差,然后将其分配给data.table中的一列。如果您希望连续输入之间有时间,那么应该在获得差异之前对persondatetime进行排序。

下面的代码使用data.table的setkey函数一次完成所有排序。这样可以加快分组速度,并避免为每个组调用sort(persondatetime)。

setkey(data, firstname, surname, birthdate, persondatetime)
data[, timebetweenentries := mean(abs(diff(persondatetime)), na.rm=T)