Skip to article frontmatterSkip to article content

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

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]" altair

imports

import pandas as pd

# here is how to import altair
import altair as alt

the 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.py

this will behave a bit like jupyter lab, in that it will

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://localhost:8000/docs/
just open a web browser and cut-n-paste that URL

as you might have guessed now, this means

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/1933

so, try it out from your browser and enter this URL: http://localhost:8000/api/yields/1931/1933

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

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

exo: inspect the data

take some time to get a glimpse at what the data looks like...

import itables
itables.init_notebook_mode()

source
Loading...
# your code here
# feel free to create extra cells if needed

exo: 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 ?

# your code

a few useful additions

here’s a few additions to that sample chart, that will make our life easier:

# 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()
)
Loading...

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: