9  Работа с датами

Работа с датами в R требует больше внимания, чем работа с другими классами объектов. Ниже мы представим некоторые инструменты и пример для облегчения этого процесса. К счатью, с практикой и благодаря полезным пакетам, таким как lubridate с датами становится легко работать.

При импорте сырых данных R часто интерпретирует даты как объекты текстового класса - это означает, что их невозможно использовать для общих операций с датами, например, построения временных рядов и расчета временных интервалов. Добавляет сложностей и то, что дата может быть форматирована по-разному, и вам нужно помочь R понять, чем является конкретная часть даты (месяц, день, час и т.п.).

Даты в R являются отдельным классом объекта - классом Date (Дата). Необходимо отметить, что также существует класс, который хранит объекты с датой и временем. Объекты даты времени формально называются классами POSIXt, POSIXct, и/или POSIXlt (разница не важна). Эти объекты неформально называют классами датавремя.

9.1 Подготовка

Загрузка пакетов

Этот фрагмент кода показывает загрузку пакетов, необходимых для данной страницы. В этом руководстве мы подчеркиваем использование p_load() из пакета pacman, которая устанавливает пакет, если необходимо, и загружает его для использования. Вы можете также загрузить установленные пакеты с помощью library() из базового R. См. страницу Основы R для получения более подробной информации о пакетах R.

# Проверяет, установлен ли пакет, устанавливает, если необходимо, и загружает пакет для текущей сессии

pacman::p_load(
  lubridate,  # общий пакет для работы и конвертации дат  
  parsedate,   # имеет функцию для "угадывания" хаотичных дат
  aweek,      # еще один вариант конвертации дат в недели, а недель - в даты
  zoo,        # дополнительные функции даты/времени
  here,       # управление файлами
  tidyverse,  # управление данными и визуализация  
  rio)        # импорт/экспорт данных

Импорт данных

Мы импортируем набор данных по случаям из имитационной эпидемии Эболы. Если вы хотите скачать эти данные и повторять все шаги, см. инструкции на странице Скачивание руководства и данных. Мы предполагаем, что файл находится в рабочей директории, поэтому в пути к файлу не указаны подпапки.

linelist <- import("linelist_cleaned.xlsx")

9.2 Текущая дата

Вы можете получить текущую “системную” дату или системное датувремя вашего компьютера, выполнив следующую команду из базового R.

# получить системную дату - это класс ДАТА
Sys.Date()
[1] "2024-09-18"
# получить системное время - это класс ДАТАВРЕМЯ
Sys.time()
[1] "2024-09-18 23:20:17 +07"

При использовании пакета lubridate эти же сведения вы можете получить через today() и now(), соответственно. date() даст вам текущую дату и время с указанием дня недели и названием месяца.

9.3 Конвертация в дату

После импорта набора данных в R, значения столбца даты могут выглядеть как “1989/12/30”, “05/06/2014”, или “13 Jan 2020”. В таких случаях R, вероятно, все еще рассматривает эти значения как текстовый класс. R нужно указать, что эти значения являются датами… и каков формат даты (какая часть означает день, какая - месяц, какая - год и т.п.).

Как только это будет задано, R конвертирует эти значения в класс Дата. Фоново R сохранит даты как числа (количество дней с даты отсчета 1 января 1970). Вы не часто будете видеть это число даты, но это позволяет R рассматривать даты как непрерывные переменные, а также позволяет проводить специальные операции, такие как расчет дистанции между датами.

По умолчанию значения класса Дата в R отображаются в формате ГГГГ-ММ-ДД. Позднее в этом разделе мы обсудим, как менять отображение значений даты.

Ниже мы представляем два подхода к конвертации столбцы из текстового класса в Дату.

СОВЕТ: Вы можете проверить текущий класс столбца с функцией из базового R class(), например, class(linelist$date_onset).

базовый R

as.Date() является стандартной функцией базового R для конвертации объекта или столбца в класс Date (Дата) (обратите внимание на заглаувную “D”).

Использование as.Date() требует, чтобы:

  • Вы уточнили существующий формат сырой текстовой даты или дату отсчета, если даты заданы как числа (см. раздел Даты в Excel)
  • При использовании для текстового столбца все значения должны быть абсолютно в идентичном формате (если это не так, попробуйте parse_date() из пакета parsedate)

