Python Data Visualization, SQL Connectivity & EDA
Back to HIGH-IMPACT VISUAL STORYTELLING: MATPLOTLIB & SEABORN

Designing Executive Correlation Heatmaps

Guide to computing df.corr(), masking diagonal triangles, and styling diverging correlation heatmaps with Seaborn.

6 minutesReading
๐ŸŽฏ Free Guest Mode: You are learning for free. Sign in to save your completion progress and quiz answers.

Do These Metrics Move Together? The Correlation Heatmap

Your stakeholder asks: "If discount goes up, does revenue go up or down? And does that hold across order count, tenure, and average order value?" You have 5 metrics and that is 10 pairwise correlations to check. A heatmap answers all 10 in one glance โ€” but only if you build it so a non-technical reader can read it. This article builds that heatmap from a tiny table you can verify by eye.

1. The Data: Same Customers, 5 Metrics

One row per customer โ€” the kind of summary you computed after the groupby in Module 3:

customer_idrevenueorder_countavg_order_valdiscount_ratetenure_months
C0011,27434250.088
C0021,38034600.0514
C003902450.252
C00468051360.126
C0052,10073000.0418

You can already suspect: customers with high discount_rate (C003: 0.25) have the lowest revenue. A correlation matrix makes that suspicion a number.

2. What Pearson r Means โ€” Three Sentences

Pearson r measures how linearly two columns move together, on a scale from โˆ’1 to +1:

rMeaningExample from the table
+1.0Perfect uphill line โ€” when one goes up, the other always goes uprevenue vs order_count โ€” more orders โ†’ more revenue (positive)
0.0No linear relationship โ€” knowing one tells you nothing about the othertenure_months vs discount_rate โ€” roughly 0 in this data
โˆ’1.0Perfect downhill line โ€” when one goes up, the other always goes downdiscount_rate vs avg_order_val โ€” higher discount โ†’ smaller basket (negative)

In practice most r values land between โˆ’1 and +1: |r| > 0.7 strong, 0.3โ€“0.7 moderate, < 0.3 weak. An r of 0.85 (Quiz 3 Q08) is strong positive โ€” higher spend strongly associated with higher revenue, but association is not causation.

3. From Table to Matrix: What df.corr() Gives You

df[['revenue','order_count','avg_order_val','discount_rate','tenure_months']].corr() returns a 5ร—5 grid. The diagonal is always 1.00 (a column correlates perfectly with itself). The grid is symmetric โ€” r of (revenue, discount) equals r of (discount, revenue). That symmetry is why we hide half of it next.

revenueorder_countavg_order_valdiscount_ratetenure
revenue1.000.920.74โˆ’0.680.81
order_count0.921.000.51โˆ’0.420.77
avg_order_val0.740.511.00โˆ’0.610.45
discount_rateโˆ’0.68โˆ’0.42โˆ’0.611.00โˆ’0.35
tenure0.810.770.45โˆ’0.351.00

Warm (red) = positive, cool (blue) = negative โ€” that color choice is not decoration, it is the next section.

4. Making It Executive-Ready: Color, Mask, Annotation

Three tweaks turn the raw matrix into a deck-ready chart:

TweakParameterWhy
Diverging palettecmap='coolwarm', vmin=-1, vmax=1Zero is white/neutral; positives go red, negatives go blue โ€” the eye instantly separates "discount hurts revenue" from "orders drive revenue"
Hide the duplicate trianglemask=np.triu(np.ones_like(corr))Upper triangle mirrors the lower โ€” masking it cuts clutter by 50% (Quiz 2 Q15)
Print the number in each cellannot=True, fmt='.2f'Executives want the exact r without hovering (Quiz 2 Q08)
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

corr = df[['revenue', 'order_count', 'avg_order_val', 'discount_rate', 'tenure_months']].corr()

plt.figure(figsize=(9, 7))
mask = np.triu(np.ones_like(corr, dtype=bool))  # True = hide this cell

sns.heatmap(
    corr,
    mask=mask,                # cut the redundant upper triangle
    annot=True, fmt='.2f',    # write r inside each cell
    cmap='coolwarm',          # red = positive, blue = negative, white = 0
    vmin=-1.0, vmax=1.0,      # lock scale so colors mean the same in every report
    square=True,              # square cells look cleaner in decks
    linewidths=0.5,           # thin white grid between cells
    cbar_kws={'label': 'Pearson r'}
)
plt.title('Customer Behavioral Correlation Matrix', fontsize=14, fontweight='bold', pad=15)
plt.tight_layout()
plt.show()

How to read the finished chart: Find discount_rate row โ€” every cell is blue/negative, darkest at avg_order_val (โˆ’0.61). One sentence for the stakeholder: "Higher discounts are associated with smaller baskets." Then find order_count column โ€” mostly deep red. One more sentence: "Customers who order more often generate substantially more revenue." Two insights, one chart.

Loading progress...