如何根据以前的日期以编程方式对 R 中的日期进行子集化?

How to programmatically subset dates in R based on previous dates?

我正在尝试在 R 中编写一个函数,以编程方式 select 一组日期,每次迭代都依赖于前一个日期 selection。我无法解决的挑战是如何系统地分析数据集,select 分析每个阶段的日期,然后以该日期为起点 select 下一个日期。对于每次新的迭代,一次一个,这样做是微不足道的。问题是如何编写一个函数,当数据集中没有更多符合条件的日期时自动停止?我知道有一个解决方案,可能使用 for() and/or while() 循环,可能使用 break() 命令。但到目前为止我找不到答案。任何帮助,将不胜感激。作为我试图解决的过程的一个简单例子:

 # create fake data for 12 months with dates
library("xts")
 set.seed(67)
 dat <-xts(rnorm(12)+100,seq(as.Date("2001/1/1"), as.Date("2001/12/1"),    "1 months"))

查看原始数据:

 dat
                 [,1]
 2001-01-01 101.21940
 2001-02-01  99.87560
 2001-03-01  99.04250
 2001-04-01  99.92083
 2001-05-01  98.85659
 2001-06-01  98.94281
 2001-07-01  99.61547
 2001-08-01 100.60834
 2001-09-01 101.67247
 2001-10-01  98.46271
 2001-11-01  98.62171
 2001-12-01 100.49543

接下来,创建第一个函数 select 第一个日期,在本例中只是 select 第二个日期条目:

f.1 <-function(x) { 
a <-as.Date(index(dat[2]))
 }

并创建第二个函数,它查看第一个日期之后的日期并且 selects 这些日期 >101。

 f.2 <-function(x,y) { # x=dat, y=previous foo.date
   a <-x[paste0(y+1, "/")]
   b <-as.Date(index(a[a >101]))
 }

最后,运行 函数和收集日期...

 foo.date.1 <-f.1(dat)
 foo.date.2 <-f.2(dat,foo.date.1)
 foo.date.3 <-f.2(dat,foo.date.2)

并汇总 3 个 foo.date 文件的输出:

 dat.all <-c(foo.date.1, foo.date.2, foo.date.3)
 dat.all
 [1] "2001-02-01" "2001-09-01"

请注意,最后一次 select 编辑日期是 foo.date.2。第三次尝试 - 根据 foo.date.3 - 没有执行,因为在 2001-09-01 之后没有值大于 101 的日期。然而,对于具有数千甚至数万个日期的数据集,找到符合条件的确切日期集是非常低效的。关于如何以编程方式找到解决方案的任何想法?在上面的示例中,通过函数的解决方案将 a) 发现只有 2 个日期符合条件,因此该函数将在第二次尝试后结束并且不会尝试第三次搜索; b) 在一个文件输出中汇总相关日期。

提前感谢您的回答!

我不熟悉 xts 格式,所以我用标准数据框重新创建了您的数据,date:日期字段和 x:随机值。

set.seed(4)
dat <- data.frame(date=as.Date(paste0("2001-", 1:12, "-1")), x=rnorm(12)+100)
head(dat)

        date         x
1 2001-01-01 100.21675
2 2001-02-01  99.45751
3 2001-03-01 100.89114
4 2001-04-01 100.59598
5 2001-05-01 101.63562
6 2001-06-01 100.68928    

要提取 x > 101 的日期:

lapply(1:nrow(dat), function(x){
  d2 <- dat[x:nrow(dat), ] 
  d2[d2$x > 101, "date"]
})

[[1]]
[1] "2001-05-01" "2001-09-01" "2001-10-01"

[[2]]
[1] "2001-05-01" "2001-09-01" "2001-10-01"

[[3]]
[1] "2001-05-01" "2001-09-01" "2001-10-01"

[[4]]
[1] "2001-05-01" "2001-09-01" "2001-10-01"

[[5]]
[1] "2001-05-01" "2001-09-01" "2001-10-01"

[[6]]
[1] "2001-09-01" "2001-10-01"

[[7]]
[1] "2001-09-01" "2001-10-01"

[[8]]
[1] "2001-09-01" "2001-10-01"

[[9]]
[1] "2001-09-01" "2001-10-01"

[[10]]
[1] "2001-10-01"

[[11]]
character(0)

[[12]]
character(0)

如果我没理解错的话,你想找到每个观察值>101之后的观察值的索引值。

一个简单有效的解决方案是先 lag 您的系列,然后 select 所有大于 101 的观测值的索引值。

datlag <- lag(dat)
index(datlag[datlag > 101])
# [1] "2001-02-01" "2001-10-01"

基于此评论:

[T]he "criteria" (goal) is to identify the date(s) when the weights in an investment portfolio deviate from the target weights by x% for a given return series. This is easy to do for each date, one at a time. The first function identifies the first date; the second function does the same with the distinction of using the previous date. The second function may be repeated, depending on the # of rebal dates beyond the first one.

这个问题似乎是真正的递归问题,这是使用循环的一个很好的理由(尽管您仍然需要小心在循环内增长对象)。

在这种情况下,您会定期将投资组合权重重置回目标值。这意味着您必须重新计算所有未来的投资组合余额。

这是一个包含 2 个资产的示例。

# asset return data
set.seed(67)
dat <- xts(matrix(rnorm(24, 0, 0.02),12,2),
           seq(as.Date("2001/1/1"), as.Date("2001/12/1"), "1 months"))

# constraints
target_weights <- c(0.5, 0.5)
tol <- 0.01                # each asset must be +/-1% of its target
rebal_dates <- start(dat)  # assume allocation on first observation

# loop until break
while (last(rebal_dates) < end(dat)) {
  # date range, starting from period after last rebalance date
  date_range <- paste0(last(rebal_dates) + 1, "/")
  # portfolio balance over date range
  bal <- cumprod(1 + dat[date_range,])
  # portfolio weights
  wts <- bal / rowSums(bal)
  # deviations from target portfolio
  dev <- abs(wts - rep(target_weights, nrow(wts))) > tol
  # next rebalance date
  next_rebal <- which(rowSums(dev) > 0)
  # break the loop if there are no more rebalance dates
  if (length(next_rebal) == 0)
    break
  # append rebalance date to our vector
  # (yes, this is growing an object, but it's small and not very frequent)
  rebal_dates <- c(rebal_dates, index(dev)[next_rebal[1]])
}
rebal_dates
# [1] "2001-01-01" "2001-06-01" "2001-09-01" "2001-10-01" "2001-11-01"