разделение данных в месяц на основе столбцов начало / конец - PullRequest
0 голосов
/ 30 апреля 2018

Мне нужно "разделить" 15 миллионов строк df следующей формы:

library(lubridate)
dateStart <- c(lubridate::ymd("2010-01-01"))
dateEnd <- c(lubridate::ymd("2010-03-06"))
length <- c(65)
Amt <- c(348.80)

df1 <- data.frame(dateStart, dateEnd, length, Amt)

df1
#    dateStart    dateEnd length   Amt
# 1 2010-01-01 2010-03-06     65 348.8

в нечто вроде:

dateStart    dateEnd length   Amt
1 2010-01-01 2010-01-31     31 166.35
2 2010-02-01 2010-02-28     28 150.55
3 2010-03-01 2010-03-06     6 32.19

Где длина - это количество дней, а Ам - пропорциональное количество дней. Кто-нибудь знает, как это сделать? Кто-то упомянул мне пакет padr, но я не знаю, как использовать его для этой конкретной цели.

Заранее спасибо

1 Ответ

0 голосов
/ 30 апреля 2018

Я предполагаю, что у вас есть какое-то уникальное поле идентификатора в вашем наборе данных, поэтому у вас есть уникальная запись. В противном случае это не сработает. Я также добавил 1 дополнительную запись, чтобы мы могли видеть, что все работает на нескольких записях.

Данные:

library(lubridate)
id <- c(1:2) # added id field needed for unique record and needed for grouping
dateStart <- c(lubridate::ymd("2010-01-01", "2011-01-09"))
dateEnd <- c(lubridate::ymd("2010-03-06", "2011-04-09"))
length <- c(65, 91)
Amt <- c(348.80, 468.70)

df1 <- data.frame(id , dateStart, dateEnd, length, Amt)

Сначала создайте data.frame с идентификатором и отсутствующими месяцами. Нам нужны dplyr, tidyr и padr. Создайте группы для уникального идентификатора, gather даты, чтобы у нас была дата начала и окончания в 1 столбце Чтобы padr продлил месяцы, нам сначала нужно thicken data.frame. Избавьтесь от ненужных столбцов и заполните пропущенные месяцы.

library(dplyr)
library(tidyr)
library(padr)

#create last_day function for later use
last_day <- function(date) {
  ceiling_date(date, "month") - days(1)
}

dates <- df1 %>% 
  select(id, dateStart, dateEnd) %>% 
  group_by(id) %>% 
  gather(names, dates, -id) %>% 
  arrange(id, dates) %>% 
  thicken(interval = "month") %>% # need to thicken first for month interval
  select(-c(names, dates)) %>% 
  pad(interval = "month")

dates
# A tibble: 7 x 2
# Groups:   id [2]
     id dates_month
  <int> <date>     
1     1 2010-01-01 
2     1 2010-02-01 
3     1 2010-03-01 
4     2 2011-01-01 
5     2 2011-02-01 
6     2 2011-03-01 
7     2 2011-04-01 

Далее присоедините данные к исходному data.frame

df_extended <- inner_join(dates, df1, by = "id") 

df_extended
# A tibble: 7 x 6
# Groups:   id [2]
     id dates_month dateStart  dateEnd    length   Amt
  <int> <date>      <date>     <date>      <dbl> <dbl>
1     1 2010-01-01  2010-01-01 2010-03-06     65  349.
2     1 2010-02-01  2010-01-01 2010-03-06     65  349.
3     1 2010-03-01  2010-01-01 2010-03-06     65  349.
4     2 2011-01-01  2011-01-09 2011-04-09     91  469.
5     2 2011-02-01  2011-01-09 2011-04-09     91  469.
6     2 2011-03-01  2011-01-09 2011-04-09     91  469.
7     2 2011-04-01  2011-01-09 2011-04-09     91  469.

Теперь, чтобы добраться до конечного результата. нужно использовать case_when, ifelse по какой-то причине не возвращает данные в формате даты. Замена case_when устанавливает правильные даты начала и окончания (я предполагаю, что вам нужна точная дата начала, а не первое число месяца, в противном случае измените код, чтобы вместо него использовать date_month.) Я создаю переменную суммы в день (amt_pd), чтобы можно умножить это на количество дней в месяце, чтобы получить пропорциональную сумму для количества дней в месяце.

df_end <- df_extended %>% 
  mutate(dateEnd = case_when(last_day(dates_month) <= dateEnd ~ last_day(dates_month),
                             TRUE ~ dateEnd),
         dateStart  = case_when(dates_month <= dateStart ~ dateStart,
                                TRUE ~ dates_month),
         amt_pd = Amt / length, 
         length = dateEnd - dateStart + 1,
         Amt = amt_pd * length) %>% 
  select(-c(dates_month, amt_pd)) # get rid of not needed columns

df_end
# A tibble: 7 x 5
# Groups:   id [2]
     id dateStart  dateEnd    length Amt             
  <int> <date>     <date>     <time> <time>          
1     1 2010-01-01 2010-01-31 31     166.350769230769
2     1 2010-02-01 2010-02-28 28     150.252307692308
3     1 2010-03-01 2010-03-06 6      32.1969230769231
4     2 2011-01-09 2011-01-31 23     118.462637362637
5     2 2011-02-01 2011-02-28 28     144.215384615385
6     2 2011-03-01 2011-03-31 31     159.667032967033
7     2 2011-04-01 2011-04-09 9      46.354945054945 

Все это можно сделать за один раз. Но если у вас есть 15 миллионов строк, возможно, лучше проверить, работают ли промежуточные шаги. Также обратите внимание, что pad имеет опцию break_above.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...