Skip to content

Commit 98e1c56

Browse files
committed
all
1 parent 4b05edd commit 98e1c56

File tree

10 files changed

+345
-46
lines changed

10 files changed

+345
-46
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export(frollmean)
5656
export(frollsum)
5757
export(frollmax)
5858
export(frollapply)
59+
export(frolladapt)
5960
export(nafill)
6061
export(setnafill)
6162
export(.Last.updated)

NEWS.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@
3232
```
3333
Additionally argument names in `frollapply` has been renamed from `x` to `X` and `n` to `N` to avoid conflicts with common argument names that may be passed to `...`, aligning to base R API of `lapply`. `x` and `n` continue to work with a warning, for now.
3434
35+
5. Adaptive rolling functions no longer tolerate `NA`s and negative values passed to `n` argument.
36+
```r
37+
n = c(2,NA,2)
38+
frollsum(1:3, n, adaptive=TRUE)
39+
#Error in froll(fun = "sum", x = x, n = n, fill = fill, algo = algo, align = align, :
40+
# 'n' must be non-negative integer values (>= 0)
41+
```
42+
If for some reason previous `NA`s behavior is needed, it can be achieved by replacing `NA`s with a value big enough
43+
```r
44+
n = nafill(c(2,NA,2), fill=.Machine$integer.max)
45+
frollsum(1:3, n, adaptive=TRUE)
46+
```
47+
3548
### NOTICE OF INTENDED FUTURE POTENTIAL BREAKING CHANGES
3649
3750
1. `data.table(x=1, <expr>)`, where `<expr>` is an expression resulting in a 1-column matrix without column names, will eventually have names `x` and `V2`, not `x` and `V1`, consistent with `data.table(x=1, <expr>)` where `<expr>` results in an atomic vector, for example `data.table(x=1, cbind(1))` and `data.table(x=1, 1)` will both have columns named `x` and `V2`. In this release, the matrix case continues to be named `V1`, but the new behavior can be activated by setting `options(datatable.old.matrix.autoname)` to `FALSE`. See point 5 under Bug Fixes for more context; this change will provide more internal consistency as well as more consistency with `data.frame()`.
@@ -210,6 +223,40 @@
210223
#[1] TRUE
211224
```
212225

226+
18. New `frolladapt` helper function has been added to aid in preparation of adaptive length of rolling window width when dealing with _irregularly spaced ordered data_. This lets the user to apply a rolling function over a period without having to deal with gaps in a data where some periods might be missing.
227+
```r
228+
idx = as.Date("2022-10-23") + c(0,1,4,5,6,7,9,10,14)
229+
dt = data.table(index=idx, value=seq_along(idx))
230+
dt
231+
# index value
232+
# <Date> <int>
233+
#1: 2022-10-23 1
234+
#2: 2022-10-24 2
235+
#3: 2022-10-27 3
236+
#4: 2022-10-28 4
237+
#5: 2022-10-29 5
238+
#6: 2022-10-30 6
239+
#7: 2022-11-01 7
240+
#8: 2022-11-02 8
241+
#9: 2022-11-06 9
242+
dt[, c("rollmean3","rollmean3days") := list(
243+
frollmean(value, 3),
244+
frollmean(value, frolladapt(index, 3), adaptive=TRUE)
245+
)]
246+
dt
247+
# index value rollmean3 rollmean3days
248+
# <Date> <int> <num> <num>
249+
#1: 2022-10-23 1 NA NA
250+
#2: 2022-10-24 2 NA NA
251+
#3: 2022-10-27 3 2 3.0
252+
#4: 2022-10-28 4 3 3.5
253+
#5: 2022-10-29 5 4 4.0
254+
#6: 2022-10-30 6 5 5.0
255+
#7: 2022-11-01 7 6 6.5
256+
#8: 2022-11-02 8 7 7.5
257+
#9: 2022-11-06 9 8 9.0
258+
```
259+
213260
### BUG FIXES
214261

215262
1. `fread()` no longer warns on certain systems on R 4.5.0+ where the file owner can't be resolved, [#6918](https://github.com/Rdatatable/data.table/issues/6918). Thanks @ProfFancyPants for the report and PR.

