An open API service indexing awesome lists of open source software.

https://github.com/ielbadisy/mimar

Compact R tools for missing-data analysis, multiple imputation, diagnostic evaluation, and post-imputation pooling.
https://github.com/ielbadisy/mimar

data-analysis imputation machine-learning missing-data multiple-imputation r r-package statistics

Last synced: about 9 hours ago
JSON representation

Compact R tools for missing-data analysis, multiple imputation, diagnostic evaluation, and post-imputation pooling.

Awesome Lists containing this project

README

          

# mimar

`mimar` implements a compact chained-imputation workflow in R for
missing-data analysis, artificial amputation, native and learner-backed
single and multiple imputation, diagnostic evaluation, and
post-imputation pooling.

The package is built around a complete missing-data workflow: describe
the missingness, create benchmark amputations when needed, impute with
native or learner-backed update rules, inspect diagnostics, evaluate
recovered cells when truth is available, and pool post-fit quantities.
The goal is a concise grammar for the whole workflow, not a replacement
for every specialist feature in larger imputation systems.

The package owns the imputation loop. Every imputer, whether implemented
natively or backed by a learner package, is called the same way:

``` r
impute(data, imputer = "pmm", m = 5, maxit = 5, seed = 1)
impute(data, imputer = "rf", m = 5, seed = 1)
impute(data, imputer = "xgboost", m = 5, seed = 1)
```

There is no dependency on `funcml`. Learner-backed imputers call their
original packages directly, and those backend packages are hard
dependencies so users can run any registered imputer without manually
resolving learner installations.

## Installation

Install the released version from CRAN:

``` r
install.packages("mimar")
```

The CRAN package page is .

You can install the development version from GitHub:

``` r
install.packages("remotes")
remotes::install_github("ielbadisy/mimar")
```

Then load the package:

``` r
library(mimar)
```

## Quick use

For normal use, `impute()` is the only function you need. The input data
can contain `NA`, and the completed outputs returned by `complete()` do
not. Set `verbose = TRUE` when you want a concise progress log for the
chained imputation workflow.

``` r
i <- impute(a, imputer = "knn", m = 5, maxit = 5, seed = 1)
complete(i, 1)
complete(i, "all")
```

## Grammar

``` r
describe()
ampute()
imputer_registry()
imputer()
impute()
complete()
evaluate()
pool()
plot()
```

## Short example

``` r
library(mimar)

set.seed(1)
dat <- data.frame(
age = rnorm(120, 50, 10),
bmi = rnorm(120, 25, 4),
sex = factor(sample(c("F", "M"), 120, TRUE)),
group = factor(sample(c("A", "B", "C"), 120, TRUE)),
smoker = sample(c(TRUE, FALSE), 120, TRUE)
)

a <- ampute(
dat,
prop = 0.25,
mechanism = "MAR",
target = c("bmi", "group"),
by = c("age", "sex"),
seed = 1
)

i <- impute(a, imputer = "knn", m = 5, maxit = 5, seed = 1, ncore = 2)
complete(i, 1)
```

## # A tibble: 120 × 5
## age bmi sex group smoker
##
## 1 43.7 23.0 M C FALSE
## 2 51.8 30.4 F A FALSE
## 3 41.6 24.1 F B TRUE
## 4 66.0 24.3 F C TRUE
## 5 53.3 24.6 F A FALSE
## 6 41.8 27.9 M C TRUE
## 7 54.9 24.7 M B TRUE
## 8 57.4 24.8 F B FALSE
## 9 55.8 22.3 F B FALSE
## 10 46.9 30.8 M A FALSE
## # ℹ 110 more rows

``` r
summary(i)
```

