Waterfall Chart
E-commerce Order Economics
Order profitability waterfall from gross merchandise value to net profit per order.
Output
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch
categories = ['Gross\nMerchandise', 'Discounts &\nCoupons', 'Net\nRevenue', 'Product\nCost',
'Fulfillment', 'Payment\nFees', 'Returns\nReserve', 'Net\nProfit']
values = [0, -18, 0, -42, -12, -3, -5, 0]
initial = 100
running_total = initial
bottoms, heights, colors = [], [], []
for i, (cat, val) in enumerate(zip(categories, values)):
if 'Gross' in cat:
bottoms.append(0)
heights.append(initial)
colors.append('#27D3F5')
elif 'Net\nRevenue' in cat:
running_total = initial - 18
bottoms.append(0)
heights.append(running_total)
colors.append('#F5B027')
elif 'Net\nProfit' in cat:
bottoms.append(0)
heights.append(running_total)
colors.append('#6CF527' if running_total > 0 else '#F5276C')
else:
bottoms.append(running_total + val)
heights.append(abs(val))
colors.append('#F5276C')
running_total += val
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)
for i, (bar, val, bot, height) in enumerate(zip(bars, values, bottoms, heights)):
y_pos = bot + height / 2
if 'Gross' in categories[i] or 'Net' in categories[i]:
label = str(height)
ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, ha='center', va='center',
fontsize=11, fontweight='bold', color='#0a0a0f' if height > 50 else 'white')
else:
label = str(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):
if categories[i] in ['Gross\nMerchandise', 'Net\nRevenue']:
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)
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='#e2e8f0')
ax.set_ylabel('Per Order Value', fontsize=12, color='#e2e8f0', fontweight='500')
ax.set_title('E-commerce Order Unit Economics', 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')
margin = (running_total / initial) * 100
ax.annotate('Net Margin: ' + str(round(margin, 1)) + ' pct per order', xy=(0.98, 0.95), xycoords='axes fraction',
fontsize=11, color='#6CF527', ha='right', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.4', facecolor='#1e293b', edgecolor='#6CF527', alpha=0.9))
legend_elements = [Patch(facecolor='#27D3F5', label='GMV'), Patch(facecolor='#F5B027', label='Net Revenue'),
Patch(facecolor='#F5276C', label='Costs'), Patch(facecolor='#6CF527', label='Net Profit')]
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
More Waterfall Chart examples
☕