如何在数据帧上矢量化和加速strtime()logtime转换

时间:2011-12-27 19:39:02

标签: r dataframe string-conversion date-conversion vectorization

(编辑:这里的问题之一是规模,即对于一行有用的东西会在200,000 * 50数据帧上爆炸/崩溃。例如,strptime必须按列方式应用,而不是按行方式应用于避免挂。 我正在寻找工作代码解决方案,你实际上在200,000 * 50上运行,包括你的测量运行时间,而不仅仅是偶然的“这很容易”的评论。很容易获得运行时> 12小时,如果你选错了fn。接下来,我还要求你让我的零时间调整代码更快,工作还没完成,直到完成。到目前为止没人试过。)


我希望矢量化并加速以下多步骤对数时间转换,精确到毫秒,包括将 strtime() 转换为单个数字,然后进行减法,然后log() (省略了200,000行* 300列;其他(非时间)列)。 代码如下。 除了使其矢量化和快速,一个额外的问题是我不确定如何最好地表示每一步(高维)中间值,例如作为strtime,matrix,vector的列表。我已经尝试了 apply,sapply,lapply,vapply,ddply::maply(),... ,但是中间格式的不兼容性一直让我搞砸了......

每行有50列 time1..time50 (chr,format =“HH:MM:SS.sss”),表示时间为字符串,以毫秒为单位。 I需要毫秒精度。 在每行中,列 time1..time50 的顺序不是递减,我希望将它们转换为 time50 之前的时间日志。转换fn parse_hhmmsecms() 位于底部,需要严格的矢量化和加速,您可以看到注释掉的替代版本。我到目前为止所知: strtime() 比(多个) substr() 调用更快,然后我以某种方式转换为三个数字的列表(hh,mm,sec.ms) ,然后转换为向量,假设下一步应该是使用 %*% c(3600,60,1) 进行向量乘法转换为数字秒。 这是我为每一行做的伪代码,以及每个时间字符串;完整代码位于底部:

 for each row in dataframe { # vectorize this, loop_apply(), or whatever...
 #for each time-column index i ('time1'..'time50') { # vectorize this...
 hhmmsecms_50 <- parse_hhmmsecms(xx$time50[i])
 # Main computation
 xx[i,Clogtime] <- -10*log10(1000*(hhmmsecms_50 - parse_hhmmsecms(xx[i,Ctime]) ))
 # Minor task: fix up all the 'zero-time' events to be evenly spaced between -3..0
 #}
 }

