Skip to content

Commit 79154d1

Browse files
committed
froll support for n=0, closes #7285
1 parent 73b00b3 commit 79154d1

File tree

8 files changed

+168
-31
lines changed

8 files changed

+168
-31
lines changed

R/frollapply.R

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,10 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
209209
cpy = copy
210210
ansMask = function(len, n) {
211211
mask = rep(TRUE, len)
212-
mask[seq_len(n-1L)] = FALSE
212+
if (n) mask[seq_len(n - 1L)] = FALSE ## handle n==0
213213
mask
214214
}
215+
tight0 = function(i, dest, src, n) FUN(dest, ...) ## skip memcpy when n==0
215216
if (by.column) {
216217
allocWindow = function(x, n) x[seq_len(n)]
217218
tight = function(i, dest, src, n) FUN(.Call(CmemcpyVector, dest, src, i, n), ...)
@@ -237,9 +238,9 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
237238
if (by.column) {
238239
allocWindow = function(x, n) x[seq_len(max(n, na.rm=TRUE))]
239240
if (has.growable) {
240-
tight = function(i, dest, src, n) FUN(.Call(CmemcpyVectoradaptive, dest, src, i, n), ...)
241+
tight = function(i, dest, src, n) FUN(.Call(CmemcpyVectoradaptive, dest, src, i, n), ...) # CmemcpyVectoradaptive handles k[i]==0
241242
} else {
242-
tight = function(i, dest, src, n) FUN(src[(i-n[i]+1L):i], ...) # nocov
243+
tight = function(i, dest, src, n) {stopf("internal error: has.growable should be TRUE, if enabled it requires to implement support for n==0"); FUN(src[(i-n[i]+1L):i], ...)} # nocov
243244
}
244245
} else {
245246
if (!list.df) {
@@ -248,12 +249,12 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
248249
allocWindow = function(x, n) lapply(x, `[`, seq_len(max(n)))
249250
}
250251
if (has.growable) {
251-
tight = function(i, dest, src, n) FUN(.Call(CmemcpyDTadaptive, dest, src, i, n), ...)
252+
tight = function(i, dest, src, n) FUN(.Call(CmemcpyDTadaptive, dest, src, i, n), ...) # CmemcpyDTadaptive handles k[i]==0
252253
} else {
253254
if (!list.df) { # nocov
254-
tight = function(i, dest, src, n) FUN(src[(i-n[i]+1L):i, , drop=FALSE], ...) # nocov
255+
tight = function(i, dest, src, n) {stopf("internal error: has.growable should be TRUE, if enabled it requires to implement support for n==0"); FUN(src[(i-n[i]+1L):i, , drop=FALSE], ...)} # nocov
255256
} else {
256-
tight = function(i, dest, src, n) FUN(lapply(src, `[`, (i-n[i]+1L):i), ...) # nocov
257+
tight = function(i, dest, src, n) {stopf("internal error: has.growable should be TRUE, if enabled it requires to implement support for n==0"); FUN(lapply(src, `[`, (i-n[i]+1L):i), ...)} # nocov
257258
}
258259
}
259260
}
@@ -288,6 +289,7 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
288289
w = allocWindow(thisx, thisn) ## prepare window, handles adaptive
289290
ansmask = ansMask(thislen, thisn)
290291
ansi = which(ansmask)
292+
tightFUN = if (adaptive || thisn) tight else tight0 ## handle n==0 for !adaptive, for !adaptive thisn should be scalar
291293
if (use.fork) { ## !windows && getDTthreads()>1L
292294
ths = min(DTths, length(ansi))
293295
ii = split(ansi, sort(rep_len(seq_len(ths), length(ansi)))) ## assign row indexes to threads
@@ -298,7 +300,7 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
298300
# nocov start ## fork processes seem not to be tracked by codecov, at least when parallel not in suggests
299301
setDTthreads(1L) ## disable nested parallelism
300302
lapply(ii[[th]], ## loops over indexes for that thread
301-
FUN = tight, ## handles adaptive and by.column
303+
FUN = tightFUN, ## handles adaptive and by.column
302304
dest = cpy(w), ## allocate own window for each thread, if we would not copy here, then copy would be handled later on by fork's copy-on-write
303305
src = thisx, ## full input
304306
n = thisn) ## scalar or in adaptive case a vector
@@ -339,7 +341,7 @@ frollapply = function(X, N, FUN, ..., by.column=TRUE, fill=NA, align=c("right","
339341
oldDTthreads = setDTthreads(1L) ## for consistency, anyway window size is unlikely to be big enough to benefit any parallelism
340342
withCallingHandlers(
341343
tryCatch(
342-
thisans <- lapply(ansi, FUN = tight, dest = cpy(w), src = thisx, n = thisn),
344+
thisans <- lapply(ansi, FUN = tightFUN, dest = cpy(w), src = thisx, n = thisn),
343345
error = function(e) h$err = conditionMessage(e)
344346
), warning = function(w) {h$warn = c(h$warn, conditionMessage(w)); invokeRestart("muffleWarning")}
345347
)

inst/tests/froll.Rraw

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ test(6000.019, frollmean(x, TRUE), error="n must be integer")
9494
test(6000.020, frollmean(x, list(1:10)), error="n must be integer, list is accepted for adaptive TRUE")
9595
test(6000.021, frollmean(x, list(NA), adaptive=TRUE), error="n must be an integer vector or list of an integer vectors")
9696
test(6000.022, frollmean(x, list(c(1:5,1:5), NA), adaptive=TRUE), error="n must be an integer vector or list of an integer vectors")
97-
test(6000.0221, frollmean(1:2, list(c(0L, 0L)), adaptive=TRUE), error="n must be positive integer values")
9897

9998
#### various length list vectors
10099
l = list(1:6/2, 3:10/4)
@@ -369,9 +368,8 @@ test(6000.084, frollmean(list(1:3, numeric()), 2), list(c(NA_real_, 1.5, 2.5), n
369368
#### length(n)==0
370369
test(6000.085, frollmean(1:3, integer()), error="n must be non 0 length")
371370
test(6000.086, frollmean(list(1:3, 2:4), integer()), error="n must be non 0 length")
372-
#### n==0
373-
test(6000.087, frollmean(1:3, c(2,0)), error="n must be positive integer values")
374-
test(6000.088, frollmean(list(1:3, 2:4), 0), error="n must be positive integer values")
371+
#### n==0 (k==0, k[i]==0)
372+
##moved to 6001.
375373
#### n<0
376374
test(6000.089, frollmean(1:3, -2), error="n must be positive integer values")
377375
#### n[[1L]]>0 && n[[2L]]<0
@@ -1006,6 +1004,91 @@ test(6000.502, frollmax(c(5,NaN,1), 1L), c(5,NaN,1))
10061004
test(6000.503, frollmax(c(5,1,1,NaN,1,1,1), 2L), c(NA,5,1,NaN,NaN,1,1))
10071005
test(6000.504, frollmax(c(5,1,NA,NaN,1,1,1), 2L), c(NA,5,NA,NA,NaN,1,1))
10081006

1007+
# n==0, k==0, k[i]==0
1008+
test(6001.111, frollmean(1:3, 0), c(NaN,NaN,NaN), options=c("datatable.verbose"=TRUE), output="window width of size 0")
1009+
test(6001.112, frollmean(1:3, 0, fill=99), c(NaN,NaN,NaN))
1010+
test(6001.113, frollmean(c(1:2,NA), 0), c(NaN,NaN,NaN))
1011+
test(6001.114, frollmean(c(1:2,NA), 0, na.rm=TRUE), c(NaN,NaN,NaN))
1012+
test(6001.115, frollmean(1:3, 0, algo="exact"), c(NaN,NaN,NaN), options=c("datatable.verbose"=TRUE), output="window width of size 0")
1013+
test(6001.116, frollmean(c(1:2,NA), 0, algo="exact"), c(NaN,NaN,NaN))
1014+
test(6001.117, frollmean(c(1:2,NA), 0, algo="exact", na.rm=TRUE), c(NaN,NaN,NaN))
1015+
test(6001.121, frollmean(adaptive=TRUE, 1:3, c(2,0,2)), c(NA,NaN,2.5))
1016+
test(6001.122, frollmean(adaptive=TRUE, 1:3, c(2,0,2), fill=99), c(99,NaN,2.5))
1017+
test(6001.123, frollmean(adaptive=TRUE, c(1:2,NA), c(2,0,2)), c(NA,NaN,NA))
1018+
test(6001.124, frollmean(adaptive=TRUE, c(1:2,NA), c(2,0,2), na.rm=TRUE), c(NA,NaN,2))
1019+
test(6001.125, frollmean(adaptive=TRUE, 1:3, c(2,0,2), algo="exact"), c(NA,NaN,2.5))
1020+
test(6001.126, frollmean(adaptive=TRUE, 1:3, c(2,0,2), fill=99, algo="exact"), c(99,NaN,2.5))
1021+
test(6001.127, frollmean(adaptive=TRUE, c(1:2,NA), c(2,0,2), algo="exact"), c(NA,NaN,NA))
1022+
test(6001.128, frollmean(adaptive=TRUE, c(1:2,NA), c(2,0,2), algo="exact", na.rm=TRUE), c(NA,NaN,2))
1023+
test(6001.129, frollmean(adaptive=TRUE, c(1:2,NA), c(2,0,2), fill=99, algo="exact", na.rm=TRUE), c(99,NaN,2))
1024+
test(6001.181, frollapply(FUN=mean, 1:3, 0), c(NaN,NaN,NaN))
1025+
test(6001.182, frollapply(FUN=mean, 1:3, 0, fill=99), c(NaN,NaN,NaN))
1026+
test(6001.183, frollapply(FUN=mean, c(1:2,NA), 0), c(NaN,NaN,NaN))
1027+
test(6001.184, frollapply(FUN=mean, c(1:2,NA), 0, na.rm=TRUE), c(NaN,NaN,NaN))
1028+
test(6001.1910, frollapply(FUN=mean, adaptive=TRUE, 1:3, c(2,0,2)), c(NA,NaN,2.5))
1029+
test(6001.1911, frollapply(FUN=mean, adaptive=TRUE, list(1:3,2:4), c(2,0,2)), list(c(NA, NaN, 2.5), c(NA, NaN, 3.5)))
1030+
test(6001.1912, frollapply(FUN=mean, adaptive=TRUE, 1:3, list(c(2,0,2), c(0,2,0))), list(c(NA,NaN,2.5), c(NaN,1.5,NaN)))
1031+
test(6001.1913, frollapply(FUN=mean, adaptive=TRUE, list(1:3,2:4), list(c(2,0,2), c(0,2,0))), list(c(NA,NaN,2.5), c(NaN,1.5,NaN), c(NA,NaN,3.5), c(NaN,2.5,NaN)))
1032+
test(6001.192, frollapply(FUN=mean, adaptive=TRUE, 1:3, c(2,0,2), fill=99), c(99,NaN,2.5))
1033+
test(6001.193, frollapply(FUN=mean, adaptive=TRUE, c(1:2,NA), c(2,0,2)), c(NA,NaN,NA))
1034+
test(6001.194, frollapply(FUN=mean, adaptive=TRUE, c(1:2,NA), c(2,0,2), na.rm=TRUE), c(NA,NaN,2))
1035+
1036+
test(6001.211, frollsum(1:3, 0), c(0,0,0), options=c("datatable.verbose"=TRUE), output="window width of size 0")
1037+
test(6001.212, frollsum(1:3, 0, fill=99), c(0,0,0))
1038+
test(6001.213, frollsum(c(1:2,NA), 0), c(0,0,0))
1039+
test(6001.214, frollsum(c(1:2,NA), 0, na.rm=TRUE), c(0,0,0))
1040+
test(6001.215, frollsum(1:3, 0, algo="exact"), c(0,0,0), options=c("datatable.verbose"=TRUE), output="window width of size 0")
1041+
test(6001.216, frollsum(c(1:2,NA), 0, algo="exact"), c(0,0,0))
1042+
test(6001.217, frollsum(c(1:2,NA), 0, algo="exact", na.rm=TRUE), c(0,0,0))
1043+
test(6001.221, frollsum(adaptive=TRUE, 1:3, c(2,0,2)), c(NA,0,5))
1044+
test(6001.222, frollsum(adaptive=TRUE, 1:3, c(2,0,2), fill=99), c(99,0,5))
1045+
test(6001.223, frollsum(adaptive=TRUE, c(1:2,NA), c(2,0,2)), c(NA,0,NA))
1046+
test(6001.224, frollsum(adaptive=TRUE, c(1:2,NA), c(2,0,2), na.rm=TRUE), c(NA,0,2))
1047+
test(6001.225, frollsum(adaptive=TRUE, 1:3, c(2,0,2), algo="exact"), c(NA,0,5))
1048+
test(6001.226, frollsum(adaptive=TRUE, 1:3, c(2,0,2), fill=99, algo="exact"), c(99,0,5))
1049+
test(6001.227, frollsum(adaptive=TRUE, c(1:2,NA), c(2,0,2), algo="exact"), c(NA,0,NA))
1050+
test(6001.228, frollsum(adaptive=TRUE, c(1:2,NA), c(2,0,2), algo="exact", na.rm=TRUE), c(NA,0,2))
1051+
test(6001.229, frollsum(adaptive=TRUE, c(1:2,NA), c(2,0,2), fill=99, algo="exact", na.rm=TRUE), c(99,0,2))
1052+
test(6001.281, frollapply(FUN=sum, as.numeric(1:3), 0), c(0,0,0))
1053+
test(6001.282, frollapply(FUN=sum, as.numeric(1:3), 0, fill=99), c(0,0,0))
1054+
test(6001.283, frollapply(FUN=sum, c(1:2,NA_real_), 0), c(0,0,0))
1055+
test(6001.284, frollapply(FUN=sum, c(1:2,NA_real_), 0, na.rm=TRUE), c(0,0,0))
1056+
test(6001.2910, frollapply(FUN=sum, adaptive=TRUE, as.numeric(1:3), c(2,0,2)), c(NA,0,5))
1057+
test(6001.2911, frollapply(FUN=sum, adaptive=TRUE, list(as.numeric(1:3), as.numeric(2:4)), c(2,0,2)), list(c(NA,0,5), c(NA,0,7)))
1058+
test(6001.2912, frollapply(FUN=sum, adaptive=TRUE, as.numeric(1:3), list(c(2,0,2), c(0,2,0))), list(c(NA,0,5), c(0,3,0)))
1059+
test(6001.2913, frollapply(FUN=sum, adaptive=TRUE, list(as.numeric(1:3), as.numeric(2:4)), list(c(2,0,2), c(0,2,0))), list(c(NA,0,5), c(0,3,0), c(NA,0,7), c(0,5,0)))
1060+
test(6001.292, frollapply(FUN=sum, adaptive=TRUE, as.numeric(1:3), c(2,0,2), fill=99), c(99,0,5))
1061+
test(6001.293, frollapply(FUN=sum, adaptive=TRUE, c(1:2,NA_real_), c(2,0,2)), c(NA,0,NA))
1062+
test(6001.294, frollapply(FUN=sum, adaptive=TRUE, c(1:2,NA_real_), c(2,0,2), na.rm=TRUE), c(NA,0,2))
1063+
1064+
test(6001.311, frollmax(1:3, 0), c(-Inf,-Inf,-Inf), options=c("datatable.verbose"=TRUE), output="window width of size 0")
1065+
test(6001.312, frollmax(1:3, 0, fill=99), c(-Inf,-Inf,-Inf))
1066+
test(6001.313, frollmax(c(1:2,NA), 0), c(-Inf,-Inf,-Inf))
1067+
test(6001.314, frollmax(c(1:2,NA), 0, na.rm=TRUE), c(-Inf,-Inf,-Inf))
1068+
test(6001.315, frollmax(1:3, 0, algo="exact"), c(-Inf,-Inf,-Inf), options=c("datatable.verbose"=TRUE), output="window width of size 0")
1069+
test(6001.316, frollmax(c(1:2,NA), 0, algo="exact"), c(-Inf,-Inf,-Inf))
1070+
test(6001.317, frollmax(c(1:2,NA), 0, algo="exact", na.rm=TRUE), c(-Inf,-Inf,-Inf))
1071+
test(6001.321, frollmax(adaptive=TRUE, 1:3, c(2,0,2)), c(NA,-Inf,3))
1072+
test(6001.322, frollmax(adaptive=TRUE, 1:3, c(2,0,2), fill=99), c(99,-Inf,3))
1073+
test(6001.323, frollmax(adaptive=TRUE, c(1:2,NA), c(2,0,2)), c(NA,-Inf,NA))
1074+
test(6001.324, frollmax(adaptive=TRUE, c(1:2,NA), c(2,0,2), na.rm=TRUE), c(NA,-Inf,2))
1075+
test(6001.325, frollmax(adaptive=TRUE, 1:3, c(2,0,2), algo="exact"), c(NA,-Inf,3))
1076+
test(6001.326, frollmax(adaptive=TRUE, 1:3, c(2,0,2), fill=99, algo="exact"), c(99,-Inf,3))
1077+
test(6001.327, frollmax(adaptive=TRUE, c(1:2,NA), c(2,0,2), algo="exact"), c(NA,-Inf,NA))
1078+
test(6001.328, frollmax(adaptive=TRUE, c(1:2,NA), c(2,0,2), algo="exact", na.rm=TRUE), c(NA,-Inf,2))
1079+
test(6001.329, frollmax(adaptive=TRUE, c(1:2,NA), c(2,0,2), fill=99, algo="exact", na.rm=TRUE), c(99,-Inf,2))
1080+
test(6001.381, frollapply(FUN=max, 1:3, 0), c(-Inf,-Inf,-Inf))
1081+
test(6001.382, frollapply(FUN=max, 1:3, 0, fill=99), c(-Inf,-Inf,-Inf))
1082+
test(6001.383, frollapply(FUN=max, c(1:2,NA_real_), 0), c(-Inf,-Inf,-Inf))
1083+
test(6001.384, frollapply(FUN=max, c(1:2,NA_real_), 0, na.rm=TRUE), c(-Inf,-Inf,-Inf))
1084+
test(6001.3910, frollapply(FUN = max, adaptive = TRUE, as.numeric(1:3), c(2,0,2)), c(NA, -Inf, 3))
1085+
test(6001.3911, frollapply(FUN=max, adaptive=TRUE, list(as.numeric(1:3), as.numeric(2:4)), c(2,0,2)), list(c(NA,-Inf,3), c(NA,-Inf,4)))
1086+
test(6001.3912, frollapply(FUN=max, adaptive=TRUE, as.numeric(1:3), list(c(2,0,2), c(0,2,0))), list(c(NA,-Inf,3), c(-Inf,2,-Inf)))
1087+
test(6001.3913, frollapply(FUN=max, adaptive=TRUE, list(as.numeric(1:3), as.numeric(2:4)), list(c(2,0,2), c(0,2,0))), list(c(NA,-Inf,3), c(-Inf,2,-Inf), c(NA,-Inf,4), c(-Inf,3,-Inf)))
1088+
test(6001.392, frollapply(FUN=max, adaptive=TRUE, as.numeric(1:3), c(2,0,2), fill=99), c(99,-Inf,3))
1089+
test(6001.393, frollapply(FUN=max, adaptive=TRUE, c(1:2,NA_real_), c(2,0,2)), c(NA,-Inf,NA))
1090+
test(6001.394, frollapply(FUN=max, adaptive=TRUE, c(1:2,NA_real_), c(2,0,2), na.rm=TRUE), c(NA,-Inf,2))
1091+
10091092
## partial
10101093
x = 1:6/2
10111094
n = 3

man/froll.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
}
2626
\arguments{
2727
\item{x}{ Vector, \code{data.frame} or \code{data.table} of integer, numeric or logical columns over which to calculate the windowed aggregations. May also be a list, in which case the rolling function is applied to each of its elements. }
28-
\item{n}{ Integer vector giving rolling window size(s). This is the \emph{total} number of included values in aggregate function. Adaptive rolling functions also accept a list of integer vectors when applying multiple window sizes. }
28+
\item{n}{ Integer, non-negative, vector giving rolling window size(s). This is the \emph{total} number of included values in aggregate function. Adaptive rolling functions also accept a list of integer vectors when applying multiple window sizes. }
2929
\item{fill}{ Numeric; value to pad by. Defaults to \code{NA}. }
3030
\item{algo}{ Character, default \code{"fast"}. When set to \code{"exact"}, a slower (but more accurate) algorithm is used. It
3131
suffers less from floating point rounding errors by performing an extra pass, and carefully handles all non-finite values.

man/frollapply.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
}
1313
\arguments{
1414
\item{X}{ Atomic vector, \code{data.frame}, \code{data.table} or \code{list}. When \code{by.column=TRUE} (default) then a non-atomic \code{X} is processed as \emph{vectorized} input, so rolling function is calculated for each column/vector (non-atomic columns/vectors are not supported). When \code{by.column=FALSE} then \code{X} expects to be a data.frame, data.table or a list of equal length vectors (non-atomic columns/vectors are not supported), so rolling function is calculated for \code{X} as data.frame/data.table/list rather than atomic vector. It supports \emph{vectorized} input as well, passing list of data.frames/data.tables, but not list of lists. }
15-
\item{N}{ Integer vector giving rolling window size(s). This is the \emph{total} number of included values in aggregate function. Adaptive rolling functions also accept a list of integer vectors when applying multiple window sizes, see \code{adaptive} argument description for details. In both \code{adaptive} cases \code{N} may also be a list, supporting \emph{vectorized} input, then rolling function is calculated for each element of the list. }
15+
\item{N}{ Integer, non-negative, vector giving rolling window size(s). This is the \emph{total} number of included values in aggregate function. Adaptive rolling functions also accept a list of integer vectors when applying multiple window sizes, see \code{adaptive} argument description for details. In both \code{adaptive} cases \code{N} may also be a list, supporting \emph{vectorized} input, then rolling function is calculated for each element of the list. }
1616
\item{FUN}{ The function to be applied on a subsets of \code{X}. }
1717
\item{\dots}{ Extra arguments passed to \code{FUN}. Note that arguments passed to \dots cannot have same names as arguments of \code{frollapply}. }
1818
\item{by.column}{ Logical. When \code{TRUE} (default) then \code{X} of types list/data.frame/data.table is treated as vectorized input rather an object to apply rolling window on. Setting to \code{FALSE} allows rolling window to be applied on multiple variables, using data.frame, data.table or a list, as a whole. For details see \emph{\code{by.column} argument} section below. }

0 commit comments

Comments
 (0)