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.

Quantile Regression Forest (QRF) imputation

This notebook demonstrates how to use Microimpute’s QRF imputer to impute values using Quantile Regression Forests. QRF extends traditional random forests to predict the entire conditional distribution of a target variable.

Variable type support

The QRF model automatically handles both numerical and categorical variables. For numerical targets, it applies quantile regression forests. For categorical targets (strings, booleans, or numerically-encoded categorical variables), it switches to using a random forest classifier. This automatic adaptation happens internally without requiring any manual configuration.

The QRF model supports sequential imputation with a single object and workflow. Pass a list of imputed_variables with all variables you want to impute, and the model imputes them sequentially. This means that previously imputed variables will serve as predictors for subsequent variables, capturing complex dependencies between the imputed variables.

How sequential imputation works

  1. Variable 1: Uses only the original predictors

  2. Variable 2: Uses original predictors + Variable 1’s imputed values

  3. Variable 3: Uses original predictors + Variables 1 & 2’s imputed values

  4. And so on...

QRF class

class QRF(
    log_level: Optional[str] = "WARNING",
    memory_efficient: bool = False,
    batch_size: Optional[int] = None,
    cleanup_interval: int = 10
)
ParameterTypeDefault usedDescription
log_levelstr“WARNING”Logging verbosity level
memory_efficientboolFalseEnable memory-efficient mode for large datasets
batch_sizeintNoneBatch size for memory-efficient processing
cleanup_intervalint10Interval for garbage collection in memory-efficient mode

fit() method

def fit(
    X_train: pd.DataFrame,
    predictors: List[str],
    imputed_variables: List[str],
    weight_col: Optional[str] = None,
    tune_hyperparameters: bool = False,
    n_estimators: int = 100,
    max_depth: Optional[int] = None,
    min_samples_split: int = 2,
    min_samples_leaf: int = 1,
    max_features: Union[str, float] = 1.0,
) -> Union[QRFResults, Tuple[QRFResults, Dict]]
ParameterTypeDefault usedDescription
X_trainpd.DataFrame-Training data with predictors and target variables
predictorsList[str]-Column names to use as predictors
imputed_variablesList[str]-Column names to impute
weight_colstrNoneColumn name for sampling weights
tune_hyperparametersboolFalseEnable automatic hyperparameter tuning
n_estimatorsint100Number of trees in the forest
max_depthintNoneMaximum depth of trees (None = unlimited)
min_samples_splitint2Minimum samples to split a node
min_samples_leafint1Minimum samples at a leaf node
max_featuresstr or float1.0Features to consider for best split

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

QRFResults.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 at which to return predictions

It returns a dictionary mapping quantiles to DataFrames of imputed values. QRF learns the full conditional distribution empirically, providing flexible quantile predictions that can capture skewness and heteroskedasticity.

Setup and data preparation

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 QRF imputer

Now we’ll train the QRF 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. To understand QRF’s power to capture variability across quantiles, let’s find and plot the prediction closest to the true value across quantiles for each data point.

Loading...

This scatter plot compares actual observed values with those imputed by a Quantile Regression Forest (QRF) model, providing a visual assessment of imputation accuracy. Each point represents a data record, with the x-axis showing the true value and the y-axis showing the model’s predicted value. The red dashed line represents the ideal 1:1 relationship, where predictions perfectly match actual values. Most points cluster around this line, suggesting that the QRF model effectively captures the underlying structure of the data. Importantly, the model does not appear to systematically over- or under-predict across the range, and while performance at the extremes may be weaker, the overall pattern indicates that QRF provides a reasonably accurate and unbiased approach to imputing missing values. Additionally, it is important to consider the characteristics of the diabetes dataset, which seems to show a strong linear relationship between predictors and the imputed variable. QRF’s behavior suggests strength in accurately imputing variables for datasets when such linearity assumptions do not hold.

Examining quantile predictions

