Skip to content

Commit dd875ac

Browse files
committed
updated changes
1 parent 94649d5 commit dd875ac

5 files changed

Lines changed: 97 additions & 28 deletions

File tree

R/shift.R

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ shift = function(x, n=1L, fill, type=c("lag", "lead", "shift", "cyclic"), give.n
2626
ans
2727
}
2828

29-
nafill = function(x, type=c("const","locf","nocb"), fill=NA, nan=NA) {
29+
nafill = function(x, type=c("const","locf","nocb"), fill=NA, nan=NA, limit=Inf) {
3030
type = match.arg(type)
31-
.Call(CnafillR, x, type, fill, nan_is_na(nan), FALSE, NULL)
31+
if (!is.numeric(limit) || length(limit) != 1L || limit < 0)
32+
stopf("limit must be a non-negative scalar numeric or Inf")
33+
.Call(CnafillR, x, type, fill, nan_is_na(nan), FALSE, NULL, as.double(limit))
3234
}
3335

34-
setnafill = function(x, type=c("const","locf","nocb"), fill=NA, nan=NA, cols=seq_along(x)) {
36+
setnafill = function(x, type=c("const","locf","nocb"), fill=NA, nan=NA, cols=seq_along(x), limit=Inf) {
3537
type = match.arg(type)
36-
invisible(.Call(CnafillR, x, type, fill, nan_is_na(nan), TRUE, cols))
38+
if (!is.numeric(limit) || length(limit) != 1L || limit < 0)
39+
stopf("limit must be a non-negative scalar numeric or Inf")
40+
invisible(.Call(CnafillR, x, type, fill, nan_is_na(nan), TRUE, cols, as.double(limit)))
3741
}

inst/tests/tests.Rraw

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21831,3 +21831,9 @@ test(2378.92, .Call(CresizeVector, x, 2:3), error = "must be length 1 non-NA non
2183121831
test(2378.93, .Call(CresizeVector, x, -2L), error = "must be length 1 non-NA non-negative integer value")
2183221832
test(2378.94, .Call(CresizeVector, x, NA_integer_), error = "must be length 1 non-NA non-negative integer value")
2183321833
test(2378.95, .Call(CresizeVector, NULL, 2L), error = "must be a vector")
21834+
21835+
# #7677: add limit argument to nafill()
21836+
test(2379.01, nafill(c(1, NA, NA, NA, 5), type="locf", limit=1), c(1, 1, NA, NA, 5))
21837+
test(2379.02, nafill(c(1, NA, NA, 5, NA, NA, NA, 9), type="locf", limit=2), c(1, 1, 1, 5, 5, 5, NA, 9))
21838+
test(2379.03, nafill(c(1, NA, NA, 4, NA), type="locf", limit=Inf), c(1, 1, 1, 4, 4))
21839+
test(2379.04, nafill(c(NA, NA, 3, NA, NA), type="locf", limit=1), c(NA, NA, 3, 3, NA))

man/nafill.Rd

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
Fast fill missing values using constant value, \emph{last observation carried forward} or \emph{next observation carried backward}.
1111
}
1212
\usage{
13-
nafill(x, type=c("const", "locf", "nocb"), fill=NA, nan=NA)
14-
setnafill(x, type=c("const", "locf", "nocb"), fill=NA, nan=NA, cols=seq_along(x))
13+
nafill(x, type=c("const", "locf", "nocb"), fill=NA, nan=NA, limit=Inf)
14+
setnafill(x, type=c("const", "locf", "nocb"), fill=NA, nan=NA, cols=seq_along(x), limit=Inf)
1515
}
1616
\arguments{
1717
\item{x}{ Vector, list, data.frame or data.table of logical, numeric or character columns. }
1818
\item{type}{ Character, one of \emph{"const"}, \emph{"locf"} or \emph{"nocb"}. Defaults to \code{"const"}. }
1919
\item{fill}{ Value to be used to replace missing observations. See examples. }
2020
\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. }
2121
\item{cols}{ Numeric or character vector specifying columns to be updated. }
22+
\item{limit}{ The maximum number of consecutive \code{NA} values to fill. Must be a non-negative scalar numeric. Default is \code{Inf}. }
2223
}
2324
\details{
2425
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}.
@@ -51,6 +52,12 @@ nafill(dt, "nocb")
5152

5253
setnafill(dt, "locf", cols=c("v2","v3"))
5354
dt
55+
56+
# limit= restricts the number of consecutive fills
57+
y = c(1, NA, NA, NA, 5)
58+
nafill(y, "locf", limit=1) # Only fills the first NA
59+
nafill(y, "locf", limit=2) # Fills the first two NAs
60+
5461
}
5562
\seealso{
5663
\code{\link{shift}}, \code{\link{data.table}}, \code{\link{fcoalesce}}

src/data.table.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,11 @@ SEXP copyAsGrowable(SEXP x);
311311
SEXP resizeVector(SEXP x, SEXP size);
312312

313313
// nafill.c
314-
void nafillDouble(double *x, uint_fast64_t nx, unsigned int type, double fill, bool nan_is_na, ans_t *ans, bool verbose);
315-
void nafillInteger(int32_t *x, uint_fast64_t nx, unsigned int type, int32_t fill, ans_t *ans, bool verbose);
316-
SEXP nafillR(SEXP obj, SEXP type, SEXP fill, SEXP nan_is_na_arg, SEXP inplace, SEXP cols);
314+
void nafillDouble(double *x, uint_fast64_t nx, unsigned int type, double fill, bool nan_is_na, ans_t *ans, bool verbose, double limit);
315+
void nafillInteger(int32_t *x, uint_fast64_t nx, unsigned int type, int32_t fill, ans_t *ans, bool verbose, double limit);
316+
void nafillInteger64(int64_t *x, uint_fast64_t nx, unsigned int type, int64_t fill, ans_t *ans, bool verbose, double limit);
317+
void nafillString(const SEXP *x, uint_fast64_t nx, unsigned int type, SEXP fill, ans_t *ans, bool verbose, double limit);
318+
SEXP nafillR(SEXP obj, SEXP type, SEXP fill, SEXP nan_is_na_arg, SEXP inplace, SEXP cols, SEXP limit);
317319

318320
// between.c
319321
SEXP between(SEXP x, SEXP lower, SEXP upper, SEXP incbounds, SEXP NAbounds, SEXP check);

src/nafill.c

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "data.table.h"
22

3-
void nafillDouble(double *x, uint_fast64_t nx, unsigned int type, double fill, bool nan_is_na, ans_t *ans, bool verbose) {
3+
void nafillDouble(double *x, uint_fast64_t nx, unsigned int type, double fill, bool nan_is_na, ans_t *ans, bool verbose, double limit) {
44
double tic=0.0;
55
if (verbose)
66
tic = omp_get_wtime();
@@ -15,34 +15,52 @@ void nafillDouble(double *x, uint_fast64_t nx, unsigned int type, double fill, b
1515
}
1616
}
1717
} else if (type==1) { // locf
18+
uint_fast64_t fills = 0;
1819
if (nan_is_na) {
1920
ans->dbl_v[0] = ISNAN(x[0]) ? fill : x[0];
21+
if (ISNAN(x[0])) fills = 1;
2022
for (uint_fast64_t i=1; i<nx; i++) {
21-
ans->dbl_v[i] = ISNAN(x[i]) ? ans->dbl_v[i-1] : x[i];
23+
if (ISNAN(x[i])) {
24+
if (fills < limit) { ans->dbl_v[i] = ans->dbl_v[i-1]; fills++; }
25+
else ans->dbl_v[i] = x[i];
26+
} else { ans->dbl_v[i] = x[i]; fills = 0; }
2227
}
2328
} else {
2429
ans->dbl_v[0] = ISNA(x[0]) ? fill : x[0];
30+
if (ISNA(x[0])) fills = 1;
2531
for (uint_fast64_t i=1; i<nx; i++) {
26-
ans->dbl_v[i] = ISNA(x[i]) ? ans->dbl_v[i-1] : x[i];
32+
if (ISNA(x[i])) {
33+
if (fills < limit) { ans->dbl_v[i] = ans->dbl_v[i-1]; fills++; }
34+
else ans->dbl_v[i] = x[i];
35+
} else { ans->dbl_v[i] = x[i]; fills = 0; }
2736
}
2837
}
2938
} else if (type==2) { // nocb
39+
uint_fast64_t fills = 0;
3040
if (nan_is_na) {
3141
ans->dbl_v[nx-1] = ISNAN(x[nx-1]) ? fill : x[nx-1];
42+
if (ISNAN(x[nx-1])) fills = 1;
3243
for (int_fast64_t i=nx-2; i>=0; i--) {
33-
ans->dbl_v[i] = ISNAN(x[i]) ? ans->dbl_v[i+1] : x[i];
44+
if (ISNAN(x[i])) {
45+
if (fills < limit) { ans->dbl_v[i] = ans->dbl_v[i+1]; fills++; }
46+
else ans->dbl_v[i] = x[i];
47+
} else { ans->dbl_v[i] = x[i]; fills = 0; }
3448
}
3549
} else {
3650
ans->dbl_v[nx-1] = ISNA(x[nx-1]) ? fill : x[nx-1];
51+
if (ISNA(x[nx-1])) fills = 1;
3752
for (int_fast64_t i=nx-2; i>=0; i--) {
38-
ans->dbl_v[i] = ISNA(x[i]) ? ans->dbl_v[i+1] : x[i];
53+
if (ISNA(x[i])) {
54+
if (fills < limit) { ans->dbl_v[i] = ans->dbl_v[i+1]; fills++; }
55+
else ans->dbl_v[i] = x[i];
56+
} else { ans->dbl_v[i] = x[i]; fills = 0; }
3957
}
4058
}
4159
}
4260
if (verbose)
4361
snprintf(ans->message[0], 500, _("%s: took %.3fs\n"), __func__, omp_get_wtime()-tic);
4462
}
45-
void nafillInteger(int32_t *x, uint_fast64_t nx, unsigned int type, int32_t fill, ans_t *ans, bool verbose) {
63+
void nafillInteger(int32_t *x, uint_fast64_t nx, unsigned int type, int32_t fill, ans_t *ans, bool verbose, double limit) {
4664
double tic=0.0;
4765
if (verbose)
4866
tic = omp_get_wtime();
@@ -51,20 +69,30 @@ void nafillInteger(int32_t *x, uint_fast64_t nx, unsigned int type, int32_t fill
5169
ans->int_v[i] = x[i]==NA_INTEGER ? fill : x[i];
5270
}
5371
} else if (type==1) { // locf
72+
uint_fast64_t fills = 0;
5473
ans->int_v[0] = x[0]==NA_INTEGER ? fill : x[0];
74+
if (x[0]==NA_INTEGER) fills = 1;
5575
for (uint_fast64_t i=1; i<nx; i++) {
56-
ans->int_v[i] = x[i]==NA_INTEGER ? ans->int_v[i-1] : x[i];
76+
if (x[i]==NA_INTEGER) {
77+
if (fills < limit) { ans->int_v[i] = ans->int_v[i-1]; fills++; }
78+
else ans->int_v[i] = x[i];
79+
} else { ans->int_v[i] = x[i]; fills = 0; }
5780
}
5881
} else if (type==2) { // nocb
82+
uint_fast64_t fills = 0;
5983
ans->int_v[nx-1] = x[nx-1]==NA_INTEGER ? fill : x[nx-1];
84+
if (x[nx-1]==NA_INTEGER) fills = 1;
6085
for (int_fast64_t i=nx-2; i>=0; i--) {
61-
ans->int_v[i] = x[i]==NA_INTEGER ? ans->int_v[i+1] : x[i];
86+
if (x[i]==NA_INTEGER) {
87+
if (fills < limit) { ans->int_v[i] = ans->int_v[i+1]; fills++; }
88+
else ans->int_v[i] = x[i];
89+
} else { ans->int_v[i] = x[i]; fills = 0; }
6290
}
6391
}
6492
if (verbose)
6593
snprintf(ans->message[0], 500, _("%s: took %.3fs\n"), __func__, omp_get_wtime()-tic);
6694
}
67-
void nafillInteger64(int64_t *x, uint_fast64_t nx, unsigned int type, int64_t fill, ans_t *ans, bool verbose) {
95+
void nafillInteger64(int64_t *x, uint_fast64_t nx, unsigned int type, int64_t fill, ans_t *ans, bool verbose, double limit) {
6896
double tic=0.0;
6997
if (verbose)
7098
tic = omp_get_wtime();
@@ -73,21 +101,31 @@ void nafillInteger64(int64_t *x, uint_fast64_t nx, unsigned int type, int64_t fi
73101
ans->int64_v[i] = x[i]==NA_INTEGER64 ? fill : x[i];
74102
}
75103
} else if (type==1) { // locf
104+
uint_fast64_t fills = 0;
76105
ans->int64_v[0] = x[0]==NA_INTEGER64 ? fill : x[0];
106+
if (x[0]==NA_INTEGER64) fills = 1;
77107
for (uint_fast64_t i=1; i<nx; i++) {
78-
ans->int64_v[i] = x[i]==NA_INTEGER64 ? ans->int64_v[i-1] : x[i];
108+
if (x[i]==NA_INTEGER64) {
109+
if (fills < limit) { ans->int64_v[i] = ans->int64_v[i-1]; fills++; }
110+
else ans->int64_v[i] = x[i];
111+
} else { ans->int64_v[i] = x[i]; fills = 0; }
79112
}
80113
} else if (type==2) { // nocb
114+
uint_fast64_t fills = 0;
81115
ans->int64_v[nx-1] = x[nx-1]==NA_INTEGER64 ? fill : x[nx-1];
116+
if (x[nx-1]==NA_INTEGER64) fills = 1;
82117
for (int_fast64_t i=nx-2; i>=0; i--) {
83-
ans->int64_v[i] = x[i]==NA_INTEGER64 ? ans->int64_v[i+1] : x[i];
118+
if (x[i]==NA_INTEGER64) {
119+
if (fills < limit) { ans->int64_v[i] = ans->int64_v[i+1]; fills++; }
120+
else ans->int64_v[i] = x[i];
121+
} else { ans->int64_v[i] = x[i]; fills = 0; }
84122
}
85123
}
86124
if (verbose)
87125
snprintf(ans->message[0], 500, _("%s: took %.3fs\n"), __func__, omp_get_wtime()-tic);
88126
}
89127

