Making dashboards using altair

Any Altair chart can be saved as HTML using chart.save("filename.html"). If you open the resulting file with a web browser, you'll see the chart without any of the associated Python code.

Alternatively, you can use chart.to_json() to get out the JSON chart specification, which can then be embedded in any web page using vega-embed... this is exactly what is done in the page exported by chart.save.


As to your second question (please in the future try to limit your StackOverflow posts to a single question): Altair works with JupyterLab, Jupyter notebook, CoLab, nteract, and Hydrogen. You can use any of these frontends, though some require some extra setup. See https://altair-viz.github.io/getting_started/installation.html for details. I use JupyterLab, and would suggest starting with that.


In addition to creating standalone HTML files and using vega-embed, Altair is also compatible with common dashboarding packages such as Panel, Streamlit, and Dash. I have provided a simple example for each below:

Panel

Panel works both in the notebook and as a standalone dashboard. It also lets you embed dashboards in single HTML files.

import altair as alt
from vega_datasets import data
import panel as pn
from panel.interact import interact


pn.extension('vega')

cars = data.cars()

def scatter_plot(x_column):
    chart = alt.Chart(cars).mark_point().encode(
        x=x_column,
        y='Displacement')
    return chart

interact(scatter_plot, x_column=cars.select_dtypes('number').columns)

enter image description here

The capability to listen to Vega events and define custom callback for e.g. selected points was recently merged in Panel and is included in the 0.13 release! This is the only Python dashboarding package that supports custom callbacks on selections in Altair charts. Here is an example from the docs:

penguins_url = "https://raw.githubusercontent.com/vega/vega/master/docs/data/penguins.json"
brush = alt.selection_interval(name='brush')  # selection of type "interval"

chart = alt.Chart(penguins_url).mark_point().encode(
    x=alt.X('Beak Length (mm):Q', scale=alt.Scale(zero=False)),
    y=alt.Y('Beak Depth (mm):Q', scale=alt.Scale(zero=False)),
    color=alt.condition(brush, 'Species:N', alt.value('lightgray'))
).properties(
    width=250,
    height=250
).add_selection(
    brush
)

vega_pane = pn.pane.Vega(chart, debounce=10)

vega_pane

df = pd.read_json(penguins_url)

def filtered_table(selection):
    if not selection:
        return '## No selection'
    query = ' & '.join(
        f'{crange[0]:.3f} <= `{col}` <= {crange[1]:.3f}'
        for col, crange in selection.items()
    )
    return pn.Column(
        f'Query: {query}',
        pn.pane.DataFrame(df.query(query), width=600, height=300)
    )

pn.Row(vega_pane, pn.bind(filtered_table, vega_pane.selection.param.brush))

image

Streamlit

Streamlit aims to be as close as possible to a regular Python script without having to focus programming a front end.

from vega_datasets import data
import streamlit as st
import altair as alt


cars = data.cars()

x_column = st.selectbox('Select x-axis column', cars.select_dtypes('number').columns)

chart = alt.Chart(cars).mark_point().encode(
    x=x_column,
    y='Displacement')

st.altair_chart(chart, use_container_width=True)

enter image description here

Dash

Dash is more verbose than the other two as it requires you to be explicit about many things in the frontend and the callbacks (which also gives more fine-grained control). There is no specific object for Altair graphs, so we're plugging the HTML plot into an iframe (I've asked about this).

import altair as alt
from vega_datasets import data
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output


cars = data.cars()

# Setup app and layout/frontend
app = dash.Dash(__name__,  external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'])
app.layout = html.Div([
    dcc.Dropdown(
        id='x_column-widget',
        value='Miles_per_Gallon',  # REQUIRED to show the plot on the first page load
        options=[{'label': col, 'value': col} for col in cars.columns]),
    html.Iframe(
        id='scatter',
        style={'border-width': '0', 'width': '100%', 'height': '400px'})])

# Set up callbacks/backend
@app.callback(
    Output('scatter', 'srcDoc'),
    Input('x_column-widget', 'value'))
def plot_altair(x_column):
    chart = alt.Chart(cars).mark_point().encode(
        x=x_column,
        y='Displacement',
        tooltip='Horsepower').interactive()
    return chart.to_html()

if __name__ == '__main__':
    app.run_server(debug=True)

enter image description here


In addition to what suggested by @jakevdp, I found really useful to export the chart definition in json format and render it within the (still beta) Observable Notebook from Mike Bostock: graphs/interactions are produced with altair while boilerplate UI are easily added in plain HTML or javascript in a "reactive" environment (i.e. ... cells are automatically re-evaluated in topological order whenever their inputs change). The code is almost entirely hidden there, and, at the same time, you could exploit the idea of "computational essay" that has made Jupyter so popular. Create a reasonably complex and clean UI/Dashboard was for me easier there than using Jupyter + widgets and, thanks to altair, without the effort to program "complex" charts by hand.

Tags:

Python

Altair