根据列开始/结束

时间:2018-04-30 10:02:32

标签: r dataframe

我需要“分割”以下形式的1500万行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

长度是天数,Amt是天数的比例。有人知道怎么做这个吗?有人向我提到padr包,但我不知道如何将它用于此特定用途。

提前谢谢

1 个答案:

答案 0 :(得分:0)

我假设你的数据集中有一些独特的id字段,所以你有一个独特的记录。否则这不会起作用。我还添加了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)

首先创建一个具有id和缺少月份的data.frame。我们需要dplyrtidyrpadr。为每个唯一ID创建组,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_whenifelse由于某种原因不会以日期格式返回数据。 case_when替换设置正确的开始和结束日期(我假设您需要确切的开始日期,而不是本月的第一天,否则调整代码以使用dates_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 

所有这一切都可以一次完成。但是如果你有1500万行,那么看看中间步骤是否有效会更好。另请注意,padbreak_above选项。

  

这是一个数值,表示以百万为单位的行数   功能将在其上面破坏。安全网适用于哪些情况   间隔与预期不同,填充产生非常大的   大数据帧,可能会溢出内存。