90-
void nafillString(const SEXP *x, uint_fast64_t nx, unsigned int type, SEXP fill, ans_t *ans, bool verbose) {
128+
void nafillString(const SEXP *x, uint_fast64_t nx, unsigned int type, SEXP fill, ans_t *ans, bool verbose, double limit) {
91129
double tic=0.0;
92130
if (verbose)
93131
tic = omp_get_wtime();
@@ -96,16 +134,26 @@ void nafillString(const SEXP *x, uint_fast64_t nx, unsigned int type, SEXP fill,
96134
SET_STRING_ELT(ans->char_v, i, x[i]==NA_STRING ? fill : x[i]);
97135
}
98136
} else if (type==1) { // locf
137+
uint_fast64_t fills = 0;
99138
SET_STRING_ELT(ans->char_v, 0, x[0]==NA_STRING ? fill : x[0]);
139+
if (x[0]==NA_STRING) fills = 1;
100140
const SEXP* thisans = SEXPPTR_RO(ans->char_v); // takes out STRING_ELT from loop
101141
for (uint_fast64_t i=1; i<nx; i++) {
102-
SET_STRING_ELT(ans->char_v, i, x[i]==NA_STRING ? thisans[i-1] : x[i]);
142+
if (x[i]==NA_STRING) {
143+
if (fills < limit) { SET_STRING_ELT(ans->char_v, i, thisans[i-1]); fills++; }
144+
else SET_STRING_ELT(ans->char_v, i, x[i]);
145+
} else { SET_STRING_ELT(ans->char_v, i, x[i]); fills = 0; }
103146
}
104147
} else if (type==2) { // nocb
148+
uint_fast64_t fills = 0;
105149
SET_STRING_ELT(ans->char_v, nx-1, x[nx-1]==NA_STRING ? fill : x[nx-1]);
150+
if (x[nx-1]==NA_STRING) fills = 1;
106151
const SEXP* thisans = SEXPPTR_RO(ans->char_v); // takes out STRING_ELT from loop
107152
for (int_fast64_t i=nx-2; i>=0; i--) {
108-
SET_STRING_ELT(ans->char_v, i, x[i]==NA_STRING ? thisans[i+1] : x[i]);
153+
if (x[i]==NA_STRING) {
154+
if (fills < limit) { SET_STRING_ELT(ans->char_v, i, thisans[i+1]); fills++; }
155+
else SET_STRING_ELT(ans->char_v, i, x[i]);
156+
} else { SET_STRING_ELT(ans->char_v, i, x[i]); fills = 0; }
109157
}
110158
}
111159
if (verbose)
@@ -117,7 +165,7 @@ void nafillString(const SEXP *x, uint_fast64_t nx, unsigned int type, SEXP fill,
117165
over columns of the input data. This includes handling different data types
118166
and applying the designated filling method to each column in parallel.
119167
*/
120-
SEXP nafillR(SEXP obj, SEXP type, SEXP fill, SEXP nan_is_na_arg, SEXP inplace, SEXP cols) {
168+
SEXP nafillR(SEXP obj, SEXP type, SEXP fill, SEXP nan_is_na_arg, SEXP inplace, SEXP cols, SEXP limit) {
121169
int protecti=0;
122170
const bool verbose = GetVerbose();
123171

@@ -128,6 +176,8 @@ SEXP nafillR(SEXP obj, SEXP type, SEXP fill, SEXP nan_is_na_arg, SEXP inplace, S
128176
if (verbose)
129177
tic = omp_get_wtime();
130178

179+
double limit_val = REAL(limit)[0];
180+
131181
bool copy = !LOGICAL(inplace)[0];
132182
if (!IS_TRUE_OR_FALSE(nan_is_na_arg))
133183
error(_("'%s' must be TRUE or FALSE"), "nan_is_na"); // # nocov
@@ -253,16 +303,16 @@ SEXP nafillR(SEXP obj, SEXP type, SEXP fill, SEXP nan_is_na_arg, SEXP inplace, S
253303
switch (TYPEOF(VECTOR_ELT(x, i))) {
254304
case REALSXP : {
255305
if (isInt64[i]) {
256-
nafillInteger64(i64x[i], inx[i], itype, hasFill ? ((int64_t *)fillp[i])[0] : NA_INTEGER64, &vans[i], verbose);
306+
nafillInteger64(i64x[i], inx[i], itype, hasFill ? ((int64_t *)fillp[i])[0] : NA_INTEGER64, &vans[i], verbose, limit_val);
257307
} else {
258-
nafillDouble(dx[i], inx[i], itype, hasFill ? ((double *)fillp[i])[0] : NA_REAL, nan_is_na, &vans[i], verbose);
308+
nafillDouble(dx[i], inx[i], itype, hasFill ? ((double *)fillp[i])[0] : NA_REAL, nan_is_na, &vans[i], verbose, limit_val);
259309
}
260310
} break;
261311
case LGLSXP: case INTSXP : {
262-
nafillInteger(ix[i], inx[i], itype, hasFill ? ((int32_t *)fillp[i])[0] : NA_INTEGER, &vans[i], verbose);
312+
nafillInteger(ix[i], inx[i], itype, hasFill ? ((int32_t *)fillp[i])[0] : NA_INTEGER, &vans[i], verbose, limit_val);
263313
} break;
264314
case STRSXP : {
265-
nafillString(sx[i], inx[i], itype, hasFill ? ((SEXP *)fillp[i])[0] : NA_STRING, &vans[i], verbose);
315+
nafillString(sx[i], inx[i], itype, hasFill ? ((SEXP *)fillp[i])[0] : NA_STRING, &vans[i], verbose, limit_val);
266316
} break;
267317
}
268318
}

0 commit comments

Comments
 (0)