Skip to content
>GLB_
Go back

Feature engineering from claims data for a Random Forest classifier

Claims data has hundreds of potential input signals for patient risk models. Most of them are noisy, correlated, or both. For a Random Forest classifier predicting HCC suspecting gaps, choosing the right four features and excluding the rest is more important than any hyperparameter tuning.

What features are available after a Tuva run

After running the Tuva Project on 167k Medicare claims, the available patient-level features include:

From hcc_suspecting__summary:

From core__medical_claim aggregated to patient level:

From financial_pmpm__*:

Why four features were chosen

patient_age: The single strongest demographic predictor of chronic condition burden. The CMS HCC model itself uses age as a base coefficient. Patients 75+ accumulate HCC conditions faster than patients 65-70. This is non-redundant with condition_count because it captures prospective risk that hasn’t yet materialized in prior claims.

sex_male: Males in Medicare have higher rates of cardiovascular and renal HCC conditions and lower preventive care utilization on average. The correlation with suspecting_gaps is modest but consistent. Binary encoding is correct — Random Forest doesn’t need ordinal encoding for a two-category variable.

total_paid_amount: Proxy for clinical complexity. Higher paid amounts indicate more utilization, which correlates with more diagnosis codes, which correlates with more HCC conditions and thus more potential gaps. Partially redundant with condition_count, but captures utilization intensity beyond just condition count.

condition_count: The most predictive feature. If a patient had 8 active HCC conditions last year, there are 8 conditions that need re-documentation this year. This is the direct upstream driver of suspecting_gaps. Using condition count from the summary table (rather than deriving it from raw claims) means you’re working from the same HCC mapping logic Tuva uses, not a re-derived version.

Features that were considered and excluded

Encounter count: Highly correlated with total_paid_amount. Adding it doesn’t improve cross-validated AUC and increases the risk of fitting to noise.

Distinct provider count: Similarly redundant — more providers generally means more conditions. Already captured by condition_count.

HCC tier counts by severity: These are components of the target variable. A patient’s HCC tier distribution is derived from the same prior-year diagnosis codes that determine suspecting_gaps. Using them as features is data leakage — the model would be predicting Y from Y’s ingredients rather than from truly independent inputs.

PMPM spend by service category: Potentially valuable, but in a 4-feature demo model the marginal improvement is small and the added complexity (multiple correlated spend columns) isn’t worth it.

Constructing the feature table

import duckdb
import pandas as pd

con = duckdb.connect("tuva.duckdb", read_only=True)

df = con.execute("""
    SELECT
        patient_id,
        patient_age,
        CASE WHEN patient_sex = 'M' THEN 1 ELSE 0 END AS sex_male,
        total_paid_amount,
        condition_count,
        suspecting_gaps
    FROM hcc_suspecting__summary
    WHERE patient_age IS NOT NULL
      AND total_paid_amount IS NOT NULL
      AND condition_count IS NOT NULL
""").df()

con.close()

No scaling is needed. Random Forest is invariant to monotonic transformations of features — adding a log1p transform on total_paid_amount (which is right-skewed) doesn’t change the model’s splits or feature importances in any meaningful way.

Validating the features carry real signal

Use partial dependence plots to check that each feature’s relationship with the predicted probability is directionally sensible:

from sklearn.inspection import PartialDependenceDisplay
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(12, 4))
PartialDependenceDisplay.from_estimator(
    clf,
    X_train,
    features=["patient_age", "condition_count"],
    ax=axes,
)
plt.tight_layout()

Expected behavior:

If the partial dependence for a feature is flat or noisy, the feature isn’t contributing. On the synthetic dataset, all four features show the expected directional relationships.

What would improve this in production

The four-feature model achieves ~0.80 accuracy on synthetic data. For real Medicare data, the highest-value additions would be:

These require additional source data beyond what’s in a standard medical claims dataset but would be present in a full Medicare Advantage data environment including pharmacy and eligibility data from the plan.


Share this post:

Related Posts


Previous Post
DuckDB concurrency in 2026: why you can't run dbt and DBeaver at the same time
Next Post
From raw claims to RAF: what the data pipeline actually looks like