I am trying to apply a simple function in a rolling manner to a vector in R using the rollapply
function from the zoo
package. However, I am encountering an unexpected behavior where the result has multiple columns instead of the expected single column.
Here is my code:
library(zoo)
set.seed(101)
foo <- data.frame("V1" = sample(seq(-5, 5), replace = TRUE, size = 100))
result <- rollapply(foo$V1,
width = 5,
FUN = function(x) ifelse(x < 0, "Yes", "No"),
align = "right",
by.column = FALSE,
fill = NA)
head(result)
I expected result
to be a single column vector containing the "Yes" and "No" values for each rolling window of 5 elements in the "V1" column. However, the output I get is a matrix with 6 rows and 5 columns instead.
Can someone please help me understand why I am getting multiple columns in the result and how I can modify the code to obtain a single column result as intended?
Thank you in advance for your assistance!
Dirty way to solve the issue:
# Loop through the elements in foo$V1 using a rolling window of 5
for (i in 1:(length(foo$V1) - 4)) {
# Check if there is a negative number in the current 5-element window
foo$V2[i + 4] <- any(foo$V1[i:(i + 4)] < 0)
}
You should use any()
to return a single value from a length 5 input.
rollapply(foo$V1,
width = 5, FUN = function(x) ifelse(any(x < 0), "Yes", "No"),
align = "right", fill = NA)
# [1] NA NA NA NA "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes"
# [21] "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "No" "No" "No" "No"
# [41] "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes"
# [61] "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes"
# [81] "Yes" "Yes" "Yes" "Yes" "Yes" "No" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes"
As suggested by @G.Grothendieck, you can use rollapplyr
(with suffix r
) to skip align = "right"
, and move ifelse()
outside to avoid calling it repeatedly.
rollapplyr(foo$V1 < 0, width = 5, FUN = any, fill = NA) |>
ifelse("Yes", "No")