如何根据以前的日期以编程方式对 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"
我正在尝试在 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"