Векторизованное преобразование часовых поясов с помощью lubridate

У меня есть фрейм данных со столбцом строк даты и времени:

library(tidyverse)
library(lubridate)

testdf = data_frame(
  mytz = c('Australia/Sydney', 'Australia/Adelaide', 'Australia/Perth'),
  mydt = c('2018-01-17T09:15:00', '2018-01-17T09:16:00', '2018-01-17T09:18:00'))

testdf

#  A tibble: 3 x 2
#   mytz               mydt
#   <chr>              <chr>
# 1 Australia/Sydney   2018-01-17T09:15:00
# 2 Australia/Adelaide 2018-01-17T09:16:00
# 3 Australia/Perth    2018-01-17T09:18:00

Я хочу преобразовать эти строки даты и времени в объекты даты и времени POSIX с соответствующими часовыми поясами:

testdf %>% mutate(mydt_new = ymd_hms(mydt, tz = mytz))

Ошибка в mutate_impl (.data, точки): ошибка оценки: аргумент tz должен быть односимвольной строкой. Дополнительно: Предупреждение: в if (tz! = "UTC") {: длина условия> 1, и будет использоваться только первый элемент.

Я получаю тот же результат, если использую ymd_hms без часового пояса и перенаправляю его в force_tz. Справедливо ли заключить, что lubridate не поддерживает какую-либо векторизацию, когда дело касается операций с часовыми поясами?


person jimjamslam    schedule 17.01.2018    source источник
comment
Возможно testdf %>% rowwise %>% mutate(mydt_new = ymd_hms(mydt, tz = mytz))?   -  person jazzurro    schedule 17.01.2018
comment
Это работает! Я не понимаю, зачем нужен rowwise ...   -  person jimjamslam    schedule 17.01.2018
comment
Я полагаю, если он работает с группами, и каждая группа имеет одну строку, тогда tz имеет длину только 1 в каждом вызове. Это сработало бы, даже если бы у меня было несколько строк с одним и тем же часовым поясом. Спасибо!   -  person jimjamslam    schedule 17.01.2018
comment
Рад принять это как принятый ответ, если хотите!   -  person jimjamslam    schedule 17.01.2018
comment
OK. Позвольте мне разобраться с этим.   -  person jazzurro    schedule 17.01.2018
comment
Интересно, что вывод тиббла из этого выводит новый столбец в Australia/Perth, хотя я нахожусь в Australia/Sydney. Интересно, произвольно ли это отображение?   -  person jimjamslam    schedule 17.01.2018


Ответы (2)


Другой вариант - map2. Может быть лучше сохранить разные tz выходные данные в list, так как это может быть приведено к одному tz

library(tidyverse)
out <- testdf %>%
         mutate(mydt_new = map2(mydt, mytz, ~ymd_hms(.x, tz = .y)))

При необходимости его можно unnestед

out %>%
   unnest

Значения в list равны

out %>%
   pull(mydt_new)
#[[1]]
#[1] "2018-01-17 09:15:00 AEDT"

#[[2]]
#[1] "2018-01-17 09:16:00 ACDT"

#[[3]]
#[1] "2018-01-17 09:18:00 AWST"
person akrun    schedule 17.01.2018
comment
Ааа, так что лучше оставить мои даты в виде столбца списка? Я не думал об этом! - person jimjamslam; 17.01.2018
comment
@rensa Это лучше, потому что столбец допускает только один часовой пояс, а принуждение к одному может быть другим. - person akrun; 17.01.2018
comment
В моем конкретном случае использования сейчас принудительное использование одного часового пояса не проблема, но я согласен с тем, что это в целом предпочтительнее :) - person jimjamslam; 17.01.2018
comment
@rensa Что интересно, если я использую unnest, я получаю другое значение по сравнению со значением jazzuro. Значит, это будет принуждение к разным tz - person akrun; 17.01.2018
comment
Хотя rowwise и ungroup - хорошее решение, я думаю, что это, вероятно, предпочтительнее, чтобы сохранить информацию о часовом поясе как есть. - person jimjamslam; 17.01.2018
comment
@rensa Я думаю, что jazzurro также верен, поскольку вы действительно не против одного часового пояса. Его ответ вынуждает к `Australia / Perth`, который является последней записью, в то время как мой принуждает к UTC после unnest, т.е. out %>% unnest %>% pull(mydt_new) %>% tz(.) - person akrun; 17.01.2018
comment
Если бы у меня была возможность, я бы принял оба ваших ответа ???? Но я думаю, что в общем случае было бы неплохо расставить приоритеты в отношении предсказуемости результатов. - person jimjamslam; 17.01.2018
comment
@akrun Недавно я ответил на этот вопрос, что напомнило мне об этом вопросе. Мне было интересно, безопасно и / или лучше держать часовые пояса подальше от времени. В связанной публикации я создал часовой пояс как новый столбец и преобразовал время в каждый часовой пояс. Даже после unnest я вижу правильное время. Я хочу знать, как вы обычно относитесь ко времени. Любой совет? - person jazzurro; 22.01.2020
comment
@jazzurro Рад вас слышать. Я предполагаю, что вы стандартизируете часовой пояс на основе «GMT», верно? Если это так, я бы сделал то же самое, чтобы получить часовой пояс индивидуально. - person akrun; 23.01.2020
comment
@akrun Спасибо за ответ. Рад слышать, что вы поступили бы так же. Я буду придерживаться подхода. Кстати, ты еще здесь качаешься. Это круто. Мне еще есть чему у вас поучиться. - person jazzurro; 23.01.2020
comment
@akrun Я думаю, что ты внесешь основной вклад в мою репутацию. Очень признателен. :) - person jazzurro; 24.01.2020

