Skip to main content

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:

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

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:

  1. Range slider for date selection
  2. Buttons for different time ranges (1M, 3M, 6M, 1Y, All)
  3. Multiple traces with hover information
  4. Annotations for key events

Exercise 2: Animated Geographic Data

Build an animated choropleth map showing:

  1. Data changing over time
  2. Custom hover templates
  3. Play/pause controls
  4. Color scale representing values

Exercise 3: Multi-Page Dashboard

Create a dashboard with:

  1. At least 6 different chart types
  2. Consistent color scheme
  3. Interactive elements (dropdowns, sliders)
  4. Export functionality

Key Takeaways

Further Resources