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:
patient_agepatient_sextotal_paid_amountcondition_count(distinct HCC categories documented in prior year)- HCC tier counts by severity level
From core__medical_claim aggregated to patient level:
- Total encounter count
- Distinct rendering provider count
- Inpatient admission count
- ED visit count
- Outpatient encounter count
From financial_pmpm__*:
- Per-member-per-month spend by service category (inpatient, outpatient, professional, pharmacy)
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:
condition_count: monotonically positive — more prior conditions, higher predicted probability of high suspecting gap countpatient_age: increasing after ~68, plateau or modest increase after 80total_paid_amount: increasing with diminishing returns at the high end
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:
- Prior year HCC code list — direct information about which specific conditions to suspect (requires multi-hot encoding or embeddings)
- Days since last primary care visit — recency of care engagement predicts documentation gap closure
- Dual eligibility flag — dual-eligible patients (Medicare + Medicaid) have different utilization patterns that affect HCC documentation rates
- Part D fill count — medication adherence from pharmacy data correlates with overall care engagement
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.