QRF provides predictions at different quantiles, allowing us to capture the entire conditional distribution of the missing values.

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 visualizes the prediction intervals produced by the Quantile Regression Forest (QRF) model for imputing total serum cholesterol values across ten data records. Each vertical bar represents an 80% (light gray) or 40% (dark gray) prediction interval, capturing the model’s estimated range of plausible values based on the Q10–Q90 and Q30–Q70 quantiles, respectively. Red dots mark the model’s median predictions (Q50), while black dots show the actual observed values. In most cases, the true values fall within the wider intervals, indicating that the QRF model is appropriately capturing uncertainty in its imputation. The fact that the intervals are sometimes asymmetrical around the median reflects the model’s flexibility in estimating skewed or heteroskedastic distributions. Overall, the plot demonstrates that the QRF model not only provides accurate point estimates but also yields informative prediction intervals that account for uncertainty in the imputed values.

Assessing the method’s performance

To verify our model doesn’t overfit and ensure robust results, we can perform cross-validation and visualize the results.

[Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers.
[Parallel(n_jobs=-1)]: Done   2 out of   5 | elapsed:   19.2s remaining:   28.7s
[Parallel(n_jobs=-1)]: Done   3 out of   5 | elapsed:   19.2s remaining:   12.8s
[Parallel(n_jobs=-1)]: Done   5 out of   5 | elapsed:   19.3s finished
Quantile loss results:
           0.05      0.10      0.15      0.20      0.25  ...      0.75      0.80      0.85      0.90      0.95
train  0.001967  0.003999  0.005333  0.007202  0.008227  ...  0.005756  0.005267  0.004522  0.003761  0.002310
test   0.004520  0.007870  0.011132  0.013861  0.016690  ...  0.018722  0.016499  0.013710  0.010794  0.006778

[2 rows x 19 columns]
Loading...

Tuning the QRF model

The QRF imputer supports various parameters that you can adjust to improve performance. To set specific values you know improve performance for your dataset, see below. Additionally, automatic hyperparameter tuning specific to the target dataset is available by setting the parameter tune_hyperparameters to True.

{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'default_quantiles': 0.5, 'max_depth': None, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None, 'max_samples_leaf': 1, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 5, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100, 'n_jobs': None, 'oob_score': False, 'random_state': 42, 'verbose': 0, 'warm_start': False}
{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'default_quantiles': 0.5, 'max_depth': 5, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None, 'max_samples_leaf': 1, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 10, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 200, 'n_jobs': None, 'oob_score': False, 'random_state': 42, 'verbose': 0, 'warm_start': False}

Categorical variable imputation

QRF automatically handles categorical variables through random forest classification. Let’s evaluate its performance on categorical imputation tasks.

Categorical variable distribution:
risk_level
medium    150
low       148
high      144
Name: count, dtype: int64

Percentage distribution:
risk_level
medium    0.339367
low       0.334842
high      0.325792
Name: proportion, dtype: float64

Data types: {'age': dtype('float64'), 'sex': dtype('float64'), 'bmi': dtype('float64'), 'bp': dtype('float64'), 'risk_level': <StringDtype(na_value=nan)>}

Training set size: 353 records
Testing set size: 89 records
QRF model fitted for categorical variable imputation

Assessing categorical imputation performance

We can look at the accuracy of the model’s predictions to understand the quality of its categorical imputations. Cross-validation will employ log loss to evaluate the performance of the random forest classifier method used.

Categorical imputation accuracy: 44.94%

Confusion matrix:
                Predicted: low  Predicted: medium  Predicted: high
Actual: low                 14                  4               12
Actual: medium               8                 13                8
Actual: high                 4                 13               13
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers.
[Parallel(n_jobs=-1)]: Done   2 out of   5 | elapsed:    3.9s remaining:    5.8s
[Parallel(n_jobs=-1)]: Done   3 out of   5 | elapsed:    4.1s remaining:    2.7s
[Parallel(n_jobs=-1)]: Done   5 out of   5 | elapsed:    4.4s finished
Categorical imputation cross-validation results (log loss):
Mean train log loss: 0.2666
Mean test log loss: 1.2308
Loading...