The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
missknn is an R package for fast masked k-nearest
neighbor imputation of missing values in tabular data.
Install the development version from GitHub with:
install.packages("remotes")
remotes::install_github("ielbadisy/missknn")It is fast by design:
k selection and a batched
per-column API that avoids per-cell R/C++ round tripsk and estimator (distance-weighted mean
vs. a local weighted-regression fit) are chosen automatically from a
holdout evaluation, rather than fixed globallydonor_cap bounds the neighbor-search candidate pool
so cost stays roughly linear in n instead of quadratic,
which matters at large ndata.table is used for lightweight tabular
orchestrationm > 1) can run multicore by
default on Unix-like systems via parallel::mclapplyk/estimator tuning and the deterministic (non-stochastic)
neighbor search are parallelized across columns/receivers with
RcppParallel, on all platforms including WindowsIt supports:
data.table-friendly workflowsmissknn is developed and maintained by Imad El Badisy
(elbadisyimad@gmail.com), released under the MIT license.
The current implementation parallelizes the per-column
k/estimator tuning and the deterministic single-imputation
path with RcppParallel, including on Windows. The
m > 1 stochastic path still uses process-level
parallelism via parallel::mclapply on Unix-like
systems.
missknn parallelizes at two independent levels:
m > 1): each of
the m completed datasets is generated in a separate process
via parallel::mclapply, controlled by
parallel_cores (defaults to
detectCores() - 1). This only applies on Unix-like systems;
it falls back to serial on Windows.k and estimator, and the
deterministic (m = 1) neighbor search/aggregation, are
split across threads with RcppParallel::parallelFor -
across target columns for tuning, across receiver rows for imputation.
This runs on every platform, including Windows, and benefits
m = 1 runs that get no benefit from
parallel_cores. The stochastic sampling path used when
m > 1 stays single-threaded per process, since R’s RNG
isn’t thread-safe; that case still parallelizes across the
m processes instead.Thread count for the RcppParallel layer follows the
usual RcppParallel::setThreadOptions() /
RCPP_PARALLEL_NUM_THREADS conventions.
Two benchmark scripts write reports, plots, and CSV tables to
inst/benchmark/output/:
# accuracy/runtime vs. missForest, missMDA, VIM::kNN at n = 200, 30 simulations
Rscript inst/benchmark/benchmark_30.R
# runtime/accuracy vs. missMDA and missForest as n scales from 1,000 to 100,000
Rscript inst/benchmark/benchmark_bigdata.Rmissknn(data, k = 5L, m = 1L, scale = TRUE, add_indicator = FALSE, seed = NULL, weights = c("distance", "uniform"), numeric_estimator = c("regression", "mean"), ridge = 1e-4, donor_cap = 2000L, max_iter = 1L, parallel_cores = missknn_default_cores())Fits masked KNN imputation and returns a missknn
object.
| Argument | Description |
|---|---|
data |
A data.frame or matrix. |
k |
Number of nearest donors used per target column. |
m |
Number of completed datasets. m = 1 is single
(deterministic) imputation; m > 1 is multiple
(stochastic donor-sampling) imputation. |
scale |
Standardize numeric variables before computing distances. |
add_indicator |
Append TRUE/FALSE missingness-indicator
columns to the completed output. |
seed |
Integer seed for reproducible multiple imputation. |
weights |
"distance" (inverse-distance weighted aggregation) or
"uniform" (equal weights) over the k
donors. |
numeric_estimator |
"regression" fits a distance-weighted local linear
regression of the target on the receiver’s observed numeric predictors
over the k donors, falling back to the weighted mean when
the local design is degenerate; "mean" always uses the
weighted mean. |
ridge |
Ridge penalty added to the predictor covariance under
numeric_estimator = "regression". |
donor_cap |
Caps the donor pool searched per target column, subsampling once per
column when exceeded, so search cost stays roughly linear in
n. Does not affect the global mean/mode fallback, which
always uses every donor. |
max_iter |
Number of single-pass refinement iterations over the whole dataset. |
parallel_cores |
Cores used across the m completed datasets when
m > 1 (via parallel::mclapply, Unix-like
only). Defaults to detectCores() - 1. Independent of the
RcppParallel threading used within a single imputation (see
Parallel computing). |
Columns with no locally exploitable signal are detected during fitting and filled from the global mean/mode directly, without a neighbor search.
Returns a missknn object: a list with data
(input as a data.table), completed (one
completed data.table, or a list of m when
m > 1), imputations (always a list of
m), plus the arguments used to fit (k,
m, scale, weights, etc.) and
internal meta.
complete(object, action = c("all", "single"), index = 1L, ...)S3 generic (complete.missknn) that extracts completed
data from a missknn object.
action = "all" (default): returns every completed
dataset (a single data.table when m = 1, a list of
m data.tables when m > 1).action = "single": returns one completed data.table,
selected by index (ignored when m = 1, since
there is only one).print(x) /
summary(object)print.missknn reports n, p,
k, m, scale, and
add_indicator. summary.missknn additionally
reports per-variable and total missing counts
(missing_by_variable, missing_total) and has
its own print.summary.missknn method.
library(missknn)
x <- data.frame(
a = c(1, 2, NA, 4),
b = c(2, NA, 3, 5),
g = factor(c("x", "x", "y", NA))
)
imp <- missknn(x, k = 2, m = 1)
imp
summary(imp)
complete(imp)
# multiple imputation
mi <- missknn(x, k = 2, m = 5, seed = 1)
complete(mi, action = "single", index = 2)Runtime and NRMSE from n = 1,000 up to n = 100,000 (5 numeric
columns, 20% MCAR per column). missForest and
VIM::kNN are only run up to n = 5,000 (per-tree cost and
O(n^2) distance search respectively make larger n impractical);
missknn and missRanger run across the full
grid. Reproduce with:
Rscript inst/benchmark/benchmark_bigdata.R
| n | method | runtime_sec | nrmse |
|---|---|---|---|
| 1,000 | missknn | 0.0131 | 1.00223 |
| 1,000 | missRanger | 0.1630 | 1.15781 |
| 1,000 | missForest | 3.1537 | 1.07461 |
| 1,000 | VIM::kNN | 0.6088 | 1.16132 |
| 5,000 | missknn | 0.0321 | 1.00020 |
| 5,000 | missRanger | 1.3632 | 1.14478 |
| 5,000 | missForest | 75.1881 | 1.03685 |
| 5,000 | VIM::kNN | 10.9076 | 1.15992 |
| 20,000 | missknn | 0.0456 | 0.99998 |
| 20,000 | missRanger | 10.3372 | 1.14941 |
| 100,000 | missknn | 0.0938 | 1.00000 |
| 100,000 | missRanger | 85.6539 | 1.14435 |
missForest’s runtime already becomes impractical at n =
5,000 (over a minute) and would take hours at n = 100,000;
VIM::kNN’s distance search is similarly infeasible past a
few thousand rows. missknn’s capped-donor masked-KNN search
stays close to linear in n and keeps running in well under
a second even at n = 100,000. missRanger also scales to n =
100,000 but at substantially higher runtime.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.