R/froll.R

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,47 @@ make.roll.names = function(x.len, n.len, n, x.nm, n.nm, fun, adaptive) {
111111
ans
112112
}
113113

114+
# irregularly spaced time series, helper for creating adaptive window size
115+
frolladapt = function(x, n, align="right", partial=FALSE, give.names=FALSE) {
116+
x = unclass(x)
117+
if (!is.numeric(x))
118+
stopf("'x' must be of a numeric type")
119+
if (!is.integer(x))
120+
x = as.integer(x)
121+
if (!is.numeric(n)) {
122+
stopf("'n' must be an integer")
123+
} else {
124+
nms = names(n) ## only for give.names
125+
if (!is.integer(n)) {
126+
if (!fitsInInt32(n))
127+
stopf("'n' must be an integer")
128+
n = as.integer(n)
129+
}
130+
}
131+
if (!length(n))
132+
stopf("'n' must be non 0 length")
133+
if (anyNA(n))
134+
stopf("'n' must not have NAs")
135+
if (!identical(align, "right"))
136+
stopf("'align' other than 'right' has not yet been implemented")
137+
if (!isTRUEorFALSE(partial))
138+
stopf("'partial' must be TRUE or FALSE")
139+
if (!isTRUEorFALSE(give.names))
140+
stopf("'give.names' must be TRUE or FALSE")
141+
142+
if (length(n) == 1L) {
143+
ans = .Call(Cfrolladapt, x, n, partial)
144+
} else {
145+
ans = lapply(n, function(.n) .Call(Cfrolladapt, x, .n, partial))
146+
if (give.names) {
147+
if (is.null(nms))
148+
nms = paste0("n", as.character(n))
149+
setattr(ans, "names", nms)
150+
}
151+
}
152+
ans
153+
}
154+
114155
froll = function(fun, x, n, fill=NA, algo=c("fast","exact"), align=c("right","left","center"), na.rm=FALSE, has.nf=NA, adaptive=FALSE, partial=FALSE, give.names=FALSE, hasNA) {
115156
stopifnot(!missing(fun), is.character(fun), length(fun)==1L, !is.na(fun))
116157
if (!missing(hasNA)) {

R/frollapply.R

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,14 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
141141
stopf("'N' must be non 0 length")
142142
if (!adaptive) {
143143
if (is.list(N))
144-
stopf("'N' must be integer, list is accepted for adaptive TRUE")
144+
stopf("'N' must be an integer, list is accepted for adaptive TRUE")
145145
else if (!is.numeric(N))
146-
stopf("'N' must be integer vector")
146+
stopf("'N' must be an integer")
147147
nnam = names(N) ## used for give.names
148148
if (!is.integer(N))
149149
N = as.integer(N)
150+
if (anyNA(N))
151+
stopf("'N' must be non-negative integer values (>= 0)")
150152
nn = length(N) ## top level loop for vectorized n
151153
} else {
152154
if (length(unique(len)) > 1L) ## vectorized x requires same nrow for adaptive
@@ -156,6 +158,8 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
156158
stopf("length of integer vector(s) provided as list to 'N' argument must be equal to number of observations provided in 'X'")
157159
if (!is.integer(N))
158160
N = as.integer(N)
161+
if (anyNA(N))
162+
stopf("'N' must be non-negative integer values (>= 0)")
159163
nn = 1L
160164
N = list(N)
161165
nnam = character()
@@ -165,13 +169,15 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
165169
if (!equal.lengths(N))
166170
stopf("adaptive windows provided in 'N' must not to have different lengths")
167171
if (!all(vapply_1b(N, is.numeric, use.names=FALSE)))
168-
stopf("n must be an integer vector or list of an integer vectors")
172+
stopf("'N' must be an integer vector or list of integer vectors")
169173
if (!all(vapply_1b(N, is.integer, use.names=FALSE)))
170174
N = lapply(N, as.integer)
175+
if (any(vapply_1b(N, anyNA, use.names=FALSE)))
176+
stopf("'N' must be non-negative integer values (>= 0)")
171177
nn = length(N)
172178
nnam = names(N)
173179
} else
174-
stopf("n must be an integer vector or list of an integer vectors")
180+
stopf("'N' must be an integer vector or list of integer vectors")
175181
}
176182
## partial
177183
if (partial) {

0 commit comments

Comments
 (0)