## mimar imputation summary
## # A tibble: 1 × 11
## rows columns n_imputations imputer maxit ncore stochastic
##
## 1 120 5 5 knn 5 2 TRUE
## # ℹ 4 more variables: total_missing_before , total_imputed ,
## # remaining_missing , variables_imputed
##
## Variables:
## # A tibble: 5 × 9
## variable type method n_missing_before prop_missing_before n_imputed
##
## 1 age numeric none 0 0 0
## 2 bmi numeric knn 26 0.217 26
## 3 sex factor none 0 0 0
## 4 group factor knn 27 0.225 27
## 5 smoker logical none 0 0 0
## # ℹ 3 more variables: prop_imputed , remaining_missing ,
## # between_imputation_sd

``` r
evaluate(i)
```

## mimar imputation evaluation
## # A tibble: 1 × 4
## n_imputations imputer total_missing evaluated_cells
##
## 1 5 knn 53 53

``` r
plot(i, type = "density")
```

![](README_files/figure-gfm/unnamed-chunk-1-1.png)

## Imputers

Inspect available imputers with:

``` r
imputer_registry()
```

## # A tibble: 23 × 10
## imputer implementation package supports_numeric supports_binary
##
## 1 mean mimar internal TRUE TRUE
## 2 median mimar internal TRUE TRUE
## 3 mode mimar internal TRUE TRUE
## 4 naive mimar internal TRUE TRUE
## 5 norm mimar internal TRUE TRUE
## 6 pmm mimar internal TRUE TRUE
## 7 spmm mimar internal TRUE TRUE
## 8 logreg mimar internal TRUE TRUE
## 9 polyreg mimar internal TRUE TRUE
## 10 rf wrapped ranger TRUE TRUE
## # ℹ 13 more rows
## # ℹ 5 more variables: supports_multiclass , stochastic ,
## # description , available , status

``` r
describe("imputers")
```

## mimar available imputers
## # A tibble: 23 × 10
## imputer implementation package supports_numeric supports_binary
##
## 1 mean mimar internal TRUE TRUE
## 2 median mimar internal TRUE TRUE
## 3 mode mimar internal TRUE TRUE
## 4 naive mimar internal TRUE TRUE
## 5 norm mimar internal TRUE TRUE
## 6 pmm mimar internal TRUE TRUE
## 7 spmm mimar internal TRUE TRUE
## 8 logreg mimar internal TRUE TRUE
## 9 polyreg mimar internal TRUE TRUE
## 10 rf wrapped ranger TRUE TRUE
## # ℹ 13 more rows
## # ℹ 5 more variables: supports_multiclass , stochastic ,
## # description , available , status

Core native imputers:

- `mean`, `median`, `mode`
- `naive`: median/mode chained baseline
- `norm`: linear normal draw
- `pmm`, `spmm`: predictive mean matching
- `logreg`: binary logistic regression draw
- `polyreg`: one-vs-rest multinomial draw
- `knn`: nearest-neighbor donor imputation
- `hotdeck`: stochastic donor imputation

Learner-backed imputers:

- `rf`: MissForest-style chained random forest imputer through `ranger`
- `ranger`: random forest through `ranger`
- `rpart`: tree imputer through `rpart`
- `nbayes`: naive Bayes through `naivebayes`
- `svm`: support vector machine through `e1071`
- `bart`: Bayesian additive regression trees through `BART`
- `glmnet`: penalized regression through `glmnet`
- `gbm`: gradient boosting through `gbm`
- `xgboost`: gradient boosted trees through `xgboost`
- `famd`: FAMD-assisted donor imputation through `missMDA`
- `superlearner`, `sl`: cross-validated Super Learner-style ensemble
imputer

Imputer names are strict: use the names shown by `imputer_registry()`.
Learner-backed imputers are applied as requested to numeric, binary, and
multiclass targets; `mimar` does not silently swap them for another
imputer inside benchmark runs.

## Parallel imputation

The `ncore` argument runs independent completed datasets in parallel.
The parallel boundary is the outer imputation index: each completed
dataset gets a deterministic seed offset, so a fixed `seed`, `m`,
`maxit`, and imputer remain reproducible.

``` r
i <- impute(a, imputer = "knn", m = 5, maxit = 5, seed = 1, ncore = 2)
```

Use `ncore = 1` for sequential execution, small examples, and the most
conservative behavior in constrained environments.