tz argument must be a single character string. означает, что в ymd_hms() выбрано несколько часовых поясов. Чтобы убедиться, что в функцию добавлен только один часовой пояс, я использовал rowwise(). Обратите внимание, что я не в австралийском часовом поясе. Поэтому я не уверен, что результат, который у меня есть, идентичен вашему.

testdf <- data_frame(mytz = c('Australia/Sydney', 'Australia/Adelaide', 'Australia/Perth'),
                     mydt = c('2018-01-17 09:15:00', '2018-01-17 09:16:00', '2018-01-17 09:18:00'))

testdf %>% 
rowwise %>% 
mutate(mydt_new = ymd_hms(mydt, tz = mytz))

  mytz               mydt                mydt_new           
  <chr>              <chr>               <dttm>             
1 Australia/Sydney   2018-01-17 09:15:00 2018-01-17 06:15:00
2 Australia/Adelaide 2018-01-17 09:16:00 2018-01-17 06:46:00
3 Australia/Perth    2018-01-17 09:18:00 2018-01-17 09:18:00
person jazzurro    schedule 17.01.2018
comment
Да, я получаю тот же результат. Также кажется, что метод печати тиббл выбирает часовой пояс из значений в столбце, а не из местного часового пояса пользователя. - person jimjamslam; 17.01.2018
comment
@rensa Понятно. Это полезно знать. Спасибо за информацию. :) - person jazzurro; 17.01.2018
comment
@jazzurro Я опубликовал как решение, так как это было интересное сравнение того, как приводятся часовые пояса. Надеюсь, ты не против. - person akrun; 17.01.2018
comment
@akrun Я понял, что вы просили проверить код в удаленном комментарии. Я просто не мог тебе ответить в тот момент. Извините. Я думаю, вы расследовали интересное дело. Мне есть о чем спросить еще в этом пункте, но я не могу взять на себя обязательство здесь прямо сейчас. Могу я связаться с вами здесь позже? - person jazzurro; 17.01.2018
comment
@jazzurro конечно, я думал, что это даст правильный ответ, но смена часового пояса происходит раньше. Итак, упаковка с list не помогла, поэтому я удалил этот код - person akrun; 17.01.2018
comment
@akrun Наконец-то я устроился. Увидев разницу, я сначала задался вопросом, как работает мой код. Я думал, что создаю объект даты с часовым поясом (например, первая строка с Сиднеем). Потом я понял, что результаты отображаются с часовым поясом Перта. Код изначально создавал объекты даты в соответствии с часовыми поясами? У меня тоже был другой вопрос, но для этого нужно набрать больше текста. Сможете ли вы поболтать, когда у вас будет время? - person jazzurro; 17.01.2018
comment
@jazzurro Конечно, сейчас мне позвонят через 10 минут. Поговорим с вами позже, я думаю, что построчное управление каким-то образом заставляет его использовать один часовой пояс, иначе упаковка с list сохранила бы его. - person akrun; 17.01.2018
comment
@akrun Понял. Дай мне знать, когда у тебя появится немного свободного времени. - person jazzurro; 17.01.2018
comment
@jazzurro Если вы сделаете as.character, тип будет другим, но он должен получить аналогичные значения testdf %>% rowwise %>% mutate(mydt_new = as.character(ymd_hms(mydt, tz = mytz))) - person akrun; 17.01.2018
comment
@akrun Если бы я последовал твоей идее и сделал следующее. testdf %>% rowwise %>% mutate(mydt_new = format(ymd_hms(mydt, tz = mytz), usetz = TRUE)). - person jazzurro; 17.01.2018
comment
@akrun Да, я тоже так думаю. Пока результат остается в списке, мы не можем видеть, что там есть. Так что иметь такую ​​информацию - это хорошо. Кстати, из-за некоторой разницы во времени может быть сложно поболтать. Позвольте мне вкратце объяснить свой вопрос. Как и здесь, у меня были моменты, когда я получал сообщение об ошибке в mutate (). Я не могу придумать какой-то конкретный случай, так как сейчас не помню ни одного. В этих случаях решение было построчным. Вы сталкивались с подобными моментами? Я думаю, что главное, чего мне не хватает, это когда требуется построчное выполнение. - person jazzurro; 18.01.2018
comment
@akrun Я не особенно удивлен, увидев, почему OP запутался, поскольку он ожидал, что два элемента в каждой строке будут переданы в ymd_hms (). Но это было не так. Хотел бы я привести пример, но это все, что я могу сказать прямо сейчас. - person jazzurro; 18.01.2018
comment
@jazzurro Я думаю, что функции map могли бы быть быстрее по сравнению с rowwise (если я не ошибаюсь). В этом конкретном случае, если он не хранится в списке, часовой пояс разрушится. Но, допустим, если у нас есть построчная операция и возвращаемые значения имеют один и тот же тип и т. Д., Тогда может быть полезно одно из расширений карты, например map_lgl, map_dbl и т. Д. - person akrun; 18.01.2018
comment
@akrun Да, я думаю, что rowwise медленнее (или медленнее). Можем ли мы также использовать Map()? Вчера у меня не было проблем с testdf %>% mutate(new.time = Map(function(x, y) ymd_hms(x, tz = y), mytz, mydt). Но сегодня я получаю предупреждающие сообщения. - person jazzurro; 18.01.2018
comment
@akrun Моя плохая. порядок аргументов x и y был обратным. В чем преимущество использования map2 здесь по сравнению с классической картой? - person jazzurro; 18.01.2018
comment
@jazzurro Я предполагаю, что функции purrr согласованы в порядке аргументов функций. Я не проверял, есть ли преимущество в скорости. Но я предполагаю, что эти функции оптимизированы - person akrun; 18.01.2018
comment
@akrun Понятно. Спасибо за эту информацию. Возвращаясь к тому, что я упоминал ранее, что код OP не работал? Очевидно, что в аргумент tz входило несколько часовых поясов, но почему? - person jazzurro; 18.01.2018
comment
@jazzurro Причина в том, что ymd_hms принимает один tz и не векторизуется для tz. Поскольку есть несколько tz, он распадается. Если вы сделаете testdf %>% mutate(mydt_new = ymd_hms(mydt, tz = mytz[1])), это сработает. Ошибка не связана с mutate, т.е. если вы делаете это вне цепочки, аргумент ymd_hms(testdf$mydt, tz = testdf$mytz)# Error in C_force_tz(time, tz = tzone, roll) : tz` должен быть односимвольной строкой - person akrun; 18.01.2018
comment
@akrun Понял. Итак, если вы видите аналогичное поведение в mutate, вы хотите подозревать, что аналогичная причина остается за сообщением об ошибке? - person jazzurro; 18.01.2018
comment
@jazzurro Есть определенные типы классов, в которых mutate может выдать ошибку. Например, POSIXlt класс - person akrun; 18.01.2018