--- title: "Getting started with missknn" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with missknn} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Overview `missknn` imputes missing values in tabular data with a masked k-nearest-neighbor engine: distances are computed only on jointly observed variables, the compute-heavy steps (distance, neighbor ranking, aggregation) run in C++, and each column's neighborhood size and estimator are chosen automatically from a small internal holdout evaluation rather than fixed globally. ```{r setup} library(missknn) ``` ## Single imputation ```{r} set.seed(1) x <- data.frame( a = c(1, 2, NA, 4, 5), b = c(2, NA, 3, 4, 6), g = factor(c("x", "x", "y", NA, "y")) ) imp <- missknn(x, k = 2, m = 1) missknn::complete(imp) ``` `missknn()` detects numeric and categorical (factor/character/logical) columns automatically, and imputes both from the same masked-distance neighborhood: numeric targets get a distance-weighted mean or a local regression fit (whichever validates better for that column), categorical targets get a distance-weighted majority vote. ### A note on `complete()` `complete()` is a common generic name - packages like `mimar` or `tidyr` may also define one. If another attached package's `complete()` shadows `missknn`'s, call it explicitly: ```{r, eval = FALSE} missknn::complete(imp) ``` ## Multiple imputation Setting `m > 1` draws `m` completed datasets by sampling (rather than averaging) from each receiver's nearest donors, using the same distance model throughout: ```{r} imp_mi <- missknn(x, k = 2, m = 5, seed = 1) length(missknn::complete(imp_mi)) ``` ## Per-column adaptive tuning Rather than using one fixed `k` for every column, `missknn()` runs a small internal holdout evaluation per column to choose: - how many neighbors (`k`) to use, - whether a distance-weighted mean or a local weighted-regression fit predicts held-out values better, and - whether the column has any locally exploitable signal at all -- if not, it is filled directly from the global mean/mode, skipping neighbor search entirely for that column. This is inspected via the fitted object's `meta`: ```{r} imp$meta$k_per_col imp$meta$estimator_per_col imp$meta$is_global_col ``` ## Why this matters for larger data Naive KNN imputation searches every donor for every missing value: cost grows quadratically with the number of rows. `missknn` bounds the neighbor-search candidate pool with `donor_cap` (default 2000), so cost stays close to linear in `n` even for large datasets, while the per-column global fast path above further skips neighbor search for columns where it wouldn't help anyway. See `vignette` companions in `inst/benchmark/` for runtime and accuracy comparisons against `missForest`, `missRanger`, and `VIM::kNN`, including a scaling study from n = 1,000 to n = 100,000. ## Options at a glance ```{r, eval = FALSE} missknn( data, k = 5L, # default neighbor count (tuned per column) m = 1L, # 1 = single imputation, >1 = multiple imputation scale = TRUE, # standardize numeric variables before distances weights = "distance", # "distance" (inverse-distance) or "uniform" numeric_estimator = "regression", # "regression" or "mean" donor_cap = 2000L, # cap on the neighbor-search candidate pool add_indicator = FALSE, # append missingness indicator columns seed = NULL # reproducible multiple imputation ) ```