## Tuning imputers

Learner-backed imputers expose their hyperparameters through `imputer()`
or directly through `...` in `impute()`. Donor-based imputers use the
explicit `donors` argument.

``` r
rf_spec <- imputer("rf", num.trees = 500)
xgb_spec <- imputer("xgboost", nrounds = 100, max_depth = 3)

i1 <- impute(a, imputer = rf_spec, m = 5, maxit = 5, seed = 1)
i2 <- impute(a, imputer = "xgboost", m = 5, maxit = 5, seed = 1,
nrounds = 100, max_depth = 3)
i3 <- impute(a, imputer = "knn", m = 5, maxit = 5, seed = 1, donors = 10)
```

The same hyperparameter set is reused across all incomplete variables
that a given imputer supports, which keeps the full chained-imputation
pipeline reproducible and easy to tune.

## Super Learner imputation

`superlearner` combines candidate imputers by cross-validating them on
observed cells, assigning non-negative loss-based weights, and using the
weighted ensemble inside the chained-imputation loop.

``` r
sl <- imputer(
"superlearner",
library = c("pmm", "knn", "rpart"),
folds = 5,
metalearner = "inverse_loss"
)

i_sl <- impute(a, imputer = sl, m = 5, maxit = 5, seed = 1)
```

The short alias `imputer = "sl"` is equivalent to
`imputer = "superlearner"`.

## Diagnostic Plots

`plot()` methods return `ggplot` objects. For `mimar_imputation`
objects, the main diagnostic types are:

``` r
plot(i) # imputed cell counts
```

![](README_files/figure-gfm/unnamed-chunk-6-1.png)

``` r
plot(i, type = "missing") # observed/imputed cell map
```

![](README_files/figure-gfm/unnamed-chunk-6-2.png)

``` r
plot(i, type = "trace", statistic = "mean") # convergence-screening trace
```

![](README_files/figure-gfm/unnamed-chunk-6-3.png)

``` r
plot(i, type = "density", variable = "bmi") # line-only density overlays
```

![](README_files/figure-gfm/unnamed-chunk-6-4.png)

``` r
plot(i, type = "boxplot", variable = "bmi") # observed vs imputation 1:m
```

![](README_files/figure-gfm/unnamed-chunk-6-5.png)

``` r
plot(i, type = "strip", variable = "bmi") # individual values by imputation
```

![](README_files/figure-gfm/unnamed-chunk-6-6.png)

Formula diagnostics are available for bivariate and categorical checks:

``` r
plot(i, type = "xy", formula = bmi ~ age | sex)
```

![](README_files/figure-gfm/unnamed-chunk-7-1.png)

``` r
plot(i, type = "proportion", formula = group ~ sex)
```

![](README_files/figure-gfm/unnamed-chunk-7-2.png)

For `type = "xy"`, formulas use `y ~ x` or `y ~ x | group`. For
`type = "proportion"`, formulas use `categorical_variable ~ strata`.
Density diagnostics use line-only overlays so several imputations remain
visible rather than obscuring each other with filled areas.

## Chained Imputation Model

Let `X` be an `n x p` data frame and let `R_ij = 1` when cell `(i, j)`
is missing. For each incomplete variable `X_j`:

- `O_j = {i : R_ij = 0}` are the observed rows
- `M_j = {i : R_ij = 1}` are the missing rows

At each chained update, `mimar` fits an imputer-specific model from the
observed rows and then predicts the missing rows from the current
completed data. In compact form:

``` text
fit model on X_-j, O_j -> X_j, O_j
update X_j, M_j using the fitted model
```

Multiple imputation repeats the same chained procedure `m` times with
controlled seeds, bootstrap samples of observed rows, and stochastic
prediction where supported.

Learner-backed imputers are practical stochastic update rules inside
this chained workflow. They can improve predictive recovery, but users
should still inspect trace, distribution, categorical-proportion, and
downstream sensitivity diagnostics rather than assuming every learner
automatically supplies proper multiple-imputation uncertainty for every
analysis.

