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)
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))
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)
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)
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.