【问题标题】:Create dummy variables in one table based on range of dates in another table根据另一个表中的日期范围在一个表中创建虚拟变量
【发布时间】:2013-05-07 16:14:15
【问题描述】:

我有两张桌子。 table1 是这样的

  date       hour     data
2010-05-01     3        5
2010-05-02     7        7
2010-05-02     10       8
2010-07-03     18       3
2011-12-09     22       1
2012-05-01     3        0

这存储为data.table,并在datehour 上设置了密钥。 我有另一张桌子,看起来像这样。这是我的outages 表。

 resource        date_out                date_back
   joey       2010-04-30 4:00:00      2010-05-02 8:30:00
   billy      2009-04-20 7:00:00      2009-02-02 5:30:00
   bob        2011-11-15 12:20:00     2010-12-09 23:00:00
   joey       2012-04-28 1:00:00      2012-05-02 17:00:00

我想向table1 添加列,其中这些列是outages 表中的资源。我希望这些列中的值在没有中断时为 0,在有中断时为 1。

这个例子的结果应该是。

  date       hour     data     joey      billy      bob
2010-05-01     3        5       1          0         0        
2010-05-02     7        7       1          0         0 
2010-05-02     10       8       0          0         0 
2010-07-03     18       3       0          0         0 
2011-12-09     22       1       0          0         1
2012-05-01     3        0       1          0         0 

实际上,我的table1 有大约 2500 行,而我的outages 表有 19000 行。我能想到的唯一方法是遍历outages 表的每一行,然后将 1 插入到@987654334 @在正确的地方。我的代码依赖于table1,因此至少它不必为outages 的每一行扫描该表的100%。但是,我的数据需要 4 个多小时。

for (out in 1:length(outages$resource)) {
  a<-as.character(outages[out]$resource)
  #if column doesn't exist then create it
  if (a %in% colnames(table1)==FALSE) {
    table1$new<-0
    setnames(table1, "new", a)
    }
  midpoint<-round(length(table1$date)/2,0)
  if (table1$date[midpoint]+table1$hour[midpoint]*60*60>=outages[out]$due_out && table1$date[midpoint]+table1$hour[midpoint]*60*60<=outages    [out]$due_back)
  {
    while(table1$date[midpoint]+table1$hour[midpoint]*60*60>=outages[out]$due_out && midpoint>=1 && midpoint<=length(table1$date)) {
      table1[midpoint,a:=1,with=FALSE]
      midpoint<-midpoint-1
    }
    midpoint<-round(length(table1$date)/2,0)
    while(table1$date[midpoint]+table1$hour[midpoint]*60*60<=outages[out]$due_back && midpoint>=1 && midpoint<=length(table1$date)) {
      table1[midpoint,a:=1,with=FALSE]
      midpoint<-midpoint+1
    }
  } else {
    if (table1$date[midpoint]+table1$hour[midpoint]*60*60>outages[out]$due_back) {
      while(table1$date[midpoint]+table1$hour[midpoint]*60*60>outages[out]$due_back && midpoint>=1 && midpoint<=length(table1$date)) {
        midpoint<-midpoint-1
      }
      while(table1$date[midpoint]+table1$hour[midpoint]*60*60>=outages[out]$due_out && midpoint>=1 && midpoint<=length(table1$date)) {
        table1[midpoint,a:=1,with=FALSE]
        midpoint<-midpoint-1
      }
    } 
    midpoint<-round(length(table1$date)/2,0)
    if (table1$date[midpoint]+table1$hour[midpoint]*60*60<outages[out]$due_out) {
      while(table1$date[midpoint]+table1$hour[midpoint]*60*60<outages[out]$due_out && midpoint>=1 && midpoint<=length(table1$date)) {
        midpoint<-midpoint+1
      }
      while(table1$date[midpoint]+table1$hour[midpoint]*60*60<=outages[out]$due_back && midpoint>=1 && midpoint<=length(table1$date)) {
        table1[midpoint,a:=1,with=FALSE]
        midpoint<-midpoint+1
 }
 }
 }
if (sum(table1[,a,with=FALSE])==0) {
  table1[,a:=NULL,with=FALSE]
}
}

