Waterfall Chart

Profit Margin Breakdown

Dark-themed waterfall chart showing the step-by-step breakdown of profit margins from gross to net.

Output
Profit Margin Breakdown
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch

# Profit margin components (percentages)
categories = ['Gross\nMargin', 'R&D', 'Sales &\nMarketing', 'G&A', 
              'Operating\nMargin', 'Interest', 'Taxes', 'Net\nMargin']
values = [0, -12, -18, -8, 0, -2, -5, 0]

# Calculate running total
initial = 65  # Gross margin %
running_total = initial
bottoms = []
heights = []
colors = []

for i, (cat, val) in enumerate(zip(categories, values)):
    if cat == 'Gross\nMargin':
        bottoms.append(0)
        heights.append(initial)
        colors.append('#27D3F5')
    elif cat == 'Operating\nMargin':
        bottoms.append(0)
        heights.append(running_total)
        colors.append('#F5B027')
    elif cat == 'Net\nMargin':
        bottoms.append(0)
        heights.append(running_total)
        colors.append('#6CF527')
    else:
        bottoms.append(running_total + val)
        heights.append(abs(val))
        colors.append('#F5276C')
        running_total += val

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

x = np.arange(len(categories))
bars = ax.bar(x, heights, bottom=bottoms, color=colors, width=0.6, 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 categories[i] in ['Gross\nMargin', 'Operating\nMargin', 'Net\nMargin']:
        label = f"{height}%"
        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"-{abs(val)}%"
        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 categories[i] in ['Gross\nMargin', 'Operating\nMargin']:
        y = heights[i]
    else:
        y = bottoms[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, initial * 1.15)
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=10, color='#e2e8f0')
ax.set_ylabel('Margin (%)', fontsize=12, color='#e2e8f0', fontweight='500')
ax.set_title('Profit Margin Waterfall Analysis', 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')

# Legend outside plot
legend_elements = [
    Patch(facecolor='#27D3F5', label='Gross Margin'),
    Patch(facecolor='#F5276C', label='Expenses'),
    Patch(facecolor='#F5B027', label='Operating Margin'),
    Patch(facecolor='#6CF527', label='Net Margin')
]
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