Во-первых, проверьте класс вашего столбца с помощью class() из базового R. Если вы не уверены или запутались в классе ваших данных (например, вы видите “POSIXct” и т.п.), может быть проще сначала конвертировать столбец в текстовый класс с помощью as.character(), а затем конвертировать его в класс Даты.

Во-вторых, внутри функции as.Date() используйте аргумент format =, чтобы указать R текущий формат текстовых компонентов даты - какие знаки означают месяц, какие - день, а какие - год и то, как они отделены. Если ваши значения уже находятся в одном из стандартных форматов даты в R (“ГГГГ-ММ-ДД” или “ГГГГ/ММ/ДД”) аргумент format = не обязателен.

В аргументе format = задайте текстовую последовательность (в кавычках), которая представляет собой текущий формат даты, используя специальные сокращения “strptime”, представленные ниже. Например, если ваши текстовые даты сейчас заданы в формате “ДД/ММ/ГГГГ”, как “24/04/1968”, тогда вы используете format = "%d/%m/%Y" для конвертации значений в даты. Необходимо поместить формат в кавычки. И не забудьте дефисы или слэши!

# Конвертация в класс даты
linelist <- linelist %>% 
  mutate(date_onset = as.Date(date_of_onset, format = "%d/%m/%Y"))

Большинство сокращений strptime перечислены ниже. Вы можете увидеть полный список, выполнив ?strptime.

%d = Номер дня месяца (5, 17, 28, и т.п.)
%j = Номер дня года (день по Юлианскому календарю 001-366)
%a = Сокращенный день недели (Mon, Tue, Wed, и т.п.)
%A = Полный день недели (Monday, Tuesday, и т.п.) %w = Номер дня недели (0-6, где воскресенье - это 0)
%u = Номер дня недели (1-7, где понедельник - это 1)
%W = Номер недели (00-53, начало недели в понедельник)
%U = Номер недели (01-53, начало недели в воскресенье)
%m = Номер месяца (например, 01, 02, 03, 04)
%b = Сокращенный месяц (Jan, Feb, и т.п.)
%B = Полный месяц (January, February, и т.п.)
%y = 2-значный год (например, 89)
%Y = 4-значный год (например, 1989)
%h = часы (24-часовые часы)
%m = минуты
%s = секунды %z = Отсчет от GMT
%Z = Часовой пояс (текстовый)

СОВЕТ: Аргумент format = в функции as.Date() не говорит R, в каком формате вы хотите, чтобы была дата, а скорее показывает, как определить части даты до выполнения команды.

СОВЕТ: Убедитесь, что в аргументе format = вы используете разделитель частей даты (например, /, -, или пробел), который используется в ваших датах.

Как только значения будут в классе Дата, R по умолчанию отобразит их в стандартном формате, а именно ГГГГ-ММ-ДД.

lubridate

Конвертация текстовых объектов в даты может быть упрощена с пакетом lubridate. Это пакет из tidyverse, разработанный для облегчения и более последовательной работы с датами и временем, чем в базовом R. По этим причинам lubridate часто считается золотым стандартом для использования для даты и времени и всегда рекомендуется при работе с ними.

Пакет lubridate предоставляет несколько функций-помощников, разработанных для конвертации текстовых объектов в даты интуитивно понятным и удобным образом, чем указание формата через as.Date(). Эти функции специфичны для приблизительного формата даты, но распознают ряд разделителей и синонимов дат (например, 01 или Jan или January) - они названы по аббревиатурам форматов даты.

# установка/загрузка lubridate 
pacman::p_load(lubridate)

Функция ymd() гибко конвертирует значения даты, представленыне в формате год, потом месяц, потом день.

# чтение даты в формате год-месяц-день
ymd("2020-10-11")
[1] "2020-10-11"
ymd("20201011")
[1] "2020-10-11"

Функция mdy() гибко конвертирует значения даты, представленные в формате месяц, потом день, потом год.

# чтение даты в формате месяц-день-год
mdy("10/11/2020")
[1] "2020-10-11"
mdy("Oct 11 20")
[1] "2020-10-11"

