Plotly for Interactive Dashboards
Create Stunning Interactive Visualizations! 🚀
Plotly is a powerful Python library that creates interactive, publication-quality graphs. Unlike static visualizations, Plotly charts respond to user interactions - hover for details, zoom into areas of interest, pan across data, and even update in real-time. Perfect for dashboards, presentations, and data exploration!
Why Plotly?
Plotly brings your data to life with interactivity that static plots can't match:
- 🎯 Interactive by Default: Hover tooltips, zoom, pan, and selection tools built-in
- 📊 40+ Chart Types: From basic to advanced statistical and 3D visualizations
- 🌐 Web-Ready: Export as HTML for easy sharing and embedding
- ⚡ Plotly Express: Create complex plots with one line of code
- 🎨 Professional Styling: Beautiful themes and customization options
- 📱 Responsive: Automatically adjusts to different screen sizes
Installation and Setup
# Install Plotly
# pip install plotly
# Import necessary libraries
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
# For displaying in Jupyter notebooks
import plotly.offline as pyo
pyo.init_notebook_mode(connected=True)
# For saving static images (requires kaleido)
# pip install kaleido
print("Plotly version:", plotly.__version__)
Plotly Express - Quick Interactive Plots
Plotly Express is the high-level API that creates complete figures with one function call:
# Load sample data
df = px.data.iris()
tips = px.data.tips()
gapminder = px.data.gapminder()
# Scatter plot with hover data
fig = px.scatter(df, x="sepal_width", y="sepal_length",
color="species", size="petal_length",
hover_data=['petal_width'],
title="Iris Dataset Explorer")
fig.show()
# Bar chart with animation
fig = px.bar(tips, x="day", y="total_bill", color="sex",
title="Average Bill by Day and Gender",
barmode="group", height=400)
fig.show()
# Line plot with multiple traces
df_stocks = px.data.stocks()
fig = px.line(df_stocks, x='date', y=df_stocks.columns[1:],
title='Stock Prices Over Time',
labels={'value': 'Price', 'variable': 'Company'})
fig.show()
Interactive Features
Hover Information
# Customizing hover information
fig = px.scatter(tips, x="total_bill", y="tip",
color="time", size="size",
hover_name="day", # Bold title in hover
hover_data={
"total_bill": ":.2f", # Format with 2 decimals
"tip": ":.2f",
"size": True, # Show size
"time": False # Hide time (already in color)
},
labels={"total_bill": "Total Bill ($)",
"tip": "Tip ($)",
"size": "Party Size"},
title="Restaurant Tips Analysis")
# Custom hover template
fig.update_traces(
hovertemplate="%{hovertext}
" +
"Bill: $%{x:.2f}
" +
"Tip: $%{y:.2f}
" +
"Party of %{marker.size}
" +
" " # Removes trace name
)
fig.show()
Zoom and Pan Controls
# Configure zoom and pan behavior
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species")
# Update layout with specific zoom settings
fig.update_layout(
dragmode='zoom', # 'pan', 'zoom', 'select', 'lasso'
hovermode='closest', # 'x', 'y', 'closest', False
xaxis=dict(
rangeslider=dict(visible=True), # Add range slider
rangeselector=dict( # Add range selector buttons
buttons=list([
dict(count=1, label="1m", step="month", stepmode="backward"),
dict(count=6, label="6m", step="month", stepmode="backward"),
dict(count=1, label="YTD", step="year", stepmode="todate"),
dict(step="all")
])
)
)
)
fig.show()
Creating Animated Visualizations
# Animated bubble chart
fig = px.scatter(gapminder, x="gdpPercap", y="lifeExp",
animation_frame="year", # Animation through years
animation_group="country", # Group by country
size="pop", color="continent",
hover_name="country",
log_x=True, # Log scale for x-axis
size_max=60,
range_x=[100, 100000], range_y=[25, 90],
title="Life Expectancy vs GDP Per Capita Over Time")
# Customize animation
fig.layout.updatemenus[0].buttons[0].args[1]["frame"]["duration"] = 1000 # 1 second per frame
fig.layout.updatemenus[0].buttons[0].args[1]["transition"]["duration"] = 500 # 0.5 second transition
fig.show()
# Animated bar race
df_continents = gapminder.groupby(['year', 'continent']).sum().reset_index()
fig = px.bar(df_continents, x="pop", y="continent",
animation_frame="year", orientation='h',
range_x=[0, 4000000000],
title="Population by Continent Over Time")
fig.show()
3D Visualizations
# 3D scatter plot
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',
color='species', symbol='species',
title="Iris Dataset in 3D")
fig.update_layout(scene=dict(
xaxis_title="Sepal Length",
yaxis_title="Sepal Width",
zaxis_title="Petal Width"
))
fig.show()
# 3D surface plot
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 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)])
fig.update_layout(title='3D Surface: sin(sqrt(x² + y²))',
scene=dict(
xaxis_title='X',
yaxis_title='Y',
zaxis_title='Z'
),
autosize=False,
width=700, height=700)
fig.show()
Statistical Charts
Box Plots and Violin Plots
# Interactive box plot
fig = px.box(tips, x="day", y="total_bill", color="smoker",
notched=True, # Show confidence interval
title="Bill Distribution by Day",
hover_data=["tip"])
fig.update_traces(quartilemethod="exclusive") # or "inclusive", "linear"
fig.show()
# Violin plot with box inside
fig = px.violin(tips, x="day", y="total_bill", color="sex",
box=True, # Show box plot inside
points="all", # Show all points
title="Bill Distribution (Violin + Box + Points)")
fig.show()
Heatmaps and Correlation Matrices
# Correlation heatmap
df_corr = df.select_dtypes(include=[np.number]).corr()
fig = px.imshow(df_corr,
text_auto=True, # Show values
aspect="auto",
color_continuous_scale="RdBu",
title="Correlation Matrix")
fig.update_xaxes(side="bottom")
fig.show()
# Time series heatmap
# Create sample time series data
dates = pd.date_range('2024-01-01', periods=365, freq='D')
hours = np.arange(24)
data = np.random.randn(365, 24) + np.sin(np.arange(365) * 2 * np.pi / 365)[:, np.newaxis]
fig = go.Figure(data=go.Heatmap(
z=data,
x=hours,
y=dates,
colorscale='Viridis',
hoverongaps=False,
hovertemplate='Date: %{y|%Y-%m-%d}
Hour: %{x}:00
Value: %{z:.2f} '
))
fig.update_layout(title="Hourly Data Throughout the Year",
xaxis_title="Hour of Day",
yaxis_title="Date")
fig.show()
Geographic Visualizations
# Choropleth map
fig = px.choropleth(gapminder,
locations="iso_alpha",
color="lifeExp",
hover_name="country",
animation_frame="year",
color_continuous_scale="Viridis",
title="Life Expectancy by Country Over Time",
range_color=[20, 80])
fig.update_layout(geo=dict(showframe=False, showcoastlines=True))
fig.show()
# Scatter map
fig = px.scatter_geo(gapminder[gapminder['year']==2007],
locations="iso_alpha",
size="pop",
hover_name="country",
color="continent",
projection="natural earth",
title="World Population in 2007")
fig.show()
# Map with custom data
import plotly.graph_objects as go
fig = go.Figure(data=go.Scattergeo(
lon = [-73.9842, -118.2437, -87.6298],
lat = [40.7128, 34.0522, 41.8781],
text = ['New York', 'Los Angeles', 'Chicago'],
mode = 'markers+text',
marker = dict(
size = [30, 20, 25],
color = ['red', 'blue', 'green'],
line = dict(width=1, color='white')
),
textposition="bottom center"
))
fig.update_layout(
title = 'Major US Cities',
geo = dict(
scope='usa',
showland = True,
landcolor = 'rgb(243, 243, 243)',
countrycolor = 'rgb(204, 204, 204)',
),
)
fig.show()
Creating Subplots
# Create subplots with different chart types
from plotly.subplots import make_subplots
fig = make_subplots(
rows=2, cols=2,
specs=[[{"type": "scatter"}, {"type": "bar"}],
[{"type": "heatmap"}, {"type": "scatter3d"}]],
subplot_titles=("Scatter Plot", "Bar Chart",
"Heatmap", "3D Scatter"),
vertical_spacing=0.1,
horizontal_spacing=0.1
)
# Add traces to subplots
fig.add_trace(
go.Scatter(x=[1, 2, 3, 4], y=[10, 11, 12, 13],
mode='lines+markers', name='Trend'),
row=1, col=1
)
fig.add_trace(
go.Bar(x=['A', 'B', 'C'], y=[5, 10, 15], name='Values'),
row=1, col=2
)
fig.add_trace(
go.Heatmap(z=[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
showscale=False),
row=2, col=1
)
fig.add_trace(
go.Scatter3d(x=[1, 2, 3], y=[2, 3, 4], z=[5, 6, 7],
mode='markers', marker=dict(size=10)),
row=2, col=2
)
fig.update_layout(height=600, showlegend=True,
title_text="Multiple Chart Types in Subplots")
fig.show()
Building an Interactive Dashboard
# Complete interactive dashboard example
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
# Generate sample data
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=365, freq='D')
# Sales data
sales_data = pd.DataFrame({
'date': dates,
'sales': np.cumsum(np.random.randn(365)) + 1000,
'costs': np.cumsum(np.random.randn(365) * 0.5) + 500,
'customers': np.random.poisson(100, 365),
'region': np.random.choice(['North', 'South', 'East', 'West'], 365),
'product': np.random.choice(['A', 'B', 'C'], 365)
})
sales_data['profit'] = sales_data['sales'] - sales_data['costs']
# Create dashboard
fig = make_subplots(
rows=3, cols=2,
subplot_titles=('Revenue & Costs Over Time', 'Customer Distribution',
'Sales by Region', 'Product Performance',
'Profit Trend', 'Daily Metrics Heatmap'),
specs=[[{"secondary_y": True}, {"type": "box"}],
[{"type": "bar"}, {"type": "pie"}],
[{"secondary_y": False}, {"type": "heatmap"}]],
vertical_spacing=0.1,
horizontal_spacing=0.15
)
# 1. Revenue & Costs Over Time (with secondary y-axis)
fig.add_trace(
go.Scatter(x=sales_data['date'], y=sales_data['sales'],
name='Sales', line=dict(color='green', width=2)),
row=1, col=1, secondary_y=False
)
fig.add_trace(
go.Scatter(x=sales_data['date'], y=sales_data['costs'],
name='Costs', line=dict(color='red', width=2)),
row=1, col=1, secondary_y=False
)
fig.add_trace(
go.Scatter(x=sales_data['date'], y=sales_data['customers'],
name='Customers', line=dict(color='blue', width=1, dash='dot')),
row=1, col=1, secondary_y=True
)
# 2. Customer Distribution
for region in sales_data['region'].unique():
region_data = sales_data[sales_data['region'] == region]
fig.add_trace(
go.Box(y=region_data['customers'], name=region),
row=1, col=2
)
# 3. Sales by Region
region_sales = sales_data.groupby('region')['sales'].sum()
fig.add_trace(
go.Bar(x=region_sales.index, y=region_sales.values,
marker_color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']),
row=2, col=1
)
# 4. Product Performance (Pie Chart)
product_sales = sales_data.groupby('product')['sales'].sum()
fig.add_trace(
go.Pie(labels=product_sales.index, values=product_sales.values,
hole=0.3), # Donut chart
row=2, col=2
)
# 5. Profit Trend with Moving Average
window = 30
sales_data['profit_ma'] = sales_data['profit'].rolling(window=window).mean()
fig.add_trace(
go.Scatter(x=sales_data['date'], y=sales_data['profit'],
name='Daily Profit', mode='lines',
line=dict(color='lightgray', width=1)),
row=3, col=1
)
fig.add_trace(
go.Scatter(x=sales_data['date'], y=sales_data['profit_ma'],
name=f'{window}-Day MA', mode='lines',
line=dict(color='purple', width=2)),
row=3, col=1
)
# 6. Daily Metrics Heatmap
# Prepare data for heatmap
pivot_data = sales_data.pivot_table(
index=sales_data['date'].dt.dayofweek,
columns=sales_data['date'].dt.week,
values='sales',
aggfunc='mean'
)
fig.add_trace(
go.Heatmap(z=pivot_data.values,
x=pivot_data.columns,
y=['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
colorscale='Viridis'),
row=3, col=2
)
# Update layout
fig.update_xaxes(title_text="Date", row=1, col=1)
fig.update_xaxes(title_text="Region", row=2, col=1)
fig.update_xaxes(title_text="Date", row=3, col=1)
fig.update_xaxes(title_text="Week of Year", row=3, col=2)
fig.update_yaxes(title_text="Amount ($)", row=1, col=1, secondary_y=False)
fig.update_yaxes(title_text="Customers", row=1, col=1, secondary_y=True)
fig.update_yaxes(title_text="Customers", row=1, col=2)
fig.update_yaxes(title_text="Sales ($)", row=2, col=1)
fig.update_yaxes(title_text="Profit ($)", row=3, col=1)
fig.update_yaxes(title_text="Day of Week", row=3, col=2)
fig.update_layout(
title_text="Sales Dashboard - 2024",
showlegend=True,
height=900,
hovermode='x unified'
)
fig.show()
Customizing Appearance
Themes and Templates
# Available themes
themes = ['plotly', 'plotly_white', 'plotly_dark', 'ggplot2',
'seaborn', 'simple_white', 'none']
# Apply a theme
fig = px.scatter(df, x="sepal_width", y="sepal_length",
color="species", template="plotly_dark",
title="Dark Theme Example")
fig.show()
# Custom theme
import plotly.io as pio
# Create custom template
pio.templates["custom"] = go.layout.Template(
layout=go.Layout(
colorway=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'],
font=dict(family="Arial, sans-serif", size=12, color="#2C3E50"),
title=dict(font=dict(size=24, color="#34495E")),
hovermode="closest",
plot_bgcolor="#F8F9FA",
paper_bgcolor="white",
xaxis=dict(gridcolor="#E0E0E0", zerolinecolor="#E0E0E0"),
yaxis=dict(gridcolor="#E0E0E0", zerolinecolor="#E0E0E0")
)
)
# Use custom template
fig = px.bar(tips, x="day", y="total_bill", color="sex",
template="custom", title="Custom Theme Example")
fig.show()
Annotations and Shapes
# Add annotations
fig = px.scatter(tips, x="total_bill", y="tip")
# Add text annotation
fig.add_annotation(
x=50, y=10,
text="High spender, high tipper",
showarrow=True,
arrowhead=2,
arrowsize=1,
arrowwidth=2,
arrowcolor="red",
font=dict(size=12, color="red")
)
# Add shapes
fig.add_shape(
type="rect",
x0=30, y0=5, x1=50, y1=10,
fillcolor="LightSalmon",
opacity=0.3,
layer="below",
line=dict(width=2, color="red")
)
# Add reference lines
fig.add_hline(y=tips['tip'].mean(), line_dash="dash",
annotation_text="Average Tip")
fig.add_vline(x=tips['total_bill'].mean(), line_dash="dash",
annotation_text="Average Bill")
fig.show()
Saving and Sharing
# Save as HTML (interactive)
fig.write_html("dashboard.html")
# Save as static image (requires kaleido)
fig.write_image("plot.png", width=1200, height=800, scale=2)
fig.write_image("plot.pdf")
fig.write_image("plot.svg")
# Save with custom config
config = {
'displayModeBar': True, # Show toolbar
'displaylogo': False, # Hide Plotly logo
'modeBarButtonsToRemove': ['pan2d', 'lasso2d'],
'toImageButtonOptions': {
'format': 'png',
'filename': 'custom_image',
'height': 500,
'width': 700,
'scale': 1
}
}
fig.show(config=config)
# Export to JSON
fig.write_json("figure.json")
# Share online (requires Chart Studio account)
import chart_studio.plotly as py
py.plot(fig, filename='my-dashboard', auto_open=True)
Integration with Dash
For full web applications with Plotly, use Dash:
# Basic Dash app example
# pip install dash
import dash
from dash import dcc, html, Input, Output
import plotly.express as px
# Initialize app
app = dash.Dash(__name__)
# Layout
app.layout = html.Div([
html.H1("Interactive Dashboard"),
dcc.Dropdown(
id='dropdown-selection',
options=[
{'label': 'Sepal Length', 'value': 'sepal_length'},
{'label': 'Sepal Width', 'value': 'sepal_width'},
{'label': 'Petal Length', 'value': 'petal_length'},
{'label': 'Petal Width', 'value': 'petal_width'}
],
value='sepal_length'
),
dcc.Graph(id='graph-content')
])
# Callback for interactivity
@app.callback(
Output('graph-content', 'figure'),
Input('dropdown-selection', 'value')
)
def update_graph(value):
df = px.data.iris()
fig = px.histogram(df, x=value, color='species',
nbins=30, title=f'Distribution of {value}')
return fig
# Run app
if __name__ == '__main__':
app.run_server(debug=True)
Performance Tips
- Use Scattergl for Large Datasets: Replace
go.Scatterwithgo.Scatterglfor 10,000+ points - Aggregate Data: Pre-aggregate data when possible instead of plotting raw points
- Limit Frames in Animations: Too many animation frames can slow down rendering
- Use DataShader: For millions of points, consider using datashader with Plotly
- Optimize Hover Data: Limit hover_data to essential information
- WebGL Rendering: Use
render_mode='webgl'for better performance
Common Patterns
graph TD
A[Data Type] --> B{Static or Interactive?}
B -->|Static| C[Use Matplotlib/Seaborn]
B -->|Interactive| D[Use Plotly]
D --> E{Quick or Custom?}
E -->|Quick| F[Plotly Express]
E -->|Custom| G[Graph Objects]
F --> H[px.scatter]
F --> I[px.line]
F --> J[px.bar]
G --> K[go.Figure]
K --> L[add_trace]
K --> M[update_layout]
K --> N[update_traces]
D --> O{Deployment}
O --> P[HTML Export]
O --> Q[Dash App]
O --> R[Jupyter]
Practice Exercises
Exercise 1: Interactive Time Series
Create an interactive time series plot with:
- Range slider for date selection
- Buttons for different time ranges (1M, 3M, 6M, 1Y, All)
- Multiple traces with hover information
- Annotations for key events
Exercise 2: Animated Geographic Data
Build an animated choropleth map showing:
- Data changing over time
- Custom hover templates
- Play/pause controls
- Color scale representing values
Exercise 3: Multi-Page Dashboard
Create a dashboard with:
- At least 6 different chart types
- Consistent color scheme
- Interactive elements (dropdowns, sliders)
- Export functionality
Key Takeaways
- 📊 Plotly creates interactive visualizations perfect for exploration and presentation
- ⚡ Plotly Express provides one-line solutions for common plots
- 🎯 Graph Objects offer fine-grained control over every aspect
- 🌐 Export options include HTML, static images, and online sharing
- 🚀 Dash integration enables full web applications
- 📱 Responsive design works across devices automatically