Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to show altair charts from console programs? #3330

Closed
fhg-isi opened this issue Feb 14, 2024 · 27 comments
Closed

How to show altair charts from console programs? #3330

fhg-isi opened this issue Feb 14, 2024 · 27 comments
Labels

Comments

@fhg-isi
Copy link

fhg-isi commented Feb 14, 2024

I tried to use altair in a python script using PyCharm.

If I run

chart.show()

I first got an error, that I need altair_viewer to show charts. After installing altair_viewer, I got an error

altair_viewer._utils.NoMatchingVersions: No matches for version='5.16.3' among ['4.0.2', '4.8.1', '4.17.0'].
Often this can be fixed by updating altair_viewer:
    pip install -U altair_viewer

Reading the documentation, I saw that altair_view does not support altair 5.
I do not want users to install an extra PyCharm extension.

=> How can I use altair outside JupyterNotebooks?

Expected: Open a plot or browser window and show chart, similar to the features provided by Matplotlib or Plotly.

Actual: Shows misleading error messages.

Related:

@fhg-isi fhg-isi added the bug label Feb 14, 2024
@fhg-isi
Copy link
Author

fhg-isi commented Feb 14, 2024

The latest version of altair that is supported by altair_viewer seems to be 4.2.2.
However, altair_viewer does not include an explict version number in its requirements.txt.
The version "4.17.0" mentioned in the error message does not seem to exist.

@mattijn
Copy link
Contributor

mattijn commented Feb 14, 2024

Just print chart to show the chart. There is no need to append .show()

@fhg-isi
Copy link
Author

fhg-isi commented Feb 14, 2024

chart
or
print(chart)
Does not show a chart in non-notebook environment.
I would like to run my code with
python main.py

chart.show() works as expected when using

pip install altair==4.2.2
pip install altair_viewer  

@mattijn
Copy link
Contributor

mattijn commented Feb 14, 2024

I see, hm. Good point! We are trying to integrate features from altair_viewer into altair, so we can deprecate the altair_viewer package. This should be one of them. Thanks for reporting (again).

@jonmmease
Copy link
Contributor

Cross reference #2866. I think we're going to remove the altair_viewer dependency and replace it with a solution based on vl-convert. The current behavior of altair_viewer (when it worked with version 4) is to display all charts in a kernel session in a single browser tab, where each new chart replaces the prior chart. I'm proposing that the new behavior with vl-convert be to open a new browser tab each time chart.show() is called.

Let us know if you have opinions on this workflow @fhg-isi.

@fhg-isi
Copy link
Author

fhg-isi commented Feb 14, 2024

a) The behavior of matplotlib is to wait for the first chart window do be closed before the next chart window is shown.
b) The behavior of plotly is to create several tabs in the browser.

I agree to you suggestion and would prefer a solution with several tabs because it allows to view several charts and easily compare them if wanted.

@franzhaas
Copy link
Contributor

Cross reference #2866. I think we're going to remove the altair_viewer dependency and replace it with a solution based on vl-convert. The current behavior of altair_viewer (when it worked with version 4) is to display all charts in a kernel session in a single browser tab, where each new chart replaces the prior chart. I'm proposing that the new behavior with vl-convert be to open a new browser tab each time chart.show() is called.

Let us know if you have opinions on this workflow @fhg-isi.

Honestly, I really like the "replace privious" behaviour.. It allows to overwrite my old visualisation when new data arrived (slow measurement) with a new visualisation...

with altair, it is really convenient to add a new chart next/above/below an already existing one if that is preferred.

best regards,
Franz

@joelostblom
Copy link
Contributor

It allows to overwrite my old visualisation when new data arrived (slow measurement) with a new visualisation...

If you want this behavior, would saving the chart as html to a file that you have open in a browser tab and keep overwriting the same file path when you make changes to the chart be an option? Or is there something that this workflow does not capture?

with altair, it is really convenient to add a new chart next/above/below an already existing one if that is preferred.

I don't quite understand how this is related to showing charts from the terminal, could you elaborate a bit and explain if there is a difference in this regard if charts are opened in new tabs instead of overwriting the same tab?

@franzhaas
Copy link
Contributor

It allows to overwrite my old visualisation when new data arrived (slow measurement) with a new visualisation...

If you want this behavior, would saving the chart as html to a file that you have open in a browser tab and keep overwriting the same file path when you make changes to the chart be an option? Or is there something that this workflow does not capture?

Well as the charts update frequently, I need a Webserver that pushes the changed html, or a local html page which refreshes from that location all the time...

I will look into that. This probably is a good solution.

Thanks for the hint!

@franzhaas
Copy link
Contributor

I tried. I did not have any luck with HTML, but json + a local webserver + some javascript did the trick.

is there a file format more recomendable than json for this purpose? I think there is something for large data which renders in rust... would that be an option here?

This is the HTML which needs to be served with for example "python -m http.server" which renders the "chart.json".

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Altair JSON from File in Webpage</title>
    <!-- Load Vega-Lite and Vega Embed libraries -->
    <script src="https://cdn.jsdelivr.net/npm/vega"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-lite"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-embed"></script>
</head>
<body>
    <!-- Container for the visualization -->
    <div id="vis"></div>

    <script>
            // Function to fetch the JSON specification from file
            function fetchJSONFile(path, callback) {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    callback(JSON.parse(xhr.responseText));
                }
            };
            xhr.open('GET', path, true);
            xhr.send();
        }

        // Function to render the visualization
        function renderVisualization() {
            // Call the function to fetch JSON from file
            fetchJSONFile('chart.json', function (altairSpec) {
                // Render the visualization using Vega Embed
                vegaEmbed('#vis', altairSpec);
            });
        }

        // Render the visualization initially
        renderVisualization();

        // Set interval to refresh the visualization every second
        setInterval(renderVisualization, 500);
    </script>
