Waterfall Chart

Effective Tax Rate Reconciliation

Tax waterfall bridging statutory rate to effective tax rate through adjustments.

Output
Effective Tax Rate Reconciliation
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch

# Tax rate reconciliation (in percentage points)
categories = ['Statutory\nRate', 'State\nTaxes', 'Foreign\nRate Diff', 'R&D\nCredits', 
              'Stock Comp\nDeduction', 'GILTI\nInclusion', 'Valuation\nAllowance', 'Effective\nRate']
values = [0, 3.2, -2.8, -4.5, -2.1, 1.8, 0.9, 0]

# Calculate running total
initial = 21.0
running_total = initial
bottoms = []
heights = []
colors = []

for i, (cat, val) in enumerate(zip(categories, values)):
    if 'Statutory' in cat:
        bottoms.append(0)
        heights.append(initial)
        colors.append('#4927F5')
    elif 'Effective' in cat:
        bottoms.append(0)
        heights.append(running_total)
        colors.append('#6CF527' if running_total < initial else '#F5B027')
    elif val > 0:
        bottoms.append(running_total)
        heights.append(val)
        colors.append('#F5276C')
        running_total += val
    else:
        bottoms.append(running_total + val)
        heights.append(abs(val))
        colors.append('#27F5B0')
        running_total += val

# Create figure
fig, ax = plt.subplots(figsize=(14, 8), facecolor='#0a0a0f')
ax.set_facecolor('#0a0a0f')

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

# Add value labels
for i, (bar, val, bot, height) in enumerate(zip(bars, values, bottoms, heights)):
    y_pos = bot + height / 2
    if 'Statutory' in categories[i]:
        label = f"{height:.1f}%"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, 
                ha='center', va='center', fontsize=11, fontweight='bold', color='white')
    elif 'Effective' in categories[i]:
        label = f"{height:.1f}%"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, 
                ha='center', va='center', fontsize=11, fontweight='bold', color='#0a0a0f')
    else:
        label = f"+{val:.1f}%" if val > 0 else f"{val:.1f}%"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, 
                ha='center', va='center', fontsize=10, fontweight='bold', color='white')

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

# Styling
ax.set_xlim(-0.6, len(categories) - 0.4)
ax.set_ylim(0, max(bottoms[i] + heights[i] for i in range(len(heights))) * 1.2)
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=9, color='#e2e8f0')
ax.set_ylabel('Tax Rate (%)', fontsize=12, color='#e2e8f0', fontweight='500')
ax.set_title('Statutory to Effective Tax Rate Reconciliation', fontsize=16, color='white', fontweight='bold', pad=20)

ax.tick_params(axis='y', colors='#e2e8f0', labelsize=10)
ax.yaxis.grid(True, linestyle='--', alpha=0.3, color='#334155')
ax.set_axisbelow(True)

for spine in ax.spines.values():
    spine.set_color('#334155')

# Tax savings annotation
savings = initial - running_total
pretax_income = 850
tax_saved = pretax_income * (savings / 100)
ax.annotate(f'Tax Savings: ${tax_saved:.0f}M ({savings:.1f} pp reduction)', xy=(0.98, 0.95), xycoords='axes fraction',
            fontsize=10, color='#6CF527', ha='right', fontweight='bold',
            bbox=dict(boxstyle='round,pad=0.4', facecolor='#1e293b', edgecolor='#6CF527', alpha=0.9))

# Legend outside plot
legend_elements = [
    Patch(facecolor='#4927F5', label='Statutory Rate'),
    Patch(facecolor='#F5276C', label='Rate Increases'),
    Patch(facecolor='#27F5B0', label='Rate Reductions'),
    Patch(facecolor='#6CF527', label='Effective Rate')
]
ax.legend(handles=legend_elements, loc='upper left', bbox_to_anchor=(0, -0.1), 
          ncol=4, fontsize=9, facecolor='#1e293b', edgecolor='#334155', labelcolor='white')

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