Repository navigation
nafill new type: approx #4066
Copy link
Copy link
Open
Labels
Description
This is a FR. Also I apologize ahead of time if this is not an appropriate post for here.
From this StackOverflow post:
library(data.table)
dt <- data.table(dist = c(31091.33, NA, 31100.00, 31103.27, NA, NA, NA, NA, 31124.98))
dt[, time := .I]
## dist time
##1: 31091.33 1
##2: NA 2
##3: 31100.00 3
##4: 31103.27 4
##5: NA 5
##6: NA 6
##7: NA 7
##8: NA 8
##9: 31124.98 9
OP wants to do a linear interpolation to fill in the NA values. The most simple solution is to use base approx() or zoo:na.approx() per user dww:
copy(dt)[, dist := zoo::na.approx(dist)][]
copy(dt)[, dist := approx(.I, dist, .I)$y][]
# dist time
#1: 31091.33 1
#2: 31095.67 2
#3: 31100.00 3
#4: 31103.27 4
#5: 31107.61 5
#6: 31111.95 6
#7: 31116.30 7
#8: 31120.64 8
#9: 31124.98 9
The current nafill types are: c("const", "locf", "nocb") - an "approx" type would be helpful. I wrote an Rcpp implementation to show that there is room for improvement as it's ~10 times faster than the base method which is already a C based method:
#data per @chinsoon12
nr <- 1e7
nNA <- nr/2
DT <- data.table(time=1:nr, dist=replace(rnorm(nr), sample(1:nr, nNA), NA_real_))
bench::mark(
rcpp_appr = rcpp_approx(DT[['dist']]),
base_approx = DT[, approx(.I, dist, .I)$y],
zoo_approx = zoo::na.approx(DT[['dist']]),
time_unit = 's'
)
# A tibble: 3 x 13 (in seconds)
expression min median `itr/sec` mem_alloc
<bch:expr> <dbl> <dbl> <dbl> <bch:byt>
1 rcpp_appr 0.126 0.132 7.08 76MB
2 base_approx 1.34 1.34 0.745 617MB
3 zoo_approx 2.22 2.22 0.450 1630MB ##manually changed from GB to MB
# this is the rcpp code
Rcpp::sourceCpp(code = '
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector rcpp_approx(NumericVector y) {
double start = 0, slope = 0;
int count = 0;
NumericVector y1 = clone(y); //added to not update-by-reference
for(int i = 0; i < y1.size(); ++i){
if (NumericVector::is_na(y1[i])){
count++;
} else {
if (count != 0) {
start = y1[i - (count+1)];
slope = (y1[i] - start) / (count + 1);
for (int j = 0; j < count; j++){
y1[i-(count-j)] = start + slope * (j + 1);
}
count = 0;
}
}
}
return(y1);
}
')
Thank you for your consideration.
Reactions are currently unavailable
Activity
MichaelChirico commentedon Nov 20, 2019
Agree linear interpolation is common enough we could potentially support. Could you help to clarify though:
The
zooversion doesn't use.I, and neither version uses yourtimecolumn. I also think maybe the example is a bit too simple sincetimeis always increasing by one, and there are no duplicates intime, could you add some expected output in more complicated scenarios? Is this doing "local linear" interpolation (find the "nearest" non-NAvalues and draw a line between them), or is it "global linear" (we fit a 2-D [with constant] regression on all non-NAvalues andpredictthe fill value)?Also worth mentioning that IINM this would be an API departure since it requires two input columns (the other
types only need one) -- the "target" column and the "reference" column.jangorecki commentedon Nov 21, 2019
It is proper place to fill FR. Thanks
Related to #3241, in case where we want to fill approx NAs just to apply rolling function on that, after #3241 we wouldn't need to materialize approximated values.
ColeMiller1 commentedon Nov 21, 2019
The examples of the original post were for simple "local linear" interpolation. If you are open for 2D implementation, I could see it as:
Examples:
Regarding rolling joins, chinsoon12 provided two additional methods, one with rolling joins and the other with
nafill. The rolling join method was approximately the same speed asbase::approx()jangorecki commentedon Nov 21, 2019
When presenting benchmarks please use same unit for all approaches. Showing seconds and milliseconds values for the same measure is unnecessarily confusing.
ColeMiller1 commentedon Nov 22, 2019
Updated all benchmarks with one unit and included the full implementation of
Rcppfor reference.One thing to note for any 2D interpolation is that the class of
xmatters as well so that would be one more thing to deal with. This shows up in the benchmarks as it appears the template forNumericVectorwill cause a deep copy if you are sending an integer R object (...I think).hope-data-science commentedon Mar 15, 2020
May I ask when can
nafillandsetnafillsupport filling non-numeric variables?jangorecki commentedon Mar 15, 2020
@hope-data-science no ETA, unless there is open PR already, so we can prioritise it to include for next release.
hope-data-science commentedon Mar 15, 2020
I do have a prototype using R codes:
They work, but not fast. I think they might be faster in C. And these codes could be more efficient too.
hope-data-science commentedon Mar 17, 2020
I've got a new one ('down' for LOCF, 'up' for NOCB):
This should be faster, especially when there are shorter NA gaps.
tdhock commentedon May 5, 2020
@hope-data-science nafill for non-numeric values is discussed in #3992 , which has a workaround
hope-data-science commentedon May 6, 2020
Thank you for noting. Can you wrap it in a function? I have actually, in tidyfst::fill_na_dt. But I'm not sure if it is fast enough.
Thanks.