【问题标题】:How to spread projects across active years?如何在活跃年份分布项目?
【发布时间】:2025-12-31 22:35:01
【问题描述】:

我有一个包含 10000 多个项目的 Excel 电子表格,每个项目的开始日期和结束日期都不同。我需要将项目分布在它们活跃的年份中,以便按年份进行准确计数。从这个例子开始我该怎么做?

start date   end date     entityNo  amount
4/1/2001     8/31/2012      1         500
1/1/2005     12/31/2007     2         100

我最初想到的解决方案是在 Excel 中获取开始日期和结束日期之间的差异(结束日期 - 开始日期 + 1),以便我获得每个项目的持续时间(以天为单位)。在获得天数后,例如 4100 天,我将持续时间除以一年中的总天数(365.25 天),这将给出我的年份范围,但是当我将每个项目分配给正确的年份。

超过 10,000 个项目的数据集的预期输出:

start date   end date         no of years  entityNo  amount
4/1/2001     8/31/2012                11            1         500
2002
2003
2004
2005
2006
2007 upto 8/31/2012

随后是另一个具有不同实体编号的项目

start date       end date      entity no      amount
   1/1/2005     12/31/2007     2              100

输出

years       entity no     amount
1/1/2005    2             100
2006        2             100
2/31/2007   2             100

【问题讨论】:

  • 你想做什么?你想用 Sql、R、Excel 做什么?为什么是三个标签。你试过什么了?你的例子不是很清楚。 “传播项目......”是什么意思?
  • 我想要一个基于 r、excel 或 sql 的响应。我的意思是,如果一个项目从 2001 年到 2012 年运行了 11 年,它应该出现 11 次,如果另一个运行了 5 年,它应该出现 5 次。
  • “出现”是什么意思?你能显示一个预期的输出样本吗?您只是想将数据复制到 X 多行吗?
  • @BruceWayne 是的,这就是我想做的事
  • @daisym 如果您不断更改预期输出,我无能为力。

标签: sql r excel excel-2013


【解决方案1】:

这个解决方案需要r包lubridate

library(tidyverse)
library(lubridate)

# Create example data frame
dat <- tribble(
  ~`start date`, ~`end date`, ~`entityNo`, ~`amount`,
  "4/1/2001",  "8/31/2012",   1,         500,
  "1/1/2005",  "12/31/2007",  2,         100
)

dat %>%
  mutate(`start date` = mdy(`start date`), `end date` = mdy(`end date`)) %>%
  mutate(`start year` = year(`start date`), `end year` = year(`end date`)) %>%
  mutate(`no of years` = `end year` - `start year`) %>%
  select(`start date`, `end date`, `no of years`, entityNo, amount)

或者你可以使用一些字符串操作方法。

dat %>%
  mutate(`no of years` = as.numeric(substring(`end date`, nchar(`end date`) - 3)) -
           as.numeric(substring(`start date`, nchar(`start date`) - 3))) %>%
  select(`start date`, `end date`, `no of years`, entityNo, amount)

【讨论】:

    【解决方案2】:

    根据最新的编辑,OP 希望按日历年细分每个项目的持续时间。这可以通过使用data.table 包的foverlaps() 函数来完成。

    读取数据

    library(data.table)
    projects <- fread(
    "start_date   end_date     entityNo  amount
    4/1/2001     8/31/2012      1         500
    1/1/2005     12/31/2007     2         100")
    

    fread() 可用于从磁盘快速读取csv 文件。这里使用了一个方便的特性,允许从字符变量中读取数据。

    准备数据

    library(lubridate)
    # convert dates from character to Date class
    date_cols <- c("start_date", "end_date")
    projects[, (date_cols) := lapply(.SD, mdy), .SDcols = date_cols]
    
    # compute duration of project = number of years in which project was active
    projects[, years_active := year(end_date) - year(start_date) + 1]
    

    请注意,years_active 与 OP 给出的 no of years 不同。 years_active 是传播数据所需的行数。

    为计算重叠创建日期范围

    date_range <- projects[, .(year = seq(year(min(start_date)), 
                                          year(max(end_date))))]
    date_range[, start_in_year := ymd(paste0(year, "-01-01"))]
    date_range[, end_in_year := ymd(paste0(year, "-12-31"))]
    setkey(date_range, start_in_year, end_in_year)
    
    date_range
    #    year start_in_year end_in_year
    # 1: 2001    2001-01-01  2001-12-31
    # 2: 2002    2002-01-01  2002-12-31
    # 3: 2003    2003-01-01  2003-12-31
    # ...
    #10: 2010    2010-01-01  2010-12-31
    #11: 2011    2011-01-01  2011-12-31
    #12: 2012    2012-01-01  2012-12-31
    

    请注意,可以扩展此方法以创建按季度、月、ISO 周或天划分的持续时间。

    计算重叠区间

    projects_by_year <- foverlaps(projects, date_range, by.x = date_cols)
    # adjust start_in_year to coincide with project start date
    projects_by_year[, start_in_year := pmax(start_in_year, start_date)]
    # adjust end_in_year to coincide with project end date
    projects_by_year[, end_in_year := pmin(end_in_year, end_date)]
    
    projects_by_year
    #    year start_in_year end_in_year start_date   end_date entityNo amount years_active
    # 1: 2001    2001-04-01  2001-12-31 2001-04-01 2012-08-31        1    500           12
    # 2: 2002    2002-01-01  2002-12-31 2001-04-01 2012-08-31        1    500           12
    # 3: 2003    2003-01-01  2003-12-31 2001-04-01 2012-08-31        1    500           12
    # ...
    #10: 2010    2010-01-01  2010-12-31 2001-04-01 2012-08-31        1    500           12
    #11: 2011    2011-01-01  2011-12-31 2001-04-01 2012-08-31        1    500           12
    #12: 2012    2012-01-01  2012-08-31 2001-04-01 2012-08-31        1    500           12
    #13: 2005    2005-01-01  2005-12-31 2005-01-01 2007-12-31        2    100            3
    #14: 2006    2006-01-01  2006-12-31 2005-01-01 2007-12-31        2    100            3
    #15: 2007    2007-01-01  2007-12-31 2005-01-01 2007-12-31        2    100            3
    

    项目 1 分 12 年/行,项目 2 分 3 年。调整start_in_yearend_in_year 以匹配每个项目的相应开始和结束中的正确开始和结束日期

    希望这是预期的结果。


    按年份计算聚合

    长格式非常适合计算每年的聚合。例如,每年的项目数:

    projects_by_year[, .N, by = year]
    #    year N
    # 1: 2001 1
    # 2: 2002 1
    # 3: 2003 1
    # 4: 2004 1
    # 5: 2005 2
    # 6: 2006 2
    # 7: 2007 2
    # 8: 2008 1
    # 9: 2009 1
    #10: 2010 1
    #11: 2011 1
    #12: 2012 1
    

    或每年的总金额:

    projects_by_year[, sum(amount), by = year]
    #    year  V1
    # 1: 2001 500
    # 2: 2002 500
    # 3: 2003 500
    # 4: 2004 500
    # 5: 2005 600
    # 6: 2006 600
    # 7: 2007 600
    # 8: 2008 500
    # 9: 2009 500
    #10: 2010 500
    #11: 2011 500
    #12: 2012 500 
    

    【讨论】:

    • 其中一个答案是否解决了您的问题?如果是这样,请接受和/或投票,以便将问题标记为已回答。否则,请让我们知道为什么答案对您不起作用,以便可以改进它以使您和其他 SO 用户受益。谢谢。