this notebook shows a very basic dashboard made in altair / vega-lite from dynamic data (fetched at a web service)
what we will demonstrate is
how to write a simple web server that generates random data - as an anticipation of the S2 courses
how to create an altair chart from that data, as an example of using altair’s “graphic grammar” approach
how to transform the result into a standalone web app
one of the pros of using altair is the ability to produce an interactive visualisation as a self-contained HTML file
dependencies¶
you may need to
pip install "fastapi[standard]" altairimports¶
import pandas as pd
# here is how to import altair
import altair as altthe fastapi web service¶
we provide you with the source code for a small web service that is able to produce random data for the leases, in a format compatible with the one in leases.csv
this is the purpose of fastapi_random_yields.py
how to start¶
assuming you have installed fastapi as above, you can start the web service by typing in your terminal
fastapi dev fastapi_random_yields.pythis will behave a bit like jupyter lab, in that it will
start a web server
display the URL to use to join it
and it will block, meaning the terminal is busy, and no longer responding to your commands
so first off, leave this terminal running, and create another one if needed
how to get the docs¶
inside the terminal where you triggered the server, you’ll see a URL
something like http://
just open a web browser and cut-n-paste that URL
link maybe clickable ?
in some terminal setups, you may be able to e.g. Command-click or Alt-click on the link in the terminal to open it in your browser (this is rather likely on linux and MacOS, not sure wbout Windows)
as you might have guessed now, this means
use the http protocol
to reach a service running on the computer named
localhost(your own laptop, that is)on port 8000
and on the
/docs/route
the /docs/ route is a predefined route, auto-generated by FastAPI, that gives us.. the doc of how to use this web service
how to use¶
and so, from this doc, you can see that you can send requests with this pattern
/api/yields/1931/1933so, try it out from your browser and enter this URL: http://
also from the terminal
you can achieve the same result from the terminal if you prefer
first you need topip install httpie
and then you just dohttp :8000/api/yields/1931/1933
then take a look at jq that can filter this JSON stream for you
the API outcome¶
either way - from the browser or from the terminal - you should see a JSON stream with the random data generated
it’s actually a list of dicts, and its structure mimicks this example taken from altair’s documentation
import altair as alt
from vega_datasets import data
source = data.barley()
source.head(10)so our API call above would issue a variation around this predefined data, over 3 years 1931 .. 1933
and there’s one generated entry for each combination of site x variety x year
altair visualisations¶
altair offers a “grammar-oriented” visualisation paradigm where the visualisation is defined in a declarative way
a stacked bar from altair’s doc¶
here’s an example taken from the altair documentation
# stolen from https://altair-viz.github.io/gallery/stacked_bar_chart.html
(
alt.Chart(source)
.mark_bar()
.encode(
x='variety',
y='sum(yield)',
color='site'
)
)exo: inspect the data¶
take some time to get a glimpse at what the data looks like...
import itables
itables.init_notebook_mode()
source# your code here
# feel free to create extra cells if neededexo: write your own pivot¶
you should be able to see a resemblance with some sort of pivot table here
would you be able to compute a pivot table that resonates with this visualisation ?
solution
source.pivot_table(
values='yield',
aggfunc="sum",
index="site",
columns="variety",
)# your codea few useful additions¶
here’s a few additions to that sample chart, that will make our life easier:
we set a width and height
as well as a title
and make it interactive: try to scroll up or down in the figure with 2 fingers
not interactive ?
Oh but no, the interactive thing is not working for us here; it is because the X axis does not have numeric values !
but let’s keep this trick in mind for later, it will come in handy at some point
# once and for good
alt.renderers.enable("mimetype")
# same beginning
(
alt.Chart(source)
.mark_bar()
.encode(
x='variety',
y='sum(yield)',
color='site'
).properties(
height=300,
width=800,
title=f"Barley yields",
)
.interactive()
)dynamic data¶
ofcourse, we can easily switch from the (static) data source to our web service
# just replace the source with this
URL = "http://localhost:8000/api/yields/1931/1933"
chart = (
alt.Chart(URL)
.mark_bar()
.encode(
# here we need to help altair (actually vega-lite)
# and be explicit on the types of the various fields
x='variety:N',
y='sum(yield):Q',
color='site:N'
).properties(
height=300,
width=800,
title=f"Barley yields",
)
.interactive()
)save as an HTML standalone app¶
import altair as alt
# Convert to HTML, adding a custom button and reload script
html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Altair Chart with Refresh</title>
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body style="font-family:sans-serif">
<h2>Yield Chart (with Refresh)</h2>
<button id="refresh">🔄 Refresh</button>
<div id="visual"></div>
<script type="text/javascript">
const spec = {chart.to_json(indent=None)};
const container = document.getElementById('visual');
function render() {{
vegaEmbed(container, spec, {{ actions: false }});
}}
// Initial render
render();
// Reload button handler
document.getElementById('refresh').addEventListener('click', () => {{
// Force Vega-Lite to reload the URL by tweaking it with a cache-busting parameter
const url = new URL(spec.data.url);
url.searchParams.set('_t', Date.now());
spec.data.url = url.toString();
render();
}});
</script>
</body>
</html>
"""
# Save to a standalone file
with open("yield-chart.html", "w", encoding="utf-8") as f:
f.write(html)
print("✅ Saved to yield-chart.html")✅ Saved to yield-chart.html
try it out¶
the previous cell should have created a local file named yield-chart.html
the simplest way to try it is to open it in your browser (e.g. double-click it from your file explorer)
of course this can also be inserted into another app, etc...
conclusion¶
with altair:
the way the diagram is built, using this so-called graphic grammar, reminds a bit of the way seaborn lets us use several dimensions to outline various aspects of the data
you have a rather easy way to write nice web apps with mostly Python, and a limited knowledge of the web technos like HTML and the like
you can easily use dynamic data sources
it can come in handy sometimes to come up with great demos in a reasonable time