## Algorithm

``` text
Input: X, R, h, m, T
Initialize: X~(0) <- init(X)
For k = 1,...,m:
X~_k(0) <- X~(0)
For t = 1,...,T:
For each incomplete variable j:
B_j <- bootstrap sample of O_j
fit h on X~_k, B_j, -j and X_Bj,j
update missing rows M_j using the fitted model
restore observed rows O_j to their original values
Return: {X~_1(T), ..., X~_m(T)}
```

## Evaluation

When imputation is run on an `ampute()` object, `evaluate()` uses the
retained truth and scores only artificially removed cells. Numeric
recovery reports RMSE, MAE, bias, and correlation. Categorical recovery
reports accuracy and balanced accuracy.

## Pooling

`pool()` combines post-fit quantities estimated separately in each
completed dataset. The statistical target is the quantity itself, not a
data frame. A quantity can be a scalar, coefficient vector,
covariance-aware parameter vector, matrix of survival probabilities, or
a scalar metric. Data frames are accepted only as a tidy adapter for
scalar model output.

For survival-probability matrices, `pool_survmat()` applies the
complementary log-log transform internally, pools on that scale, and
back-transforms the result.

For scalar quantities with complete-data variance estimates, `pool()`
applies Rubin-style pooling:

``` text
Q_bar = mean(Q_k)
U_bar = mean(U_k)
B = sample variance of Q_k
T = U_bar + (1 + 1/m) * B
```

``` r
results <- data.frame(
term = rep(c("age", "bmi"), each = 3),
estimate = c(0.10, 0.11, 0.09, 0.30, 0.32, 0.29),
std.error = c(0.04, 0.05, 0.04, 0.08, 0.09, 0.08),
imputation = rep(1:3, times = 2)
)

pool(results)
```

## mimar pooled results
## # A tibble: 2 × 14
## term estimate std.error statistic df p.value conf.low conf.high m
##
## 1 age 0.1 0.0451 2.22 465. 0.0271 0.0114 0.189 3
## 2 bmi 0.303 0.0853 3.56 1094. 0.000393 0.136 0.471 3
## # ℹ 5 more variables: within_variance , between_variance ,
## # total_variance , relative_increase_variance , rule

Direct quantity inputs are preferred when available:

``` r
pool(c(0.10, 0.11, 0.09), std.error = c(0.04, 0.05, 0.04), name = "age")
```

## mimar pooled results
## # A tibble: 1 × 14
## term estimate std.error statistic df p.value conf.low conf.high m
##
## 1 age 0.1 0.0451 2.22 465. 0.0271 0.0114 0.189 3
## # ℹ 5 more variables: within_variance , between_variance ,
## # total_variance , relative_increase_variance , rule

``` r
betas <- list(
c(age = 0.10, bmi = 0.30),
c(age = 0.11, bmi = 0.32),
c(age = 0.09, bmi = 0.29)
)
covariances <- list(
diag(c(0.04, 0.08)^2),
diag(c(0.05, 0.09)^2),
diag(c(0.04, 0.08)^2)
)
pool(betas, covariance = covariances)
```

## mimar pooled results
## # A tibble: 2 × 14
## term estimate std.error statistic df p.value conf.low conf.high m
##
## 1 age 0.1 0.0451 2.22 465. 0.0271 0.0114 0.189 3
## 2 bmi 0.303 0.0853 3.56 1094. 0.000393 0.136 0.471 3
## # ℹ 5 more variables: within_variance , between_variance ,
## # total_variance , relative_increase_variance , rule

When no reliable complete-data variance is supplied, as is common for
some performance metrics, `pool()` reports robust summaries by default:
median, interquartile range, and range across imputations.

## Installation notes

Learner backends are hard dependencies. Installing `mimar` installs the
packages needed by the registered learner-backed imputers, including
`ranger`, `rpart`, `naivebayes`, `e1071`, `BART`, `glmnet`, `gbm`,
`xgboost`, and `missMDA`.