Как векторизовать и ускорить преобразование времени выполнения strtime () во фрейм данных - PullRequest
0 голосов
/ 27 декабря 2011

(РЕДАКТИРОВАТЬ: одна из проблем здесь - масштабирование, а именно то, что работает для одной строки, взорвется / аварийно завершится R на 200 000 * 50 данных. Например, strptime должен применяться по столбцам, а не по строкам, чтобыизбегайте зависаний. Я ищу решения для работающего кода, которые на самом деле работали на 200 000 * 50, включая измеренное время выполнения , а не просто случайные замечания "это просто". Легко получить время выполнения> 12 часов, если вывыберите неправильный fn. Затем я также попросил вас ускорить мой код настройки нулевого времени, пока работа не завершена. Пока никто не пытался.)


Я хочу векторизация и ускорение следующего многошагового преобразования времени журнала с точностью до миллисекунды, включая преобразование strtime() в одно числовое значение с последующим вычитанием, а затем log() для больших данных-frame (200 000 строк * 300 столбцов; другие (не временные) столбцы опущены).Код ниже.Помимо того, что это делает его векторизованным и быстрым, дополнительная проблема заключается в том, что я не знаю, как лучше всего представлять (многомерные) промежуточные значения на каждом шаге , например, в виде списка из strtime, matrix, vector).Я уже пробовал apply,sapply,lapply,vapply,ddply::maply(),..., но несовместимость промежуточного формата (-ов) продолжает мешать мне ...

В каждой строке по 50 столбцов time1..time50 (chr, format = "HH: MM: SS.sss") представляет время в виде строки с разрешением в миллисекундах. Мне нужна точность в миллисекундах.В каждой строке столбцы 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 промежуточный объект.(Используем ли мы для внутреннего использования список-список? Матрица списков? Массив списков?)
  2. Как векторизовать всю функцию parse_hhmmsecms()?
  3. Тогдавыполнить вычитание и записать в журнал
  4. Также векторизовать код исправления нулевого времени (сейчас это самая медленная часть)
  5. Как ускорить шаги 1 ... 4.?

Ниже приведен фрагмент кода с десятью примерами столбцов time41..50 (используйте random_hhmmsecms(), если вы хотите больший образец)

Я приложил все усилияследовать этим рекомендациям , это настолько воспроизводимо, насколько я могу получить за шесть часов работы:

# 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 Ответ

2 голосов
/ 28 декабря 2011

Возможно, вы слишком усложняете вещи.

Начните с базовых классов, которые делают миллисекунды очень хорошо (и в соответствующих операционных системах даже микросекунды), но учтите, что

  1. вам нужно установить options("digits.secs"=7) (это максимум, который можно отобразить), чтобы увидеть их отображение

  2. вам нужен дополнительный символ синтаксического анализа для strptime и др.

все из которых есть в документах, и бесчисленное множество примеров здесь на 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> 

И ключевые функции, такие как strptime или as.POSIXct все векторизованы, и вы можете бросить в них целые столбцы.

...