【问题标题】:Looping through Months and counting rows循环月份并计算行数
【发布时间】:2018-08-23 13:25:23
【问题描述】:

我有一个包含三列的 df:StartDate、EndDate 和 SubscriptionType。

df = data.frame(StartDate = as.Date(c('2018-05-01', '2018-06-01', '2018-01-01', '2018-07-01', '2018-03-01')), EndDate = as.Date(c('2019-04-30', '2019-05-31', '2018-12-31', '2019-06-30', '2019-02-28')), SubscriptionType = c('monthly', 'monthly', 'yearly', 'yearly', 'yearly'))

例如,通过使用以下代码:

df %>% filter(StartDate <= ymd('2018-5-15') & EndDate >= ymd('2018-5-15')) %>% count()

我知道 5 月份有多少订阅处于活动状态。

我想打印 2018 年每个月的结果并将其存储在数据框中。

到目前为止,我尝试使用以下代码:

z = NULL

m = c(01,02,03,04,05,06,07,08,09,10,11,12)

for (i in m) {z = rbind(z, data.frame(df %>% filter(StartDate <= ymd('2018-i-15') & EndDate >= ymd('2018-i-15')) %>% count()))}

但我得到的是 24 个警告和一个仅填充零的数据框 z。

任何帮助将不胜感激,谢谢!

【问题讨论】:

  • 也许你可以使用lubridate包中的month()和一些dplyr,你能发布类似dput(df)的东西吗?
  • 你的意思是开始日期还是结束日期应该在 2018 年?

标签: r


【解决方案1】:

我们可以分组使用map 来执行此操作。使用paste 将“m”创建为Date 类对象,然后根据条件创建filter 数据集并应用count

m <- ymd(paste0('2015-', sprintf('%02d', 1:12), '-15'))
map_df(m, ~ 
       df %>%      
         filter(Start_Date <=  .x & EndDate  >= .x) %>% 
         count)

【讨论】:

  • @vhcandido 看起来你删除了评论。无法阅读
  • 我打算编辑您的答案,并认为评论会更好,但后来我在原始问题中遇到了一个小逻辑错误,并决定添加一个新答案。为您的答案 +1。
【解决方案2】:

ymd('2018-i-15') 不会产生 date 对象。您传递了ymd() 一个字符串,其中2018 为年,i 为月,15 为日。在这种情况下,i 是固定的,不会被循环变量i 替换。这就是您收到错误All formats failed to parse. No formats found. 的原因,它本质上是在告诉您它无法识别以i 为月份的日期。

要保留i 变量,请尝试ymd(paste0("2018-", i, "-15"))

for (i in m) {
     z = rbind(z, data.frame(df %>% filter(StartDate <= ymd(paste0("2018-", i, "-15")) & EndDate >= ymd(paste0("2018-", i, "-15"))) %>% count()))
}

【讨论】:

    【解决方案3】:

    改进@akrun 的答案,为循环问题提供了最简单直接的解决方案。

    但是,如果您希望在与两个日期之间的间隔重叠的任何月份获得 TRUE,最好将它们转换为月份,而不是使用某个固定的日期。您可以针对包含'2018-05-16''2018-06-14'(分别为开始日期和结束日期)的某些行进行测试,在原始条件下,'2018-05''2018-06' 均不计算在内。

    f <- '%Y-%m'  # it'll be used a few times
    
    m <- seq(ymd(180101), ymd(191231), by='month')
    m <- format(m, f)  # 'e.g. 2018-05'
    
    # purrr::map_dfr() works like apply, it'll map each value from m into .x
    # inside the expression and use dplyr::bind_rows() to concatenate the
    # resulting rows (see documentation for details).
    map_df(m, ~ df %>%
            filter(format(StartDate, f) <=  .x &
                    format(EndDate, f)  >= .x) %>%
            group_by(month = .x) %>%
            count()
    )
    
    # For the data.frame you provided this is the result:
    ## A tibble: 18 x 2
    ## Groups:   month [18]
    #   month       n
    #   <chr>   <int>
    # 1 2018-01     1
    # 2 2018-02     1
    # 3 2018-03     2
    # 4 2018-04     2
    # 5 2018-05     3
    # 6 2018-06     4
    # 7 2018-07     5
    # 8 2018-08     5
    # 9 2018-09     5
    #10 2018-10     5
    #11 2018-11     5
    #12 2018-12     5
    #13 2019-01     4
    #14 2019-02     4
    #15 2019-03     3
    #16 2019-04     3
    #17 2019-05     2
    #18 2019-06     2  
    

    【讨论】:

      【解决方案4】:

      dplyr 的解决方案。

      获取日期数据框的一些示例数据:

      library(tidyverse)
      library(lubridate)
      
      df <- tibble(as.Date(c("2018-05-02", "2018-05-03", "2018-05-04", "2018-05-04", "2018-05-02", "2018-06-03", "2018-06-04", "2018-07-04", "2018-07-04"))) %>%
          rename(Date = 1)
      

      添加指定数字月份的列:

      df <- df %>% 
      mutate(Month = month(Date))
      

      创建一个数字月份 (1-12) 的数据框,并为其提供一个名为“subs”的订阅计数空白列。

      subs_by_month <- as.tibble(1:12) %>% 
          rename(Month = 1)
      subs_by_month$subs <- NA
      

      使用tally循环计算观察次数:

      for(i in 1:12){
        subs_by_month$subs[[i]] <- unlist(
          df %>%
          tally(Month==i)
          )
      }
      

      生成的帧具有数字月份和该月的订阅人数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        相关资源
        最近更新 更多