Периодически возникают задачи в R, которые просты по своей сути, но не очевидны для тех, кто только начинает свой путь.
Представим, что в нашей организации каждый последний понедельник месяца происходит учет товара. В эти дни нет продаж. И мы бы хотели учесть это в наших прогнозах. Стоит вопрос: как в данных "выловить" эти понедельники, не используя function.
Посмотрим как это можно сделать
Для начала нам нужно импортировать библиотеки tidyverse
и lubridate.
Tidyverse
- это набор пакетов для языка программирования R, разработанных командой Hadley Wickham и его коллегами. Он содержит несколько пакетов, которые упрощают чтение, обработку, визуализацию и моделирование данных, используя единый фреймворк и стиль программирования. Ключевыми пакетами в Tidyverse
являются ggplot2
для визуализации данных, dplyr
для манипуляции с данными, tidyr
для работы с данными в "длинном" формате, readr
для чтения и записи данных в формате CSV и других форматах, а также purrr
для функционального программирования. Tidyverse
стремится к тому, чтобы программы на R были более понятными, лаконичными и легко читаемыми благодаря единому подходу к обработке и визуализации данных.
Lubridate
- это пакет для языка программирования R, который облегчает работу с датами и временем. Он предоставляет удобный интерфейс для различных задач, таких как извлечение даты, времени и интервалов времени из строк, форматирование дат и времени, арифметические операции с датами и временем, конвертация между различными форматами дат и времени, и многое другое.
# импортируем библиотеки tidyverse и lubridate
library(tidyverse)
library(lubridate)
# создадим набор данных с 2023-01-01 по 2024-12-31
df <- seq(ymd("2023-01-01"), ymd("2024-12-31"), by = "day")
df <- as_tibble(df)
Создадим дополнительные столбцы: w_day
(день недели), m_th
(месяц) и y_r
(год):
df_wmy <- df %>%
mutate(
w_day = wday(value, week_start = 1),
m_th = month(value),
y_r = year(value)
)
Аргумент week_start
в функции wday()
используется для указания дня недели, который будет считаться началом недели при вычислении дня недели для заданной даты. По умолчанию, week_start
равен 7, что означает, что неделя начинается с воскресенья и заканчивается в субботу.
Если установить week_start = 1
, то неделя будет начинаться с понедельника, а заканчиваться в воскресенье. Другими словами, если установить week_start = 1
и вызвать функцию wday()
для даты, которая выпадает на понедельник, то функция вернет значение 1, а для даты, которая выпадает на воскресенье, функция вернет значение 7.
Например, вызов wday("2023-02-13", week_start = 1)
вернет значение 1, так как 13 февраля 2023 года - это понедельник. А вызов wday("2023-02-19", week_start = 1)
вернет значение 7, так как 19 февраля 2023 года - это воскресенье.
Функции month
и year
позволяют извлекать информацию в формате целого числа.
Теперь, имея дополнительные столбы, мы можем выбрать нужные нам понедельники:
df_monday <- df_w_m %>%
filter(w_day == 1) %>%
group_by(m_th, y_r) %>%
filter(row_number() == n()) %>%
ungroup()
Здесь мы применили фильтр, чтобы работать только в рамках того дня недели, который нас интересует. Затем группируем данные по месяцу и году, чтобы, применив выражение filter(row_number() == n()),
найти последнее значение в сгруппированном фрейме данных.
Конструкция row_number() == n()
является логическим выражением, используемым для фильтрации данных в R с помощью библиотеки dplyr
.
Функция row_number()
создает новый столбец с номерами строк в наборе данных, а n()
является функцией-аргументом внутри filter()
, которая возвращает номер строки, на которой находится текущая итерация фильтрации.
Таким образом, row_number() == n()
сравнивает номера строк с текущим номером итерации, возвращенным n()
, и возвращает TRUE, если номер строки соответствует номеру текущей итерации фильтрации.
Использование этого выражения в filter()
позволяет выбрать только одну строку из набора данных, которая соответствует текущей итерации фильтрации.
В нашем случае мы выбираем каждое последнее значение в каждой группе. Если бы мы хотели, например, найти каждый второй понедельник месяца - мы бы изменили наше выражение на
df_w_m %>%
filter(w_day == 1) %>%
group_by(m_th, y_r) %>%
filter(row_number() == 2) %>%
ungroup()
Комментарии (2)
Ad_fesha
00.00.0000 00:00+1Спасибо за статью.
Часто сталкиваюсь с подобными задачами, но вместо написаний собственных функций прибегаю к пакету Алексея Селезнева ( https://habr.com/ru/post/469215/ )
lea
Алгоритм: находим все понедельники (wday == 1); среди них находим первые понедельники месяца (mday <= 7); среди всех понедельников выбираем те, что предшествуют первым понедельникам.
Для большего быстродействия нужно однократно сосчитать последние понедельники месяца за нужные годы (600 значений за 50 лет), после чего в любом наборе дат можно будет найти последние понедельники месяца оператором %in%.