Преобразовать данные о ежемесячной оплате в еженедельные с помощью complete и заполнить dplyr

#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