Функция dmy() гибко конвертирует значения даты, представленные в формате день, потом месяц, потом год.

# чтение даты в формате день-месяц-год
dmy("11 10 2020")
[1] "2020-10-11"
dmy("11 October 2020")
[1] "2020-10-11"

Если используете канал, конвертация текстового столбца в даты с помощью lubridate может выглядеть следующим образом:

linelist <- linelist %>%
  mutate(date_onset = lubridate::dmy(date_onset))

После завершения вы можете выполнить команду class(), чтобы проверить класс столбца

# Проверка класса столбца
class(linelist$date_onset)  

Как только значения будут в классе Даты, R по умолчанию отобразит их в стандартном формате, то есть ГГГГ-ММ-ДД.

Обратите внимание, что указанные выше функции лучше всего работают с четырехзначными годами. 2-значные годы могут дать неожиданные результаты, поскольку lubridate будет пытаться угадать век.

Чтобы конвертировать 2-значный год в 4-значный (все в одном веке), вы можете конвертировать в текстовый класс и затем объединить существующие знаки с префиксом, используя str_glue() из пакета stringr (см. Текст и последовательности). Затем конвертировать в дату.

two_digit_years <- c("15", "15", "16", "17")
str_glue("20{two_digit_years}")
2015
2015
2016
2017

Объединение столбцов

Вы можете использовать функции lubridate make_date() и make_datetime() для объединения нескольких числовых столбцов в один столбец даты. Например, у вас могут быть числовые столбцы onset_day (день заболевания), onset_month (месяц заболевания) и onset_year (год заболевания) в датафрейме linelist:

linelist <- linelist %>% 
  mutate(onset_date = make_date(year = onset_year, month = onset_month, day = onset_day))

9.4 Даты Excel

