【问题标题】:count number of observations between two overlapping dates r计算两个重叠日期 r 之间的观察次数
【发布时间】:2018-02-28 07:46:26
【问题描述】:

假设我们有一个如下定义的数据框:

mydata <- data.frame(id = c('A', 'B', 'C', 'D'),
                     start_date = as.Date(c('2012-08-05',
                                            '2013-05-04',
                                            '2012-02-01',
                                            '2015-03-02')),
                     end_date = as.Date(c('2014-01-12',
                                          '2015-06-05',
                                          '2016-05-06',
                                          '2017-09-12')))

start_date 谈论员工加入的那一天,end_date 谈论他离开的那一天,id 是唯一的员工 ID。

从 2012 年 8 月 5 日(最早的 start_date)到 2017 年 9 月 12 日(最晚的 end_date)的每个月 我希望按月计算员工数。 最终输出应为一种类似于下面的格式:(不管是宽格式还是长格式)

在上面的表格中,列表示月份(1 到 12),行表示年份,表格中的单元格表示该月的员工人数。

我们将不胜感激。

【问题讨论】:

    标签: r date dataframe


    【解决方案1】:

    你可以试试:

    table(unlist(lapply(1:nrow(mydata), function(x) {
        format(seq(from=mydata[x,2],to=mydata[x,3],by="month"),"%Y-%m")
        })))
    

    【讨论】:

      【解决方案2】:

      这是一个在基础 R 中使用 mapply 的解决方案。

      # Function to get date of first day of a month (by @digEmAll)
      toFirstDayOfMonth <- function(dates) dates - as.POSIXlt(dates)$mday + 1
      
      # Generate all dates
      dates <- Reduce(c, with(mydata, mapply(seq, toFirstDayOfMonth(start_date), end_date,
                                             by = "month")))
      
      # Count occurrences of year/month combinations
      table(format(dates, "%Y"), format(dates, "%m"))
      

      结果:

             01 02 03 04 05 06 07 08 09 10 11 12
        2012  0  1  1  1  1  1  1  2  2  2  2  2
        2013  2  2  2  2  3  3  3  3  3  3  3  3
        2014  3  2  2  2  2  2  2  2  2  2  2  2
        2015  2  2  3  3  3  3  2  2  2  2  2  2
        2016  2  2  2  2  2  1  1  1  1  1  1  1
        2017  1  1  1  1  1  1  1  1  1  0  0  0
      

      【讨论】:

      • @PoGibas 是的,seq.Date 是一个方便的功能。
      • 请注意:seq(from=as.Date("2014-01-12"),to=as.Date("2014-02-01"),by="month") 仅返回“2014-01-12”,因此我认为您应该在传递给 mapply 之前将日期标准化为该月的第一天......例如使用toFirstDayOfMonth &lt;- function(dates) dates - as.POSIXlt(dates)$mday + 1
      • @digEmAll 很好的发现!感谢您指出。我将您的功能添加到答案中。
      【解决方案3】:

      来自tidyverselubridate 的解决方案。

      library(tidyverse)
      library(lubridate)
      
      mydata2 <- mydata %>%
        mutate(Dates = map2(start_date, end_date, ~seq(.x, .y, by = "day"))) %>%
        unnest() %>%
        mutate(Year = year(Dates), Month = month(Dates)) %>%
        group_by(Year, Month) %>%
        summarise(Employee = n_distinct(id)) %>%
        spread(Month, Employee, fill = 0)
      mydata2
      # A tibble: 6 x 13
      # Groups:   Year [6]
         Year   `1`   `2`   `3`   `4`   `5`   `6`   `7`   `8`   `9`  `10`  `11`  `12`
      * <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
      1  2012     0     1     1     1     1     1     1     2     2     2     2     2
      2  2013     2     2     2     2     3     3     3     3     3     3     3     3
      3  2014     3     2     2     2     2     2     2     2     2     2     2     2
      4  2015     2     2     3     3     3     3     2     2     2     2     2     2
      5  2016     2     2     2     2     2     1     1     1     1     1     1     1
      6  2017     1     1     1     1     1     1     1     1     1     0     0     0
      

      【讨论】:

      • 这也是一个很好的答案,但 Sven Hohenstein 的答案很简洁。
      • @JashShah 我同意。如果我是你,我也会接受 Sven Hohenstein 的回答。
      【解决方案4】:

      您还可以使用data.table 包或data.tabledplyr 的组合。

      我将展示dplyrdata.table 版本(我使用dplyr 的唯一原因是%&gt;% 运算符。您也可以在一行中完成整个操作而不使用%&gt;%)。

      # load data.table
      library(data.table)
      # load dplyr
      library(dplyr)
      
      #for each employee id, list first days of months during which employee was working, then transform from long to wide format using dcast function
      dt <- setDT(mydata)[, list(date = seq(as.Date(format(min(start_date), "%Y-%m-01")),
       as.Date(format(max(end_date), "%Y-%m-01")),
       by = "month")), by = id] %>% dcast(year(date) ~ month(date))
      

      上面的代码究竟做了什么?

      by = id 告诉您data.table 中的操作(本例中为日期的计算)将为每个员工 ID 执行。

      format(max(end_date), "%Y-%m-01") 为您提供每位员工工作的上个月的开始时间。

      format(min(start_date), "%Y-%m-01)"员工开始工作的月初。

      seq(..., ...., by = "month") 为您提供每位员工工作的所有月份的第一天。

      %&gt;% 是管道操作符,意思是“那么”。与使用 setDT(mydata)[..., ..., ...] 的结果作为dcast函数的第一个参数相同。

      dcast 函数将long 格式(在本例中为setDT[...] 的结果)转换为wide 格式。

      结束就是结束:)

      【讨论】:

        猜你喜欢
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        • 2019-02-12
        • 1970-01-01
        • 2017-11-05
        • 1970-01-01
        • 2012-12-07
        相关资源
        最近更新 更多