引用每个人最喜欢的电视广告台词“必须有更好的方法”。

【问题讨论】:

  • 您能否为您的数据发布str 输出,以便我们查看数据类型?或者更好的是,dput 输出?
  • 可能类似于table1[,is_out:=any(outages$date_out &lt;time &amp; time &lt; outages$date_back)]?这取决于您首先创建一个 table1$time 变量来组合您现在拥有的两列。
  • 请提供dput 输出。很难复制/粘贴带有空格的列(yyyy-mm-dd 和 hh:mm:ss 之间的列)。
  • eddi 下面的回答完美。

标签: r data.table


【解决方案1】:

这是实现您想要的目标的方法。这假设您的 table1 的时间精度为 1 小时。尽管可以将其修改为任意精度,但对于较大的时间间隔,它会表现得更好,因为它构造了date_out-date_back 范围内的可能时间的完整序列。请注意,我使用了与 OP 略有不同的表格来说明重叠间隔并纠正 OP 中的一些错误。

table1 = data.table(date = c("2010-05-01", "2010-05-02", "2010-05-02", "2010-07-03", "2011-12-09", "2012-05-01"), hour = c(3,7,10,18,22,3), data = c(5,7,8,3,1,0))
outages = data.table(resource = c("joey", "bob", "billy", "bob", "joey"), date_out = c("2010-04-30 4:00:00", "2010-04-30 4:00:00", "2009-04-20 7:00:00", "2011-11-15 12:20:00", "2012-04-28 1:00:00"), date_back=c("2010-05-02 8:30:00", "2010-05-02 8:30:00", "2009-06-02 5:30:00", "2011-12-09 23:00:00", "2012-05-02 17:00:00"))

# round up date_out and round down date_back
# and create a sequence in-between spaced by 1 hour
outages[, list(datetime = seq(as.POSIXct(round(as.POSIXct(date_out) + 30*60-1, "hours")),
                              as.POSIXct(round(as.POSIXct(date_back) - 30*60, "hours")),
                              60*60)),
          by = list(resource, date_out)] -> outages.expanded
setkey(outages.expanded, datetime)

# merge with the original table, then run "table" to get the frequencies/occurences
# and cbind back with the original table
cbind(table1, unclass(table(
                outages.expanded[table1[, list(datetime=as.POSIXct(paste0(date, " ", hour, ":00:00")))],
                                 resource])))

#         date hour data bob joey
#1: 2010-05-01    3    5   1    1
#2: 2010-05-02    7    7   1    1
#3: 2010-05-02   10    8   0    0
#4: 2010-07-03   18    3   0    0
#5: 2011-12-09   22    1   1    0
#6: 2012-05-01    3    0   0    1

【讨论】:

  • 好的,这看起来可以完美地满足我的需求。我只用虚构的数据成功地复制了这个。但是,当我尝试使用我的真实数据执行此操作时,我在产生 outages.expanded 的行出现错误。错误是 Error in seq.POSIXt(as.POSIXct(round(as.POSIXct(date_out) + 30 * 60 - 1, : 'to' must be of length 1) 我不确定我应该寻找什么来修复这个。关于那个错误的任何指针?
  • @DeanMacGregor 您有重复的 (resource,date_out) 对。您可以删除重复项,或者如果您想保留它们,请按行名而不是​​ by=row.names(outages)
  • @DeanMacGregor 经过一番思考-您需要对其进行一些修改以使其与行名一起使用,我建议您删除重复项,因为它们不应该从您的数据描述
  • 嗨。最好使用roll=TRUE, iiuc 来完成。在 outages$date_out 上设置一个键。从 table1 滚动连接到它。将连接结果保留在 table1.date
  • +1 @MatthewDowle - 我懒得替换这个答案,但这是一个非常相似的问题,我使用了roll 参数而不是上面的参数:stackoverflow.com/questions/16649885/replacing-nested-loop-in-r/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 2021-01-19
相关资源
最近更新 更多