diff --git a/NEWS.md b/NEWS.md index 150540389..60362dd1f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -40,6 +40,8 @@ 9. The `give.names` argument of rolling functions (`froll*()`, `frollapply()`, and `frolladapt()`) now accepts a character vector to directly specify output names, [#5744](https://github.com/Rdatatable/data.table/issues/5744). Thanks to @jangorecki for the suggestion and @ben-schwen for the implementation. +10. `setnafill()` now accepts a logical vector for the `cols` argument, which must be the same length as the number of columns in `x`, [#4113](https://github.com/Rdatatable/data.table/issues/4113). Thanks to @MichaelChirico for the suggestion and @venom1204 for the PR. + ### BUG FIXES 1. `fread()` with `skip=0` and `(header=TRUE|FALSE)` no longer skips the first row when it has fewer fields than subsequent rows, [#7463](https://github.com/Rdatatable/data.table/issues/7463). Thanks @emayerhofer for the report and @ben-schwen for the fix. diff --git a/R/shift.R b/R/shift.R index 1c68d13c4..56ef2a209 100644 --- a/R/shift.R +++ b/R/shift.R @@ -33,5 +33,11 @@ nafill = function(x, type=c("const","locf","nocb"), fill=NA, nan=NA) { setnafill = function(x, type=c("const","locf","nocb"), fill=NA, nan=NA, cols=seq_along(x)) { type = match.arg(type) + if (!is.list(x)) stop("in-place update is supported only for list", call. = FALSE) + if (is.logical(cols)) { + if (length(cols) != length(x)) stopf("'cols' is a logical vector of length %d but there are %d columns", length(cols), length(x)) + if (anyNA(cols)) stopf("'cols' contains NA at position %d", which(is.na(cols))[1L]) + cols = which(cols) + } invisible(.Call(CnafillR, x, type, fill, nan_is_na(nan), TRUE, cols)) } diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 915d6f464..74dce2d97 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21868,3 +21868,11 @@ DT2 = copy(DT) test(2381.2, truelength(DT2$y[[1L]]) > 0L) test(2381.3, DT2$y[[1L]][, b := 2][, b], 2) rm(inner, DT, DT2) + +# #4113 setnafill could accept cols=logical(ncol(x)) +test(2382.01, {DT = data.table(a=c(1,NA,3), b=c(4,NA,6), c=c(7,NA,9)); DT1 = copy(DT); DT2 = copy(DT); setnafill(DT1, type="locf", cols=c(TRUE,FALSE,TRUE)); setnafill(DT2, type="locf", cols=c(1L,3L)); identical(DT1, DT2)}, TRUE) +test(2382.02, {DT = data.table(a=c(1,NA), b=c(2,NA), c=c(3,NA)); DT1 = copy(DT); DT2 = copy(DT); setnafill(DT1, type="locf", cols=c(TRUE,FALSE,TRUE)); setnafill(DT2, type="locf", cols=c("a","c")); identical(DT1, DT2)}, TRUE) +test(2382.03, {DT = data.table(a=c(1,NA), b=c(2,NA)); before = copy(DT); setnafill(DT, type="locf", cols=c(FALSE,FALSE)); identical(DT, before)}, TRUE) +test(2382.04, {DT = data.table(a=c(1,NA), b=c("x",NA), c=c(3,NA)); cols = sapply(DT, is.numeric); setnafill(DT, type="locf", cols=cols); DT}, data.table(a=c(1,1), b=c("x",NA), c=c(3,3))) +test(2382.05, setnafill(data.table(a=1,b=2,c=3), type="locf", cols=c(TRUE,NA,FALSE)), error="'cols' contains NA at position 2") +test(2382.06, setnafill(data.table(a=1,b=2,c=3), type="locf", cols=c(TRUE,FALSE)), error="'cols' is a logical vector of length 2 but there are 3 columns") diff --git a/man/nafill.Rd b/man/nafill.Rd index 90c4b1c5c..af04ff4f0 100644 --- a/man/nafill.Rd +++ b/man/nafill.Rd @@ -18,7 +18,7 @@ setnafill(x, type=c("const", "locf", "nocb"), fill=NA, nan=NA, cols=seq_along(x) \item{type}{ Character, one of \emph{"const"}, \emph{"locf"} or \emph{"nocb"}. Defaults to \code{"const"}. } \item{fill}{ Value to be used to replace missing observations. See examples. } \item{nan}{ Either \code{NaN} or \code{NA}; if the former, \code{NaN} is treated as distinct from \code{NA}, otherwise, they are treated the same during replacement. See Examples. } - \item{cols}{ Numeric or character vector specifying columns to be updated. } + \item{cols}{ Numeric, character or logical vector specifying columns to be updated. A logical vector must be the same length as the number of columns in \code{x}. } } \details{ Supported types are \emph{logical}, \emph{integer}, \emph{double}, \emph{character}, and \emph{factor}, as well as classes built on top of these such as \code{Date}, \code{IDate}, and \code{POSIXct}.