Introduction
Modern data analysis requires interactive visualizations that allow users to explore data dynamically. Today we'll explore three powerful Python libraries: Streamlit, Dash, and Bokeh - each offering unique advantages for different use cases.
π Streamlit: Simple and Fast
Streamlit is perfect for rapid prototyping with minimal code. It's the go-to choice for data scientists who want to create web apps without web development knowledge.
Key Features
β
Zero HTML/CSS knowledge required
β
Real-time updates
β
Easy deployment
β
Rich widget ecosystem
Example: Sales Dashboard
import streamlit as st import pandas as pd import plotly.express as px import numpy as np # Page configuration st.set_page_config(page_title="Sales Dashboard", layout="wide") st.title("π Sales Dashboard") # Generate sample data @st.cache_data def load_data(): dates = pd.date_range('2024-01-01', '2024-12-31', freq='D') data = { 'date': dates, 'sales': np.random.normal(1000, 200, len(dates)), 'region': np.random.choice(['North', 'South', 'East', 'West'], len(dates)), 'product': np.random.choice(['Product A', 'Product B', 'Product C'], len(dates)) } df = pd.DataFrame(data) df['sales'] = df['sales'].clip(lower=0) # No negative sales return df df = load_data() # Sidebar filters st.sidebar.header("Filters") region = st.sidebar.selectbox("Select Region", df['region'].unique()) product = st.sidebar.selectbox("Select Product", df['product'].unique()) # Filter data filtered_df = df[(df['region'] == region) & (df['product'] == product)] # Metrics row col1, col2, col3 = st.columns(3) with col1: st.metric("Total Sales", f"${filtered_df['sales'].sum():,.2f}") with col2: st.metric("Average Daily Sales", f"${filtered_df['sales'].mean():.2f}") with col3: st.metric("Number of Days", len(filtered_df)) # Charts col1, col2 = st.columns(2) with col1: # Time series chart fig_line = px.line( filtered_df, x='date', y='sales', title=f"Sales Trend - {region} ({product})" ) st.plotly_chart(fig_line, use_container_width=True) with col2: # Distribution chart fig_hist = px.histogram( filtered_df, x='sales', title="Sales Distribution", nbins=30 ) st.plotly_chart(fig_hist, use_container_width=True) # Data table if st.checkbox("Show Raw Data"): st.subheader("Raw Data") st.dataframe(filtered_df.head(100), use_container_width=True)
π’ Dash: Enterprise-Ready
Dash offers more control and customization for production applications. It's built on Flask and React, making it perfect for enterprise-grade dashboards.
Key Features
β
Production-ready
β
Highly customizable
β
Advanced callback system
β
Enterprise authentication support
Example: Stock Portfolio Tracker
import dash from dash import dcc, html, Input, Output, callback import plotly.express as px import plotly.graph_objects as go import pandas as pd import yfinance as yf # Initialize the Dash app app = dash.Dash(__name__) # Portfolio data STOCKS = ['AAPL', 'GOOGL', 'TSLA', 'MSFT', 'AMZN'] # Layout app.layout = html.Div([ html.Div([ html.H1("πΌ Stock Portfolio Tracker", style={'textAlign': 'center', 'color': '#2E86AB', 'marginBottom': 30}), ]), html.Div([ html.Div([ html.Label("Select Stock:", style={'fontWeight': 'bold'}), dcc.Dropdown( id='stock-dropdown', options=[{'label': stock, 'value': stock} for stock in STOCKS], value='AAPL', style={'marginBottom': 20} ), html.Label("Time Period:", style={'fontWeight': 'bold'}), dcc.Dropdown( id='period-dropdown', options=[ {'label': '1 Month', 'value': '1mo'}, {'label': '3 Months', 'value': '3mo'}, {'label': '6 Months', 'value': '6mo'}, {'label': '1 Year', 'value': '1y'} ], value='3mo' ) ], style={'width': '30%', 'display': 'inline-block', 'verticalAlign': 'top'}), html.Div([ html.Div(id='stock-info') ], style={'width': '70%', 'display': 'inline-block', 'paddingLeft': 20}) ]), html.Div([ dcc.Graph(id='stock-chart') ]), html.Div([ dcc.Graph(id='volume-chart') ]) ]) @app.callback( [Output('stock-chart', 'figure'), Output('volume-chart', 'figure'), Output('stock-info', 'children')], [Input('stock-dropdown', 'value'), Input('period-dropdown', 'value')] ) def update_dashboard(selected_stock, period): # Fetch stock data try: stock_data = yf.download(selected_stock, period=period) # Stock price chart fig_price = go.Figure() fig_price.add_trace(go.Candlestick( x=stock_data.index, open=stock_data['Open'], high=stock_data['High'], low=stock_data['Low'], close=stock_data['Close'], name=selected_stock )) fig_price.update_layout( title=f"{selected_stock} Stock Price", yaxis_title="Price ($)", xaxis_title="Date" ) # Volume chart fig_volume = px.bar( x=stock_data.index, y=stock_data['Volume'], title=f"{selected_stock} Trading Volume" ) # Stock info current_price = stock_data['Close'].iloc[-1] price_change = current_price - stock_data['Close'].iloc[-2] price_change_pct = (price_change / stock_data['Close'].iloc[-2]) * 100 info = html.Div([ html.H3(f"{selected_stock} Information"), html.P(f"Current Price: ${current_price:.2f}"), html.P(f"Change: ${price_change:.2f} ({price_change_pct:.2f}%)", style={'color': 'green' if price_change >= 0 else 'red'}), html.P(f"Period High: ${stock_data['High'].max():.2f}"), html.P(f"Period Low: ${stock_data['Low'].min():.2f}") ]) return fig_price, fig_volume, info except Exception as e: # Error handling empty_fig = go.Figure() error_info = html.P(f"Error loading data: {str(e)}") return empty_fig, empty_fig, error_info if __name__ == '__main__': app.run_server(debug=True)
β‘ Bokeh: Advanced Visualizations
Bokeh handles large datasets and complex interactions efficiently. It's perfect for creating sophisticated, publication-ready visualizations.
Key Features
β
High performance with large datasets
β
Advanced interactions
β
Real-time capabilities
β
Server and standalone applications
Example: Real-time Data Monitor
from bokeh.plotting import figure, curdoc from bokeh.layouts import column, row from bokeh.models import ColumnDataSource, Select, Button from bokeh.models.widgets import DataTable, TableColumn import numpy as np import pandas as pd from datetime import datetime from functools import partial # Global data source source = ColumnDataSource(data=dict( x=[], y=[], timestamp=[], sensor_id=[], color=[] )) # Create main plot def create_time_plot(): p = figure( title="Real-time Sensor Data Monitor", x_axis_label="Time", y_axis_label="Value", width=800, height=400, x_axis_type='datetime' ) p.line('timestamp', 'y', source=source, line_width=2, color='blue', alpha=0.8) p.circle('timestamp', 'y', source=source, size=8, color='color', alpha=0.6) return p # Create histogram def create_histogram(): p = figure( title="Value Distribution", x_axis_label="Value", y_axis_label="Frequency", width=400, height=300 ) return p # Controls sensor_select = Select( title="Sensor ID:", value="All", options=["All", "Sensor_1", "Sensor_2", "Sensor_3"] ) start_button = Button(label="Start Monitoring", button_type="success") stop_button = Button(label="Stop Monitoring", button_type="danger") # Data table columns = [ TableColumn(field="timestamp", title="Timestamp"), TableColumn(field="y", title="Value"), TableColumn(field="sensor_id", title="Sensor ID") ] data_table = DataTable(source=source, columns=columns, width=400, height=200) # Callback for data generation def update_data(): """Generate new data point""" sensors = ["Sensor_1", "Sensor_2", "Sensor_3"] colors = ["red", "blue", "green"] new_data = { 'timestamp': [datetime.now()], 'y': [np.random.normal(50, 10)], 'sensor_id': [np.random.choice(sensors)], 'color': [np.random.choice(colors)] } # Update data source current_data = source.data.copy() for key in new_data: if key in current_data: current_data[key].extend(new_data[key]) # Keep only last 100 points if len(current_data[key]) > 100: current_data[key] = current_data[key][-100:] else: current_data[key] = new_data[key] source.data = current_data # Button callbacks def start_monitoring(): curdoc().add_periodic_callback(update_data, 1000) def stop_monitoring(): curdoc().remove_periodic_callback(update_data) start_button.on_click(start_monitoring) stop_button.on_click(stop_monitoring) # Create plots time_plot = create_time_plot() histogram = create_histogram() # Layout controls = column(sensor_select, row(start_button, stop_button)) plots = row(time_plot, column(histogram, data_table)) layout = column(controls, plots) # Add to document curdoc().add_root(layout) curdoc().title = "Real-time Data Monitor"
π¦ Deployment Guide
Streamlit Cloud (Recommended)
Create requirements.txt:
txtstreamlit==1.28.0 pandas==2.0.3 plotly==5.15.0 numpy==1.24.3
Deploy Steps:
# Push to GitHub git add . git commit -m "Initial commit" git push origin main # Visit share.streamlit.io # Connect your GitHub repository # Auto-deploy!
Dash on Heroku
Prepare files:
# Add to your Dash app server = app.server if __name__ == '__main__': app.run_server(debug=False)
Create Procfile:
web: gunicorn app:server
Deploy:
heroku create your-app-name git push heroku main
***Bokeh Server*
Start Bokeh server:**
bashbokeh serve --show --allow-websocket-origin=yourdomain.com app.py
For cloud deployment:
bashbokeh serve --port=$PORT --allow-websocket-origin=* app.py
π Comparison Matrix
Conclusion
Interactive data visualization has never been more accessible. Whether you choose Streamlit for rapid prototyping, Dash for enterprise applications, or Bokeh for advanced visualizations, you now have the power to create compelling, interactive dashboards that transform how users interact with data.
Start with Streamlit to get your feet wet, graduate to Dash when you need production features, and leverage Bokeh when performance and advanced interactions are critical.
The future of data visualization is interactive - and these tools are your gateway to that future.
Top comments (1)
nice