Фоново большинство программ хранят даты как числа. R хранит даты с точки отсчета 1го января 1970 года. Таким образом, если вы выполните as.numeric(as.Date("1970-01-01)) вы получите 0.

Microsoft Excel хранит даты с точкой отсчета либо 30 декабря 1899 (Windows) или 1 января 1904 (Mac), в зависимости от вашей операционной системы. См. это руководство Microsoft для получения более детальной информации.

Даты Excel часто импортируются в R как эти числовые значения, а не текст. Если набор данных, который вы импортировали из Excel, показывает даты как числа или знаки в формате “41369”… используйте as.Date() (или функцию as_date() из lubridate) для конвертации, но вместо указания “формата”, как мы это делали выше, укажите дату отсчета Excel в аргументе origin =.

Это не сработает, если дата Excel хранится в R в текстовом типе, поэтому убедитесь, что число находится в числовом классе!

ПРИМЕЧАНИЕ: Вы должны указать дату отсчета в формате даты по умолчанию для R (“ГГГГ-ММ-ДД”).

# Пример предоставления 'даты отсчета' Excel при конвертации числовых дат Excel
data_cleaned <- data %>% 
  mutate(date_onset = as.numeric(date_onset)) %>%   # убедитесь, что класс числовой
  mutate(date_onset = as.Date(date_onset, origin = "1899-12-30")) # Конвертируйте в дату, используя точку отсчета Excel

9.5 Хаотичные даты

Функция parse_date() из пакета parsedate попытается прочитать столбец с “хаотичными” датами, содержащий даты в разных форматах, и конвертировать даты в стандартный формат. Вы можете прочитать больше деталей о parse_date() онлайн.

Например, parse_date() увидит вектор следующих текстовых дат “03 Jan 2018”, “07/03/1982” и “08/20/85” и конвертирует их в класс Дата в виде: 2018-01-03, 1982-03-07 и 1985-08-20.

parsedate::parse_date(c("03 January 2018",
                        "07/03/1982",
                        "08/20/85"))
[1] "2018-01-03 UTC" "1982-07-03 UTC" "1985-08-20 UTC"
# Пример использования parse_date() в столбце date_onset
linelist <- linelist %>%      
  mutate(date_onset = parse_date(date_onset))

9.6 Работа с классом дата-время

Как указывалось ранее, R также поддерживает класс datetime - столбец, который содержит информацию о дате и времени. Как и в случае с классом Date, их часто нужно конвертировать из текстовых объектов character в объекты датывремени datetime.

Конвертация дат со временем

Стандартный объект datetime форматируется с указанием сначала даты, а затем компонента времени - например, 01 Jan 2020, 16:30. Как и в случае с датами, существует много способов форматирования, а также существует ряд уровней точности (часы, минуты, секунды).

К счастью, также существуют функции-помощники lubridate для конвертации этих последовательностей в объекты datetime. Эти функции являются расширениями функций-помощников по дате, с добавлением _h (указаны только часы), _hm (указаны часы и минуты) или _hms (указаны часы, минуты и секунды) в конце (например, dmy_hms()). Их можно использовать следующим образом:

Конвертация датывремени только с часами в объект датывремени

ymd_h("2020-01-01 16hrs")
[1] "2020-01-01 16:00:00 UTC"
ymd_h("2020-01-01 4PM")
[1] "2020-01-01 16:00:00 UTC"

Конвертация датывремени с часами и минутами в объект датывремени

dmy_hm("01 January 2020 16:20")
[1] "2020-01-01 16:20:00 UTC"

Конвертация датывремени с часами,минутами и секундами в объект датывремени

mdy_hms("01 January 2020, 16:20:40")
[1] "2020-01-20 16:20:40 UTC"

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

mdy_hms("01 January 2020, 16:20:40 PST")
[1] "2020-01-20 16:20:40 UTC"

При работе с датафреймом, столбцы времени и даты можно объединить, чтобы создать столбец датавремя, используя str_glue() из пакета stringr и соответствующую функцию lubridate. См. страницу Текст и последовательности для получения детальной информации о stringr.

Вэтом примере в датафрейме linelist есть столбец в формате “часы:минуты”. Чтобы конвертировать его в датувремя, мы выполняем несколько шагов:

  1. Создаем столбец “чистое” время госпитализации, где отсутствующие значения заполняются медианным значением столбца. Мы это делаем, поскольку lubridate не будет работать с отсутствующими значениями. Объедините со столбцом date_hospitalisation, а затем используйте для конвертации функцию ymd_hm().
# пакеты
pacman::p_load(tidyverse, lubridate, stringr)

# time_admission - это столбец в формате часы:минуты
linelist <- linelist %>%
  
  # когда время госпитализации не указано, присвойте медианное время госпитализации
  mutate(
    time_admission_clean = ifelse(
      is.na(time_admission),         # если время отсутствует
      median(time_admission),        # присваиваем медиану
      time_admission                 # если не отсутствует, оставить как есть
  ) %>%
  
    # используйте str_glue() для объединения столбцов дата и время для создания одного текстового столбца
    # затем используйте ymd_hm() для конвертации в датувремя
  mutate(
    date_time_of_admission = str_glue("{date_hospitalisation} {time_admission_clean}") %>% 
      ymd_hm()
  )

Конвертация только времени

Если ваши данные содержат только текстовое время (часы и минуты), вы можете их конвертировать и манипулировать как временем, используя strptime() из базового R. Например, чтобы получить разницу между двумя значениями времени:

# сырое текстовое время
time1 <- "13:45" 
time2 <- "15:20"

# Время конвертируется в класс датавремя
time1_clean <- strptime(time1, format = "%H:%M")
time2_clean <- strptime(time2, format = "%H:%M")

# Разница по умолчанию в классе "difftime", здесь конвертируется в числовые часы 
as.numeric(time2_clean - time1_clean)   # разница в часах
[1] 1.583333

Обратите внимание, что без указания значения даты, система предполагает, что эта дата - сегодня. Чтобы объединить последовательность даты и последовательность времени, см. раздел stringr выше. Дополнительную информацию о strptime() можно получить тут.

Чтобы конвертировать однозначные цифры в двухзначные (например, добавить нули к часам или минутам, чтобы получить два знака), см. раздел “Наращивание длины” страницы Текст и последовательности.

Извлечение времени

Вы можете извлечь элементы времени с помощью hour(), minute() или second() из lubridate.

Ниже приведен пример извлечения часа и затем классификации по времени суток. Мы начинаем со столбца time_admission, который относится к текстовому классу в формате “ЧЧ:ММ”. Сначала используется strptime(), как описано выше, чтобы конвертировать текст в класс датывремени. Затем извлекаем час с помощью hour(), что даст нам число от 0-24. Наконец, создается столбец time_period, используя логику с помощью case_when() для классификации строк на утро/день/вечер/ночь в зависимости от часа госпитализации.

linelist <- linelist %>%
  mutate(hour_admit = hour(strptime(time_admission, format = "%H:%M"))) %>%
  mutate(time_period = case_when(
    hour_admit > 06 & hour_admit < 12 ~ "Morning",
    hour_admit >= 12 & hour_admit < 17 ~ "Afternoon",
    hour_admit >= 17 & hour_admit < 21 ~ "Evening",
    hour_admit >=21 | hour_admit <= 6 ~ "Night"))

Чтобы получить дополнительную информацию о фукнции case_when(), см. страницу Вычистка данных и ключевые функции.

9.7 Работа с датами

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

Ниже мы определяем дату для использования в данном примере:

# создаем объект в классе Дата
example_date <- ymd("2020-03-01")

Извлекаем компоненты даты

Можно извлечь общие аспекты, такие как месяц, день, день недели:

month(example_date)  # номер месяца
[1] 3
day(example_date)    # день (номер) в месяце
[1] 1
wday(example_date)   # номер дня недели (1-7)
[1] 1

Вы можете также извлечь компоненты времени из объекта или столбца datetime. Это может быть полезно, если вы хотите увидеть распределение времени госпитализации.

example_datetime <- ymd_hm("2020-03-01 14:45")

hour(example_datetime)     # извлекаем час
minute(example_datetime)   # извлекаем минуту
second(example_datetime)   # извлекаем секунду

Существует ряд вариантов для извлечения недель. См. раздел Эпидемиологические недели ниже.

Обратите внимание, что если вы стремитесь отобразить дату определенным образом (например, “Jan 2020” или “Thursday 20 March” или “Week 20, 1977”), вы можете это более гибко сделать в соответствии с описанием в разделе Отображение даты.

Вычисления с датами

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

# добавляем 3 дня к этой дате
example_date + days(3)
[1] "2020-03-04"
# добавляем 7 недель и отнимаем два дня от этой даты
example_date + weeks(7) - days(2)
[1] "2020-04-17"

Интервалы дат

Разницу между датами можно рассчитать следующим образом:

  1. Убедитесь, что обе даты относятся к классу дата
  2. Используйте вычитание, чтобы получить разницу “difftime” между двумя датами
  3. Если необходимо, конвертируйте результат в числовой класс, чтобы провести дальнейшие математические расчеты

Ниже рассчитывается и отображается интервал между двумя датами. Вы можете найти интервалы, используя знак вычитания “минус” для значений, которые относятся к классу Дата. Однако обратите внимание, что класс выданного значения будет “difftime”, как указано ниже, и его нужно конвертировать в числовой.

# находим интервал между этой датой и 20 февраля 2020 
output <- example_date - ymd("2020-02-20")
output    # печать
Time difference of 10 days
class(output)
[1] "difftime"

Чтобы провести дальнейшие операции с разницей времени “difftime”, конвертируйте ее в числовой формат с помощью as.numeric().

Это все можно соединить для работы с данными - например:

pacman::p_load(lubridate, tidyverse)   # загружаем пакеты

linelist <- linelist %>%
  
  # конвертируем дату заболевания из текстового класса в объект даты, указав формат dmy
  mutate(date_onset = dmy(date_onset),
         date_hospitalisation = dmy(date_hospitalisation)) %>%
  
  # отфильтровываем все случаи, не имеющие дату заболевания в марте
  filter(month(date_onset) == 3) %>%
    
  # находим разницу дней между заболеванием и госпитализацией
  mutate(days_onset_to_hosp = date_hospitalisation - date_of_onset)

В контексте датафрейма, если одна из указанных выше дат отсутствует, операция будет неуспешна для этой строки. Это приведет к результату NA вместо числового значения. Когда вы используете этот столбец для расчетов, убедитесь, что вы задали аргумент na.rm = как TRUE. Например:

# рассчитываем медианное количество дней до госпитализации для всех случаев, где имеются данные
median(linelist_delay$days_onset_to_hosp, na.rm = T)

9.8 Отображение даты

Как только даты относятся к правильному классу, часто вам их нужно отобразить в другом виде, например, “Monday 05 January” вместо “2018-01-05”. Вам может быть также необходимо откорректировать отображение, чтобы потом сгруппировать строки по отображенным элементам даты - например, по месяцу-году.

format()

Корректируйте отображение даты с помощью функции из базового R format(). Эта функция принимает последовательность знаков (в кавычках), указывающую желаемый формат на выходе в виде сокращений “%” strptime (тот же синтаксис используется для as.Date()). Ниже приведены наиболее частые сокращения.

Примечание: использование format() конвертирует значения в текстовый класс, поэтому, как правило, эта операция проводится в конце анализа и только для целей отображения! Полный список вы можете увидеть, выполнив команду ?strptime.

%d = номер дня месяца (5, 17, 28, и т.п.)
%j = номер дня года (дни по Юлианскому календарю 001-366)
%a = сокращенный день недели (Mon, Tue, Wed, и т.п.)
%A = полный день недели (Monday, Tuesday, и т.п.)
%w = номер дня недели (0-6, где воскресенье - 0)
%u = номер дня недели (1-7, где понедельник - 1)
%W = номер недели (00-53, понедельник - начало недели)
%U = номер недели (01-53, воскресенье - начало недели)
%m = номер месяца (например, 01, 02, 03, 04)
%b = сокращенный месяц (Jan, Feb, и т.п.)
%B = полный месяц (January, February, и т.п.)
%y = 2-значный год (например, 89)
%Y = 4-значный год (например, 1989)
%h = часы (24-часовые часы)
%m = минуты
%s = секунды
%z = отсчет от GMT
%Z = часовой пояс (текстовый)

Пример форматирования сегодняшней даты:

# сегодняшняя дата с форматированием
format(Sys.Date(), format = "%d %B %Y")
[1] "18 September 2024"
# легкий способ получения полной даты и времени (форматирование по умолчанию)
date()
[1] "Wed Sep 18 23:20:18 2024"
# отформатированные комбинированные дата, время и часовой пояс, используя функцию str_glue()
str_glue("{format(Sys.Date(), format = '%A, %B %d %Y, %z  %Z, ')}{format(Sys.time(), format = '%H:%M:%S')}")
Wednesday, September 18 2024, +0000  UTC, 23:20:18
# Использование формата для отображения недель
format(Sys.Date(), "%Y Week %W")
[1] "2024 Week 38"

Обратите внимание, что при использовании str_glue() необходимо помнить, что внутри ожидаемых двойных кавычек ” вы должны использовать одинарные кавычки (как показано выше).

Месяц-Год

Чтобы конвертировать столбец даты в формат месяц-год, мы предлагаем использовать функцию as.yearmon() из пакета zoo. Она конвертирует дату в класс “yearmon” и сохраняет правильный порядок. Для сравнения, использование format(column, "%Y %B") конвертирует в текстовый класс и упорядочит значения алфавитно (неправильно).

Ниже создается новый столбец yearmonth из столбца date_onset, используя функцию as.yearmon(). Упорядочивание по умолчанию (правильное) полученных в результате значений показано в таблице.

# создаем новый столбец 
test_zoo <- linelist %>% 
     mutate(yearmonth = zoo::as.yearmon(date_onset))

# печать таблицы
table(test_zoo$yearmon)

Apr 2014 May 2014 Jun 2014 Jul 2014 Aug 2014 Sep 2014 Oct 2014 Nov 2014 
       7       64      100      226      528     1070     1112      763 
Dec 2014 Jan 2015 Feb 2015 Mar 2015 Apr 2015 
     562      431      306      277      186 

Для сравнения, вы можете увидеть, что format() даст желаемый формат отображения, но не даст правильного порядка.

# создаем новый столбец
test_format <- linelist %>% 
     mutate(yearmonth = format(date_onset, "%b %Y"))

# печать таблицы
table(test_format$yearmon)

Apr 2014 Apr 2015 Aug 2014 Dec 2014 Feb 2015 Jan 2015 Jul 2014 Jun 2014 
       7      186      528      562      306      431      226      100 
Mar 2015 May 2014 Nov 2014 Oct 2014 Sep 2014 
     277       64      763     1112     1070 

Примечание: Если вы работаете в рамках ggplot() и хотите откорректировать только то, как отображаются даты, может быть достаточно просто задать формат в аргументе date_labels = в scale_x_date() - вы можете использовать "%b %Y" или "%Y %b". См. страницу советы по использованию ggplot.

zoo также предлагает функцию as.yearqtr(), и вы можете использовать scale_x_yearmon() при использовании ggplot().

9.9 Эпидемиологические недели

lubridate

См. страницу [Группирование данных] для получения более подробных примеров по группированию данных по дате. Ниже мы кратко рассказываем о группировании данных по неделям.

Мы, как правило, рекомендуем использовать функцию floor_date() из lubridate с аргументом unit = "week". Это округляет дату в меньшую сторону до “начала” недели, которое задано аргументом week_start =. По умолчанию неделя начинается на 1 (для понедельников), но вы можете уточнить любой день недели, как ее начало (например, 7 для воскресенья). floor_date() является очень гибкой и может использоваться для округления в нижнюю сторону других единиц времени, установив unit = на “second” (секунда), “minute” (минута), “hour” (час), “day” (день), “month” (месяц), или “year” (год).

Полученное значение - дата начала недели в классе Дата. Класс Дата полезен при построении графика данных, поскольку его легко сможет распознать и упорядочить ggplot().

Если вас интересует только корректировка дат для отображения по неделям на графике, см. раздел на этой странице по Отображению данных. Например, при построении эпидкривой вы можете отформатировать отображение даты путем указания необходимой вам номенклатуры strptime “%”. Например, используйте “%Y-%W” или “%Y-%U”, чтобы получить год и номер недели (с началом недели в понедельник или воскресенье, соответственно).

Еженедельное количество

См. страницу [Группирование данных], где приводится детальное объяснение группирования данных с помощью count(), group_by() и summarise(). Краткий пример представлен ниже.

  1. Создайте новый столбец ‘week’ с помощью mutate(), используя floor_date() с unit = "week"
  2. Подсчитайте количество строк (случаев) в неделю с помощью count(); отфильтруйте случаи с отсутствующей датой
  3. Завершите функцией complete() из tidyr, чтобы убедиться, что все недели отразились в данных - даже те, где нет строк/случаев. По умолчанию значения подсчета для любых “новых” строк - NA, но вы можете превратить их в 0 с помощью аргумента fill =, который ожидает именованного списка (ниже n - это имя столбца с подсчетом количества).
# Создаем агрегированный набор данных еженедельного количества случаев
weekly_counts <- linelist %>% 
  drop_na(date_onset) %>%             # удаляем случаи с отсутствующей датой заболевания
  mutate(weekly_cases = floor_date(   # создаем новый столбец, неделя заболевания
    date_onset,
    unit = "week")) %>%            
  count(weekly_cases) %>%           # группируем данные по неделе и считаем количество строк в группе (создает столбец 'n')
  tidyr::complete(                  # убедитесь, что все недели есть в наличии, даже те, в которых не зарегистрировано случаев
    weekly_cases = seq.Date(          # переопределение столбца "weekly_cases" в качестве полной последовательности,
      from = min(weekly_cases),       # от минимальной даты
      to = max(weekly_cases),         # до максимальной даты
      by = "week"),                   # по неделям
    fill = list(n = 0))             # заполняем NA в столбце подсчета количества n цифрой 0

Здесь вы видите первые строки получившегося датафрейма:

Альтернативы для эпиднедель

Обратите внимание, что в lubridate также есть функции week(), epiweek() и isoweek(), у каждой из которых немного разные даты начала и другие нюансы. В целом, вам должно быть достаточно floor_date(). Детальную информацию об этих функциях можно прочитать, введя ?week в консоль, либо вы можете прочитать документацию тут.

Вы можете рассмотреть возможность использования пакета aweek, чтобы задать эпидемиологические недели. Более детально вы можете почитать о нем на сайте RECON. В нем есть функции date2week() и week2date(), в которых вы можете установить день начала недели с помощью week_start = "Monday". Этот пакет самый простой, если вам нужны выходные данные в неделях (например, “2020-W12”). Еще одним преимуществом aweek является то, что когда применяется date2week() к столбцу даты, полученный в результате столбец (формат недели) автоматически классифицируется как фактор и включает уровни для всех недель во временном диапазоне (это позволяет избежать дополнительного шага с complete(), который описывался выше). Однако aweek не имеет функционала по округлению дат до других единиц времени, таких как месяцы, годы и т.п.

Еще одной альтернативой для временных рядов, которая также хорошо работает, является демонстрация формата “недель” (“2020 W12”) с помощью yearweek() из пакета tsibble, что демонстрируется на странице [Временные ряды и обнаружение вспышек].

9.10 Конвертация дат/часовых поясов

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

В R каждый объект датывремени имеет компонент часового пояса. По умолчанию все объекты датывремени будут относиться к местному часовому поясу, в котором работает компьютер, как правило, это устанавливается по локали, а не по именованному часовому поясу, так как время в локали может меняться на зимнее/летнее время. Невозможно провести правильную компенсацию часовых поясов без компонента времени для даты, поскольку событие, которое представляет столбец даты, не может быть отнесено к конкретному времени, следовательно, невозможно правильно учесть промежутки времени в часах.

Для работы с часовыми поясами существует ряд функций-помощников в lubridate, которые можно использовать для изменения часового пояса объекта датывремени с местного на другой часовой пояс. Часовые пояса устанавливаются путем присваивания действительного часового пояса из базы данных tz объекту датафрейма. Этот список можно найти здесь - если локаль, из которой вы используете данные, не включена в этот список, доступны соседние крупные города в том же часовом поясе, которые могут быть использованы для этих целей.

https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

# присваиваем столбцу текущее время
time_now <- Sys.time()
time_now
[1] "2024-09-18 23:20:18 +07"
# используем with_tz(), чтобы присвоить столбцу новый часовой пояс, при этом МЕНЯЯ время на часах
time_london_real <- with_tz(time_now, "Europe/London")

# use force_tz(), чтобы присвоить столбцу новый часовой пояс, при этом СОХРАНЯЯ время на часах
time_london_local <- force_tz(time_now, "Europe/London")


# Обратите внимание, что если компьютер, используемый для выполнения этого кода работает НЕ по Лондонскому времени,
# возникнет разница во времени 
# (количество часов разницы между часовым поясом компьютера и Лондоном)
time_london_real - time_london_local
Time difference of -6 hours

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

9.11 Отстающие и опережающие расчеты

lead() и lag() - функции из пакета dplyr, которые помогают найти предыдущее (отстающее) или последующее (опережающее) значение вектора - как правило, числового вектора или вектора даты. Это полезно при проведении расчетов изменения/разницы между единицами времени.

Представим, что вам нужно рассчитать разницу случаев между текущей неделей и предыдущей. Данные изначально представлены в количестве в неделю, как показано ниже.

При использовании lag() или lead() очень важен порядок строк в датафрейме! - обращайте внимание на то, ваши даты/числа находятся в возрастающем или убывающем порядке

Сначала создадим новый столбец, содержащий значение предыдущей (отстающей) недели.

  • Контролируйте число единиц назад/вперед с помощью n = (должно быть не отрицательным целым числом)
  • Используйте default =, чтобы определить значение, размещенное в несуществующих строках (например, первая строка, по которой нет отстающего значения). По умолчанию это будет NA.
  • Используйте order_by = TRUE, если ваши строки не упорядочены по вашему референс столбцу.
counts <- counts %>% 
  mutate(cases_prev_wk = lag(cases_wk, n = 1))

Далее создаем новый столбец, который является разницей между двумя столбцами случаев:

counts <- counts %>% 
  mutate(cases_prev_wk = lag(cases_wk, n = 1),
         case_diff = cases_wk - cases_prev_wk)

Вы можете более подробно прочитать о lead() и lag() в документации тут, либо введя в консоли ?lag.

9.12 Ресурсы

lubridate страница tidyverse
lubridate RStudio шпаргалка
R for Data Science страница даты и время
Онлайн самоучитель Форматы даты