#r #datetime #dplyr
#r #дата и время #dplyr
Вопрос:
У меня есть данные о заработной плате работников, и некоторым работникам платят ежемесячно, а другим еженедельно. Я хотел бы объединить данные в панель по рабочим и неделям (года). Чтобы сделать это, мне нужно расширить ежемесячные строки.
Данные выглядят следующим образом:
pay_data <- tibble(worker="Jim", start=ymd("2020-1-3"), end=ymd("2020-2-2"), rate=10, hours=50, wages=rate*hours) %>%
mutate(f_week=week(start), l_week=week(end))
# A tibble: 1 x 8
worker start end rate hours wages f_week l_week
<chr> <date> <date> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Jim 2020-01-03 2020-02-02 10 50 500 1 5
Есть ли способ использовать complete, fill или любую другую функцию dplyr, чтобы данные выглядели так, как показано ниже?
# A tibble: 5 x 5
worker week rate hours wage
<chr> <int> <dbl> <dbl> <dbl>
1 Jim 1 10 50 500
2 Jim 2 10 50 500
3 Jim 3 10 50 500
4 Jim 4 10 50 500
5 Jim 5 10 50 500
(Затем я бы, конечно, разделил суммы, чтобы перевести их все в общие единицы измерения).
Спасибо!
Ответ №1:
Другим tidyverse
способом было бы :
library(tidyverse)
pay_data %>%
mutate(week = map2(f_week, l_week, seq)) %>%
unnest(week) %>%
select(worker, rate:wages, week)
# worker rate hours wages week
# <chr> <dbl> <dbl> <dbl> <int>
#1 Jim 10 50 500 1
#2 Jim 10 50 500 2
#3 Jim 10 50 500 3
#4 Jim 10 50 500 4
#5 Jim 10 50 500 5
Комментарии:
1. Спасибо, Ронак! Знаете ли вы способ создать переменную this week с большей экономией памяти, чем огромную символьную переменную? Данные, с которыми я работаю, очень большие, и некоторые зарплаты выплачиваются ежегодно, поэтому в этом случае будет создана символьная переменная длиной> 50.
2. неделя — это не символьная переменная. Это целое число.
3. Я полагаю, что после шага map2 это длинная символьная переменная. Но да, это целое число в конце. Не потребляет ли он память, если она находится в канале? У меня также возникли проблемы с масштабированием до полного набора данных с разумным временем выполнения на случай, если у вас есть предложения по этому вопросу. Большое спасибо
Ответ №2:
tidyverse
Подход, использующий tidyr::separate_rows
может выглядеть так. Чтобы сделать данные более интересными, я добавил данные для второго работника.
library(tidyverse)
tbl %>%
rowwise() %>%
mutate(weeks = paste(seq(f_week, l_week, by = 1), collapse = ", ")) %>%
ungroup() %>%
separate_rows(weeks) %>%
select(-ends_with("_week"), -start, -end)
#> # A tibble: 13 x 5
#> worker rate hours wages weeks
#> <chr> <int> <int> <int> <chr>
#> 1 Jim 10 50 500 1
#> 2 Jim 10 50 500 2
#> 3 Jim 10 50 500 3
#> 4 Jim 10 50 500 4
#> 5 Jim 10 50 500 5
#> 6 John 20 100 1000 1
#> 7 John 20 100 1000 2
#> 8 John 20 100 1000 3
#> 9 John 20 100 1000 4
#> 10 John 20 100 1000 5
#> 11 John 20 100 1000 6
#> 12 John 20 100 1000 7
#> 13 John 20 100 1000 8
ДАННЫЕ
tbl <- read.table(text="worker start end rate hours wages f_week l_week
1 Jim 2020-01-03 2020-02-02 10 50 500 1 5n
2 John 2020-01-03 2020-02-02 20 100 1000 1 8", header = TRUE)
tbl
#> worker start end rate hours wages f_week l_week
#> 1 Jim 2020-01-03 2020-02-02 10 50 500 1 5
#> 2 John 2020-01-03 2020-02-02 20 100 1000 1 8
Комментарии:
1. Стефан — у вас есть какие-либо советы о том, как я мог бы справиться со случаем, когда даты начала и окончания пересекаются годами? Итак:
tbl <- read.table(text="worker start end rate hours wages f_week l_week 1 Jim 2019-12-18 2020-01-18 10 50 500 1 5n 2 John 2020-01-03 2020-02-02 20 100 1000 1 8", header = TRUE)
2. Меня смущает то, что R, похоже, не имеет собственного формата хранения «еженедельная дата»
3. Итак, я полагаю, что я решил это, просто создав новый набор данных с пользовательскими идентификаторами week_ids на основе пересечения недели x года. Однако мне было любопытно, знаете ли вы более эффективный способ создания переменной «недели», которая есть в вашем коде. Данные, с которыми я работаю, очень большие, и иногда проверки оплаты могут охватывать целый год (52 недели), так что это создает огромные переменные хранилища.
4. Привет, Бенни. Я только что провел несколько тестов. И если для вас важна эффективность, я бы рекомендовал использовать подход, предложенный @RonakShah в его ответе. Его подход не только более эффективен в использовании памяти, но и быстрее, чем мой подход.
5. Большое спасибо за вашу помощь, Стефан, я ценю это
Ответ №3:
Попробуйте это:
#Code
pay_data <- pay_data[rep(seq_len(nrow(pay_data)), unique(pay_data$l_week)),
c('worker','rate','hours','wages')]
pay_data$week <- 1:nrow(pay_data)
Вывод:
# A tibble: 5 x 5
worker rate hours wages week
<chr> <dbl> <dbl> <dbl> <int>
1 Jim 10 50 500 1
2 Jim 10 50 500 2
3 Jim 10 50 500 3
4 Jim 10 50 500 4
5 Jim 10 50 500 5