</body>
</html>

@jonmmease
Copy link
Contributor

We might be able to replicate the altair-viewer behavior with panel's repl support: https://panel.holoviz.org/how_to/server/programmatic.html.

With this approach we could also potentially integrate with JupyterChart to get two-way interactivity (including the ability to replace charts in place).

Maybe jchart.show() on a JupyterChart instance launches a browser window using panel. And then jchart.chart = new_chart would replace the chart. And you could use jchart.params and jchart.selections to access the chart's params and selections from Python.

@joelostblom
Copy link
Contributor

joelostblom commented Mar 12, 2024

The two-way interactivity via Jupyter Charts + Panel sounds like an interesting and powerful combo. Although I still see value in the "new tab" approach you suggested earlier in #2866 (comment), since this wouldn't have any external dependencies and could be baked into the altair base package. Adding panel + bokeh is a bigger change that maybe is better suited as part of the extras/optional requirements?

@jonmmease
Copy link
Contributor

Agreed.

It would be neat to have a renderers style system for chart.show() so that you could enable different display methods for different contexts. Back when I was working on JupyterDash I wrote a small JupyterLab extension that would display the app as a pane inside JupyterLab, so this could be another display mode that you could trigger with chart.show(). With JupyterChart, I think we could achieve this with jupyter sidecar rather than a custom extension.

The new-tab approach I described above is probably the only mode that wouldn't require external dependencies.

@franzhaas
Copy link
Contributor

Using panel, achieving what I want is incredibly easy. Thanks a lot for the tip!

import panel as pn
import altair as alt
from threading import Thread


_s = pn.panel((alt.Chart().mark_point().encode()))

def _t(_s):
    _s.show(threaded=False)

_thread = Thread(target=_t, args=[_s])
_thread.daemon = True
_thread.start()

def liveVisualizeAltairChart(chart):
    global _s
    _s.object=chart

@jonmmease
Copy link
Contributor

Awesome, thanks for sharing your solution!

@jonmmease
Copy link
Contributor

@manzt Out of curiosity, have you thought about what it would take to launch an AnyWidget widget from a non-jupyter REPL in a standalone browser window? Similar to what panel does in https://panel.holoviz.org/how_to/server/programmatic.html.

@manzt
Copy link
Contributor

manzt commented Mar 26, 2024

Hi @jonmmease, thanks for pinging me. In short, yes I have thought about it but just haven't had time to poke around with it. Long list of experiments I'd like to explore. In theory, you could launch a dedicated "widget view" in a browser tab and use the ipython kernel to drive updates. It's worth noting that such an extension wouldn't be specific to anywidget, but widgets in general would benefit. Akin to the altair_viewer, but more broadly using the widget's comm messaging.

@jonmmease
Copy link
Contributor

I appreciate your thoughts @manzt! I didn't know if AnyWidget's standardization would make this any easier than the general widget case, but makes sense.

@manzt
Copy link
Contributor

manzt commented Mar 26, 2024

I didn't know if AnyWidget's standardization would make this any easier than the general widget case, but makes sense.

Ah, I hadn't considered that. I think you're right actually ;) Good thought!

@franzhaas
Copy link
Contributor

@manzt the only thing I am missing with the solution I am using right now is the ability to use vegafusion, would that work with the anywidget approach?

@jonmmease
Copy link
Contributor

@franzhaas, you should be able to use JupyterChart with Panel's ipywidget support with alt.data_transformers.enable("vegafusion"). In the current release of Altair (5.2), this will use VegaFusion once to pre-transform the spec and then pass the result to the browser. In the coming 5.3 release (should be in the next week or two), this will also use VegaFusion in response to interaction events to support charts with interactive filtering + aggregation without sending the full dataset to the browser (covering the functionality of VegaFusionWidget).

If we come up with a future version based on anywidget, it would work the same way as the above, but without pulling in Panel's dependencies. There's nothing wrong with Panel's dependencies, it's just always better to consider whether there's an approach with less of them 😄

@joelostblom
Copy link
Contributor

I'm closing this since Altair 5.3 now includes the functionality of Altair Viewer via the vl-convert package instead. See the docs on how to use the browser renderer to display charts via .show(), and the PR #3379 for more details.

@franzhaas
Copy link
Contributor

@joelostblom Awesome!

unfortunately due to the efficiencies of the browser renderer, i can not use it in my usecase.

I use altair to monitor a measurement as it is running and need the window to update when new data becomes available.

Any plans to support this use case in the future?

Thanks,
Franz

@mattijn
Copy link
Contributor

mattijn commented Mar 31, 2024

In this blog post I explain a method how I was able to stream data into an Altair chart: https://mattijn.github.io/posts/2024-01-22-stream-piano-midi-to-altair/. Hope you find it useful to work out an approach for your usecase

@franzhaas
Copy link
Contributor

franzhaas commented Mar 31, 2024

Yes this is exactly the feature set i am looking for. Due to the way my environment is set up, having the measurement start a browser to drive the visualisation is way less intrusive than having the jupyterlab session initiating the measurement...

imagine the counting of the notes to be started by github-ci, offering a live visualisation in case someone looks, but normally no human interaction.

For now i am happy with the panel solution.

@jonmmease
Copy link
Contributor

Hi @franzhaas, I think your panel solution is a good method for now. We could look into incorporating this into Altair in the future, but I'd prefer to find a solution without that many external dependencies. I'll keep thinking about it!

@joelostblom
Copy link
Contributor

Thanks for the additional info everyone. Feel free to reopen this issue if you want to continue to track the developments here or create a new issue specifically for this part.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants