π Plotly
24 topics • Click any card to expand
plotly.express (px) creates interactive charts in one line. Charts are HTML/JavaScript β hover, zoom, and pan by default.
import plotly.express as px
# Built-in dataset
df = px.data.gapminder().query("year == 2007")
fig = px.scatter(
df, x='gdpPercap', y='lifeExp',
size='pop', color='continent',
hover_name='country',
log_x=True,
title='GDP per Capita vs Life Expectancy (2007)',
labels={'gdpPercap': 'GDP per Capita (log)', 'lifeExp': 'Life Expectancy'}
)
fig.show()import plotly.express as px
df = px.data.gapminder().query("continent == 'Europe'")
fig = px.line(
df, x='year', y='lifeExp',
color='country',
hover_name='country',
title='Life Expectancy in Europe Over Time',
labels={'lifeExp': 'Life Expectancy', 'year': 'Year'}
)
fig.update_layout(showlegend=False) # too many countries for legend
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
months = pd.date_range('2024-01', periods=12, freq='MS')
df = pd.DataFrame({
'month': list(months) * 3,
'revenue': np.concatenate([
100 + np.cumsum(np.random.randn(12) * 5),
80 + np.cumsum(np.random.randn(12) * 4),
60 + np.cumsum(np.random.randn(12) * 3),
]),
'region': ['North']*12 + ['South']*12 + ['West']*12,
'target': np.concatenate([np.full(12, 110), np.full(12, 85), np.full(12, 65)]),
})
df['vs_target'] = (df['revenue'] - df['target']).round(1)
fig = px.line(
df, x='month', y='revenue',
color='region', markers=True,
hover_data=['target', 'vs_target'],
title='Monthly Revenue by Region with Target Context',
labels={'revenue': 'Revenue ($K)', 'month': 'Month'},
color_discrete_sequence=px.colors.qualitative.Bold,
)
fig.update_traces(marker=dict(size=8))
fig.update_layout(height=420, hovermode='x unified')
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(7)
dates = pd.date_range('2020-01-01', periods=365*4, freq='D')
price = 100 + np.cumsum(np.random.randn(len(dates)) * 1.2)
df = pd.DataFrame({'date': dates, 'price': price.round(2)})
fig = px.line(
df, x='date', y='price',
title='Stock Price β Interactive Range Selector',
labels={'price': 'Price ($)', 'date': 'Date'},
)
fig.update_traces(line=dict(color='#636EFA', width=1.5))
# Range selector buttons (1M, 3M, 6M, 1Y, All)
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=[
dict(count=1, label='1M', step='month', stepmode='backward'),
dict(count=3, label='3M', step='month', stepmode='backward'),
dict(count=6, label='6M', step='month', stepmode='backward'),
dict(count=1, label='1Y', step='year', stepmode='backward'),
dict(step='all', label='All'),
]
)
)
fig.update_layout(height=460, xaxis_rangeslider_thickness=0.05)
fig.show()
print("Trace type:", fig.data[0].type)
print("Date range:", df['date'].min().date(), "to", df['date'].max().date())import plotly.express as px
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 300
df = pd.DataFrame({
'department': rng.choice(['Engineering','Sales','Marketing','Finance'], n),
'level': rng.choice(['Junior','Mid','Senior'], n),
'salary': rng.normal(85000, 20000, n).clip(40000, 180000),
})
fig = px.box(
df, x='level', y='salary',
facet_col='department', facet_col_wrap=2,
color='level',
color_discrete_sequence=px.colors.qualitative.Set2,
points='outliers',
title='Salary Distribution by Department & Level',
labels={'salary': 'Annual Salary ($)', 'level': 'Level'},
width=900, height=600,
category_orders={'level': ['Junior', 'Mid', 'Senior']},
)
fig.update_traces(marker=dict(size=4, opacity=0.6))
fig.update_layout(showlegend=False, title_font_size=15)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
markets = ['US','UK','DE','FR','JP','BR','IN','AU','CA','MX']
df = pd.DataFrame({
'market': markets,
'ad_spend': np.random.uniform(50, 500, 10).round(1),
'revenue': np.random.uniform(200, 2000, 10).round(1),
'customers': np.random.randint(500, 10000, 10),
'region': ['Americas','Europe','Europe','Europe',
'Asia','Americas','Asia','Pacific','Americas','Americas'],
})
df['roi'] = (df['revenue'] / df['ad_spend']).round(2)
fig = px.scatter(
df, x='ad_spend', y='revenue',
size='customers', color='region',
text='market',
hover_data=['roi', 'customers'],
title='Marketing Spend vs Revenue by Market',
labels={'ad_spend': 'Ad Spend ($K)', 'revenue': 'Revenue ($K)'},
size_max=50
)
fig.update_traces(textposition='top center', textfont_size=10)
fig.update_layout(height=450)
fig.show()import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 60
dates = pd.date_range('2024-01-01', periods=n, freq='B')
close = 100 + np.cumsum(rng.normal(0, 1.5, n))
high = close + rng.uniform(0.5, 3, n)
low = close - rng.uniform(0.5, 3, n)
open_ = close - rng.uniform(-1.5, 1.5, n)
vol = rng.integers(500000, 2000000, n)
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
row_heights=[0.7, 0.3],
vertical_spacing=0.03)
fig.add_trace(go.Candlestick(
x=dates, open=open_, high=high, low=low, close=close,
name='OHLC', increasing_line_color='#26a69a',
decreasing_line_color='#ef5350'), row=1, col=1)
colors = ['#26a69a' if c >= o else '#ef5350'
for c, o in zip(close, open_)]
fig.add_trace(go.Bar(x=dates, y=vol, marker_color=colors,
name='Volume', opacity=0.7), row=2, col=1)
# 10-day moving average
ma10 = pd.Series(close).rolling(10).mean()
fig.add_trace(go.Scatter(x=dates, y=ma10, mode='lines',
line=dict(color='orange', width=2, dash='dot'),
name='MA10'), row=1, col=1)
fig.update_layout(title='Stock OHLC + Volume', xaxis_rangeslider_visible=False,
height=550, template='plotly_dark',
hovermode='x unified')
fig.show()import plotly.graph_objects as go
items = ['Starting Revenue','Product A', 'Product B', 'Churn',
'Upsell', 'Discounts', 'Net Revenue']
values = [1000, 250, 180, -120, 90, -60, 0]
values[-1] = sum(values[:-1])
measure = ['absolute'] + ['relative']*(len(items)-2) + ['total']
fig = go.Figure(go.Waterfall(
name='Revenue',
orientation='v',
measure=measure,
x=items,
y=values,
text=[f'${v:+,}' if v != 0 else f'${abs(values[-1]):,}' for v in values],
textposition='outside',
connector={'line': {'color': 'rgb(63, 63, 63)'}},
increasing={'marker': {'color': '#26a69a'}},
decreasing={'marker': {'color': '#ef5350'}},
totals={'marker': {'color': '#7e57c2'}},
))
fig.update_layout(
title='Annual Revenue Waterfall Analysis',
title_font_size=16,
yaxis_title='Revenue ($K)',
waterfallgap=0.3,
height=500,
template='plotly_white',
showlegend=False,
)
fig.show()import plotly.express as px
df = px.data.gapminder()
# Pick 5 countries
countries = ['United States', 'China', 'India', 'Brazil', 'Germany']
# TODO: filtered = df[df['country'].isin(countries)]
# TODO: fig = px.line(
# filtered, x='year', y='lifeExp',
# color='country', markers=True,
# hover_data=['pop', 'gdpPercap'],
# title='Life Expectancy Trends β 5 Countries',
# )
# TODO: fig.update_traces(marker=dict(size=8))
# TODO: fig.update_layout(hovermode='x unified', height=450)
# TODO: fig.show()px.bar for categorical comparisons; px.pie and px.sunburst for part-to-whole. All support hover, faceting, and animation.
import plotly.express as px
df = px.data.tips()
fig = px.bar(
df, x='day', y='total_bill',
color='sex', barmode='group',
facet_col='time',
title='Total Bill by Day, Gender, and Meal Time',
labels={'total_bill': 'Total Bill ($)', 'day': 'Day'},
color_discrete_sequence=px.colors.qualitative.Set2
)
fig.update_layout(height=400)
fig.show()import plotly.express as px
import pandas as pd
# Pie chart
df_pie = pd.DataFrame({
'channel': ['Organic', 'Paid Search', 'Social', 'Email', 'Direct'],
'sessions': [35, 25, 20, 12, 8],
})
fig1 = px.pie(df_pie, names='channel', values='sessions',
title='Traffic by Channel', hole=0.4)
fig1.show()
# Sunburst β hierarchical
df_sun = px.data.gapminder().query("year==2007 and continent in ['Europe','Americas']")
fig2 = px.sunburst(df_sun, path=['continent','country'],
values='pop', color='lifeExp',
color_continuous_scale='RdYlGn',
title='Population & Life Expectancy')
fig2.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(7)
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
products = ['Widget', 'Gadget', 'Gizmo']
rows = []
for q in quarters:
for p in products:
rows.append({'quarter': q, 'product': p,
'revenue': round(np.random.uniform(40, 120), 1)})
df = pd.DataFrame(rows)
# Grouped bars
fig1 = px.bar(df, x='quarter', y='revenue', color='product',
barmode='group',
title='Quarterly Revenue β Grouped',
color_discrete_sequence=px.colors.qualitative.Pastel)
fig1.update_layout(height=380)
fig1.show()
# Stacked bars (same data)
fig2 = px.bar(df, x='quarter', y='revenue', color='product',
barmode='stack',
title='Quarterly Revenue β Stacked',
color_discrete_sequence=px.colors.qualitative.Pastel)
fig2.update_layout(height=380)
fig2.show()import plotly.graph_objects as go
# P&L waterfall: starting revenue, additions, deductions, net
labels = ['Gross Revenue', 'COGS', 'Gross Profit',
'Operating Exp', 'EBITDA', 'D&A', 'Tax', 'Net Income']
measures = ['absolute', 'relative', 'total',
'relative', 'total', 'relative', 'relative', 'total']
values = [500, -180, None, -120, None, -30, -42, None]
fig = go.Figure(go.Waterfall(
name='P&L 2024',
orientation='v',
measure=measures,
x=labels,
y=[500, -180, 0, -120, 0, -30, -42, 0],
text=['+$500', '-$180', '$320', '-$120', '$200', '-$30', '-$42', '$128'],
textposition='outside',
connector=dict(line=dict(color='#444', width=1, dash='dot')),
increasing=dict(marker=dict(color='#00CC96')),
decreasing=dict(marker=dict(color='#EF553B')),
totals=dict(marker=dict(color='#636EFA')),
))
fig.update_layout(
title='2024 P&L Waterfall Chart',
yaxis_title='Amount ($K)',
height=450,
showlegend=False,
plot_bgcolor='#1a1a2e',
paper_bgcolor='#16213e',
font=dict(color='#e0e0e0'),
yaxis=dict(gridcolor='#333'),
)
fig.show()
print("Waterfall traces:", len(fig.data))
print("Net Income: $128K")import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(5)
regions = ['North America', 'Europe', 'Asia Pacific']
categories = ['Electronics', 'Clothing', 'Home', 'Food']
skus_per = 3
rows = []
for region in regions:
for cat in categories:
for sku_i in range(1, skus_per + 1):
rows.append({
'region': region,
'category': cat,
'sku': f'{cat[:3]}-{sku_i:03d}',
'revenue': round(np.random.uniform(50, 500), 1),
})
df = pd.DataFrame(rows)
fig = px.sunburst(
df, path=['region', 'category', 'sku'],
values='revenue',
color='revenue',
color_continuous_scale='Blues',
title='Q2 2024 Revenue Drill-Down (Region β Category β SKU)',
)
fig.update_layout(height=550, coloraxis_showscale=False)
fig.show()import plotly.express as px
import pandas as pd
# Build a simple hierarchical dataset
rows = [
# continent, country, city, population, gdp_index
('Americas', 'USA', 'New York', 8_000_000, 95),
('Americas', 'USA', 'Los Angeles', 4_000_000, 88),
('Americas', 'Brazil', 'Sao Paulo',12_000_000, 55),
('Americas', 'Brazil', 'Rio', 6_500_000, 50),
('Europe', 'Germany', 'Berlin', 3_600_000, 82),
('Europe', 'Germany', 'Munich', 1_500_000, 90),
('Europe', 'France', 'Paris', 2_100_000, 78),
('Europe', 'France', 'Lyon', 500_000, 70),
('Asia', 'Japan', 'Tokyo', 13_900_000, 87),
('Asia', 'Japan', 'Osaka', 2_700_000, 80),
('Asia', 'India', 'Mumbai', 20_000_000, 42),
('Asia', 'India', 'Delhi', 30_000_000, 38),
]
df = pd.DataFrame(rows, columns=['continent','country','city','population','gdp_index'])
# TODO: fig = px.sunburst(
# df, path=['continent', 'country', 'city'],
# values='population',
# color='gdp_index',
# color_continuous_scale='Blues',
# title='Population Drill-Down: Continent -> Country -> City',
# )
# TODO: fig.update_layout(height=520)
# TODO: fig.show()px.histogram and px.box create interactive distribution charts. Hover shows exact statistics; click legend to toggle groups.
import plotly.express as px
df = px.data.tips()
fig = px.histogram(
df, x='total_bill',
color='time', barmode='overlay',
marginal='box', # adds mini box plot on top
opacity=0.7,
nbins=25,
title='Total Bill Distribution by Meal Time',
labels={'total_bill': 'Total Bill ($)'},
color_discrete_sequence=['#636EFA','#EF553B']
)
fig.show()import plotly.express as px
import pandas as pd
df = px.data.tips()
fig1 = px.box(df, x='day', y='tip', color='smoker',
notched=True,
title='Tip Distribution β Box Plot (notched)',
labels={'tip': 'Tip ($)'})
fig1.show()
fig2 = px.violin(df, x='day', y='total_bill', color='sex',
box=True, points='outliers',
title='Total Bill β Violin + Box',
labels={'total_bill': 'Total Bill ($)'})
fig2.show()import plotly.express as px
import numpy as np
import pandas as pd
np.random.seed(42)
groups = ['Group A', 'Group B', 'Group C']
rows = []
for g in groups:
mean = {'Group A': 50, 'Group B': 65, 'Group C': 80}[g]
vals = np.random.normal(mean, 12, 200)
for v in vals:
rows.append({'group': g, 'score': round(v, 1)})
df = pd.DataFrame(rows)
fig = px.histogram(
df, x='score', facet_col='group',
color='group', nbins=30,
opacity=0.75,
marginal='violin',
title='Score Distribution Faceted by Group',
labels={'score': 'Score'},
color_discrete_sequence=px.colors.qualitative.Set1,
)
fig.update_layout(height=420, showlegend=False)
fig.show()import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
np.random.seed(42)
departments = ['Engineering', 'Sales', 'Marketing', 'Support']
rows = []
for dept in departments:
base = {'Engineering': 95, 'Sales': 72, 'Marketing': 68, 'Support': 60}[dept]
scores = np.random.normal(base, 10, 60).clip(0, 100)
for s in scores:
rows.append({'department': dept, 'score': round(s, 1)})
df = pd.DataFrame(rows)
# Notched violin with embedded box and all points
fig = px.violin(
df, x='department', y='score',
color='department',
box=True,
points='all',
title='Performance Score Distribution by Department',
labels={'score': 'Score', 'department': 'Department'},
color_discrete_sequence=px.colors.qualitative.Set2,
)
fig.update_traces(
meanline_visible=True,
jitter=0.3,
pointpos=-1.5,
marker=dict(size=3, opacity=0.5),
)
fig.update_layout(height=480, showlegend=False)
fig.show()
for dept in departments:
vals = df[df['department']==dept]['score']
print(f"{dept}: median={vals.median():.1f}, std={vals.std():.1f}")import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(42)
control = np.random.normal(45.0, 12, 500)
variant = np.random.normal(49.5, 11, 500)
df = pd.DataFrame({
'revenue': np.concatenate([control, variant]),
'group': ['Control']*500 + ['Variant B']*500,
})
t, p = stats.ttest_ind(control, variant)
fig = px.histogram(
df, x='revenue', color='group',
barmode='overlay', opacity=0.65, nbins=35,
marginal='violin',
color_discrete_map={'Control':'#636EFA','Variant B':'#EF553B'},
title=f'A/B Test Revenue Distribution | p-value={p:.4f} {"β Significant" if p<0.05 else "β Not significant"}',
labels={'revenue': 'Revenue ($)'},
)
fig.add_vline(x=control.mean(), line_dash='dash', line_color='#636EFA',
annotation_text=f'Control ΞΌ={control.mean():.1f}')
fig.add_vline(x=variant.mean(), line_dash='dash', line_color='#EF553B',
annotation_text=f'Variant ΞΌ={variant.mean():.1f}')
fig.update_layout(height=450)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
rng = np.random.default_rng(0)
n = 300
df = pd.DataFrame({
'tenure': rng.exponential(24, n).clip(1, 120),
'usage_rate': rng.beta(5, 2, n) * 100,
'support_calls':rng.poisson(3, n),
'billing_score':rng.normal(75, 15, n).clip(0, 100),
'monthly_spend':rng.lognormal(4, 0.5, n),
'churn': rng.choice([0, 1], n, p=[0.75, 0.25]),
})
fig = px.parallel_coordinates(
df,
color='churn',
color_continuous_scale=px.colors.diverging.Tealrose,
color_continuous_midpoint=0.5,
dimensions=['tenure','usage_rate','support_calls','billing_score','monthly_spend'],
labels={
'tenure': 'Tenure (mo)',
'usage_rate': 'Usage %',
'support_calls': 'Support Calls',
'billing_score': 'Billing Score',
'monthly_spend': 'Monthly Spend',
},
title='Parallel Coordinates: Customer Churn Features',
)
fig.update_layout(height=450, title_font_size=15,
coloraxis_colorbar=dict(title='Churn Risk', tickvals=[0,1],
ticktext=['Low','High']))
fig.show()import plotly.graph_objects as go
import numpy as np
categories = ['Speed', 'Accuracy', 'Recall', 'Precision', 'F1-Score', 'AUC-ROC']
models = {
'Logistic Regression': [65, 72, 68, 75, 71, 74],
'Random Forest': [80, 85, 83, 87, 85, 88],
'XGBoost': [88, 90, 89, 91, 90, 92],
'Neural Network': [75, 88, 86, 89, 87, 90],
}
colors = ['#636EFA','#EF553B','#00CC96','#AB63FA']
fig = go.Figure()
for (model, values), color in zip(models.items(), colors):
vals = values + [values[0]] # close the polygon
cats = categories + [categories[0]]
fig.add_trace(go.Scatterpolar(
r=vals, theta=cats, fill='toself',
name=model, line_color=color,
fillcolor=color, opacity=0.25,
hovertemplate='%{theta}: %{r}<extra>' + model + '</extra>',
))
fig.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[50, 100],
tickfont=dict(size=9))),
title='Model Performance Radar Chart',
title_font_size=15,
legend=dict(x=1.05, y=0.5),
height=500,
template='plotly_white',
)
fig.show()import plotly.express as px
df = px.data.tips()
# 1. Grouped violin: total_bill by day, colored by smoker
# TODO: fig1 = px.violin(
# df, x='day', y='total_bill', color='smoker',
# box=True, points='all',
# title='Total Bill Distribution by Day and Smoker Status',
# )
# TODO: fig1.update_layout(height=450)
# TODO: fig1.show()
# 2. Faceted histogram: tip by time with rug marginal
# TODO: fig2 = px.histogram(
# df, x='tip', facet_col='time',
# color='time', marginal='rug',
# nbins=20, opacity=0.8,
# title='Tip Distribution: Lunch vs Dinner',
# )
# TODO: fig2.update_layout(height=400)
# TODO: fig2.show()px.imshow renders 2D matrices as interactive heatmaps. px.scatter_matrix creates an interactive pairplot grid.
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
features = ['Revenue', 'Cost', 'Margin', 'Customers', 'NPS']
n = 200
data = np.random.randn(n, 5)
# Add correlations
data[:, 2] = 0.8*data[:,0] - 0.6*data[:,1] + np.random.randn(n)*0.3
data[:, 4] = 0.5*data[:,2] + np.random.randn(n)*0.7
df = pd.DataFrame(data, columns=features)
corr = df.corr().round(2)
fig = px.imshow(
corr,
text_auto=True,
color_continuous_scale='RdBu_r',
zmin=-1, zmax=1,
title='Feature Correlation Matrix'
)
fig.update_layout(height=450)
fig.show()import plotly.express as px
df = px.data.iris()
fig = px.scatter_matrix(
df,
dimensions=['sepal_length','sepal_width','petal_length','petal_width'],
color='species',
symbol='species',
title='Iris Dataset β Interactive Scatter Matrix',
labels={col: col.replace('_',' ') for col in df.columns}
)
fig.update_traces(diagonal_visible=False, showupperhalf=False)
fig.update_layout(height=600)
fig.show()import plotly.graph_objects as go
import numpy as np
np.random.seed(3)
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
hours = [f'{h:02d}:00' for h in range(9, 18)]
z_data = np.random.poisson(5, (len(days), len(hours))).astype(float)
# Simulate peak hours mid-day
z_data[:, 2:6] += np.random.poisson(8, (len(days), 4))
# Build annotation text
annotations = []
for i, day in enumerate(days):
for j, hr in enumerate(hours):
annotations.append(dict(
x=hr, y=day,
text=str(int(z_data[i, j])),
font=dict(color='white' if z_data[i, j] > 10 else 'black', size=11),
showarrow=False,
))
fig = go.Figure(data=go.Heatmap(
z=z_data, x=hours, y=days,
colorscale='YlOrRd',
hoverongaps=False,
))
fig.update_layout(
title='Support Tickets: Day Γ Hour (with annotations)',
annotations=annotations,
height=380,
xaxis_title='Hour', yaxis_title='Day',
)
fig.show()import plotly.graph_objects as go
import numpy as np
np.random.seed(8)
products = ['Widget', 'Gadget', 'Gizmo', 'Doohickey', 'Thingamajig']
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
# Simulate seasonal sales data
z = np.random.uniform(20, 100, (len(products), len(months)))
# Add seasonality: Q4 boost
z[:, 9:] *= 1.4
# Add product-specific trends
z[0, :] += np.linspace(0, 20, 12)
z = z.round(1)
# Custom discrete colorscale: red -> yellow -> green
custom_scale = [
[0.0, '#d73027'],
[0.25, '#f46d43'],
[0.5, '#ffffbf'],
[0.75, '#74add1'],
[1.0, '#313695'],
]
# Build cell annotations
annotations = []
for i, prod in enumerate(products):
for j, mon in enumerate(months):
annotations.append(dict(
x=mon, y=prod,
text=str(int(z[i, j])),
showarrow=False,
font=dict(size=9,
color='white' if z[i, j] > 75 or z[i, j] < 35 else 'black'),
))
fig = go.Figure(data=go.Heatmap(
z=z, x=months, y=products,
colorscale=custom_scale,
colorbar=dict(title='Units Sold'),
hovertemplate='Product: %{y}<br>Month: %{x}<br>Sales: %{z}<extra></extra>',
))
fig.update_layout(
title='Monthly Sales Heatmap β Custom Colorscale',
annotations=annotations,
height=380,
xaxis_title='Month',
yaxis_title='Product',
)
fig.show()
print("Peak month:", months[int(z.sum(axis=0).argmax())])
print("Top product:", products[int(z.sum(axis=1).argmax())])import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(7)
hours = list(range(24))
days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
# Simulate ticket volumes: high weekday business hours
data = np.random.poisson(3, (7, 24)).astype(float)
data[0:5, 9:18] += np.random.poisson(12, (5, 9)) # weekday 9-18
data[0:5, 0:6] *= 0.3 # overnight low
data[5:7, :] *= 0.5 # weekend lower
df = pd.DataFrame(data.round(0).astype(int),
index=days, columns=[f'{h:02d}:00' for h in hours])
fig = px.imshow(
df,
color_continuous_scale='YlOrRd',
aspect='auto',
title='Support Ticket Volume β Week Γ Hour Heatmap',
labels=dict(x='Hour', y='Day', color='Tickets')
)
fig.update_xaxes(tickangle=45, tickmode='array',
tickvals=list(range(0,24,2)),
ticktext=[f'{h:02d}:00' for h in range(0,24,2)])
fig.update_layout(height=380)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
regions = ['North America','Europe','Asia Pacific','Latin America']
countries = {
'North America': ['USA','Canada','Mexico'],
'Europe': ['Germany','France','UK','Spain'],
'Asia Pacific': ['China','Japan','India','Australia'],
'Latin America': ['Brazil','Argentina','Colombia'],
}
rows = []
for region, clist in countries.items():
for country in clist:
for product in ['Software','Hardware','Services']:
rows.append({
'region': region, 'country': country, 'product': product,
'revenue': rng.uniform(50, 500),
'growth': rng.uniform(-10, 40),
})
df = pd.DataFrame(rows)
fig = px.treemap(
df,
path=[px.Constant('Global'), 'region', 'country', 'product'],
values='revenue',
color='growth',
color_continuous_scale='RdYlGn',
color_continuous_midpoint=15,
title='Global Revenue Treemap β Click to Drill Down',
hover_data={'revenue': ':.1f', 'growth': ':.1f'},
)
fig.update_traces(textinfo='label+value+percent parent',
hovertemplate='<b>%{label}</b><br>Revenue: $%{value:.1f}M<br>Growth: %{color:.1f}%')
fig.update_layout(height=600, title_font_size=15,
coloraxis_colorbar=dict(title='Growth %'))
fig.show()import plotly.graph_objects as go
# Marketing funnel flow
labels = [
'Website Visitors', # 0
'Ad Campaign', # 1
'Organic Search', # 2
'Social Media', # 3
'Product Page', # 4
'Add to Cart', # 5
'Checkout', # 6
'Purchased', # 7
'Abandoned', # 8
]
source = [0, 0, 0, 1, 2, 3, 4, 5, 6]
target = [1, 2, 3, 4, 4, 4, 5, 6, 7]
value = [3000, 5000, 2000, 2800, 4200, 1500, 4500, 2000, 2500]
link_colors = ['rgba(100,149,237,0.4)'] * len(source)
for i, t in enumerate(target):
if t == 8:
link_colors[i] = 'rgba(255,99,71,0.4)'
fig = go.Figure(go.Sankey(
arrangement='snap',
node=dict(
pad=15, thickness=20,
line=dict(color='white', width=0.5),
label=labels,
color=['#4a90d9','#5ab4ac','#d4b483','#8fc97e',
'#feb24c','#f03b20','#43a2ca','#2ca25f','#ef5350'],
),
link=dict(
source=source, target=target, value=value,
color=link_colors,
hovertemplate='%{source.label} β %{target.label}: %{value:,}<extra></extra>',
),
))
fig.update_layout(title_text='Marketing Funnel β Sankey Diagram',
title_font_size=16, height=500, font_size=12,
template='plotly_white')
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
countries = ['USA','China','India','Germany','UK','France','Japan','Brazil','Canada','Australia']
years = range(2018, 2025)
rows = []
for country in countries:
gdp = rng.uniform(1000, 25000)
pop = rng.uniform(50, 1400)
for year in years:
rows.append({
'country': country,
'year': year,
'gdp_growth': rng.normal(2.5, 1.5),
'gdp_pc': gdp * (1 + rng.normal(0.025, 0.01))**(year-2018),
'population': pop + rng.normal(0, 2),
'continent': 'Americas' if country in ['USA','Brazil','Canada'] else
'Europe' if country in ['Germany','UK','France'] else
'Asia' if country in ['China','India','Japan'] else 'Oceania',
})
df = pd.DataFrame(rows)
fig = px.scatter(
df, x='gdp_pc', y='gdp_growth',
size='population', color='continent',
animation_frame='year', animation_group='country',
hover_name='country',
log_x=True,
size_max=55,
color_discrete_sequence=px.colors.qualitative.Bold,
title='GDP per Capita vs Growth Rate (2018β2024)',
labels={'gdp_pc': 'GDP per Capita (USD, log)', 'gdp_growth': 'GDP Growth (%)'},
range_x=[500, 40000], range_y=[-3, 8],
)
fig.update_layout(height=550, title_font_size=15)
fig.show()import plotly.express as px
import plotly.graph_objects as go
import numpy as np
df = px.data.iris()
cols = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
corr = df[cols].corr().round(2)
# 1. px.imshow version (easy)
# TODO: fig1 = px.imshow(
# corr, text_auto='.2f',
# color_continuous_scale='RdBu_r', zmin=-1, zmax=1,
# title='Iris Correlation Matrix (px.imshow)',
# )
# TODO: fig1.update_layout(height=420)
# TODO: fig1.show()
# 2. go.Heatmap version with manual annotations
z = corr.values
labels = corr.columns.tolist()
# TODO: annotations = []
# TODO: for i in range(len(labels)):
# for j in range(len(labels)):
# annotations.append(dict(
# x=labels[j], y=labels[i],
# text=str(z[i, j]),
# showarrow=False,
# font=dict(color='white' if abs(z[i,j]) > 0.5 else 'black'),
# ))
# TODO: fig2 = go.Figure(data=go.Heatmap(
# z=z, x=labels, y=labels,
# colorscale='RdBu_r', zmin=-1, zmax=1,
# ))
# TODO: fig2.update_layout(title='Iris Correlation (go.Heatmap + annotations)',
# annotations=annotations, height=420)
# TODO: fig2.show()Plotly renders true 3D scatter and surface plots in the browser. Drag to rotate, scroll to zoom.
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'x': np.random.randn(300),
'y': np.random.randn(300),
'z': np.random.randn(300),
'cluster': np.random.choice(['A','B','C'], 300),
'size': np.random.uniform(5, 20, 300),
})
# Separate clusters
df.loc[df.cluster=='A','x'] += 2
df.loc[df.cluster=='B','y'] += 2
df.loc[df.cluster=='C','z'] += 2
fig = px.scatter_3d(df, x='x', y='y', z='z',
color='cluster', size='size',
opacity=0.7,
title='3D Cluster Visualization')
fig.update_layout(height=500)
fig.show()import plotly.graph_objects as go
import numpy as np
x = np.linspace(-3, 3, 60)
y = np.linspace(-3, 3, 60)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2)) * np.exp(-0.1*(X**2+Y**2))
fig = go.Figure(data=[
go.Surface(z=Z, x=X, y=Y,
colorscale='Viridis',
contours=dict(z=dict(show=True, usecolormap=True,
highlightcolor='white', project_z=True)))
])
fig.update_layout(
title='3D Surface: Damped Sinc Function',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'),
height=500
)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(99)
n = 500
# Spiral helix dataset
t = np.linspace(0, 4 * np.pi, n)
df = pd.DataFrame({
'x': np.cos(t) + np.random.randn(n) * 0.15,
'y': np.sin(t) + np.random.randn(n) * 0.15,
'z': t / (2 * np.pi), # height increases with angle
'intensity': np.sin(t) ** 2,
'label': [f'Point {i}' for i in range(n)],
})
fig = px.scatter_3d(
df, x='x', y='y', z='z',
color='intensity',
color_continuous_scale='Plasma',
hover_name='label',
hover_data={'x': ':.2f', 'y': ':.2f', 'z': ':.2f'},
opacity=0.75,
title='3D Helix β Color by Intensity',
size_max=6,
)
fig.update_traces(marker=dict(size=3))
fig.update_layout(height=520)
fig.show()import plotly.graph_objects as go
import numpy as np
# -- 3D line: double helix DNA-like structure --
t = np.linspace(0, 6 * np.pi, 300)
r = 1.5
fig = go.Figure()
# Strand 1
fig.add_trace(go.Scatter3d(
x=r * np.cos(t), y=r * np.sin(t), z=t / np.pi,
mode='lines',
line=dict(color='#636EFA', width=5),
name='Strand 1',
))
# Strand 2 (180 degrees offset)
fig.add_trace(go.Scatter3d(
x=r * np.cos(t + np.pi), y=r * np.sin(t + np.pi), z=t / np.pi,
mode='lines',
line=dict(color='#EF553B', width=5),
name='Strand 2',
))
# Rungs (every 15th point)
step = 15
for i in range(0, len(t), step):
fig.add_trace(go.Scatter3d(
x=[r*np.cos(t[i]), r*np.cos(t[i]+np.pi)],
y=[r*np.sin(t[i]), r*np.sin(t[i]+np.pi)],
z=[t[i]/np.pi, t[i]/np.pi],
mode='lines',
line=dict(color='#aaa', width=2),
showlegend=False,
))
# -- 3D surface: saddle function z = x^2 - y^2 --
xs = np.linspace(-2, 2, 40)
ys = np.linspace(-2, 2, 40)
Xs, Ys = np.meshgrid(xs, ys)
Zs = Xs**2 - Ys**2
fig2 = go.Figure(data=[go.Surface(
x=Xs, y=Ys, z=Zs,
colorscale='RdBu',
colorbar=dict(title='z = xΒ²-yΒ²'),
)])
fig2.update_layout(
title='3D Saddle Surface (z = xΒ² - yΒ²)',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'),
height=480,
)
fig.update_layout(
title='3D Double Helix Line Plot',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Turn'),
height=520,
showlegend=True,
)
fig.show()
fig2.show()
print("Double helix: 2 strands, rungs every 15 points")
print("Saddle surface grid:", Xs.shape)import plotly.graph_objects as go
import numpy as np
from scipy.stats import norm
def black_scholes_call(S, K, T, r, sigma):
# Avoid division by zero
T = np.where(T < 1e-6, 1e-6, T)
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
return S*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)
K = 100 # strike price
r = 0.05 # risk-free rate
sigma = 0.20 # volatility
S_vals = np.linspace(70, 130, 50) # underlying price
T_vals = np.linspace(0.02, 1.0, 50) # time to expiry (years)
S_grid, T_grid = np.meshgrid(S_vals, T_vals)
C_grid = black_scholes_call(S_grid, K, T_grid, r, sigma)
fig = go.Figure(data=[go.Surface(
x=S_grid, y=T_grid, z=C_grid,
colorscale='Plasma',
colorbar=dict(title='Call Price ($)')
)])
fig.update_layout(
title=f'Black-Scholes Call Option Price (K={K}, Ο={sigma})',
scene=dict(
xaxis_title='Underlying Price (S)',
yaxis_title='Time to Expiry (T)',
zaxis_title='Call Price ($)',
),
height=520
)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
n_per = 133
# Build three clusters
def make_cluster(cx, cy, cz, label, n):
return pd.DataFrame({
'x': np.random.randn(n) + cx,
'y': np.random.randn(n) + cy,
'z': np.random.randn(n) + cz,
'cluster': label,
'confidence': np.random.uniform(5, 20, n).round(1),
})
# TODO: df = pd.concat([
# make_cluster(0, 0, 0, 'Alpha', n_per),
# make_cluster(4, 0, 0, 'Beta', n_per),
# make_cluster(2, 4, 2, 'Gamma', n_per),
# ], ignore_index=True)
# Print cluster summary
# TODO: print(df.groupby('cluster')[['x','y','z']].mean().round(2))
# TODO: fig = px.scatter_3d(
# df, x='x', y='y', z='z',
# color='cluster', size='confidence',
# hover_data={'confidence': True, 'x': ':.2f', 'y': ':.2f', 'z': ':.2f'},
# opacity=0.75,
# title='3D Cluster Scatter β Size by Confidence',
# )
# TODO: fig.update_layout(height=520)
# TODO: fig.show()make_subplots creates multi-panel figures. Mix chart types, share axes, and control spacing β all in one interactive figure.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
np.random.seed(5)
x = np.linspace(0, 10, 100)
fig = make_subplots(rows=2, cols=2,
subplot_titles=['Line','Bar','Scatter','Histogram'])
fig.add_trace(go.Scatter(x=x, y=np.sin(x), name='sin'), row=1, col=1)
fig.add_trace(go.Bar(x=['A','B','C','D'], y=[4,7,3,8], name='bar'), row=1, col=2)
fig.add_trace(go.Scatter(x=np.random.randn(100), y=np.random.randn(100),
mode='markers', name='scatter',
marker=dict(opacity=0.5)), row=2, col=1)
fig.add_trace(go.Histogram(x=np.random.randn(500), name='hist'), row=2, col=2)
fig.update_layout(title='Multi-Type Dashboard', height=500, showlegend=False)
fig.show()import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
np.random.seed(9)
dates = pd.date_range('2024-01-01', periods=60, freq='B')
price = 100 + np.cumsum(np.random.randn(60) * 1.5)
volume = np.random.randint(1000, 8000, 60)
colors = ['green' if price[i] >= price[i-1] else 'red' for i in range(len(price))]
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
row_heights=[0.7, 0.3],
vertical_spacing=0.03)
fig.add_trace(go.Scatter(x=dates, y=price, name='Price',
line=dict(color='royalblue', width=2)), row=1, col=1)
fig.add_trace(go.Bar(x=dates, y=volume, name='Volume',
marker_color=colors, opacity=0.7), row=2, col=1)
fig.update_layout(title='Stock Price & Volume', height=500, showlegend=False)
fig.update_xaxes(rangeslider_visible=False)
fig.show()import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
np.random.seed(11)
x = np.linspace(0, 2 * np.pi, 80)
dates = pd.date_range('2024-01', periods=12, freq='MS')
revenue = 50 + np.cumsum(np.random.randn(12) * 4)
categories = ['Q1', 'Q2', 'Q3', 'Q4']
vals_a = [22, 35, 28, 41]
vals_b = [18, 29, 33, 37]
fig = make_subplots(
rows=2, cols=2,
subplot_titles=['Sine & Cosine Waves', 'Monthly Revenue',
'Grouped Bar by Quarter', 'Normal Distribution'],
vertical_spacing=0.12, horizontal_spacing=0.1,
)
# Row 1, Col 1: Two line traces
fig.add_trace(go.Scatter(x=x, y=np.sin(x), name='sin',
line=dict(color='#636EFA')), row=1, col=1)
fig.add_trace(go.Scatter(x=x, y=np.cos(x), name='cos',
line=dict(color='#EF553B', dash='dash')), row=1, col=1)
# Row 1, Col 2: Area chart
fig.add_trace(go.Scatter(x=dates, y=revenue.round(1), fill='tozeroy',
line=dict(color='#00CC96'), name='Revenue'), row=1, col=2)
# Row 2, Col 1: Grouped bars
fig.add_trace(go.Bar(x=categories, y=vals_a, name='Product A',
marker_color='#AB63FA'), row=2, col=1)
fig.add_trace(go.Bar(x=categories, y=vals_b, name='Product B',
marker_color='#FFA15A'), row=2, col=1)
# Row 2, Col 2: Histogram
fig.add_trace(go.Histogram(x=np.random.randn(400), nbinsx=25,
marker_color='#19D3F3', name='Normal'), row=2, col=2)
fig.update_layout(title='Multi-Panel Dashboard', height=580,
barmode='group', showlegend=True,
legend=dict(orientation='h', y=-0.08))
fig.show()import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
np.random.seed(17)
months = pd.date_range('2024-01', periods=12, freq='MS')
sales = np.random.randint(30, 100, 12)
target = np.full(12, 70)
cum_sales = sales.cumsum()
margin_pct = np.random.uniform(0.15, 0.45, 12).round(3)
# 3 rows: bar chart, line overlay, scatter β all share x-axis
fig = make_subplots(
rows=3, cols=1,
shared_xaxes=True,
row_heights=[0.5, 0.25, 0.25],
vertical_spacing=0.05,
subplot_titles=['Monthly Sales vs Target',
'Cumulative Sales',
'Margin %'],
)
# Row 1: Bar (actual) + Line (target)
bar_colors = ['#00CC96' if s >= 70 else '#EF553B' for s in sales]
fig.add_trace(go.Bar(x=months, y=sales, name='Actual',
marker_color=bar_colors), row=1, col=1)
fig.add_trace(go.Scatter(x=months, y=target, name='Target',
line=dict(color='white', dash='dash', width=2),
mode='lines'), row=1, col=1)
# Row 2: Cumulative area line
fig.add_trace(go.Scatter(x=months, y=cum_sales, fill='tozeroy',
name='Cumulative', line=dict(color='#636EFA')), row=2, col=1)
# Row 3: Scatter dots for margin
fig.add_trace(go.Scatter(x=months, y=(margin_pct * 100).round(1),
mode='markers+lines', name='Margin %',
marker=dict(color='#AB63FA', size=8),
line=dict(color='#AB63FA', width=1.5)), row=3, col=1)
fig.add_hline(y=30, line_dash='dot', line_color='gray',
annotation_text='30% target', row=3, col=1)
fig.update_layout(
title='Sales Performance β Bar + Line + Scatter Subplots',
height=600, showlegend=True,
legend=dict(orientation='h', y=-0.06),
template='plotly_dark',
)
fig.update_xaxes(rangeslider_visible=False)
fig.show()
print("Traces:", [t.type for t in fig.data])
print("Total sales:", int(cum_sales[-1]))import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
np.random.seed(3)
months = pd.date_range('2024-01', periods=12, freq='MS')
revenue = 100 + np.cumsum(np.random.randn(12)*8) + np.arange(12)*5
sessions= np.random.randint(8000, 20000, 12)
cvr = revenue / sessions * 100
channels = ['Organic','Paid','Social','Email','Direct']
ch_vals = [35, 25, 18, 12, 10]
funnel_stages = ['Visit','Sign Up','Trial','Paid']
funnel_vals = [10000, 3200, 1100, 420]
fig = make_subplots(
rows=2, cols=2,
subplot_titles=['Revenue Trend ($K)', 'Traffic by Channel',
'Conversion Funnel', 'CVR vs Sessions'],
specs=[[{},{}],[{},{}]]
)
# Revenue
fig.add_trace(go.Scatter(x=months, y=revenue.round(1), fill='tozeroy',
line=dict(color='#636EFA'), name='Revenue'), row=1, col=1)
# Channel pie β use domain trace
fig.add_trace(go.Pie(labels=channels, values=ch_vals, hole=0.35,
showlegend=False, textinfo='label+percent'), row=1, col=2)
# Funnel
fig.add_trace(go.Funnel(y=funnel_stages, x=funnel_vals,
marker_color=['#636EFA','#EF553B','#00CC96','#AB63FA'],
textinfo='value+percent initial'), row=2, col=1)
# CVR vs Sessions scatter
fig.add_trace(go.Scatter(x=sessions, y=cvr.round(3), mode='markers+text',
text=[m.strftime('%b') for m in months],
textposition='top center',
marker=dict(size=10, color='#FFA15A')), row=2, col=2)
fig.update_layout(title='Growth Dashboard β 2024', height=650)
fig.show()import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
np.random.seed(42)
# TODO: fig = make_subplots(
# rows=2, cols=2,
# subplot_titles=['Random Scatter', 'Category Totals',
# 'Time Series', 'Distribution'],
# )
# Panel (1,1): scatter with color (use marker colorscale)
x1 = np.random.randn(100)
y1 = np.random.randn(100)
c1 = np.random.randn(100)
# TODO: fig.add_trace(go.Scatter(x=x1, y=y1, mode='markers',
# marker=dict(color=c1, colorscale='Viridis', showscale=False),
# name='scatter'), row=1, col=1)
# Panel (1,2): bar chart
cats = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon']
vals = np.random.randint(10, 80, 5)
# TODO: fig.add_trace(go.Bar(x=cats, y=vals, name='totals',
# marker_color='#636EFA'), row=1, col=2)
# Panel (2,1): area line
dates = pd.date_range('2024-01', periods=24, freq='MS')
ts = 100 + np.cumsum(np.random.randn(24) * 3)
# TODO: fig.add_trace(go.Scatter(x=dates, y=ts.round(1), fill='tozeroy',
# line=dict(color='#00CC96'), name='series'), row=2, col=1)
# Panel (2,2): histogram
# TODO: fig.add_trace(go.Histogram(x=np.random.randn(300), nbinsx=20,
# marker_color='#EF553B', name='hist'), row=2, col=2)
# TODO: fig.update_layout(title='My 2x2 Dashboard', height=550, showlegend=False)
# TODO: fig.show()update_layout() controls the figure-level design. update_traces() modifies all traces at once. Both accept selector filtering.
import plotly.express as px
import plotly.graph_objects as go
df = px.data.gapminder().query("year == 2007 and continent == 'Europe'")
fig = px.scatter(df, x='gdpPercap', y='lifeExp',
size='pop', color='country',
hover_name='country', log_x=True)
fig.update_layout(
title=dict(text='Europe 2007: GDP vs Life Expectancy',
font=dict(size=16, color='white'), x=0.5),
plot_bgcolor='#1a1a2e',
paper_bgcolor='#16213e',
font=dict(color='#e0e0e0'),
xaxis=dict(gridcolor='#333', title='GDP per Capita (log)'),
yaxis=dict(gridcolor='#333', title='Life Expectancy'),
showlegend=False,
height=450,
)
fig.show()import plotly.express as px
import plotly.graph_objects as go
import numpy as np
np.random.seed(1)
x = np.arange(1, 13)
y = 80 + np.cumsum(np.random.randn(12) * 5)
fig = px.line(x=x, y=y, markers=True,
title='Monthly Revenue with Target Zone')
# Customise the line trace
fig.update_traces(
line=dict(color='#00CC96', width=3),
marker=dict(size=9, color='white',
line=dict(color='#00CC96', width=2))
)
# Add target band (rectangle shape)
fig.add_hrect(y0=90, y1=110, fillcolor='rgba(100,200,100,0.1)',
line_width=0, annotation_text='Target range')
fig.add_hline(y=100, line_dash='dot', line_color='gray',
annotation_text='Target')
fig.update_layout(xaxis_title='Month', yaxis_title='Revenue ($K)')
fig.show()import plotly.graph_objects as go
import plotly.express as px
import numpy as np
np.random.seed(5)
categories = ['Product A', 'Product B', 'Product C', 'Product D', 'Product E']
q1 = np.random.randint(30, 90, 5)
q2 = np.random.randint(30, 90, 5)
fig = go.Figure()
fig.add_trace(go.Bar(x=categories, y=q1, name='Q1',
marker_color='#636EFA'))
fig.add_trace(go.Bar(x=categories, y=q2, name='Q2',
marker_color='#EF553B'))
# Annotate the tallest Q2 bar
peak_idx = int(np.argmax(q2))
fig.add_annotation(
x=categories[peak_idx], y=q2[peak_idx] + 4,
text=f'Peak Q2: {q2[peak_idx]}',
showarrow=True, arrowhead=2,
font=dict(color='#EF553B', size=12),
)
# Reference line for target
fig.add_hline(y=70, line_dash='dash', line_color='gray',
annotation_text='Target 70', annotation_position='right')
fig.update_layout(
title='Q1 vs Q2 Sales by Product',
barmode='group',
template='plotly_dark',
height=420,
legend=dict(orientation='h', y=1.05),
yaxis_title='Sales ($K)',
)
fig.show()import plotly.graph_objects as go
import numpy as np
np.random.seed(13)
categories = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta']
q1 = np.random.randint(20, 100, 6)
q2 = np.random.randint(20, 100, 6)
q3 = np.random.randint(20, 100, 6)
q4 = np.random.randint(20, 100, 6)
# All four quarter traces β only Q1 visible by default
quarters_data = [
go.Bar(x=categories, y=q1, name='Q1', marker_color='#636EFA', visible=True),
go.Bar(x=categories, y=q2, name='Q2', marker_color='#EF553B', visible=False),
go.Bar(x=categories, y=q3, name='Q3', marker_color='#00CC96', visible=False),
go.Bar(x=categories, y=q4, name='Q4', marker_color='#AB63FA', visible=False),
]
fig = go.Figure(data=quarters_data)
# Dropdown buttons β each shows only one quarter
buttons = []
for i, label in enumerate(['Q1', 'Q2', 'Q3', 'Q4']):
vis = [j == i for j in range(4)]
buttons.append(dict(
label=label,
method='update',
args=[{'visible': vis},
{'title': f'{label} Sales by Product',
'yaxis': {'title': f'{label} Revenue ($K)'}}],
))
fig.update_layout(
title='Q1 Sales by Product',
yaxis_title='Q1 Revenue ($K)',
height=420,
template='plotly_dark',
updatemenus=[dict(
type='dropdown',
direction='down',
x=0.01, y=1.12, xanchor='left',
showactive=True,
buttons=buttons,
bgcolor='#1e293b',
bordercolor='#475569',
font=dict(color='white'),
)],
annotations=[dict(text='Select Quarter:', x=0, y=1.16,
xref='paper', yref='paper',
showarrow=False, font=dict(color='#94a3b8'))],
)
fig.show()
print("Dropdown buttons:", len(buttons))
print("Available quarters: Q1, Q2, Q3, Q4")import plotly.graph_objects as go
import pandas as pd
import numpy as np
np.random.seed(42)
months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
revenue = np.array([4.2,3.8,5.1,5.8,6.2,7.0,6.5,7.8,8.1,7.5,9.2,10.4])
target = np.full(12, 6.0)
prev_yr = revenue * np.random.uniform(0.75, 0.95, 12)
fig = go.Figure()
# Previous year (subtle background)
fig.add_trace(go.Bar(x=months, y=prev_yr, name='2023',
marker_color='rgba(100,100,180,0.3)', showlegend=True))
# Current year bars
fig.add_trace(go.Bar(x=months, y=revenue, name='2024',
marker_color=['#00CC96' if r>=t else '#EF553B'
for r,t in zip(revenue, target)]))
# Target line
fig.add_trace(go.Scatter(x=months, y=target, name='Target',
line=dict(color='white', dash='dash', width=2),
mode='lines'))
# YoY growth annotations
for i, (m, r, p) in enumerate(zip(months, revenue, prev_yr)):
yoy = (r-p)/p*100
fig.add_annotation(x=m, y=r+0.15, text=f'+{yoy:.0f}%',
font=dict(size=8, color='#aaa'), showarrow=False)
fig.update_layout(
title=dict(text='2024 Monthly Revenue ($M) vs Target & Prior Year',
font=dict(size=15, color='white'), x=0.5),
barmode='overlay',
plot_bgcolor='#111827', paper_bgcolor='#0f172a',
font=dict(color='#cbd5e1'),
xaxis=dict(gridcolor='#1e293b'),
yaxis=dict(gridcolor='#1e293b', title='Revenue ($M)'),
legend=dict(orientation='h', y=1.08),
height=460,
)
fig.show()import plotly.graph_objects as go
import numpy as np
np.random.seed(7)
months = ['Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec']
revenue = np.random.randint(50, 121, 12)
target = np.full(12, 80)
fig = go.Figure()
# Bar trace for actual revenue
# TODO: fig.add_trace(go.Bar(
# x=months, y=revenue, name='Actual',
# marker_color=['#00CC96' if r >= 80 else '#EF553B' for r in revenue],
# ))
# Line trace for target
# TODO: fig.add_trace(go.Scatter(
# x=months, y=target, name='Target',
# line=dict(color='white', dash='dash', width=2), mode='lines',
# ))
# Annotation at peak month
# TODO: peak = int(np.argmax(revenue))
# TODO: fig.add_annotation(
# x=months[peak], y=revenue[peak] + 3,
# text=f'Peak: {revenue[peak]}',
# showarrow=True, arrowhead=2,
# font=dict(color='#00CC96', size=12),
# )
# Dark theme layout
# TODO: fig.update_layout(
# title=dict(text='Monthly Revenue vs Target', x=0.5,
# font=dict(size=15, color='white')),
# plot_bgcolor='#111827', paper_bgcolor='#0f172a',
# font=dict(color='#cbd5e1'),
# xaxis=dict(gridcolor='#1e293b'),
# yaxis=dict(gridcolor='#1e293b', title='Revenue ($K)'),
# barmode='overlay', height=430,
# )
# TODO: fig.show()animation_frame adds a play button to animate over a variable (e.g. year, month). Great for showing trends over time.
import plotly.express as px
# Classic animated gapminder chart
df = px.data.gapminder()
fig = px.scatter(
df, x='gdpPercap', y='lifeExp',
animation_frame='year',
animation_group='country',
size='pop', color='continent',
hover_name='country',
log_x=True, size_max=55,
range_x=[100, 100000], range_y=[25, 90],
title='World Development 1952β2007',
labels={'gdpPercap': 'GDP per Capita', 'lifeExp': 'Life Expectancy'},
)
fig.update_layout(height=520)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
products = ['Widget', 'Gadget', 'Doohickey', 'Gizmo', 'Thingamajig']
quarters = ['Q1','Q2','Q3','Q4']
rows = []
sales = {p: np.random.uniform(50, 200) for p in products}
for q in quarters:
for p in products:
sales[p] += np.random.uniform(-20, 40)
rows.append({'quarter': q, 'product': p, 'sales': max(sales[p], 10)})
df = pd.DataFrame(rows)
fig = px.bar(df, x='sales', y='product',
animation_frame='quarter',
orientation='h',
color='product',
range_x=[0, 400],
title='Product Sales Race by Quarter',
labels={'sales': 'Cumulative Sales ($K)'},
color_discrete_sequence=px.colors.qualitative.Bold)
fig.update_layout(showlegend=False, height=380)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(3)
years = list(range(2015, 2026))
regions = ['North', 'South', 'East', 'West']
rows = []
for region in regions:
revenue = np.random.uniform(80, 120)
customers = np.random.randint(500, 2000)
for yr in years:
revenue += np.random.uniform(-5, 12)
customers += np.random.randint(-50, 150)
rows.append({
'year': yr,
'region': region,
'revenue': round(revenue, 1),
'customers': max(customers, 100),
'margin': round(np.random.uniform(0.1, 0.4), 2),
})
df = pd.DataFrame(rows)
fig = px.scatter(
df, x='customers', y='revenue',
animation_frame='year', animation_group='region',
color='region', size='margin', size_max=40,
hover_name='region', hover_data=['margin'],
range_x=[0, 4000], range_y=[50, 250],
title='Revenue vs Customers by Region (Animated 2015-2025)',
labels={'revenue': 'Revenue ($K)', 'customers': 'Active Customers'},
)
fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 800
fig.layout.updatemenus[0].buttons[0].args[1]['transition']['duration'] = 400
fig.update_layout(height=480)
fig.show()import plotly.graph_objects as go
import numpy as np
np.random.seed(22)
n_frames = 20
x = np.linspace(0, 4 * np.pi, 200)
# Each frame reveals more of the wave, with noise that changes each step
frames = []
for k in range(1, n_frames + 1):
end = int(len(x) * k / n_frames)
noise = np.random.randn(end) * 0.15
y = np.sin(x[:end]) * np.exp(-x[:end] * 0.08) + noise
frames.append(go.Frame(
data=[go.Scatter(x=x[:end], y=y,
mode='lines',
line=dict(color='#636EFA', width=2))],
name=str(k),
layout=go.Layout(title_text=f'Damped Wave β Step {k}/{n_frames}'),
))
# Initial trace (first frame)
y0 = np.sin(x[:1]) * np.exp(-x[:1] * 0.08)
fig = go.Figure(
data=[go.Scatter(x=x[:1], y=y0, mode='lines',
line=dict(color='#636EFA', width=2))],
frames=frames,
)
# Play/Pause buttons + slider
fig.update_layout(
title='Animated Damped Wave β Frames & Slider',
xaxis=dict(range=[0, 4*np.pi], title='x'),
yaxis=dict(range=[-1.3, 1.3], title='Amplitude'),
height=460,
updatemenus=[dict(
type='buttons', showactive=False,
y=1.05, x=0, xanchor='left',
buttons=[
dict(label='Play',
method='animate',
args=[None, {'frame': {'duration': 120, 'redraw': True},
'fromcurrent': True}]),
dict(label='Pause',
method='animate',
args=[[None], {'frame': {'duration': 0},
'mode': 'immediate'}]),
],
)],
sliders=[dict(
steps=[dict(method='animate', args=[[str(k)],
{'mode': 'immediate', 'frame': {'duration': 120}}],
label=str(k)) for k in range(1, n_frames+1)],
transition=dict(duration=80),
x=0, y=0, len=1.0,
currentvalue=dict(prefix='Step: ', visible=True),
)],
)
fig.show()
print(f"Total frames: {len(fig.frames)}")import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(10)
countries = ['US','China','India','Germany','Brazil','UK','Japan','France','Canada','Australia']
continents = ['Americas','Asia','Asia','Europe','Americas','Europe','Asia','Europe','Americas','Oceania']
years = list(range(1990, 2025, 5))
rows = []
for i, (c, cont) in enumerate(zip(countries, continents)):
gdp = np.random.uniform(5000, 60000)
co2 = np.random.uniform(2, 16)
pop = np.random.uniform(20, 1400)
for yr in years:
gdp *= np.random.uniform(1.01, 1.05)
co2 *= np.random.uniform(0.97, 1.02)
rows.append({'country':c,'continent':cont,'year':yr,
'gdp_pc':round(gdp,0),'co2_pc':round(co2,2),'pop':round(pop,1)})
df = pd.DataFrame(rows)
fig = px.scatter(
df, x='gdp_pc', y='co2_pc',
animation_frame='year', animation_group='country',
size='pop', color='continent',
hover_name='country',
log_x=True, size_max=45,
range_x=[3000, 120000], range_y=[0, 20],
title='CO2 Emissions vs GDP per Capita (1990β2024)',
labels={'gdp_pc': 'GDP per Capita ($, log)', 'co2_pc': 'CO2 per Capita (tonnes)'},
)
fig.update_layout(height=520)
fig.show()import plotly.express as px
df = px.data.gapminder()
# Print unique years that will be animation frames
# TODO: years = sorted(df['year'].unique())
# TODO: print(f"Animation frames: {len(years)} years from {years[0]} to {years[-1]}")
# Build animated choropleth
# TODO: fig = px.choropleth(
# df,
# locations='iso_alpha',
# color='lifeExp',
# hover_name='country',
# hover_data=['gdpPercap', 'pop'],
# animation_frame='year',
# color_continuous_scale='RdYlGn',
# range_color=[30, 85],
# title='World Life Expectancy 1952-2007 (Animated)',
# labels={'lifeExp': 'Life Expectancy'},
# )
# TODO: fig.update_layout(height=500,
# coloraxis_colorbar=dict(title='Life Exp (years)'))
# TODO: fig.show()px.choropleth and px.scatter_geo create world or country-level maps. px.scatter_mapbox uses tile maps for city-level data.
import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.choropleth(
df, locations='iso_alpha',
color='lifeExp',
hover_name='country',
hover_data=['gdpPercap', 'pop'],
color_continuous_scale='RdYlGn',
range_color=[40, 85],
title='Life Expectancy by Country (2007)',
labels={'lifeExp': 'Life Expectancy'}
)
fig.update_layout(height=480, coloraxis_colorbar=dict(title='Years'))
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(7)
cities = pd.DataFrame({
'city': ['New York','Los Angeles','Chicago','Houston','Phoenix',
'Philadelphia','San Antonio','San Diego','Dallas','San Jose'],
'lat': [40.71,34.05,41.88,29.76,33.45,39.95,29.42,32.72,32.78,37.34],
'lon': [-74.01,-118.24,-87.63,-95.37,-112.07,-75.16,-98.49,-117.16,-96.80,-121.89],
'revenue': np.random.uniform(50, 500, 10).round(1),
'customers':np.random.randint(1000, 50000, 10),
})
fig = px.scatter_geo(cities, lat='lat', lon='lon',
size='revenue', color='revenue',
hover_name='city',
hover_data=['customers'],
color_continuous_scale='Reds',
scope='usa',
title='US Market Revenue by City')
fig.update_layout(height=430)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(21)
# Major world cities with lat/lon
cities = pd.DataFrame({
'city': ['New York','London','Tokyo','Sydney','Sao Paulo',
'Mumbai','Cairo','Lagos','Moscow','Beijing'],
'country': ['USA','UK','Japan','Australia','Brazil',
'India','Egypt','Nigeria','Russia','China'],
'lat': [40.71, 51.51, 35.68, -33.87, -23.55,
19.08, 30.04, 6.52, 55.75, 39.91],
'lon': [-74.01, -0.13, 139.69, 151.21, -46.63,
72.88, 31.24, 3.40, 37.62, 116.41],
'gdp_bn': np.random.uniform(100, 800, 10).round(1),
'pop_m': np.random.uniform(5, 35, 10).round(1),
})
fig = px.scatter_geo(
cities, lat='lat', lon='lon',
size='gdp_bn', color='gdp_bn',
hover_name='city', hover_data=['country', 'pop_m'],
color_continuous_scale='Plasma',
projection='natural earth',
title='World City GDP β Bubble Map',
labels={'gdp_bn': 'GDP ($B)'},
size_max=40,
)
fig.update_layout(height=450)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(33)
# Sample delivery locations across a city (centred on Chicago)
n = 60
lat_center, lon_center = 41.88, -87.63
deliveries = pd.DataFrame({
'id': [f'DEL-{i:03d}' for i in range(n)],
'lat': lat_center + np.random.randn(n) * 0.08,
'lon': lon_center + np.random.randn(n) * 0.12,
'status': np.random.choice(['Delivered', 'In Transit', 'Failed'], n,
p=[0.65, 0.25, 0.10]),
'packages': np.random.randint(1, 20, n),
'duration': np.random.uniform(5, 60, n).round(1),
})
color_map = {'Delivered': '#00CC96', 'In Transit': '#636EFA', 'Failed': '#EF553B'}
fig = px.scatter_mapbox(
deliveries,
lat='lat', lon='lon',
color='status',
size='packages',
size_max=20,
hover_name='id',
hover_data={'packages': True, 'duration': ':.1f', 'lat': False, 'lon': False},
color_discrete_map=color_map,
zoom=11,
center={'lat': lat_center, 'lon': lon_center},
title='Delivery Status Map β Chicago (Open-Street Tiles)',
mapbox_style='open-street-map',
)
fig.update_layout(height=520, legend_title_text='Status')
fig.show()
status_counts = deliveries['status'].value_counts()
print("Delivery summary:")
for status, count in status_counts.items():
print(f" {status}: {count}")import plotly.express as px
import pandas as pd
import numpy as np
# ISO codes and country names
data = {
'iso_alpha': ['USA','GBR','DEU','FRA','JPN','BRA','IND','AUS','CAN','MEX',
'CHN','KOR','SGP','ZAF','NLD','SWE','NOR','CHE','ITA','ESP'],
'country': ['US','UK','Germany','France','Japan','Brazil','India','Australia',
'Canada','Mexico','China','S.Korea','Singapore','S.Africa',
'Netherlands','Sweden','Norway','Switzerland','Italy','Spain'],
'target': [1000,300,280,220,350,150,200,180,260,120,
500,180,90,80,130,110,100,120,160,140],
}
np.random.seed(42)
df = pd.DataFrame(data)
df['actual'] = (df['target'] * np.random.uniform(0.6, 1.3, len(df))).round(0)
df['vs_target'] = ((df['actual'] - df['target']) / df['target'] * 100).round(1)
fig = px.choropleth(
df, locations='iso_alpha',
color='vs_target',
hover_name='country',
hover_data={'actual': True, 'target': True, 'vs_target': True},
color_continuous_scale='RdYlGn',
range_color=[-40, 40],
title='YTD Revenue vs Target by Country (%)',
labels={'vs_target': 'vs Target (%)'}
)
fig.update_layout(
height=480,
coloraxis_colorbar=dict(title='vs Target %', ticksuffix='%')
)
fig.show()import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(55)
cities = pd.DataFrame({
'city': ['New York', 'London', 'Tokyo', 'Sydney',
'Dubai', 'Singapore', 'Toronto', 'Paris'],
'lat': [40.71, 51.51, 35.68, -33.87, 25.20, 1.35, 43.65, 48.85],
'lon': [-74.01, -0.13, 139.69, 151.21, 55.27, 103.82, -79.38, 2.35],
'revenue': np.random.uniform(100, 900, 8).round(1),
'customers': np.random.randint(5000, 50000, 8),
})
# Print highest revenue city
# TODO: top = cities.loc[cities['revenue'].idxmax(), 'city']
# TODO: print(f"Highest revenue city: {top} (${cities['revenue'].max():.1f}M)")
# TODO: fig = px.scatter_geo(
# cities, lat='lat', lon='lon',
# size='revenue', color='customers',
# hover_name='city',
# hover_data=['revenue', 'customers'],
# color_continuous_scale='Viridis',
# projection='natural earth',
# title='Global City Revenue & Customer Base',
# size_max=40,
# )
# TODO: fig.update_layout(height=460)
# TODO: fig.show()Save charts as interactive HTML, static PNG/PDF/SVG (requires kaleido), or embed in web apps via fig.to_json().
import plotly.express as px
import os
df = px.data.iris()
fig = px.scatter(df, x='sepal_length', y='sepal_width',
color='species', title='Iris β Export Demo')
# ββ Standalone HTML (fully self-contained, shareable) ββ
fig.write_html('iris_chart.html', include_plotlyjs='cdn')
print(f"HTML: {os.path.getsize('iris_chart.html') / 1024:.1f} KB")
# ββ JSON (for embedding in web apps) ββ
json_str = fig.to_json()
print(f"JSON length: {len(json_str):,} chars")
# ββ Show in browser / Jupyter ββ
fig.show()
# Cleanup
os.remove('iris_chart.html')import plotly.express as px
import os
# Note: requires pip install kaleido
df = px.data.tips()
fig = px.box(df, x='day', y='total_bill', color='time',
title='Total Bill by Day')
try:
fig.write_image('chart.png', width=800, height=500, scale=2)
fig.write_image('chart.pdf')
fig.write_image('chart.svg')
print("Saved PNG, PDF, SVG")
for f in ['chart.png','chart.pdf','chart.svg']:
if os.path.exists(f): os.remove(f)
except Exception as e:
print(f"kaleido not installed: {e}")
print("Install with: pip install kaleido")
fig.show()import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import os
np.random.seed(42)
# Build two figures
df_iris = px.data.iris()
fig1 = px.scatter(df_iris, x='sepal_length', y='petal_length',
color='species', title='Iris Scatter')
months = list(range(1, 13))
revenue = 100 + np.cumsum(np.random.randn(12) * 5)
fig2 = px.line(x=months, y=revenue, markers=True,
title='Monthly Revenue',
labels={'x': 'Month', 'y': 'Revenue ($K)'})
# Combine into one HTML file manually
html_parts = [
'<html><head><meta charset="UTF-8"><title>Combined Report</title></head><body>',
'<h1 style="font-family:sans-serif;padding:20px">Multi-Chart Report</h1>',
fig1.to_html(full_html=False, include_plotlyjs='cdn'),
fig2.to_html(full_html=False, include_plotlyjs=False),
'</body></html>',
]
combined_html = '\n'.join(html_parts)
out = 'combined_report.html'
with open(out, 'w', encoding='utf-8') as f:
f.write(combined_html)
print(f"Combined report: {out} ({os.path.getsize(out)/1024:.1f} KB)")
print("Contains 2 fully interactive charts in one file.")
os.remove(out)import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
import os
rng = np.random.default_rng(42)
df = pd.DataFrame({
'month': pd.date_range('2024-01-01', periods=12, freq='ME').strftime('%b'),
'revenue': np.cumsum(rng.uniform(50, 200, 12)) + 500,
'target': np.linspace(600, 1800, 12),
})
fig = go.Figure()
fig.add_trace(go.Bar(x=df['month'], y=df['revenue'], name='Revenue',
marker_color='steelblue', opacity=0.85))
fig.add_trace(go.Scatter(x=df['month'], y=df['target'], name='Target',
line=dict(color='tomato', width=2, dash='dash'),
mode='lines+markers', marker_size=7))
fig.update_layout(
title='Monthly Revenue vs Target', title_font_size=16,
xaxis_title='Month', yaxis_title='Revenue ($K)',
legend=dict(x=0.01, y=0.99), height=450,
template='plotly_white',
)
# Export as PNG (requires kaleido: pip install kaleido)
try:
fig.write_image('revenue_chart.png', width=900, height=450, scale=2)
size = os.path.getsize('revenue_chart.png')
print(f'PNG exported: revenue_chart.png ({size/1024:.1f} KB)')
os.remove('revenue_chart.png')
except Exception as e:
print(f'PNG export requires kaleido: pip install kaleido ({e})')
# Export as interactive HTML
html_str = fig.to_html(full_html=True, include_plotlyjs='cdn')
with open('revenue_chart.html', 'w') as f:
f.write(html_str)
size = os.path.getsize('revenue_chart.html')
print(f'HTML exported: revenue_chart.html ({size/1024:.1f} KB)')
os.remove('revenue_chart.html')
# Export figure dict (JSON-serializable)
fig_dict = fig.to_dict()
print(f'Figure dict keys: {list(fig_dict.keys())}')
print(f'Number of traces: {len(fig_dict["data"])}')import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
import numpy as np
import os
np.random.seed(42)
months = pd.date_range('2024-01', periods=12, freq='MS')
revenue = 100 + np.cumsum(np.random.randn(12) * 8) + np.arange(12) * 4
cac = np.random.uniform(30, 80, 12)
churn = np.random.uniform(0.02, 0.08, 12)
fig = make_subplots(
rows=2, cols=2,
subplot_titles=['Monthly Revenue ($K)', 'Customer Acquisition Cost ($)',
'Churn Rate (%)', 'Revenue vs CAC'],
specs=[[{},{}],[{},{}]]
)
fig.add_trace(go.Scatter(x=months, y=revenue.round(1),
fill='tozeroy', line=dict(color='#636EFA')), row=1, col=1)
fig.add_trace(go.Bar(x=months, y=cac.round(1),
marker_color='#EF553B'), row=1, col=2)
fig.add_trace(go.Scatter(x=months, y=(churn*100).round(2),
mode='lines+markers', line=dict(color='#AB63FA')), row=2, col=1)
fig.add_trace(go.Scatter(x=cac, y=revenue,
mode='markers', text=[m.strftime('%b') for m in months],
textposition='top center',
marker=dict(color='#00CC96', size=9)), row=2, col=2)
fig.update_layout(title='SaaS KPI Report β 2024', height=600,
showlegend=False, template='plotly_dark')
# Export as standalone HTML
out = 'saas_report.html'
fig.write_html(out, include_plotlyjs='cdn', full_html=True)
size_kb = os.path.getsize(out) / 1024
print(f"Report saved: {out} ({size_kb:.0f} KB)")
print("Open in any browser β fully interactive, no server needed.")
fig.show()
os.remove(out)import plotly.express as px
import os
# Figure 1: scatter
df_gap = px.data.gapminder().query("year == 2007")
# TODO: fig1 = px.scatter(df_gap, x='gdpPercap', y='lifeExp',
# size='pop', color='continent', log_x=True,
# title='GDP vs Life Expectancy 2007')
# Figure 2: bar β top 10 countries by population
# TODO: top10 = df_gap.nlargest(10, 'pop')
# TODO: fig2 = px.bar(top10, x='country', y='pop',
# color='continent', title='Top 10 Countries by Population')
# Figure 3: line β Europe average life exp over time
# TODO: eur = px.data.gapminder().query("continent == 'Europe'")
# TODO: avg = eur.groupby('year')['lifeExp'].mean().reset_index()
# TODO: fig3 = px.line(avg, x='year', y='lifeExp', markers=True,
# title='Europe Average Life Expectancy Over Time')
# Combine and save
# TODO: html_body = '\n'.join([
# '<html><head><meta charset="UTF-8"><title>My Report</title></head><body>',
# '<h1 style="font-family:sans-serif;padding:16px">My Plotly Report</h1>',
# fig1.to_html(full_html=False, include_plotlyjs='cdn'),
# fig2.to_html(full_html=False, include_plotlyjs=False),
# fig3.to_html(full_html=False, include_plotlyjs=False),
# '</body></html>',
# ])
# TODO: out = 'my_report.html'
# TODO: with open(out, 'w', encoding='utf-8') as f:
# f.write(html_body)
# TODO: print(f"Report saved: {out} ({os.path.getsize(out)/1024:.1f} KB)")
# TODO: os.remove(out)Create interactive 3D visualizations β scatter plots, surface plots, and line trajectories β that users can rotate and zoom in the browser.
import plotly.graph_objects as go
import numpy as np
np.random.seed(42)
x, y, z = np.random.randn(3, 200)
fig = go.Figure(data=[go.Scatter3d(
x=x, y=y, z=z, mode='markers',
marker=dict(size=5, color=z, colorscale='Viridis', showscale=True,
colorbar=dict(title='Z value'))
)])
fig.update_layout(title='3D Scatter Plot',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'))
fig.write_html('scatter3d.html')
print('Saved scatter3d.html')import plotly.graph_objects as go
import numpy as np
x = np.linspace(-3, 3, 50)
y = np.linspace(-3, 3, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = go.Figure(data=[go.Surface(z=Z, x=X, y=Y, colorscale='RdBu')])
fig.update_layout(
title='3D Surface: sin(sqrt(x^2+y^2))',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='sin(r)'),
width=700, height=600
)
fig.write_html('surface3d.html')
print('Saved surface3d.html')import plotly.graph_objects as go
import numpy as np
t = np.linspace(0, 10*np.pi, 500)
x = np.sin(t)
y = np.cos(t)
z = t / (10*np.pi)
fig = go.Figure(data=[go.Scatter3d(
x=x, y=y, z=z, mode='lines',
line=dict(color=t, colorscale='Plasma', width=4)
)])
fig.update_layout(
title='3D Spiral Trajectory',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Height')
)
fig.write_html('spiral3d.html')
print('Saved spiral3d.html')import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
np.random.seed(42)
fig = make_subplots(rows=1, cols=2,
specs=[[{'type':'scatter3d'},{'type':'surface'}]],
subplot_titles=['Scatter3D', 'Surface'])
x, y, z = np.random.randn(3, 100)
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode='markers',
marker=dict(size=4, color=z, colorscale='Viridis')), row=1, col=1)
t = np.linspace(-2, 2, 30)
X, Y = np.meshgrid(t, t)
Z = np.sin(X) * np.cos(Y)
fig.add_trace(go.Surface(x=X, y=Y, z=Z, colorscale='Plasma', showscale=False), row=1, col=2)
fig.update_layout(title='3D Subplots', height=500)
fig.write_html('3d_subplots.html')
print('Saved 3d_subplots.html')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
n = 100
df = pd.DataFrame({
'region': np.random.randint(1, 6, n).astype(float),
'quarter': np.random.randint(1, 5, n).astype(float),
'revenue': np.random.exponential(50000, n),
'margin': np.random.uniform(0.05, 0.40, n),
'category': np.random.choice(['Electronics','Clothing','Food'], n),
})
# TODO: 3D scatter with color by category, size by margin
# TODO: save to 'globe_sales.html'Visualize spatial data with choropleth maps, scatter geo, and mapbox β pinpoint patterns across countries, US states, and cities.
import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
states = ['CA','TX','NY','FL','IL','PA','OH','GA','NC','MI',
'WA','AZ','MA','TN','IN','MO','MD','CO','WI','MN']
df = pd.DataFrame({'state': states, 'value': np.random.randint(100, 10000, len(states))})
fig = px.choropleth(df, locations='state', color='value',
locationmode='USA-states', scope='usa',
color_continuous_scale='Blues',
title='Simulated Metric by US State')
fig.write_html('us_choropleth.html')
print('Saved us_choropleth.html')import plotly.express as px
import pandas as pd
import numpy as np
countries = ['USA','CHN','IND','BRA','RUS','AUS','CAN','DEU','GBR','FRA',
'JPN','KOR','MEX','IDN','NGA','EGY','ZAF','ARG','SAU','TUR']
np.random.seed(42)
df = pd.DataFrame({'country': countries, 'gdp_per_capita': np.random.uniform(5000, 65000, len(countries))})
fig = px.choropleth(df, locations='country', color='gdp_per_capita',
color_continuous_scale='Plasma',
title='Simulated GDP Per Capita by Country')
fig.write_html('world_choropleth.html')
print('Saved world_choropleth.html')import plotly.express as px
import pandas as pd
cities = pd.DataFrame({
'city': ['New York','Los Angeles','Chicago','Houston','Phoenix',
'Philadelphia','San Antonio','San Diego','Dallas','San Jose'],
'lat': [40.71, 34.05, 41.85, 29.76, 33.45, 39.95, 29.42, 32.72, 32.78, 37.34],
'lon': [-74.01,-118.24,-87.65,-95.37,-112.07,-75.17,-98.49,-117.16,-96.80,-121.89],
'pop': [8336817,3979576,2693976,2320268,1608139,1603797,1434625,1386932,1304379,1035317],
'region': ['NE','West','MW','South','West','NE','South','West','South','West'],
})
fig = px.scatter_geo(cities, lat='lat', lon='lon', size='pop', color='region',
hover_name='city', scope='usa', size_max=40,
title='Top 10 US Cities by Population')
fig.write_html('city_bubble.html')
print('Saved city_bubble.html')import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'lat': np.random.uniform(25, 49, 50),
'lon': np.random.uniform(-125, -67, 50),
'value': np.random.uniform(10, 100, 50),
'label': [f'Site {i}' for i in range(50)],
})
fig = px.scatter_mapbox(df, lat='lat', lon='lon', size='value',
color='value', color_continuous_scale='Reds',
hover_name='label', zoom=3,
mapbox_style='open-street-map',
title='Random Sites across the US')
fig.write_html('mapbox_scatter.html')
print('Saved mapbox_scatter.html')import plotly.express as px
import pandas as pd
import numpy as np
countries = ['USA','CHN','IND','BRA','RUS','AUS','CAN','DEU','GBR','FRA',
'JPN','KOR','MEX','IDN','NGA','EGY','ZAF','ARG','SAU','TUR']
np.random.seed(42)
df = pd.DataFrame({'country': countries, 'avg_temp': np.random.uniform(5, 30, len(countries))})
# TODO: choropleth with locations='country', color='avg_temp'
# TODO: colorscale='RdBu_r', color midpoint=15
# TODO: save to 'world_temp.html'Bring data to life with Plotly animations β animated scatter plots, bar chart races, and frame-by-frame time-lapse visualizations.
import plotly.express as px
try:
gapminder = px.data.gapminder()
fig = px.scatter(gapminder, x='gdpPercap', y='lifeExp',
animation_frame='year', animation_group='country',
size='pop', color='continent', hover_name='country',
log_x=True, size_max=55,
range_x=[100, 100000], range_y=[25, 90],
title='Gapminder: GDP vs Life Expectancy over Time')
fig.write_html('gapminder_animation.html')
print('Saved gapminder_animation.html')
except Exception as e:
print(f'Note: {e}')import plotly.graph_objects as go
import numpy as np
np.random.seed(42)
categories = ['A','B','C','D','E']
years = list(range(2018, 2024))
data = {cat: np.cumsum(np.random.randint(5,20,len(years))) for cat in categories}
frames = []
for i, year in enumerate(years):
vals = [data[c][i] for c in categories]
order = sorted(range(len(vals)), key=lambda x: vals[x])
frames.append(go.Frame(
data=[go.Bar(x=[vals[j] for j in order], y=[categories[j] for j in order],
orientation='h', marker_color='steelblue')],
name=str(year)
))
fig = go.Figure(frames=frames, data=frames[0].data)
fig.update_layout(
title='Bar Chart Race', xaxis_title='Cumulative Value',
updatemenus=[dict(type='buttons', showactive=False,
buttons=[dict(label='Play', method='animate',
args=[None, dict(frame=dict(duration=800))])])]
)
fig.write_html('bar_race.html')
print('Saved bar_race.html')import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
months = pd.date_range('2022-01', periods=24, freq='ME')
categories = ['Electronics','Clothing','Food']
rows = []
for cat in categories:
base = {'Electronics':5000,'Clothing':3000,'Food':7000}[cat]
for m in months:
rows.append({'month': m.strftime('%Y-%m'), 'category': cat,
'revenue': base + np.random.randint(-500, 1500)})
df = pd.DataFrame(rows)
df = df.sort_values('month')
fig = px.line(df, x='month', y='revenue', color='category',
animation_frame='month',
range_y=[df.revenue.min()-200, df.revenue.max()+200],
title='Monthly Revenue Animation')
fig.write_html('animated_line.html')
print('Saved animated_line.html')import plotly.graph_objects as go
import numpy as np
x = np.linspace(0, 2*np.pi, 200)
steps = []
figs_data = []
for freq in np.linspace(1, 5, 20):
figs_data.append(go.Scatter(x=x, y=np.sin(freq*x), mode='lines',
name=f'f={freq:.1f}'))
steps = [dict(method='update', args=[{'y': [np.sin(f*x)], 'name': [f'f={f:.1f}']}],
label=f'{f:.1f}') for f in np.linspace(1, 5, 20)]
fig = go.Figure(data=[figs_data[0]])
fig.update_layout(
title='Interactive Slider: sin(f*x)',
sliders=[dict(active=0, steps=steps, currentvalue=dict(prefix='Frequency: '))],
xaxis_title='x', yaxis_title='sin(f*x)', yaxis_range=[-1.2, 1.2]
)
fig.write_html('slider_sine.html')
print('Saved slider_sine.html')import plotly.graph_objects as go
import numpy as np
continents = ['Africa','Asia','Europe','Americas','Oceania']
years = list(range(2000, 2024))
np.random.seed(42)
base = [0.8, 3.7, 0.7, 0.9, 0.03]
growth = [0.03, 0.01, 0.002, 0.01, 0.015]
# TODO: build frames list (one per year)
# TODO: each frame: go.Frame with go.Bar showing populations
# TODO: fig with play button in updatemenus
# TODO: save to 'population_animation.html'# Dash apps run as web servers - this shows the code pattern
print('Dash app structure:')
print('''
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
import plotly.express as px
app = Dash(__name__)
app.layout = html.Div([
html.H1("Dashboard"),
dcc.Dropdown(id="metric", options=["Revenue","Users"], value="Revenue"),
dcc.Graph(id="chart"),
])
@app.callback(Output("chart","figure"), Input("metric","value"))
def update(metric):
import numpy as np, pandas as pd
df = pd.DataFrame({"date": pd.date_range("2024-01-01", periods=30),
metric: 100 + np.cumsum(np.random.randn(30))})
return px.line(df, x="date", y=metric, title=f"{metric} over Time")
if __name__ == "__main__":
app.run_server(debug=True)
''')
print("Run with: python app.py")import plotly.graph_objects as go
import plotly.express as px
import numpy as np
import pandas as pd
def make_dashboard_fig(category='All'):
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=90, freq='D')
cats = ['Electronics','Apparel','Food']
dfs = [pd.DataFrame({'date':dates,'revenue':np.random.exponential(1000,90),'cat':c}) for c in cats]
df = pd.concat(dfs)
if category != 'All':
df = df[df.cat == category]
fig = px.area(df.groupby(['date','cat'])['revenue'].sum().reset_index(),
x='date', y='revenue', color='cat',
title=f'Revenue: {category}', template='plotly_white')
fig.update_layout(hovermode='x unified', height=400)
return fig
fig = make_dashboard_fig('Electronics')
fig.write_html('dash_callback_fig.html')
print(f'Dashboard figure saved - traces: {len(fig.data)}')# Dash DataTable pattern
print('DataTable pattern:')
print('''
from dash import dash_table
import pandas as pd
df = pd.read_csv("data.csv")
table = dash_table.DataTable(
data=df.to_dict("records"),
columns=[{"name": c, "id": c} for c in df.columns],
filter_action="native",
sort_action="native",
page_size=10,
export_format="csv",
style_data_conditional=[{
"if": {"filter_query": "{revenue} > 1000"},
"backgroundColor": "#d4edda",
}],
)
''')
print('Key features: filter_action, sort_action, export_format, conditional styling')# Multi-page Dash 2.x pattern
print('Multi-page Dash pattern:')
print('''
# pages/home.py
import dash
dash.register_page(__name__, path="/")
layout = html.Div([html.H2("Home")])
# pages/analytics.py
dash.register_page(__name__, path="/analytics")
layout = html.Div([dcc.Graph(figure=create_fig())])
# app.py
app = Dash(__name__, use_pages=True)
app.layout = html.Div([
html.Nav([
dcc.Link("Home", href="/"),
dcc.Link("Analytics", href="/analytics"),
]),
dash.page_container
])
''')
print('Each page: dash.register_page(__name__, path="/route")')import plotly.express as px
gap = px.data.gapminder()
def make_fig(year, continent):
# TODO: filter by year and continent ('All' = no continent filter)
# TODO: return px.scatter size='pop', color='country', log_x=True
pass
for year, cont in [(1952,'Asia'), (2007,'Europe'), (2007,'All')]:
fig = make_fig(year, cont)
if fig:
fig.write_html(f'gap_{year}_{cont}.html')import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.choropleth(df, locations='iso_alpha', color='gdpPercap',
hover_name='country', color_continuous_scale='Viridis',
range_color=[0, 50000], title='GDP per Capita (2007)',
labels={'gdpPercap': 'GDP per Capita'})
fig.update_layout(geo=dict(showframe=False, showcoastlines=True))
fig.write_html('choropleth_world.html')
print(f'Choropleth saved - {len(df)} countries')import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({'lat':np.random.uniform(25,50,80),'lon':np.random.uniform(-125,-65,80),
'city':[f'City_{i}' for i in range(80)],
'sales':np.random.exponential(500,80),
'category':np.random.choice(['A','B','C'],80)})
fig = px.scatter_geo(df, lat='lat', lon='lon', size='sales', color='category',
hover_name='city', scope='usa', title='Sales Distribution USA', size_max=20)
fig.write_html('scatter_map.html')
print(f'Scatter geo saved - {len(df)} points')import plotly.express as px
df = px.data.gapminder()
fig = px.choropleth(df, locations='iso_alpha', color='lifeExp',
hover_name='country', animation_frame='year',
color_continuous_scale='RdYlGn', range_color=[30, 90],
title='Life Expectancy Over Time')
fig.update_layout(geo=dict(showframe=False))
fig.write_html('choropleth_animated.html')
print(f'Animated choropleth - {df.year.nunique()} frames')import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({'lat':np.random.normal(40.7128,0.05,400),
'lon':np.random.normal(-74.006,0.05,400),
'intensity':np.random.exponential(1,400)})
fig = px.density_mapbox(df, lat='lat', lon='lon', z='intensity', radius=15,
center=dict(lat=40.7128,lon=-74.006), zoom=10,
mapbox_style='open-street-map',
title='Event Density (NYC Area)', color_continuous_scale='Inferno')
fig.write_html('density_map.html')
print(f'Density map saved - {len(df)} events')import plotly.express as px
import pandas as pd
import numpy as np
states = ['AL','AK','AZ','AR','CA','CO','CT','DE','FL','GA','HI','ID','IL','IN','IA',
'KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ',
'NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD','TN','TX','UT','VT',
'VA','WA','WV','WI','WY']
np.random.seed(42)
df = pd.DataFrame({'state':states,'score':np.random.uniform(40,100,len(states))})
# TODO: px.choropleth locationmode='USA-states', color='score', scope='usa'
# TODO: color_continuous_scale='RdYlGn'
# TODO: save 'us_choropleth.html'import plotly.graph_objects as go
import numpy as np
x = y = np.linspace(-3, 3, 60)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = go.Figure(go.Surface(x=X, y=Y, z=Z, colorscale='Viridis',
contours={'z':{'show':True,'start':-1,'end':1,'size':0.25}}))
fig.update_layout(title='3D Surface: sin(sqrt(x2+y2))',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z',
camera=dict(eye=dict(x=1.5, y=-1.5, z=1.2))),
width=700, height=500)
fig.write_html('surface_3d.html')
print('3D surface saved')import plotly.express as px
import numpy as np
np.random.seed(42)
n = 300
x = np.random.randn(n); y = np.random.randn(n)
z = x**2 + y**2 + np.random.randn(n) * 0.5
cats = ['Inner' if v < 2 else 'Middle' if v < 5 else 'Outer' for v in z]
fig = px.scatter_3d(x=x, y=y, z=z, color=cats, symbol=cats,
color_discrete_sequence=px.colors.qualitative.Set1,
title='3D Scatter by Distance from Origin')
fig.update_traces(marker=dict(size=4, opacity=0.7))
fig.write_html('scatter_3d.html')
print(f'3D scatter saved - {n} points')import plotly.graph_objects as go
import numpy as np
t = np.linspace(0, 8*np.pi, 500)
x = np.cos(t)*np.exp(-t/20); y = np.sin(t)*np.exp(-t/20); z = t/(4*np.pi)
fig = go.Figure()
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode='lines',
line=dict(color=t, colorscale='Plasma', width=4), name='Trajectory'))
fig.add_trace(go.Scatter3d(x=[x[0]], y=[y[0]], z=[z[0]], mode='markers',
marker=dict(size=8, color='green'), name='Start'))
fig.add_trace(go.Scatter3d(x=[x[-1]], y=[y[-1]], z=[z[-1]], mode='markers',
marker=dict(size=8, color='red'), name='End'))
fig.update_layout(title='3D Spiral Trajectory',
scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Height'))
fig.write_html('trajectory_3d.html')
print('3D trajectory saved')import plotly.graph_objects as go
import numpy as np
x = y = np.linspace(-3, 3, 60)
X, Y = np.meshgrid(x, y)
Z = (np.sin(X*2)*np.cos(Y*2)*0.5 + (X**2+Y**2)*0.1 + np.exp(-((X-1)**2+(Y-1)**2))*(-1.5))
Z = (Z - Z.min()) / (Z.max() - Z.min()) * 3
px_path, py_path, pz_path = [2.5], [-2.5], [float(Z[0,-1])]
lr = 0.08
for _ in range(60):
ix = int(np.argmin(np.abs(x - px_path[-1])))
iy = int(np.argmin(np.abs(y - py_path[-1])))
gx = (Z[iy, min(ix+1,59)] - Z[iy, max(ix-1,0)]) / 2
gy = (Z[min(iy+1,59), ix] - Z[max(iy-1,0), ix]) / 2
nx, ny = float(np.clip(px_path[-1]-lr*gx,-3,3)), float(np.clip(py_path[-1]-lr*gy,-3,3))
px_path.append(nx); py_path.append(ny)
pz_path.append(float(Z[int(np.argmin(np.abs(y-ny))), int(np.argmin(np.abs(x-nx)))]))
fig = go.Figure([go.Surface(x=X, y=Y, z=Z, colorscale='RdYlGn_r', opacity=0.85),
go.Scatter3d(x=px_path, y=py_path, z=pz_path, mode='lines+markers',
line=dict(color='blue', width=5), marker=dict(size=3),
name='GD Path')])
fig.update_layout(title='Loss Landscape + Gradient Descent', width=750, height=550)
fig.write_html('loss_landscape.html')
print(f'Loss landscape saved - final loss: {pz_path[-1]:.3f}')import plotly.graph_objects as go
import numpy as np
np.random.seed(42)
centers = [(0,0,0),(3,3,0),(0,3,3)]
colors = ['blue','red','green']
fig = go.Figure()
for i,(cx,cy,cz) in enumerate(centers):
n = 50
x = np.random.randn(n)+cx; y = np.random.randn(n)+cy; z = np.random.randn(n)+cz
# TODO: add Scatter3d trace
pass
# TODO: update_layout with title, axis labels
# TODO: fig.write_html('clusters_3d.html')Plotly's go.Candlestick and go.Ohlc render OHLC price data with interactive zoom, range slectors, and volume overlays β essential for financial dashboards.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np, pandas as pd
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=60, freq='B')
close = 100 + np.cumsum(np.random.randn(60) * 1.5)
open_ = close + np.random.randn(60) * 0.8
high = np.maximum(open_, close) + np.abs(np.random.randn(60) * 0.5)
low = np.minimum(open_, close) - np.abs(np.random.randn(60) * 0.5)
volume = np.random.randint(1_000_000, 5_000_000, 60)
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
vertical_spacing=0.03, row_heights=[0.75, 0.25])
fig.add_trace(go.Candlestick(x=dates, open=open_, high=high,
low=low, close=close, name='OHLC',
increasing_line_color='#26a69a',
decreasing_line_color='#ef5350'), row=1, col=1)
colors = ['#26a69a' if c >= o else '#ef5350' for c, o in zip(close, open_)]
fig.add_trace(go.Bar(x=dates, y=volume, name='Volume',
marker_color=colors, opacity=0.7), row=2, col=1)
fig.update_layout(title='OHLC Price + Volume', xaxis_rangeslider_visible=False,
template='plotly_dark', height=500)
fig.update_yaxes(title_text='Price ($)', row=1)
fig.update_yaxes(title_text='Volume', row=2)
fig.write_html('candlestick.html')
print('Chart saved')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=120, freq='B')
close = 150 + np.cumsum(np.random.randn(120) * 2)
open_ = close + np.random.randn(120)
high = np.maximum(open_, close) + np.abs(np.random.randn(120))
low = np.minimum(open_, close) - np.abs(np.random.randn(120))
s = pd.Series(close, index=dates)
ma20 = s.rolling(20).mean()
ma50 = s.rolling(50).mean()
fig = go.Figure()
fig.add_trace(go.Candlestick(x=dates, open=open_, high=high,
low=low, close=close, name='OHLC',
increasing_fillcolor='#26a69a',
decreasing_fillcolor='#ef5350'))
fig.add_trace(go.Scatter(x=dates, y=ma20, name='MA 20',
line=dict(color='orange', width=1.5)))
fig.add_trace(go.Scatter(x=dates, y=ma50, name='MA 50',
line=dict(color='cyan', width=1.5)))
fig.update_layout(title='Candlestick with Moving Averages',
template='plotly_dark', xaxis_rangeslider_visible=False,
hovermode='x unified', height=450)
fig.write_html('candlestick_ma.html')
print('Saved with MA20/MA50')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(0)
dates = pd.date_range('2022-01-01', periods=250, freq='B')
close = 200 + np.cumsum(np.random.randn(250) * 2.5)
open_ = close + np.random.randn(250) * 1.2
high = np.maximum(open_, close) + np.abs(np.random.randn(250))
low = np.minimum(open_, close) - np.abs(np.random.randn(250))
fig = go.Figure(go.Ohlc(x=dates, open=open_, high=high,
low=low, close=close, name='OHLC Bars',
increasing_line_color='lime',
decreasing_line_color='red'))
fig.update_layout(
title='OHLC with Range Selectors',
template='plotly_dark',
xaxis=dict(
rangeselector=dict(
buttons=[
dict(count=1, label='1M', step='month', stepmode='backward'),
dict(count=3, label='3M', step='month', stepmode='backward'),
dict(count=6, label='6M', step='month', stepmode='backward'),
dict(step='all', label='All'),
]
),
rangeslider=dict(visible=True),
type='date',
),
height=450,
)
fig.write_html('ohlc_range.html')
print('OHLC chart with range selector saved')import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np, pandas as pd
np.random.seed(7)
tickers = ['AAPL', 'MSFT', 'GOOG']
dates = pd.date_range('2024-01-01', periods=40, freq='B')
fig = make_subplots(rows=len(tickers), cols=1, shared_xaxes=True,
subplot_titles=tickers, vertical_spacing=0.06)
for row, ticker in enumerate(tickers, 1):
c = 150 + row*20 + np.cumsum(np.random.randn(40)*2)
o = c + np.random.randn(40)
h = np.maximum(o,c) + np.abs(np.random.randn(40)*0.5)
l = np.minimum(o,c) - np.abs(np.random.randn(40)*0.5)
fig.add_trace(go.Candlestick(x=dates, open=o, high=h, low=l, close=c,
name=ticker, showlegend=True), row=row, col=1)
fig.update_xaxes(rangeslider_visible=False)
fig.update_layout(title='Portfolio Overview', template='plotly_dark', height=700)
fig.write_html('portfolio.html')
print(f'Portfolio chart: {len(tickers)} tickers saved')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=90, freq='B')
close = 100 + np.cumsum(np.random.randn(90) * 1.5)
open_ = close + np.random.randn(90)
high = np.maximum(open_, close) + np.abs(np.random.randn(90)*0.5)
low = np.minimum(open_, close) - np.abs(np.random.randn(90)*0.5)
s = pd.Series(close, index=dates)
# TODO: compute ma = s.rolling(20).mean()
# TODO: std = s.rolling(20).std()
# TODO: upper = ma + 2*std, lower = ma - 2*std
fig = go.Figure()
# TODO: add Candlestick trace
# TODO: add Scatter traces for upper, middle (ma), lower bands
fig.update_layout(title='Bollinger Bands', template='plotly_dark',
xaxis_rangeslider_visible=False)
fig.write_html('bollinger.html')
Treemaps and sunbursts visualize hierarchical data where area/angle encodes value. Use them for budget breakdowns, file system sizes, market cap, and org charts.
import plotly.express as px
import pandas as pd
df = pd.DataFrame({
'category': ['Electronics','Electronics','Electronics',
'Clothing','Clothing','Food','Food','Food','Food'],
'subcategory': ['Phones','Laptops','Tablets',
'Shirts','Pants','Dairy','Bakery','Produce','Frozen'],
'sales': [4500, 3200, 1800, 2100, 1600, 900, 750, 1200, 680],
'profit': [900, 480, 270, 420, 240, 135, 112, 180, 102],
})
fig = px.treemap(df,
path=['category', 'subcategory'],
values='sales',
color='profit',
color_continuous_scale='RdYlGn',
title='Sales Treemap by Category β Subcategory',
hover_data={'profit': ':,.0f'},
)
fig.update_traces(textinfo='label+value+percent root')
fig.write_html('treemap_sales.html')
print(f'Treemap saved β {len(df)} rows')import plotly.express as px
import pandas as pd
df = pd.DataFrame({
'continent': ['Americas','Americas','Americas','Europe','Europe','Asia','Asia','Asia'],
'country': ['USA','Brazil','Canada','Germany','France','China','Japan','India'],
'sector': ['Tech','Finance','Mining','Auto','Pharma','Tech','Auto','IT'],
'market_cap':[2800, 400, 350, 680, 520, 1900, 750, 420],
})
fig = px.sunburst(df,
path=['continent', 'country', 'sector'],
values='market_cap',
color='market_cap',
color_continuous_scale='Blues',
title='Market Cap: Continent β Country β Sector ($ Bn)',
)
fig.update_traces(
textinfo='label+percent parent',
insidetextorientation='radial',
)
fig.update_layout(height=550)
fig.write_html('sunburst_portfolio.html')
print('Sunburst saved')import plotly.graph_objects as go
labels = ['Total','A Div','B Div','C Div','A1','A2','B1','B2','C1','C2','C3']
parents = ['', 'Total','Total','Total','A Div','A Div','B Div','B Div','C Div','C Div','C Div']
values = [0, 0, 0, 0, 120, 80, 200, 150, 60, 90, 50]
fig = go.Figure(go.Treemap(
labels=labels,
parents=parents,
values=values,
branchvalues='total',
marker=dict(
colorscale='Viridis',
showscale=True,
colorbar=dict(title='Revenue'),
),
texttemplate='<b>%{label}</b><br>$%{value}k',
hovertemplate='<b>%{label}</b><br>Revenue: $%{value}k<br>Share: %{percentRoot:.1%}<extra></extra>',
))
fig.update_layout(title='Division Revenue Treemap', height=450)
fig.write_html('treemap_custom.html')
print('Custom treemap saved')import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(1)
depts = ['Engineering','Marketing','Operations']
services = ['Compute','Storage','Database','Network']
data = []
for d in depts:
for svc in services:
cost = np.random.randint(500, 8000)
growth = np.random.uniform(-0.1, 0.3)
data.append({'dept': d, 'service': svc, 'cost': cost, 'growth_pct': growth})
df = pd.DataFrame(data)
fig = px.treemap(df,
path=['dept', 'service'],
values='cost',
color='growth_pct',
color_continuous_scale='RdYlGn_r',
color_continuous_midpoint=0,
title='Cloud Cost by Dept β Service (color = MoM growth)',
custom_data=['growth_pct'],
)
fig.update_traces(
texttemplate='<b>%{label}</b><br>$%{value:,.0f}',
hovertemplate='%{label}<br>Cost: $%{value:,.0f}<br>Growth: %{customdata[0]:.1%}<extra></extra>',
)
fig.write_html('it_cost_treemap.html')
print(f'IT cost treemap saved β {df.cost.sum():,} total spend')import plotly.graph_objects as go
labels = ['root','docs','src','data',
'report.pdf','slides.pptx',
'main.py','utils.py','tests.py',
'train.csv','test.csv']
parents = ['','root','root','root',
'docs','docs',
'src','src','src',
'data','data']
values = [0, 0, 0, 0,
2048, 5120,
45, 30, 22,
10240, 4096]
# TODO: create go.Sunburst with branchvalues='total'
# TODO: color by values, add textinfo='label+percent parent'
fig = go.Figure()
fig.update_layout(title='File System Sunburst')
fig.write_html('filesystem.html')
Violin plots combine a KDE curve with a box plot to show distribution shape. Strip/jitter plots show individual data points β great for small-to-medium datasets.
import plotly.express as px
import numpy as np, pandas as pd
np.random.seed(42)
groups = ['Control', 'Treatment A', 'Treatment B']
data = pd.concat([
pd.DataFrame({'group': g,
'score': np.random.normal(loc, 12, 80)})
for g, loc in zip(groups, [50, 62, 71])
])
fig = px.violin(data, x='group', y='score', color='group',
box=True, points='all',
title='Score Distribution by Treatment Group',
labels={'score': 'Test Score', 'group': 'Group'},
color_discrete_sequence=px.colors.qualitative.Pastel)
fig.update_traces(meanline_visible=True, jitter=0.3, pointpos=-1.8,
marker=dict(size=4, opacity=0.5))
fig.update_layout(showlegend=False, height=450)
fig.write_html('violin_groups.html')
print('Violin chart saved')import plotly.graph_objects as go
import numpy as np
np.random.seed(10)
before = np.random.normal(65, 15, 100)
after = np.random.normal(72, 12, 100)
fig = go.Figure()
fig.add_trace(go.Violin(y=before, name='Before',
side='negative', line_color='#636EFA',
fillcolor='rgba(99,110,250,0.3)',
box_visible=True, meanline_visible=True))
fig.add_trace(go.Violin(y=after, name='After',
side='positive', line_color='#EF553B',
fillcolor='rgba(239,85,59,0.3)',
box_visible=True, meanline_visible=True))
fig.update_layout(
title='Before vs After Intervention (Split Violin)',
violingap=0, violinmode='overlay',
yaxis_title='Score',
template='plotly_white',
height=420,
)
fig.write_html('violin_split.html')
print(f'Before mean: {before.mean():.1f} After mean: {after.mean():.1f}')import plotly.express as px
import numpy as np, pandas as pd
np.random.seed(5)
categories = ['Category A', 'Category B', 'Category C', 'Category D']
df = pd.concat([
pd.DataFrame({'category': cat, 'value': np.random.exponential(scale, 50)})
for cat, scale in zip(categories, [10, 20, 15, 25])
])
fig = px.strip(df, x='category', y='value', color='category',
title='Strip Plot with Individual Data Points',
stripmode='overlay',
color_discrete_sequence=px.colors.qualitative.Set2)
# Add mean markers
for cat in categories:
mean_val = df[df.category == cat]['value'].mean()
fig.add_scatter(x=[cat], y=[mean_val], mode='markers',
marker=dict(symbol='line-ew', size=20, color='black', line_width=2),
showlegend=False, hovertemplate=f'Mean: {mean_val:.1f}')
fig.update_traces(jitter=0.4, marker_size=5, marker_opacity=0.6,
selector=dict(type='strip'))
fig.update_layout(showlegend=False, height=420)
fig.write_html('strip_plot.html')
print('Strip plot saved')import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
np.random.seed(99)
metrics = {
'Conversion Rate (%)': (np.random.beta(2, 20, 200)*100,
np.random.beta(3, 20, 200)*100),
'Session Duration (s)': (np.random.exponential(120, 200),
np.random.exponential(145, 200)),
}
fig = make_subplots(rows=1, cols=2, subplot_titles=list(metrics.keys()))
colors = ['#636EFA', '#EF553B']
for col, (metric, (ctrl, var)) in enumerate(metrics.items(), 1):
for data, name, side, color in [
(ctrl, 'Control', 'negative', colors[0]),
(var, 'Variant', 'positive', colors[1]),
]:
fig.add_trace(
go.Violin(y=data, name=name, side=side,
line_color=color, box_visible=True,
meanline_visible=True, showlegend=(col==1)),
row=1, col=col
)
fig.update_layout(title='A/B Test: Control vs Variant',
violinmode='overlay', template='plotly_white',
height=420)
fig.write_html('ab_test_violin.html')
for m, (c, v) in metrics.items():
print(f'{m}: ctrl={c.mean():.1f} | var={v.mean():.1f} | lift={((v.mean()-c.mean())/c.mean())*100:.1f}%')import plotly.express as px
import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
subjects = ['Math','Science','English','History']
means = [72, 68, 75, 65]
data = pd.concat([
pd.DataFrame({'subject': s, 'score': np.random.normal(m, 12, 60)})
for s, m in zip(subjects, means)
])
data['score'] = data['score'].clip(0, 100)
# TODO: create px.violin with box=True, points='outliers'
# TODO: add go.Scatter horizontal line at y=70 (passing threshold)
fig = go.Figure()
fig.update_layout(title='Exam Score Distributions', height=430)
fig.write_html('exam_scores.html')
Parallel coordinates show multi-dimensional continuous data on parallel axes β each line is one observation. Parallel categories (Sankey-style) show flows between categorical variables.
import plotly.express as px
from sklearn.datasets import load_iris
import pandas as pd
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['species'] = iris.target # 0, 1, 2
fig = px.parallel_coordinates(
df,
color='species',
dimensions=iris.feature_names,
color_continuous_scale=px.colors.diverging.Tealrose,
color_continuous_midpoint=1,
title='Iris Dataset β Parallel Coordinates',
labels={n: n.replace(' (cm)', '') for n in iris.feature_names},
)
fig.update_layout(height=430)
fig.write_html('parallel_coords_iris.html')
print('Drag axes to filter! Saved.')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
n = 200
df = pd.DataFrame({
'age': np.random.randint(22, 65, n),
'income': np.random.exponential(50000, n).clip(20000, 200000),
'savings': np.random.exponential(30000, n).clip(0, 150000),
'credit': np.random.randint(300, 850, n),
'loan': np.random.choice([0, 1], n, p=[0.7, 0.3]),
})
fig = go.Figure(go.Parcoords(
line=dict(color=df['loan'], colorscale='RdYlGn_r',
showscale=True, colorbar=dict(title='Default')),
dimensions=[
dict(label='Age', values=df['age'], range=[22, 65]),
dict(label='Income ($)', values=df['income'], range=[20000, 200000]),
dict(label='Savings ($)', values=df['savings'], range=[0, 150000]),
dict(label='Credit Score', values=df['credit'], range=[300, 850]),
],
))
fig.update_layout(title='Loan Default β Parallel Coordinates',
height=430, template='plotly_dark')
fig.write_html('parallel_loan.html')
print('Loan parallel coords saved')import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(3)
n = 500
df = pd.DataFrame({
'region': np.random.choice(['North','South','East','West'], n),
'segment': np.random.choice(['SMB','Enterprise','Consumer'], n),
'channel': np.random.choice(['Online','Retail','Partner'], n),
'outcome': np.random.choice(['Won','Lost','Pending'], n, p=[0.4,0.4,0.2]),
})
fig = px.parallel_categories(
df,
dimensions=['region','segment','channel','outcome'],
color=df['outcome'].map({'Won': 0, 'Pending': 0.5, 'Lost': 1}),
color_continuous_scale='RdYlGn_r',
title='Sales Pipeline Flow: Region β Segment β Channel β Outcome',
)
fig.update_layout(height=450)
fig.write_html('parallel_categories.html')
print('Parallel categories chart saved')import plotly.express as px
import numpy as np, pandas as pd
from sklearn.datasets import load_wine
wine = load_wine()
df = pd.DataFrame(wine.data, columns=wine.feature_names)
df['class'] = wine.target
df['error'] = np.random.choice([0, 1], len(df), p=[0.8, 0.2])
# Use only most informative features
top_features = ['alcohol', 'malic_acid', 'flavanoids',
'color_intensity', 'proline']
fig = px.parallel_coordinates(
df,
color='class',
dimensions=top_features + ['class'],
color_continuous_scale=px.colors.sequential.Viridis,
title='Wine Dataset β Feature Space by Class',
labels={f: f.replace('_', ' ').title() for f in top_features},
)
fig.update_layout(height=430)
fig.write_html('wine_parallel.html')
print(f'Wine parallel coords: {len(df)} samples, {len(top_features)} features')import plotly.express as px
import pandas as pd
import numpy as np
# Use built-in cars dataset
try:
df = px.data.cars()
except Exception:
np.random.seed(42)
n = 200
df = pd.DataFrame({
'mpg': np.random.normal(23, 7, n).clip(8, 46),
'cylinders': np.random.choice([4, 6, 8], n, p=[0.5, 0.3, 0.2]),
'horsepower': np.random.normal(100, 40, n).clip(40, 230),
'weight': np.random.normal(2800, 700, n).clip(1600, 5000),
'model_year': np.random.randint(70, 83, n),
'origin': np.random.choice(['USA','Europe','Japan'], n),
})
# TODO: parallel_coordinates colored by cylinders
# TODO: parallel_categories with cylinders and origin
Funnel charts show sequential stage drop-off (sales pipelines, conversion funnels). Waterfall charts show cumulative effects of positive and negative values β perfect for P&L statements.
import plotly.graph_objects as go
stages = ['Website Visits', 'Product Views', 'Add to Cart',
'Checkout Started', 'Purchase Completed']
counts = [10000, 5200, 2100, 980, 420]
conversion = [f'{counts[i]/counts[i-1]:.0%}' if i > 0 else '100%'
for i in range(len(counts))]
fig = go.Figure(go.Funnel(
y=stages,
x=counts,
textposition='inside',
textinfo='value+percent previous',
opacity=0.85,
marker=dict(
color=['#636EFA','#EF553B','#00CC96','#AB63FA','#FFA15A'],
line=dict(width=2, color='white'),
),
connector=dict(line=dict(color='#888', width=1)),
))
fig.update_layout(
title='E-Commerce Conversion Funnel',
template='plotly_white',
height=420,
margin=dict(l=200),
)
fig.write_html('sales_funnel.html')
print(f'Overall conversion: {counts[-1]/counts[0]:.1%}')import plotly.graph_objects as go
items = ['Revenue','COGS','Gross Profit','R&D',
'S&M','G&A','EBITDA','D&A','EBIT']
values = [1000, -380, None, -150, -120, -80, None, -45, None]
measure = ['absolute','relative','total','relative',
'relative','relative','total','relative','total']
text = [f'${abs(v):,}' if v else '' for v in values]
fig = go.Figure(go.Waterfall(
orientation='v',
measure=measure,
x=items,
y=values,
text=text,
textposition='outside',
increasing=dict(marker_color='#26a69a'),
decreasing=dict(marker_color='#ef5350'),
totals=dict(marker_color='#636EFA'),
connector=dict(line=dict(color='#999', width=1, dash='dot')),
))
fig.update_layout(
title='P&L Waterfall β Q4 2024',
yaxis_title='$ Thousands',
template='plotly_white',
height=450,
showlegend=False,
)
fig.write_html('pnl_waterfall.html')
print('P&L waterfall saved')import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
stages = ['Awareness','Interest','Consideration','Intent','Purchase']
values = [50000, 22000, 8500, 3200, 980]
fig = make_subplots(rows=1, cols=2,
subplot_titles=['Standard Funnel', 'Funnel Area'],
specs=[[{'type':'funnel'}, {'type':'funnelarea'}]])
fig.add_trace(
go.Funnel(y=stages, x=values, textinfo='value+percent initial',
marker_color=px.colors.sequential.Blues_r[1:]),
row=1, col=1)
fig.add_trace(
go.Funnelarea(labels=stages, values=values,
textinfo='label+percent',
marker_colors=px.colors.sequential.Greens_r[1:]),
row=1, col=2)
fig.update_layout(title='Marketing Funnel Comparison', height=420)
fig.write_html('funnel_comparison.html')
print(f'Top-of-funnel to purchase: {values[-1]/values[0]:.1%}')import plotly.graph_objects as go
import numpy as np
months = ['Jan','Feb','Mar','Apr','May','Jun']
starting_arr = 500
data_rows = []
for m in months:
new_biz = np.random.randint(15, 40)
expansion = np.random.randint(5, 20)
contraction= -np.random.randint(2, 10)
churn = -np.random.randint(5, 20)
data_rows.append((m, new_biz, expansion, contraction, churn))
items = ['Start']
values = [starting_arr]
measure = ['absolute']
for month, new, exp, con, churn in data_rows:
items += [f'{month} New', f'{month} Exp', f'{month} Con', f'{month} Churn']
values += [new, exp, con, churn]
measure += ['relative','relative','relative','relative']
items.append('End ARR')
values.append(None)
measure.append('total')
fig = go.Figure(go.Waterfall(
x=items, y=values, measure=measure,
increasing=dict(marker_color='#26a69a'),
decreasing=dict(marker_color='#ef5350'),
totals=dict(marker_color='#636EFA'),
textposition='outside', texttemplate='%{y:+.0f}',
))
fig.update_layout(title='SaaS ARR Waterfall (H1 2024)', height=450,
template='plotly_white', xaxis_tickangle=45)
fig.write_html('saas_waterfall.html')
print('SaaS waterfall saved')import plotly.graph_objects as go
departments = ['Engineering','Marketing','Sales','Operations','HR']
budget_each = [200000, 150000, 180000, 120000, 100000] # sums to 750k; rest is overhead
variances = [+15000, -8000, +22000, -5000, +3000] # actual - budget per dept
items = ['Budget Total'] + departments + ['Overhead', 'Actual Total']
# TODO: set up values list (budget_total=750000, then variances, then overhead=250000, total=None)
# TODO: set up measure list
# TODO: create go.Waterfall with increasing green, decreasing red
fig = go.Figure()
fig.update_layout(title='Budget Variance Waterfall', height=430)
fig.write_html('budget_variance.html')
go.Indicator renders KPI numbers, delta comparisons, and gauge/speedometer charts. Combine them in subplots to build executive dashboards without any extra libraries.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
fig = make_subplots(rows=1, cols=4,
specs=[[{'type':'indicator'}]*4])
kpis = [
('Revenue', '1.24M', 1_240_000, 1_100_000),
('Users', '48.3K', 48_300, 45_100),
('Churn %', '2.1%', 2.1, 2.8),
('NPS', '67', 67, 61),
]
for col, (name, num_str, val, ref) in enumerate(kpis, 1):
better_if_lower = 'churn' in name.lower()
fig.add_trace(go.Indicator(
mode='number+delta',
value=val,
title=dict(text=name, font=dict(size=14)),
delta=dict(
reference=ref,
increasing=dict(color='red' if better_if_lower else 'green'),
decreasing=dict(color='green' if better_if_lower else 'red'),
valueformat='.1%' if '%' in num_str else None,
),
number=dict(
prefix='$' if 'M' in num_str else '',
suffix='%' if '%' in num_str else '',
),
), row=1, col=col)
fig.update_layout(title='Business KPI Dashboard', height=200,
template='plotly_dark', margin=dict(t=50, b=20))
fig.write_html('kpi_indicators.html')
print('KPI dashboard saved')import plotly.graph_objects as go
from plotly.subplots import make_subplots
fig = make_subplots(rows=1, cols=3,
specs=[[{'type':'indicator'}]*3])
gauges = [
('CPU Usage', 73, '%', 'red', [(0,40,'green'),(40,70,'yellow'),(70,100,'red')]),
('Memory', 58, '%', 'orange', [(0,60,'green'),(60,80,'orange'),(80,100,'red')]),
('SLA Score', 96.5, '%', 'green', [(0,90,'red'),(90,95,'yellow'),(95,100,'green')]),
]
for col, (name, val, suffix, color, steps) in enumerate(gauges, 1):
fig.add_trace(go.Indicator(
mode='gauge+number+delta',
value=val,
title=dict(text=name, font=dict(size=14)),
delta=dict(reference=80 if 'SLA' not in name else 95),
number=dict(suffix=suffix),
gauge=dict(
axis=dict(range=[0, 100]),
bar=dict(color=color, thickness=0.25),
steps=[dict(range=[lo, hi], color=c) for lo, hi, c in steps],
threshold=dict(line=dict(color='white', width=3),
thickness=0.75, value=val),
),
), row=1, col=col)
fig.update_layout(title='Infrastructure Gauges', height=280,
template='plotly_dark', margin=dict(t=60, b=10))
fig.write_html('gauges.html')
print('Gauge dashboard saved')import plotly.graph_objects as go
from plotly.subplots import make_subplots
metrics = [
('Q1 Revenue', 1.15, 1.25, 'M$'),
('Q1 Leads', 430, 500, ''),
('Avg Deal ($)', 22500, 20000, '$'),
('Win Rate', 42, 40, '%'),
]
fig = make_subplots(rows=len(metrics), cols=1,
specs=[[{'type':'indicator'}]] * len(metrics),
vertical_spacing=0.0)
for row, (name, actual, target, unit) in enumerate(metrics, 1):
color = 'green' if actual >= target else 'red'
fig.add_trace(go.Indicator(
mode='number+gauge+delta',
value=actual,
title=dict(text=name, font=dict(size=12)),
delta=dict(reference=target,
relative=True,
increasing=dict(color='green'),
decreasing=dict(color='red')),
number=dict(prefix=unit if unit=='$' else '',
suffix=unit if unit!='$' else ''),
gauge=dict(
shape='bullet',
axis=dict(range=[0, target*1.5]),
threshold=dict(value=target,
line=dict(color='black', width=2),
thickness=0.75),
bar=dict(color=color),
),
), row=row, col=1)
fig.update_layout(title='Q1 Performance vs Target',
height=360, template='plotly_white',
margin=dict(l=160, t=60, b=20))
fig.write_html('bullet_chart.html')
print('Bullet chart saved')import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
# Simulate current vs previous week metrics
np.random.seed(42)
metrics = {
'ARR ($M)': (4.82, 4.71, False),
'MRR Growth%': (3.2, 2.8, False),
'Churn %': (1.8, 2.1, True), # lower is better
'NPS': (71, 68, False),
'CAC ($)': (1250, 1380, True), # lower is better
'LTV/CAC': (4.8, 4.2, False),
}
n = len(metrics)
fig = make_subplots(rows=2, cols=3,
specs=[[{'type':'indicator'}]*3]*2)
for idx, (name, (curr, prev, lower_better)) in enumerate(metrics.items()):
row, col = divmod(idx, 3)
fig.add_trace(go.Indicator(
mode='number+delta',
value=curr,
title=dict(text=name, font=dict(size=13)),
delta=dict(
reference=prev,
relative=True,
increasing=dict(color='red' if lower_better else 'green'),
decreasing=dict(color='green' if lower_better else 'red'),
),
), row=row+1, col=col+1)
fig.update_layout(title='SaaS Weekly KPI Report',
height=320, template='plotly_dark',
margin=dict(t=60, b=20))
fig.write_html('saas_kpi.html')
print('SaaS KPI report saved')import plotly.graph_objects as go
from plotly.subplots import make_subplots
fig = make_subplots(rows=2, cols=2,
specs=[[{'type':'indicator'}]*2]*2)
okrs = [
('Revenue Target', 85, 100, '%', [(0,60,'red'),(60,80,'yellow'),(80,100,'green')]),
('Cust. Satisfaction', 4.2, 5, '/5', [(0,3,'red'),(3,4,'yellow'),(4,5,'green')]),
('Bug Backlog', 23, 30, '', [(0,15,'green'),(15,25,'yellow'),(25,30,'red')]),
('Feature Delivery', 7, 10, '/10',[(0,5,'red'),(5,7,'yellow'),(7,10,'green')]),
]
for idx, (name, val, max_val, suffix, steps) in enumerate(okrs):
row, col = divmod(idx, 2)
# TODO: add go.Indicator with mode='gauge+number', gauge steps, threshold at target
pass
fig.update_layout(title='OKR Progress Gauges', height=480, template='plotly_dark')
fig.write_html('okr_gauges.html')
Plotly animations use frames and sliders to animate data over time. Bar chart races, animated scatter plots, and timeline maps reveal how data evolves.
import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
companies = ['Alpha','Beta','Gamma','Delta','Epsilon','Zeta']
years = list(range(2015, 2025))
# Generate cumulative revenue data
revenues = {c: np.cumsum(np.random.exponential(10, len(years))) * (1 + 0.1 * i)
for i, c in enumerate(companies)}
df = pd.DataFrame(revenues, index=years)
frames = []
for year in years:
row = df.loc[year].sort_values(ascending=True)
frames.append(go.Frame(
data=[go.Bar(x=row.values, y=row.index,
orientation='h',
marker_color=px_colors := [
'#636EFA','#EF553B','#00CC96','#AB63FA',
'#FFA15A','#19D3F3'][:len(row)],
text=[f'${v:.0f}B' for v in row.values],
textposition='outside')],
name=str(year),
layout=go.Layout(title_text=f'Company Revenue Race β {year}')
))
first = df.loc[years[0]].sort_values(ascending=True)
fig = go.Figure(
data=[go.Bar(x=first.values, y=first.index, orientation='h',
marker_color=['#636EFA','#EF553B','#00CC96',
'#AB63FA','#FFA15A','#19D3F3'][:len(first)],
text=[f'${v:.0f}B' for v in first.values],
textposition='outside')],
frames=frames,
layout=go.Layout(
title=f'Company Revenue Race β {years[0]}',
xaxis=dict(range=[0, df.values.max()*1.15], title='Revenue ($B)'),
updatemenus=[dict(type='buttons', showactive=False,
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=600, redraw=True),
fromcurrent=True)])])],
sliders=[dict(steps=[dict(method='animate', args=[[str(y)]], label=str(y))
for y in years],
currentvalue=dict(prefix='Year: '))],
height=450, template='plotly_dark',
)
)
fig.write_html('bar_race.html')
print('Bar chart race saved')import plotly.express as px
df = px.data.gapminder()
fig = px.scatter(
df, x='gdpPercap', y='lifeExp',
animation_frame='year',
animation_group='country',
size='pop', color='continent',
hover_name='country',
log_x=True,
size_max=55,
range_x=[100, 100_000],
range_y=[25, 90],
title='Gapminder: GDP vs Life Expectancy (1952-2007)',
labels={'gdpPercap': 'GDP per Capita', 'lifeExp': 'Life Expectancy'},
template='plotly_white',
)
fig.update_layout(height=500)
fig.write_html('gapminder_animation.html')
print(f'Animated scatter: {df.year.nunique()} frames, {df.country.nunique()} countries')import plotly.express as px
df = px.data.gapminder()
fig = px.choropleth(
df,
locations='iso_alpha',
color='lifeExp',
hover_name='country',
animation_frame='year',
color_continuous_scale='RdYlGn',
range_color=[25, 90],
title='World Life Expectancy Over Time (1952-2007)',
labels={'lifeExp': 'Life Expectancy'},
projection='natural earth',
)
fig.update_layout(
coloraxis_colorbar=dict(title='Life Exp.', x=1.0),
height=480,
geo=dict(showframe=False, showcoastlines=False),
)
fig.write_html('choropleth_animated.html')
print('Animated choropleth saved')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(7)
brands = ['AlphaPhone','BetaMobile','GammaDevice','DeltaTech','EpsilonX']
quarters= [f'Q{q} {y}' for y in range(2021,2025) for q in range(1,5)]
# Market share that sums to 100% each quarter
raw = np.random.dirichlet(np.ones(len(brands)), len(quarters)) * 100
df = pd.DataFrame(raw, columns=brands, index=quarters)
frames = []
for q in quarters:
row = df.loc[q].sort_values(ascending=True)
frames.append(go.Frame(
data=[go.Bar(x=row.values, y=row.index, orientation='h',
text=[f'{v:.1f}%' for v in row.values],
textposition='outside',
marker_color=['#636EFA','#EF553B','#00CC96','#AB63FA','#FFA15A'][:len(row)])],
name=q,
layout=go.Layout(title_text=f'Smartphone Market Share β {q}')
))
first = df.iloc[0].sort_values(ascending=True)
fig = go.Figure(
data=[go.Bar(x=first.values, y=first.index, orientation='h',
text=[f'{v:.1f}%' for v in first.values], textposition='outside',
marker_color=['#636EFA','#EF553B','#00CC96','#AB63FA','#FFA15A'])],
frames=frames,
layout=go.Layout(
title=f'Smartphone Market Share β {quarters[0]}',
xaxis=dict(range=[0, 45], title='Market Share (%)'),
updatemenus=[dict(type='buttons', showactive=False,
buttons=[dict(label='βΆ Play', method='animate',
args=[None, dict(frame=dict(duration=700))])])],
sliders=[dict(steps=[dict(method='animate', args=[[q]], label=q) for q in quarters],
currentvalue=dict(prefix='Quarter: '))],
template='plotly_dark', height=450,
)
)
fig.write_html('market_share_race.html')
print(f'Race saved: {len(quarters)} quarters')import plotly.graph_objects as go
import numpy as np
age_groups = ['0-9','10-19','20-29','30-39','40-49','50-59','60-69','70+']
decades = [1980, 1990, 2000, 2010, 2020]
np.random.seed(42)
frames = []
for decade in decades:
# Generate male/female populations (in millions)
base = np.random.exponential(5, len(age_groups)) + 2
male = base + np.random.randn(len(age_groups))
female = -(base + np.random.randn(len(age_groups)))
frames.append(go.Frame(
data=[
go.Bar(y=age_groups, x=male, orientation='h', name='Male'),
# TODO: add female Bar trace with negative values
],
name=str(decade),
layout=go.Layout(title_text=f'Population Pyramid β {decade}')
))
# TODO: create fig with first frame data, frames, updatemenus, sliders
fig = go.Figure()
fig.update_layout(title='Population Pyramid', barmode='overlay',
xaxis_title='Population (M)', height=450)
fig.write_html('population_pyramid.html')
Plotly templates define default colors, fonts, backgrounds, and axis styles. Create reusable brand templates with pio.templates and apply them globally or per-chart.
import plotly.graph_objects as go
import plotly.io as pio
import plotly.express as px
# Define a brand template
brand_template = go.layout.Template(
layout=go.Layout(
font=dict(family='Arial', size=13, color='#2c2c2c'),
paper_bgcolor='#f8f9fa',
plot_bgcolor='#ffffff',
colorway=['#005A9E','#E63946','#06D6A0','#FFB703','#8338EC'],
title=dict(font=dict(size=18, color='#005A9E', family='Arial Bold')),
xaxis=dict(gridcolor='#e9ecef', linecolor='#dee2e6', zeroline=False),
yaxis=dict(gridcolor='#e9ecef', linecolor='#dee2e6', zeroline=False),
legend=dict(bgcolor='rgba(255,255,255,0.8)',
bordercolor='#dee2e6', borderwidth=1),
hoverlabel=dict(bgcolor='white', font_size=12,
bordercolor='#dee2e6'),
)
)
# Register the template
pio.templates['brand'] = brand_template
pio.templates.default = 'brand' # set as global default
import numpy as np, pandas as pd
np.random.seed(42)
df = pd.DataFrame({
'month': pd.date_range('2024-01', periods=12, freq='MS'),
'product_a': np.cumsum(np.random.randn(12)*5) + 100,
'product_b': np.cumsum(np.random.randn(12)*4) + 80,
})
fig = px.line(df, x='month', y=['product_a','product_b'],
title='Monthly Sales β Brand Template',
labels={'value':'Revenue ($K)', 'variable':'Product'})
fig.write_html('brand_template.html')
print('Brand template chart saved')
# Reset to default
pio.templates.default = 'plotly'import plotly.graph_objects as go
import plotly.io as pio
import numpy as np, pandas as pd
dark_template = go.layout.Template(
layout=dict(
paper_bgcolor='#0d1117',
plot_bgcolor='#0d1117',
font=dict(color='#c9d1d9', family='JetBrains Mono, monospace'),
colorway=['#79c0ff','#ff7b72','#56d364','#d2a8ff','#ffa657','#39c5cf'],
xaxis=dict(gridcolor='#30363d', linecolor='#30363d',
tickcolor='#8b949e', title_font_color='#8b949e'),
yaxis=dict(gridcolor='#30363d', linecolor='#30363d',
tickcolor='#8b949e', title_font_color='#8b949e'),
title=dict(font=dict(color='#79c0ff', size=17)),
legend=dict(bgcolor='#161b22', bordercolor='#30363d', borderwidth=1),
hoverlabel=dict(bgcolor='#161b22', bordercolor='#30363d',
font=dict(color='#c9d1d9')),
)
)
pio.templates['github_dark'] = dark_template
np.random.seed(42)
categories = ['Q1','Q2','Q3','Q4']
products = ['Widget','Gadget','Gizmo']
fig = go.Figure()
for p in products:
fig.add_trace(go.Bar(name=p, x=categories,
y=np.random.randint(50, 200, 4),
text=np.random.randint(50, 200, 4),
texttemplate='%{text}',
textposition='outside'))
fig.update_layout(title='Quarterly Sales β GitHub Dark Theme',
template='github_dark', barmode='group', height=430)
fig.write_html('dark_theme.html')
print('Dark theme chart saved')import plotly.graph_objects as go
import numpy as np, pandas as pd
np.random.seed(42)
x = pd.date_range('2024-01', periods=52, freq='W')
actual = 100 + np.cumsum(np.random.randn(52) * 3)
forecast = actual[-1] + np.cumsum(np.random.randn(12) * 2.5)
upper_ci = forecast + 1.96 * np.arange(1, 13) * 0.8
lower_ci = forecast - 1.96 * np.arange(1, 13) * 0.8
future_x = pd.date_range(x[-1], periods=13, freq='W')[1:]
fig = go.Figure()
# Actual data
fig.add_trace(go.Scatter(x=x, y=actual, name='Actual',
line=dict(color='#636EFA', width=2.5)))
# Forecast with confidence interval fill
fig.add_trace(go.Scatter(x=future_x, y=upper_ci, name='Upper CI 95%',
line=dict(color='rgba(255,127,14,0)', width=0),
showlegend=False))
fig.add_trace(go.Scatter(x=future_x, y=lower_ci, name='Lower CI 95%',
line=dict(color='rgba(255,127,14,0)', width=0),
fill='tonexty', fillcolor='rgba(255,127,14,0.2)',
showlegend=False))
fig.add_trace(go.Scatter(x=future_x, y=forecast, name='Forecast',
line=dict(color='#FF7F0E', width=2, dash='dash')))
# Annotation for forecast start
fig.add_vline(x=x[-1], line_dash='dot', line_color='gray', opacity=0.7)
fig.add_annotation(x=x[-1], y=actual[-1], text='Forecast begins',
showarrow=True, arrowhead=2, bgcolor='rgba(0,0,0,0.6)',
font=dict(color='white'))
fig.update_layout(title='Revenue Actual + 12-Week Forecast with CI',
xaxis_title='Date', yaxis_title='Revenue ($K)',
template='plotly_dark', height=430, hovermode='x unified')
fig.write_html('forecast_styled.html')
print('Styled forecast chart saved')import plotly.graph_objects as go
import plotly.io as pio
import plotly.express as px
import numpy as np, pandas as pd
# Corporate brand template
pio.templates['corporate'] = go.layout.Template(
layout=dict(
font=dict(family='Helvetica Neue, sans-serif', size=13),
paper_bgcolor='#FFFFFF',
plot_bgcolor='#FAFAFA',
colorway=['#1A237E','#283593','#3949AB','#5C6BC0','#7986CB'],
title=dict(font=dict(size=16, color='#1A237E', family='Helvetica Neue Bold')),
xaxis=dict(gridcolor='#EEEEEE', linecolor='#BDBDBD'),
yaxis=dict(gridcolor='#EEEEEE', linecolor='#BDBDBD'),
legend=dict(bgcolor='rgba(255,255,255,0.9)'),
)
)
np.random.seed(5)
months = pd.date_range('2024-01', periods=12, freq='MS')
channels = ['Organic','Paid','Email','Social']
data = {c: np.random.randint(200, 800, 12) for c in channels}
df = pd.DataFrame(data, index=months).reset_index().rename(columns={'index':'month'})
df_melt = df.melt('month', var_name='channel', value_name='leads')
fig = px.area(df_melt, x='month', y='leads', color='channel',
title='Marketing Leads by Channel β Corporate Theme',
template='corporate')
fig.update_traces(opacity=0.8)
fig.write_html('corporate_report.html')
print(f'Corporate report saved β {df_melt.leads.sum():,} total leads')import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np, pandas as pd
np.random.seed(42)
weeks = pd.date_range('2024-01', periods=20, freq='W')
sales = 100 + np.cumsum(np.random.randn(20)*5)
regions = ['North','South','East','West']
reg_sales = np.random.randint(200, 600, 4)
spend = np.random.uniform(10, 100, 50)
revenue = spend * 3 + np.random.randn(50) * 20
channels = ['Organic','Paid','Email','Social']
channel_mix = [40, 30, 20, 10]
fig = make_subplots(rows=2, cols=2,
subplot_titles=['Weekly Sales','Sales by Region',
'Spend vs Revenue','Channel Mix'],
specs=[[{},{}],[{},{'type':'pie'}]])
# TODO: add go.Scatter for weekly sales (row=1,col=1)
# TODO: add go.Bar for regions (row=1,col=2)
# TODO: add go.Scatter mode='markers' for spend vs revenue (row=2,col=1)
# TODO: add go.Pie for channels (row=2,col=2)
fig.update_layout(title='Sales Dashboard', template='plotly_dark',
height=600, showlegend=False)
fig.write_html('dashboard.html')