Регулярное выражение для преобразования уравнений времени в дату и время R (POSIXct)

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

\* = current time 
t = current day (00:00)
mo = month 
d = days 
h = hours
m = minutes 

Например, *-3d — это текущее время минус 3 дня, t-3h — это три часа до утра сегодняшнего дня (полночь вчерашнего дня).

Я хотел бы иметь возможность вводить эти уравнения в R и получать соответствующее значение POSIXct. Я пытаюсь использовать регулярное выражение в приведенной ниже функции, но теряю числовой множитель для каждой строки:

strTimeConverter <- function(z){
  ret <- stringi::stri_replace_all_regex(
    str = z, 
    pattern = c('^\\*', 
                '^t', 
                '([[:digit:]]{1,})mo', 
                '([[:digit:]]{1,})d', 
                '([[:digit:]]{1,})h',
                '([[:digit:]]{1,})m'),
    replacement = c('Sys.time()', 
                    'Sys.Date()', 
                    '*lubridate::months(1)', 
                    '*lubridate::days(1)', 
                    '*lubridate::hours(1)', 
                    '*lubridate::minutes(1)'),
    vectorize_all = F
  )
  return(ret)
  # return(eval(expr = parse(text = ret)))
}

> strTimeConverter('*-5mo+3d+4h+2m')
[1] "Sys.time()-*lubridate::months(1)+*lubridate::days(1)+*lubridate::hours(1)+*lubridate::minutes(1)"

> strTimeConverter('t-5mo+3d+4h+2m')
[1] "Sys.Date()-*lubridate::months(1)+*lubridate::days(1)+*lubridate::hours(1)+*lubridate::minutes(1)"

Ожидаемый результат:

# *-5mo+3d+4h+2m
"Sys.time()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+4*lubridate::minutes(1)"

# t-5mo+3d+4h+2m
"Sys.Date()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+4*lubridate::minutes(1)"

Я предполагал, что заключение [[:digit]]{1,} в круглые скобки () сохранит их, но очевидно, что это не работает. Я определил шаблон следующим образом, иначе код заменяет повторяющиеся вхождения, например. * преобразуется в Sys.time(), но затем m в Sys.time() заменяется на *lubridate::minutes(1).

Я планирую преобразовать (ожидаемый) вывод в дату-время R, используя eval(parse(text = ...)) — в настоящее время закомментировано в функции.

Я открыт для использования других пакетов или подходов.

Обновлять

Немного поработав, я обнаружил, что приведенная ниже версия работает — я заменяю строки в таком порядке, чтобы вновь замененные символы не заменялись снова:

strTimeConverter <- function(z){
  ret <- stringi::stri_replace_all_regex(
    str = z, 
    pattern = c('y', 'd', 'h', 'mo', 'm', '^t', '^\\*'),
    replacement = c('*years(1)',
                    '*days(1)', 
                    '*hours(1)', 
                    '*days(30)',
                    '*minutes(1)',
                    'Sys.Date()', 
                    'Sys.time()'),
    vectorize_all = F
  )
  ret <- gsub(pattern = '\\*', replacement = '*lubridate::', x = ret)
  rdate <- (eval(expr = parse(text = ret)))
  attr(rdate, 'tzone') <- 'UTC'
  return(rdate)
}
sample_string <- '*-5mo+3d+4h+2m'
strTimeConverter(sample_string)

Это работает, но не очень элегантно и, скорее всего, потерпит неудачу, поскольку я вынужден включать другие выражения (например, yd для дня года, например, 124).

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
135
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать обратные ссылки в заменах следующим образом:

library(stringr)
x <- c("*-5mo+3d+4h+2m", "t-5mo+3d+4h+2m")
repl <- c('^\\*' = 'Sys.time()', '^t' = 'Sys.Date()', '(\\d+)mo' = '\\1*lubridate::months(1)', '(\\d+)d' = '\\1*lubridate::days(1)',  '(\\d+)h' =  '\\1*lubridate::hours(1)', '(\\d+)m' = '\\1*lubridate::minutes(1)')
stringr::str_replace_all(x, repl)
## => [1] "Sys.time()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"
##    [2] "Sys.Date()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"

Смотрите демоверсию R онлайн.

См., например, '(\\d+)mo' = '\\1*lubridate::months(1)'. Здесь (\d+)mo соответствует и захватывает в группу 1 одну или несколько цифр, а mo просто соответствует. Затем, когда совпадение найдено, \1 в \1*lubridate::months(1) вставляет содержимое группы 1 в результирующую строку.

Обратите внимание, что замена может быть более безопасной, если вы закроете совпадение периода времени границей слова (\b) справа:

repl <- c('^\\*' = 'Sys.time()', '^t' = 'Sys.Date()', '(\\d+)mo\\b' = '\\1*lubridate::months(1)', '(\\d+)d\\b' = '\\1*lubridate::days(1)',  '(\\d+)h\\b' =  '\\1*lubridate::hours(1)', '(\\d+)m\\b' = '\\1*lubridate::minutes(1)')

Это не сработает, если интервалы времени склеены друг с другом без разделителей, отличных от слов, но у вас есть + в строках примеров, так что здесь это безопасно.

На самом деле, вы можете заставить его работать и с той функцией, которую вы использовали. Просто убедитесь, что обратные ссылки имеют синтаксис $n:

x <- c("*-5mo+3d+4h+2m", "t-5mo+3d+4h+2m")
pattern = c('^\\*', '^t', '(\\d+)mo', '(\\d+)d', '(\\d+)h', '(\\d+)m')
replacement = c('Sys.time()', 'Sys.Date()', '$1*lubridate::months(1)', '$1*lubridate::days(1)', '$1*lubridate::hours(1)', '$1*lubridate::minutes(1)')
stringi::stri_replace_all_regex(x, pattern, replacement, vectorize_all=FALSE)

Выход:

[1] "Sys.time()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"
[2] "Sys.Date()-5*lubridate::months(1)+3*lubridate::days(1)+4*lubridate::hours(1)+2*lubridate::minutes(1)"

Спасибо! Я попробую - мне было интересно, есть ли у stringr лучший метод, чем stringi для моего случая использования - похоже, он есть!

Gautam 18.12.2020 22:25

@Gautam Да, я на самом деле взломал его: вы можете использовать stringi::stri_replace_all_regex, но синтаксис обратной ссылки $n, а не \n.

Wiktor Stribiżew 18.12.2020 22:32

Другой вариант прямого производства времени будет следующим:

strTimeConvert <- function(base=Sys.time(), delta = "-5mo+3d+4h+2m"){
  mo <- gsub(".*([+-]\\d+)mo.*", "\\1", x)
  ds <- gsub(".*([+-]\\d+)d.*", "\\1", x)
  hs <- gsub(".*([+-]\\d+)h.*", "\\1", x)
  ms <- gsub(".*([+-]\\d+)m.*", "\\1", x)
  out <- base + months(as.numeric(mo)) + days(as.numeric(ds)) + 
          hours(as.numeric(hs)) + minutes(as.numeric(ms))
  out
}
strTimeConvert()
# [1] "2020-07-21 20:32:19 EDT"

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