因此涉及五个子问题:

  1. 如何对 strtime() 返回的列表进行矢量化处理?因为它返回了3个项目的列表,当传递2D数据帧或1D行时间字符串时,我们将获得3D或2D中间对象。 (我们在内部使用list-of-list?列表列表?列表数组?)
  2. 如何对整个函数进行矢量化 parse_hhmmsecms()
  3. 然后进行减法并记录
  4. 矢量化零时修正码(现在这是迄今为止最慢的部分)
  5. 如何加快步骤1 ... 4.?
  6. 下面的代码段使用十个示例列 time41..50 (如果您想要更大的样本,请使用 random_hhmmsecms()

    我尽力遵循these recommendations,这是可以重现的,因为我可以在六小时的工作中得到它:

    # Each of 200,000 rows has 50 time strings (chr) like this...    
    xx <- structure(list(time41 = c("08:00:41.465", "08:00:50.573", "08:00:50.684"
    ), time42 = c("08:00:41.465", "08:00:50.573", "08:00:50.759"), 
        time43 = c("08:00:41.465", "08:00:50.573", "08:00:50.759"
        ), time44 = c("08:00:41.465", "08:00:50.664", "08:00:50.759"
        ), time45 = c("08:00:41.465", "08:00:50.684", "08:00:50.759"
        ), time46 = c("08:00:42.496", "08:00:50.684", "08:00:50.759"
        ), time47 = c("08:00:42.564", "08:00:50.759", "08:00:51.373"
        ), time48 = c("08:00:48.370", "08:00:50.759", "08:00:51.373"
        ), time49 = c("08:00:50.573", "08:00:50.759", "08:00:54.452"
        ), time50 = c("08:00:50.573", "08:00:50.759", "08:00:54.452"
        )), .Names = c("time41", "time42", "time43", "time44", "time45", 
    "time46", "time47", "time48", "time49", "time50"), row.names = 3:5, class = "data.frame")
    
    # Handle millisecond timing and time conversion
    options('digits.secs'=3)
    
    # Parse "HH:MM:SS.sss" timestring into (numeric) number of seconds (Very slow)
    parse_hhmmsecms <- function(t) {
      as.numeric(substr(t,1,2))*3600 + as.numeric(substr(t,4,5))*60 + as.numeric(substr(t,7,12)) # WORKS, V SLOW
    
      #c(3600,60,1) %*% sapply((strsplit(t[1,]$time1, ':')), as.numeric) # SLOW, NOT VECTOR
    
      #as.vector(as.numeric(unlist(strsplit(t,':',fixed=TRUE)))) %*% c(3600,60,1) # WANT TO VECTORIZE THIS
    }
    
    random_hhmmsecms <- function(n=1, min=8*3600, max=16*3600) {
    # Generate n random hhmmsecms objects between min and max (8am:4pm)
    xx <- runif(n,min,max)
    ss <- xx %%  60
    mm <- (xx %/% 60) %% 60
    hh <- xx %/% 3600
    sprintf("%02d:%02d:%05.3f", hh,mm,ss)
    }
    
    xx$logtime45 <- xx$logtime44 <- xx$logtime43 <- xx$logtime42  <- xx$logtime41  <- NA
    xx$logtime50 <- xx$logtime49 <- xx$logtime48 <- xx$logtime47  <- xx$logtime46  <- NA
    
    # (we pass index vectors as the dataframe column ordering may change) 
    Ctime <- which(colnames(xx)=='time41') : which(colnames(xx)=='time50')
    Clogtime <- which(colnames(xx)=='logtime41') : which(colnames(xx)=='logtime50')
    for (i in 40:nrow(xx)) {
      #if (i%%100==0) { print(paste('... row',i)) }
    
      hhmmsecms_50 <- parse_hhmmsecms(xx$time50[i])
      xx[i,Clogtime] <- -10*log10(1000*(hhmmsecms_50 - parse_hhmmsecms(xx[i,Ctime]) ))
    
      # Now fix up all the 'zero-time' events to be evenly spaced between -3..0
      Czerotime.p <- which(xx[i,Clogtime]==Inf | xx[i,Clogtime]>-1e-9)
      xx[i,Czerotime.p] <- seq(-3,0,length.out=length(Czerotime.p))  
    }
    

1 个答案:

答案 0 :(得分:2)

你可能会过度复杂化。

毫秒的基类开始(在适当的操作系统上甚至是微秒),但请注意

  1. 您需要设置options("digits.secs"=7)(这是可以显示的最大值)才能看到它们显示

  2. 您需要strptime

  3. 的额外解析字符

    所有这些都在文档中,在SO上有无数的例子。

    快速举例:

    R> someTime <- ISOdatetime(2011, 12, 27, 2, 3, 4.567)
    R> someTime
    [1] "2011-12-27 02:03:04.567 CST"
    R> now <- Sys.time()
    R> now
    [1] "2011-12-27 16:48:20.247298 CST"      # microsecond display on Linux
    R> 
    R> txt <- "2001-02-03 04:05:06.789123"
    R> strptime(txt, "%Y-%m-%d %H:%M:%OS")    # note the %0S for sub-seconds
    [1] "2001-02-03 04:05:06.789123"
    R> 
    

    strptimeas.POSIXct等关键函数都已进行了矢量化,您可以将整列放在其中。