How to Visualize Model Performance with Yellowbrick’s Confusion Matrix

How to Visualize Model Performance with Yellowbrick's Confusion Matrix
Image by Editor | Midjourney

Understanding the Dataset

Healthcare fraud detection presents unique modeling challenges due to the inherent class imbalance in claims data. To address these challenges, we previously explored how Yellowbrick’s ClassBalance visualizer could help us understand and handle the imbalanced distribution between fraudulent and non-fraudulent providers. Through this analysis, we examined resampling techniques like SMOTE that could improve our model’s ability to detect potential fraud cases.

We then focused on feature engineering, demonstrating how aggregating individual claims to the provider level could unveil distinctive patterns. Using Yellowbrick’s Parallel Coordinates visualizer, we identified how combinations of inpatient and outpatient claims might signal fraudulent behavior, transforming granular transaction data into meaningful provider-level insights.

Now that we’ve preprocessed our data and engineered informative features, let’s evaluate how well our model can distinguish between fraudulent and legitimate providers. We’ll use Yellowbrick’s ConfusionMatrix visualizer to gain a deeper understanding of our model’s performance beyond simple accuracy metrics.

Our dataset represents 5,410 healthcare providers, with features aggregated from over 550,000 individual claims records. For a detailed breakdown of how we obtained the provider-level dataset from the original data source, please kindly refer to our following posts: 

  1. How to Visualize Class Imbalance with Yellowbrick

  2. How to Analyze Features Using Yellowbrick

Here is a snapshot of the dataset (previously entitled ‘final_df’) where we will begin our exploration: 

 
      Provider  IP_Claims_Total  OP_Claims_Total PotentialFraud
0     PRV51001          97000.0           7640.0             No
1     PRV51003         573000.0          32670.0            Yes
2     PRV51007          19000.0          14710.0             No
3     PRV51008          25000.0          10630.0             No
4     PRV51011           5000.0          11630.0             No
...        ...              ...              ...            ...
5405  PRV57759              0.0          10640.0             No
5406  PRV57760              0.0           4770.0             No
5407  PRV57761              0.0          18470.0             No
5408  PRV57762              0.0           1900.0             No
5409  PRV57763              0.0          43610.0             No

[5410 rows x 4 columns]

Our dataset contains:

  • Provider IDs
  • Total inpatient claims (IP_Claims_Total)
  • Total outpatient claims (OP_Claims_Total)
  • Fraud labels (PotentialFraud)

Data Preprocessing and Target Encoding

Before we can train our model, we need to encode our target variable. The LabelEncoder transforms our categorical ‘Yes’/’No’ fraud labels into binary values (1 for fraud, 0 for non-fraud):

 
from sklearn.preprocessing import LabelEncoder

# Encode the PotentialFraud column as a binary variable
le = LabelEncoder()
final_df["PotentialFraud"] = le.fit_transform(final_df["PotentialFraud"])

Let’s view our encoded dataset:

 
final_df
 
      Provider  IP_Claims_Total  OP_Claims_Total  PotentialFraud
0     PRV51001          97000.0           7640.0              0
1     PRV51003         573000.0          32670.0              1
2     PRV51007          19000.0          14710.0              0
3     PRV51008          25000.0          10630.0              0
4     PRV51011           5000.0          11630.0              0
...        ...              ...              ...            ...
5405  PRV57759              0.0          10640.0              0
5406  PRV57760              0.0           4770.0              0
5407  PRV57761              0.0          18470.0              0
5408  PRV57762              0.0           1900.0              0
5409  PRV57763              0.0          43610.0              0

[5410 rows x 4 columns]

Building Our Initial Model

With our data prepared, we can now train a logistic regression model using our claims totals as features:

 
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# Define features and target variable
X = final_df[["IP_Claims_Total", "OP_Claims_Total"]]
y = final_df["PotentialFraud"]

# Standardize the features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Split the dataset into training and testing sets (80-20 split)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# Train a logistic regression model
log_reg = LogisticRegression(random_state=42)
log_reg.fit(X_train, y_train)

# Predictions
y_pred = log_reg.predict(X_test)

accuracy = accuracy_score(y_test, y_pred)
print(f"**Accuracy:** {accuracy:.4f}\n")
 
**Accuracy:** 0.9325

Our initial model achieves an accuracy of 93.25%, but accuracy alone doesn’t tell the complete story, especially with imbalanced datasets like ours. This is where Yellowbrick’s ConfusionMatrix visualizer becomes invaluable.

Visualizing Model Performance with Confusion Matrix

Yellowbrick makes it remarkably simple to create an insightful confusion matrix visualization. With just a few lines of code, we can transform our model’s predictions into a clear, interpretable format:

 
from yellowbrick.classifier import ConfusionMatrix

cm = ConfusionMatrix(log_reg, classes=['No Fraud', 'Fraud'])
cm.fit(X_train, y_train)
cm.score(X_test, y_test)
cm.finalize()

This image shows a confusion matrix for a logistic regression model, with counts for true positives, true negatives, false positives, and false negatives in fraud prediction.
Image By Author
 

A confusion matrix shows us how well our model classifies each category by breaking down its predictions into four key components:

  • True Negatives (969): Correctly identified non-fraudulent providers
  • True Positives (40): Correctly identified fraudulent providers
  • False Negatives (64): Fraudulent providers misclassified as non-fraudulent
  • False Positives (9): Non-fraudulent providers misclassified as fraudulent

This breakdown verifies our earlier accuracy score of 93.25% ((969 + 40) / (969 + 40 + 64 + 9)), but more importantly, it reveals critical insights about our model’s performance.

Of particular concern is our model’s recall for fraudulent cases. Out of 104 actual fraud cases (40 + 64), our model only correctly identified 40, missing 64 fraudulent providers. This means our recall for fraud detection is only 38.5% (40/104). In a healthcare fraud detection context, this low recall is problematic as it indicates our model fails to identify more than half of the fraudulent providers.

This performance metric is particularly important in fraud detection where the cost of missing fraudulent cases (false negatives) is typically much higher than incorrectly flagging legitimate providers (false positives). A missed fraudulent provider could continue to submit false claims, resulting in significant financial losses.

Conclusion

Yellowbrick’s ConfusionMatrix visualizer effectively translates model performance into clear, interpretable results. By seamlessly integrating with scikit-learn, it helps data scientists understand their models beyond basic accuracy scores. In our healthcare fraud detection example, the visualizer highlighted important gaps in fraud identification that accuracy metrics alone failed to capture, showing the value of comprehensive model evaluation tools.

Leave a Reply

Your email address will not be published. Required fields are marked *