Waterfall Chart

Healthcare Cost Breakdown

Patient billing waterfall from charges through adjustments to patient responsibility.

Output
Healthcare Cost Breakdown
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch

categories = ['Total\nCharges', 'Contractual\nAdjustments', 'Insurance\nPayment', 'Secondary\nInsurance', 
              'Charity\nCare', 'Bad\nDebt', 'Patient\nResponsibility']
values = [0, -42, -85, -12, -5, -3, 0]

initial = 150
running_total = initial
bottoms, heights, colors = [], [], []

for i, (cat, val) in enumerate(zip(categories, values)):
    if 'Total' in cat:
        bottoms.append(0)
        heights.append(initial)
        colors.append('#3b82f6')
    elif 'Patient' in cat:
        bottoms.append(0)
        heights.append(running_total)
        colors.append('#f59e0b')
    else:
        bottoms.append(running_total + val)
        heights.append(abs(val))
        colors.append('#22c55e' if 'Insurance' in cat else '#8b5cf6')
        running_total += val

fig, ax = plt.subplots(figsize=(12, 8), facecolor='#ffffff')
ax.set_facecolor('#ffffff')

x = np.arange(len(categories))
bars = ax.bar(x, heights, bottom=bottoms, color=colors, width=0.6, edgecolor='#e5e7eb', linewidth=1)

for i, (bar, val, bot, height) in enumerate(zip(bars, values, bottoms, heights)):
    y_pos = bot + height / 2
    if 'Total' in categories[i] or 'Patient' in categories[i]:
        label = f"${height:,}"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, ha='center', va='center', 
                fontsize=11, fontweight='bold', color='white')
    else:
        label = f"${abs(val):,}"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, ha='center', va='center', 
                fontsize=10, fontweight='bold', color='white')

for i in range(len(x) - 1):
    y = initial if i == 0 else bottoms[i]
    ax.plot([x[i] + 0.35, x[i+1] - 0.35], [y, y], color='#9ca3af', linestyle='--', linewidth=1.5, alpha=0.7)

ax.set_xlim(-0.6, len(categories) - 0.4)
ax.set_ylim(0, initial * 1.1)
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=9, color='#374151')
ax.set_ylabel('Amount ($)', fontsize=12, color='#374151', fontweight='500')
ax.set_title('Patient Billing Reconciliation', fontsize=16, color='#111827', fontweight='bold', pad=20)
ax.tick_params(axis='y', colors='#374151', labelsize=10)
ax.yaxis.grid(True, linestyle='--', alpha=0.4, color='#e5e7eb')
ax.set_axisbelow(True)
for spine in ax.spines.values():
    spine.set_color('#d1d5db')

patient_pct = (running_total / initial) * 100
ax.annotate(f'Patient Responsibility: {patient_pct:.0f}% of charges', xy=(0.98, 0.95), xycoords='axes fraction',
            fontsize=10, color='#d97706', ha='right', fontweight='bold',
            bbox=dict(boxstyle='round,pad=0.4', facecolor='#fffbeb', edgecolor='#f59e0b'))

legend_elements = [Patch(facecolor='#3b82f6', label='Total Charges'), Patch(facecolor='#22c55e', label='Insurance'),
                   Patch(facecolor='#8b5cf6', label='Adjustments'), Patch(facecolor='#f59e0b', label='Patient Owes')]
ax.legend(handles=legend_elements, loc='upper left', bbox_to_anchor=(0, -0.1), ncol=4, fontsize=9,
          facecolor='white', edgecolor='#d1d5db', labelcolor='#374151')

plt.tight_layout()
plt.subplots_adjust(bottom=0.15)
plt.show()
Library

Matplotlib

Category

Financial

Did this help you?

Support PyLucid to keep it free & growing

Support