R извлекает компоненты времени из полустандартных строк

Настроить

У меня есть столбец длительностей, хранящихся в виде строк в кадре данных. Я хочу преобразовать их в соответствующий объект времени, вероятно, POSIXlt. Большинство строк легко разобрать, используя этот метод:

> data <- data.frame(time.string = c(
+   "1 d 2 h 3 m 4 s",
+   "10 d 20 h 30 m 40 s",
+   "--"))
> data$time.span <- strptime(data$time.string, "%j d %H h %M m %S s")
> data$time.span
[1] "2012-01-01 02:03:04" "2012-01-10 20:30:40" NA

Отсутствующие длительности закодированы "--" и должны быть преобразованы в NA - это уже происходит, но должно быть сохранено.

Сложность состоит в том, что в строку сбрасываются элементы с нулевым значением Таким образом, желаемое значение 2012-01-01 02:00:14 будет строка "1 d 2 h 14 s", Однако эта строка анализирует NA с простым парсером:

> data2 <- data.frame(time.string = c(
+  "1 d 2 h 14 s",
+  "10 d 20 h 30 m 40 s",
+  "--"))
> data2$time.span <- strptime(data2$time.string, "%j d %H h %M m %S s")
> data2$time.span
[1] NA "2012-01-10 20:30:40" NA

Вопросы

  1. Что такое "путь R" для обработки всех возможных форматов строк? Может быть, проверить и извлечь каждый элемент в отдельности, а затем рекомбинировать?
  2. Является ли POSIXlt правильным целевым классом? Мне нужно, чтобы продолжительность была свободна от любого конкретного времени начала, поэтому добавление ложных данных года и месяца (2012-01-) тревожит.

Решение

У @mplourde определенно была правильная идея с динамическим созданием строки форматирования, основанной на тестировании различных условий в формате даты. Добавление cut(Sys.Date(), breaks='years') в качестве базовой линии для datediff было также хорошо, но не смог объяснить критическую причину в as.POSIXct() Примечание: я использую базу R2.11, возможно, это было исправлено в более поздних версиях.

Выход из as.POSIXct() резко меняется в зависимости от того, включен ли компонент даты:

> x <- "1 d 1 h 14 m 1 s"
> y <-     "1 h 14 m 1 s"  # Same string, no date component
> format (x)  # as specified below
[1] "%j d %H h %M m %S s"
> format (y)
[1] "% H h % M %S s"    
> as.POSIXct(x,format=format)  # Including the date baselines at year start
[1] "2012-01-01 01:14:01 EST"
> as.POSIXct(y,format=format)  # Excluding the date baselines at today start
[1] "2012-06-26 01:14:01 EDT"

Таким образом, второй аргумент в пользу difftime функция должна быть:

  • Начало первого дня текущего года, если во входной строке есть компонент дня
  • Начало текущего дня, если во входной строке нет компонента дня

Это может быть достигнуто путем изменения параметра устройства на cut функция:

parse.time <- function (x) {
  x <- as.character (x)
  break.unit <- ifelse(grepl("d",x),"years","days")  # chooses cut() unit
  format <- paste(c(if (grepl("d", x)) "%j d",
                    if (grepl("h", x)) "%H h",
                    if (grepl("m", x)) "%M m",
                    if (grepl("s", x)) "%S s"), collapse=" ")

  if (nchar(format) > 0) {
    difftime(as.POSIXct(x, format=format), 
             cut(Sys.Date(), breaks=break.unit),
             units="hours")
  } else {NA}

}

2 ответа

Решение

difftime объекты являются объектами длительности времени, которые могут быть добавлены к POSIXct или же POSIXlt объекты. Может быть, вы хотите использовать это вместо POSIXlt?

Что касается преобразования из строк во временные объекты, вы можете сделать что-то вроде этого:

data <- data.frame(time.string = c(
    "1 d 1 h",
    "30 m 10 s",
    "1 d 2 h 3 m 4 s",
    "2 h 3 m 4 s",
    "10 d 20 h 30 m 40 s",
    "--"))

f <- function(x) {
    x <- as.character(x)
    format <- paste(c(if (grepl('d', x)) '%j d',
                      if (grepl('h', x)) '%H h',
                      if (grepl('m', x)) '%M m',
                      if (grepl('s', x)) '%S s'), collapse=' ')

    if (nchar(format) > 0) {
        if (grepl('%j d', format)) {
            # '%j 1' is day 0. We add a day so that x = '1 d' means 24hrs.
            difftime(as.POSIXct(x, format=format) + as.difftime(1, units='days'), 
                    cut(Sys.Date(), breaks='years'),
                    units='hours')
        } else {
            as.difftime(x, format, units='hours')
        }
    } else { NA }
}

data$time.span <- sapply(data$time.string, FUN=f)

Я думаю вам повезет больше с lubridate

Из дат и времен Made Easy с lubridate:

5.3. Длительности

...

Продолжительность продолжительности не зависит от високосных лет, високосных секунд и летнего времени, поскольку длительности измеряются в секундах. Следовательно, длительности имеют постоянные длины и могут быть легко сопоставлены с другими длительностями. Длительности являются подходящим объектом для использования при сравнении атрибутов, основанных на времени, таких как скорости, скорости и время жизни. lubridate использует класс difftime из базы R для продолжительности. Для этого были созданы дополнительные методы difftime.

lubridate использует класс difftime из базы R для продолжительности. Для этого были созданы дополнительные методы difftime.

...

Объекты длительности могут быть легко созданы с помощью вспомогательных функций dyears(), dweeks(), ddays(), dhours(), dminutes() и dseconds(). Символ d в заголовке обозначает продолжительность и отличает эти объекты от объектов периода, которые обсуждаются в разделе 5.4. Каждый объект создает длительность в секундах, используя оценочные отношения, приведенные выше.

Тем не менее, я (пока) не нашел функцию для разбора строки по длительности.


Вы также можете взглянуть на Ruby's Chronic, чтобы увидеть, насколько элегантным может быть анализ времени. Я не нашел подобную библиотеку для R.

Другие вопросы по тегам