I’m trying to use altair to visualize data, but I struggle to use it in the way I would like to use it: by not embedding the data inside the generated .html
chart, but by referencing the local .csv
file that contains the data. Otherwise, it would result in duplicating the data and therefore doubling the required storage.
Here’s what I tried:
import os import altair as alt import numpy as np import pandas as pd # Dummy data. n = 100 df = pd.DataFrame({ "x": np.arange(n), "y": np.random.randn(n), }) # Using the dataframe directly. chartdf = alt.Chart(df).mark_line().encode( x='x:Q', y='y:Q', ) chartdf.save('chartdf.html') # Now referencing the local .csv file. filename = "data.csv" df.to_csv(filename) directory = os.getcwd() fullpath = "file://" + os.path.join(directory, filename) alt.data_transformers.enable('csv') chartcsv = alt.Chart(fullpath).mark_line().encode( x='x:Q', y='y:Q', ) chartcsv.save('chartcsv.html')
As a result, I get two .html
files. Here’s chartdf.html
, correctly showing the plot, but storing the data in the source code.
<!DOCTYPE html> <html> <head> <style> .error { color: red; } </style> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega@5"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-lite@4.8.1"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-embed@6"></script> </head> <body> <div id="vis"></div> <script> (function(vegaEmbed) { var spec = {"config": {"view": {"continuousWidth": 400, "continuousHeight": 300}}, "data": {"name": "data-1727235c61a7b22ee660b7640869d48d"}, "mark": "line", "encoding": {"x": {"type": "quantitative", "field": "x"}, "y": {"type": "quantitative", "field": "y"}}, "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json", "datasets": {"data-1727235c61a7b22ee660b7640869d48d": [{"x": 0, "y": 0.2112352635569061}, {"x": 1, "y": -0.6297931563725685}, {"x": 2, "y": -1.6669551411704615}, {"x": 3, "y": -0.8590377939062339}, {"x": 4, "y": -2.362675608121853}, {"x": 5, "y": 0.1816769959879393}, {"x": 6, "y": 0.21964717520484453}, {"x": 7, "y": 0.5924276442771413}, {"x": 8, "y": 2.062942588202062}, {"x": 9, "y": -1.411207827356746}, {"x": 10, "y": -2.2266024022172957}, {"x": 11, "y": 0.47229354076715996}, {"x": 12, "y": -0.14474440245785294}, {"x": 13, "y": 0.051738284319541146}, {"x": 14, "y": -0.4838552098939953}, {"x": 15, "y": 0.29366971719427554}, {"x": 16, "y": 0.18361573516404703}, {"x": 17, "y": 0.6734430961408209}, {"x": 18, "y": -1.6084727160210788}, {"x": 19, "y": 0.457742541113758}, {"x": 20, "y": -1.4873380353155474}, {"x": 21, "y": 0.6118088150042575}, {"x": 22, "y": 0.9889323424386781}, {"x": 23, "y": -0.4539168151678354}, {"x": 24, "y": 0.5438198860906843}, {"x": 25, "y": 0.47011997666216776}, {"x": 26, "y": -0.7562418295269627}, {"x": 27, "y": -0.27690138025681765}, {"x": 28, "y": 0.49827375786911876}, {"x": 29, "y": 1.0153141593800437}, {"x": 30, "y": 1.1782385627274985}, {"x": 31, "y": 0.0424730271073153}, {"x": 32, "y": -1.4185058816513483}, {"x": 33, "y": -1.8114268346098452}, {"x": 34, "y": -1.29594887684236}, {"x": 35, "y": 0.09894597002376446}, {"x": 36, "y": -1.4631390515148475}, {"x": 37, "y": -2.1926825852861485}, {"x": 38, "y": 1.1519292846453788}, {"x": 39, "y": -0.5818573231859363}, {"x": 40, "y": -0.02064804413385189}, {"x": 41, "y": 0.5006162765747295}, {"x": 42, "y": -0.9784096515241409}, {"x": 43, "y": 1.7117580547861466}, {"x": 44, "y": 0.8677907294763252}, {"x": 45, "y": -1.3285577876719292}, {"x": 46, "y": 0.385574566953019}, {"x": 47, "y": 1.0251802025168757}, {"x": 48, "y": 0.2255080418305147}, {"x": 49, "y": 0.8012860491915356}, {"x": 50, "y": -1.3742866681331305}, {"x": 51, "y": -0.5160585231146249}, {"x": 52, "y": 1.0086907194824801}, {"x": 53, "y": 1.8441072855180702}, {"x": 54, "y": 0.10647348146379314}, {"x": 55, "y": -0.9950070073653883}, {"x": 56, "y": -0.1209797393395647}, {"x": 57, "y": 1.2957688874221422}, {"x": 58, "y": 1.5377004050922511}, {"x": 59, "y": 0.14393885533903575}, {"x": 60, "y": -1.440499604914547}, {"x": 61, "y": 1.4762530684041733}, {"x": 62, "y": 0.3962335303842206}, {"x": 63, "y": 0.15195105628021952}, {"x": 64, "y": 1.274568257852339}, {"x": 65, "y": -1.9517895004128185}, {"x": 66, "y": -1.6847841535930692}, {"x": 67, "y": 1.3936389293839209}, {"x": 68, "y": -0.11370298418438128}, {"x": 69, "y": 1.8052343535396553}, {"x": 70, "y": -0.3443348662400949}, {"x": 71, "y": 0.8838398407281737}, {"x": 72, "y": 0.8666912228874258}, {"x": 73, "y": 2.33181288689919}, {"x": 74, "y": -1.7370617166021098}, {"x": 75, "y": -0.8447406898036955}, {"x": 76, "y": -0.27812566279331796}, {"x": 77, "y": 0.6939513790408266}, {"x": 78, "y": 1.2769804660273463}, {"x": 79, "y": 1.275176541502504}, {"x": 80, "y": -0.3843801173353608}, {"x": 81, "y": -0.14199480129344477}, {"x": 82, "y": -0.7443885322363791}, {"x": 83, "y": 0.392135684892227}, {"x": 84, "y": -1.4963957824987753}, {"x": 85, "y": -0.05395082651327829}, {"x": 86, "y": -1.1640386653528847}, {"x": 87, "y": -0.8740477495639896}, {"x": 88, "y": 2.367892815244499}, {"x": 89, "y": -1.1861757645950304}, {"x": 90, "y": 0.4862085488887814}, {"x": 91, "y": -1.907728993442288}, {"x": 92, "y": 0.6304124541928168}, {"x": 93, "y": 1.0704432779754836}, {"x": 94, "y": -0.805919444706216}, {"x": 95, "y": 0.829176968267078}, {"x": 96, "y": 0.16050957366472954}, {"x": 97, "y": 1.1663653159345964}, {"x": 98, "y": 0.33908181024409456}, {"x": 99, "y": -0.04008707863910976}]}}; var embedOpt = {"mode": "vega-lite"}; function showError(el, error){ el.innerHTML = ('<div class="error" style="color:red;">' + '<p>JavaScript Error: ' + error.message + '</p>' + "<p>This usually means there's a typo in your chart specification. " + "See the javascript console for the full traceback.</p>" + '</div>'); throw error; } const el = document.getElementById('vis'); vegaEmbed("#vis", spec, embedOpt) .catch(error => showError(el, error)); })(vegaEmbed); </script> </body> </html>
And here’s the chartcsv.html
file, not displaying the chart.
<!DOCTYPE html> <html> <head> <style> .error { color: red; } </style> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega@5"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-lite@4.8.1"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-embed@6"></script> </head> <body> <div id="vis"></div> <script> (function(vegaEmbed) { var spec = {"config": {"view": {"continuousWidth": 400, "continuousHeight": 300}}, "data": {"url": "file:///Users/MY_USER_NAME_WAS_HERE/Projects/ds-python/utilities/plotlibrary/data.csv"}, "mark": "line", "encoding": {"x": {"type": "quantitative", "field": "x"}, "y": {"type": "quantitative", "field": "y"}}, "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json"}; var embedOpt = {"mode": "vega-lite"}; function showError(el, error){ el.innerHTML = ('<div class="error" style="color:red;">' + '<p>JavaScript Error: ' + error.message + '</p>' + "<p>This usually means there's a typo in your chart specification. " + "See the javascript console for the full traceback.</p>" + '</div>'); throw error; } const el = document.getElementById('vis'); vegaEmbed("#vis", spec, embedOpt) .catch(error => showError(el, error)); })(vegaEmbed); </script> </body> </html>
What’s the correct way to implement altair charts that use data stored in a local file?
Advertisement
Answer
To use a local data file in an Altair chart, two conditions need to be met:
- The file must be specified as a URL valid for the frontend (i.e. browser) displaying the chart
- The URL must satisfy the security requirements of the browser (e.g. must satisfy cross-origin policies)
Unfortunately, the approach to ensuring (1) and (2) are met is highly dependent on what frontend you’re using (i.e. JupyterLab vs. Jupyter Notebook vs. Colab vs. Streamlit vs. …) and within each of these can even depend on what version of the notebook/server you’re running, what browser you’re using, what browser security settings you have enabled, whether you’re using http
or https
, whether or not you use an ad-blocker, what operating system you’re using, the precise details of how you launched your notebook server or opened your HTML file, and probably many other variables.
For this reason, as the primary author of Altair, I generally discourage people from attempting to use local data files, because it’s quite difficult to answer seemingly simple questions like “how do I reference a local CSV file in an Altair chart?”.
If you want to press forward, I would suggest opening your browser’s javascript console, where you’ll see warnings or errors that could help in diagnosing how you can change things to meet requirements (1) and (2) above within your own setup. If you want something that will just work, I would suggest using pandas to load the file into a dataframe, and create the chart that way.