Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Hot-deck matching imputation

This notebook demonstrates how to use Microimpute’s Matching imputer to impute values using the hot-deck matching approach.

Variable type support

The matching model can handle any variable type—numerical, categorical, boolean, or mixed. Since it transfers actual observed values from similar donor records rather than generating model-based predictions, it naturally preserves the original data type and distribution of each variable.

The matching model supports iterative imputation with a single object and workflow. Pass a list of imputed_variables with all variables that you hope to impute for and the model will do so without needing to fit and predict for each separately.

Matching class

class Matching(
    matching_hotdeck: MatchingHotdeckFn = nnd_hotdeck_using_rpy2,
    log_level: Optional[str] = "WARNING"
)
ParameterTypeDefault usedDescription
matching_hotdeckMatchingHotdeckFnnnd_hotdeck_using_rpy2Function that performs hot deck matching
log_levelstr“WARNING”Logging verbosity level

fit() method

def fit(
    X_train: pd.DataFrame,
    predictors: List[str],
    imputed_variables: List[str],
    weight_col: Optional[str] = None,
    tune_hyperparameters: bool = False,
    constrained: bool = False,
    constr_alg: str = "lpSolve",
    dist_fun: str = "Gower",
    k: int = 5,
) -> Union[MatchingResults, Tuple[MatchingResults, Dict]]
ParameterTypeDefault usedDescription
X_trainpd.DataFrame-Donor data with predictors and target variables
predictorsList[str]-Column names to use as matching variables
imputed_variablesList[str]-Column names to impute
weight_colstrNoneColumn name for sampling weights
tune_hyperparametersboolFalseEnable automatic hyperparameter tuning
constrainedboolFalseUse constrained matching (each donor used at most once)
constr_algstr“lpSolve”Algorithm for constrained matching: “lpSolve” or “hungarian”
dist_funstr“Gower”Distance function: “Gower”, “Manhattan”, “Euclidean”, etc.
kint5Number of nearest neighbors to consider

It returns a MatchingResults object (or tuple with best hyperparameters if tuning enabled).

MatchingResults.predict() method

def predict(
    X_test: pd.DataFrame,
    quantiles: Optional[List[float]] = None
) -> Dict[float, pd.DataFrame]
ParameterTypeDefault usedDescription
X_testpd.DataFrame-Data to impute (with predictors)
quantilesList[float]NoneQuantiles for output format (values are identical across quantiles)

It returns a dictionary mapping quantiles to DataFrames of imputed values. Note that matching returns the same values for all quantiles since it transfers actual donor values rather than generating distributional predictions.

Loading...
Loading...
Training set size: 353 records
Testing set size: 89 records

Simulating missing data

For this example, we’ll simulate missing data in our test set by removing the values we want to impute.

Loading...

Training and using the Matching imputer

Now we’ll train the Matching imputer and use it to impute the missing values in our test set.

Modeling these quantiles: [np.float64(0.05), np.float64(0.1), np.float64(0.15), np.float64(0.2), np.float64(0.25), np.float64(0.3), np.float64(0.35), np.float64(0.4), np.float64(0.45), np.float64(0.5), np.float64(0.55), np.float64(0.6), np.float64(0.65), np.float64(0.7), np.float64(0.75), np.float64(0.8), np.float64(0.85), np.float64(0.9), np.float64(0.95)]
Loading...

Evaluating the imputation results

Now let’s compare the imputed values with the actual values to evaluate the performance of our imputer. Matching does not have the ability to adapt its prediction to specific quantiles, which means no matter which quantile we select, we will obtain the same results

Loading...

This scatter plot presents the performance of a matching-based imputation method by comparing the actual values (x-axis) to the imputed values (y-axis). Each dot represents a data point where a missing value was imputed using the nearest matched donor based on covariates. The red dashed line represents the ideal scenario of perfect prediction, where imputed values would exactly match actual values. Unlike model-based approaches such as Quantile Regression or Random Forests, the matching method shows greater dispersion around the ideal line, with several imputed values either overestimating or underestimating the true values. The scatter reveals that while some imputations are close to accurate, many are not, and the overall alignment with the perfect prediction line is weaker. This suggests that matching may introduce higher imputation error, especially when suitable matches are not available or when the matching algorithm doesn’t capture complex relationships between covariates and the missing variable.

Examining quantile predictions

The Matching imputer can also provide predictions at different quantiles, which can be useful for understanding the uncertainty in the imputation.

Loading...

Visualizing prediction intervals

By visualizing the prediction intervals of the model’s imputations we can better understand the uncertainty in our imputed values.

Loading...

This plot displays the prediction intervals produced by a Matching model for total serum cholesterol values across ten data records. Each red dot indicates the imputed median value (Q50) for a missing observation, while black dots represent the corresponding true values. Light gray and dark gray vertical bars would represent the 80% (Q10–Q90) and 40% (Q30–Q70) prediction intervals, respectively. Unlike model-based methods, all records lack visible interval bars entirely. This reflects the limited variability inherent in matching methods, where each imputed value is drawn from a single matched donor or a small set of similar units. As a result, the model cannot capture the full uncertainty of the imputed values, as all quantile estimates collapse to the same value. Additionally, many of the imputed medians lie far from the actual values. This highlights a key limitation of matching-based imputation: while simple and interpretable, it may lack the flexibility to accurately quantify uncertainty or represent the underlying distribution, especially in complex or high-variance data.

Assesing the method’s performance

To check whether our model is overfitting and ensure robust results we can perform cross-validation and visualize the results.

[Parallel(n_jobs=1)]: Done   1 tasks      | elapsed:    0.1s
[Parallel(n_jobs=1)]: Done   4 tasks      | elapsed:    0.2s
Quantile loss results for numerical variables:
           0.05      0.10      0.15      0.20      0.25  ...     0.75      0.80      0.85      0.90      0.95
train  0.000000  0.000000  0.000000  0.000000  0.000000  ...  0.00000  0.000000  0.000000  0.000000  0.000000
test   0.024143  0.024089  0.024035  0.023982  0.023928  ...  0.02339  0.023336  0.023282  0.023229  0.023175

[2 rows x 19 columns]

Log loss results for categorical variables:
Mean train: nan
Mean test: nan
[Parallel(n_jobs=1)]: Done   5 out of   5 | elapsed:    0.3s finished
Loading...

We can observe how train loss will be 0 in this case, as each record will find itself in the training data as a perfect match, which is not the case for testing data. Note that when using donor and receiver data sets that are different from each other this will not occur.

Tuning the Matching model

The Matching imputer supports various parameters that can be adjusted to improve performance. To set specific values you know increase performance for your specific dataset see below. Additionally, automatic hyperparameter tunning specific to the target dataset is enabled by setting the parameter tune_hyperparameters to True.

{'dist_fun': 'Manhattan', 'k': 6}