【问题标题】:Starting cumsum() at a fixed day of each year在每年的固定日期启动 cumsum()
【发布时间】:2019-10-21 20:27:05
【问题描述】:

我想积累一个长期(包括不同年份)每天测量的变量。变量的累积应该从一年中的固定日期开始(例如,2 月 1 日,或者换句话说,一年中的某一天 (doy) 32 - 我以前在 doys 工作)。每年的 cumsum 应该在这个固定的日子开始。

我尝试使用setDT(df)[, whatiwant := cumsum(variable), by = rleid(DOY >= 32)]rle(DOY >= 32),但它们都没有考虑到每年的第一天。

理论上,ave() 函数应该可以正常工作,但我不知道如何在不同年份的 doy 之间创建一个标志变量(它通常只创建第一个)。

df <- data.frame(Date = seq(as.Date("2010-01-01"), by = 1, len = 1000),
                 Year = format(seq(as.Date("2010-01-01"), by = 1, len = 1000), "%Y"),
                 DOY = format(seq(as.Date("2010-01-01"), by = 1, len = 1000), "%j"),
                 Variable = rnorm(1000, mean=10, sd=3))

编辑: 谢谢你的帮助。 它如何与 data.table 包一起使用?

【问题讨论】:

标签: r flags cumsum


【解决方案1】:

让我们从一个函数式接口开始,它是一个函数列表,连同它们的输入和输出,可以解决问题。

  1. 函数reset_cum_sums 有两个元素,一个向量和该向量的重置位置列表。 输出将是一个包含累积和的向量,总和在向量的每个所需位置重新开始。一个例子应该更清楚:

    在每个重置位置,累积和重置。因此,如果输入为1:10,位置向量为3 5 7,则输出为

    input: [1 2 3 4 5 6 7 8 9 10]

    output: [1 3 3 7 5 11 7 15 24 34]

如果没有给出位置,这将产生与cumsum相同的结果。

  1. 如果日期是二月一日,is_feb_1st 将返回 TRUE,否则返回 FALSE。我会把这个留给你做练习。

  2. 函数式接口使用原始函数 whichsplitlapply,其文档留作练习阅读。

现在,解决方案的大纲可以写成:

restart_feb_first<-function(data.frame) {
   reset_cum_sums(data.frame$value,  
       which(is_feb_first(data.frame$date))
   }

如果二月第一次出现在您的数据中的位置 32,32+365,32+730,.. 这将构成您的位置向量。好处是您可以轻松适应闰年。

唯一具有挑战性的部分是写reset_cum_sums;在这里,我提供一种方法来做到这一点,不一定是最有效的。该程序将向量分成块,每个块都从正确的位置开始(在您的情况下,是二月的第一个)。请注意,此示例不需要管道运算符。您可以改用传统的函数式表示法。

另外,我以这种方式编写函数是为了说明一些 R 概念,而不一定是为了编写性能最高的代码。但是,如果你想重写,你只需将你在这个函数上的工作隔离开来。

#
# purpose: define a function that creates cumulative  sums
# of vectors, but which reset at each position given by 
# the vector `positions`, which can be null.
# reset_sum

# parameters for hypothetical example
set.seed(18)
values=runif(50)

# cumulative sums reset at these positions.
positions=c(3,13,23,33,43)

# dependencies
require(magrittr) # or tidyverse for pipe operator


reset_sum = function(vector,positions) {
   k=length(vector)
  # cut the list into pieces 
  splitter=cut(1:k,breaks=c(-Inf,positions,Inf),right = FALSE)
  pieces=split(vector,splitter)
  # do the cumsum of each piece, and then glue then back together
  pieces %>%  lapply(cumsum) %>% unlist(use.names=FALSE)
}

这是函数的调用方式

# examples
reset_sum(values,positions)
reset_sum(rep(1,50),positions)

我希望这可以指导您找到适合您需求的解决方案。关键概念是将其分解,直到找到一个用 R 原语“易于”编写的函数。如果您需要 reset_cum_sums 超级高效,用 C 或 data.table 编写应该相当容易,但让我们改天再说吧。

更新

这个函数返回一个向量,所以要与数据表包一起使用,只需添加一个assign,如

DT[,new_column:=reset_sum(value,,isFebFirst(date)]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2022-11-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-23
    相关资源
    最近更新 更多