Быстрая работа с датами в R

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

library(lubridate)
list_dates = replicate(34000,seq(as.Date("2019-03-14"),length.out = 208,by = "months"),simplify = F)
new_day = round(runif (34000,1,30))
new_day[sample(1:34000,10000)] = NA

new_dates = mapply(FUN = function(dates,day_change){
  day(dates) = ifelse(is.na(rep(day_change,length(dates))),day(dates),rep(day_change,length(dates)))
  dates = as.Date(ifelse(is.na(rep(day_change,length(dates))),dates,dates%m-%months(1)),origin = "1970-01-01")
  return(dates)
},dates = list_dates,day_change = as.list(new_day),SIMPLIFY = F)

Переменная new_dates должна содержать список исходных дат, перемещаемых соответственно переменной new_day. Функция в стороне работает следующим образом:

  1. если new_day отличается от NA, он изменит день даты на новый
  2. если new_day отличается от NA, он переместит месяцы дат на один назад.

Я открыт для любого решения, которое увеличит скорость независимо от использования пакетов (если они есть в CRAN).

РЕДАКТИРОВАТЬ

Поэтому, основываясь на комментариях, я сократил пример для списка из 2 векторов дат, каждый из которых содержит 2 даты, и создал ручной вектор новых дней:

list_dates = replicate(2,seq(as.Date("2019-03-14"),length.out = 2,by = "months"),simplify = F)

new_day = c(9,NA)

Это исходный ввод (переменная list_dates):

[[1]]
[1] "2019-03-14" "2019-04-14"

[[2]]
[1] "2019-03-14" "2019-04-14"

и ожидаемый результат функции mapply:

[[1]]
[1] "2019-02-09" "2019-03-09"

[[2]]
[1] "2019-03-14" "2019-04-14"

Как вы можете видеть, первый вектор дат был изменен на 9-й день, и каждая дата отставала на один месяц. Второй вектор дат не изменился, потому что new_dates равно NA для этого значения.

Можете ли вы сократить ввод до 10 дат и показать, что вы пытаетесь сделать вместе с ожидаемым результатом?

Ronak Shah 14.03.2019 06:00

В дополнение к комментарию Ронака, пожалуйста, используйте фиксированное случайное начальное число, чтобы обеспечить воспроизводимость выборочных данных и ожидаемого результата. Я также не уверен, что вы подразумеваете под «это переместит месяцы дат на один позади».. Вы хотите сказать, что new_month = old_month - 1 для всех дат элемента list_dates?

Maurits Evers 14.03.2019 06:16

Существует некоторая двусмысленность относительно того, хотите ли вы использовать функцию day из lubridate или индексировать аргумент day, который вы передаете mapply. Если последнее, то вы должны использовать квадратные скобки []. В любом случае избегайте называть свою переменную так же, как функции, которые вы собираетесь использовать.

Rohit 14.03.2019 06:23

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

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

Ответы 2

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

Вот решение lubridate

library(lubridate)
mapply(
    function(x, y) { if (!is.na(y)) {
            day(x) <- y;
            month(x) <- month(x) - 1
        }
        return(x) },
    list_dates, new_day, SIMPLIFY = F)
#[[1]]
#[1] "2019-02-09" "2019-03-09"
#
#[[2]]
#[1] "2019-03-14" "2019-04-14"

Или с помощью purrr

library(purrr)
library(lubridate)
map2(list_dates, new_day, function(x, y) {
    if (!is.na(y)) {
        day(x) <- y
        month(x) <- month(x) - 1
    }
    x })

Спасибо, но я сравниваю вашу функцию и мою с двумя датами длиной 208 в microbenchmark, и исходная функция mi немного быстрее.

Alejandro Andrade 15.03.2019 14:24

@AlejandroAndrade Понятно; Я добавил вариант purrr, не уверен, как это соотносится с microbenchmark сравнением. PS: отрицательный голос здесь на самом деле не оправдан и, откровенно говоря, является несколько ошибочным ответом на общую концепцию респондентов SO, дающих бесплатные советы; решение воспроизводит ожидаемый результат.

Maurits Evers 16.03.2019 10:21

Новая функция работает быстрее. Что касается отрицательного ответа, ваш первоначальный ответ был в основном той же функцией, но вместо ifelse вы использовали if, и это было медленнее, что не было предполагаемым результатом вопроса (у меня уже была функция, которая делала это быстрее). Я знаю, что это бесплатный совет, и спасибо за него, но меня также проголосовали за законный вопрос. Результат остается довольно медленным, но я думаю, вы не пробовали его с более чем 100 датами.

Alejandro Andrade 16.03.2019 16:30

В дополнение к решению Маурица, если вы хотите еще больше увеличить скорость вычислений, вы можете рассмотреть возможность использования нескольких ядер с doParallel

library(data.table)
library(doParallel)

registerDoParallel(3)
df <- data.table(new_day,list_dates)

mlply(df,
      function(new_day,list_dates){

        list_dates <- list_dates[[1]]

        if (!is.na(new_day)){
          day(list_dates) <- new_day
          list_dates <-  list_dates %m-% months(1)
        }

        return(list_dates)
      }, .parallel = T, .paropts = list(.packages='lubridate')
)

Я не могу распараллелить эту функцию, потому что я уже запускаю более крупную функцию, которая вызывает эту функцию с помощью foreach

Alejandro Andrade 15.03.2019 14:26

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