【问题标题】:in R, perform a function over rows in a data frame and join the result together in a new date frame在 R 中,对数据框中的行执行一个函数,并将结果连接到一个新的日期框架中
【发布时间】:2019-04-16 22:30:32
【问题描述】:

我觉得我处于正确答案的领域,但对于我的一生,我无法弄清楚如何做到这一点。我想在产品分类帐上输入一个条目并更改数据的形状,其中我在产品交易的每个生命月都有一行/案例。

请注意,有时这些分类帐条目会被冲销/退款(并且 date_from > date_thru)。我在下面的数据框中有一个新购买和退款的示例。此外,交易日期已预先清理为始终是交易日期的第一天,因为我只关心每月的每月经常性收入 (mrr)。

样本df:

user <- c("1001", "1002")
line_item <- c("abc123", "def456")
date_from <- as.Date(c("2015-01-01", "2015-06-01"), "%Y-%m-%d")
date_thru <- as.Date(c("2015-04-01", "2014-12-01"), "%Y-%m-%d")
mrr <- c(2.22, -4.44)
df <- cbind.data.frame(user, line_item, date_from, date_thru, mrr)

输出:

  user line_item  date_from  date_thru   mrr
1 1001    abc123 2015-01-01 2015-04-01  2.22
2 1002    def456 2015-06-01 2014-12-01 -4.44

想要的结果:

user    line_item       month           mrr
1001    abc123          2015-01-01    2.22
1001    abc123          2015-02-01    2.22
1001    abc123          2015-03-01    2.22
1001    abc123          2015-04-01    2.22
1002    def456          2015-06-01    -4.44
1002    def456          2015-05-01    -4.44
1002    def456          2015-04-01    -4.44
1002    def456          2015-02-01    -4.44
1002    def456          2015-01-01    -4.44
1002    def456          2014-12-01    -4.44

如何通过矢量化 seq(date_from, date_thru, by="months") 之类的函数来创建一个新的 month 列,然后像上面一样将所有结果向量再次连接到一个 df 中?

我一直在尝试 lapplydplyr::mutateseq,但无法让它们一起正常工作。

提前致谢!

【问题讨论】:

  • @Shree 是的,这是正确的,这是一个颠倒的line_item 的例子。反转的 lineitem 具有负值,并且 date_thru 在 date_from 之前(有点像负日期范围)。
  • 我认为您在所需的输出中缺少用户 1002 的日期 2015-03-01
  • 你应该重新定义seq.Date函数,让它优雅地接受正负序列。

标签: r dplyr


【解决方案1】:

这是使用dplyrtidyrlubridate 的解决方案。我在这个 SO 帖子 Number of months between two dates 上找到了 elapsed_months 函数。我已经为你的情况做了一些修改。

另外,我假设负的mrr 表示date_from &gt; date_thru

elapsed_months <- function(end_date, start_date) {
  ed <- as.POSIXlt(end_date)
  sd <- as.POSIXlt(start_date)
  12 * (ed$year - sd$year) + (ed$mon - sd$mon) + ifelse(ed >= sd, 1, -1)
}

df %>% 
  uncount(weights = abs(elapsed_months(date_thru, date_from)), .id = "Months") %>% 
  mutate(
    Month = date_from %m+% months(sign(mrr)*(Months - 1))
  ) %>% 
  select(user, line_item, Month, mrr)

   user line_item      Month   mrr
1  1001    abc123 2015-01-01  2.22
2  1001    abc123 2015-02-01  2.22
3  1001    abc123 2015-03-01  2.22
4  1001    abc123 2015-04-01  2.22
5  1002    def456 2015-06-01 -4.44
6  1002    def456 2015-05-01 -4.44
7  1002    def456 2015-04-01 -4.44
8  1002    def456 2015-03-01 -4.44
9  1002    def456 2015-02-01 -4.44
10 1002    def456 2015-01-01 -4.44
11 1002    def456 2014-12-01 -4.44

【讨论】:

    【解决方案2】:

    首先定义一个month_seq 函数,它根据fromto 日期生成所需的日期序列。然后Map 将它添加到date_fromdate_thru 列,生成列表类型的列,其组件是日期序列。然后unnestmonth 并选择所需的列。

    library(dplyr)
    library(tidyr)
    
    month_seq <- function(from, to) seq(from, to, paste(sign(to - from), "month"))
    
    df %>%
      mutate(month = Map(month_seq, date_from, date_thru)) %>%
      unnest %>%
      select(user, line_item, month, mrr)
    

    给予:

       user line_item      month   mrr
    1  1001    abc123 2015-01-01  2.22
    2  1001    abc123 2015-02-01  2.22
    3  1001    abc123 2015-03-01  2.22
    4  1001    abc123 2015-04-01  2.22
    5  1002    def456 2015-06-01 -4.44
    6  1002    def456 2015-05-01 -4.44
    7  1002    def456 2015-04-01 -4.44
    8  1002    def456 2015-03-01 -4.44
    9  1002    def456 2015-02-01 -4.44
    10 1002    def456 2015-01-01 -4.44
    11 1002    def456 2014-12-01 -4.44
    

    【讨论】:

      【解决方案3】:

      只是为了发现tidyrcompletefill 函数的特殊功能

      library(tidyverse)
      
      df %>% 
        # turn into a long format to handle the dates as one variable
        gather(key, date, date_from:date_thru) %>% 
        # group the dataframe to apply operations separately on each user
        group_by(user) %>% 
        # now complete the dataset with the implicit dates within the range of dates
        # Note the consideration of: date_from > date_to with 'min' and 'max'
        complete(date = seq.Date(from = min(date),to = max(date),by = "month") ) %>% 
        #now fill in the missing 'mrr' 
        fill(mrr, line_item)
      
      
      # A tibble: 11 x 5
      # Groups:   user [2]
         user  date       line_item   mrr key      
         <fct> <date>     <fct>     <dbl> <chr>    
       1 1001  2015-01-01 abc123     2.22 date_from
       2 1001  2015-02-01 abc123     2.22 <NA>     
       3 1001  2015-03-01 abc123     2.22 <NA>     
       4 1001  2015-04-01 abc123     2.22 date_thru
       5 1002  2014-12-01 def456    -4.44 date_thru
       6 1002  2015-01-01 def456    -4.44 <NA>     
       7 1002  2015-02-01 def456    -4.44 <NA>     
       8 1002  2015-03-01 def456    -4.44 <NA>     
       9 1002  2015-04-01 def456    -4.44 <NA>     
      10 1002  2015-05-01 def456    -4.44 <NA>     
      11 1002  2015-06-01 def456    -4.44 date_from
      

      如果您想删除key变量,请在fill 末尾添加%&gt;% select(-key),尽管您可能出于某种原因想要保留它...

      附:我真的很喜欢@G 在函数创建中的巧妙技巧。格洛腾迪克用sign 处理date_from > date_to

      【讨论】:

        猜你喜欢
        • 2018-08-11
        • 2022-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多