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

Understanding mismatch between Jupyter-rendered chart and HTML exported version #3712

Open
TSSlade opened this issue Dec 11, 2024 · 2 comments
Labels

Comments

@TSSlade
Copy link

TSSlade commented Dec 11, 2024

What happened?

I was trying to create a MWE for a possible bug report about data payloads not getting rendered in charts saved to html. Along the way, I spotted some unexpected (to me) behavior that I'd like to understand.

tl;dr - changing encoding from :Q to :T appears to yield notebook-based visuals that differ from HTML-in-browser-based visuals.

Setup

import pandas as pd
import altair as alt
import vegafusion as vf 
alt.data_transformers.enable("vegafusion")
vf.enable(row_limit=500000) # Possibly not directly germane for this MWE, but it's part of the currently-active environment

regime_names: list[str] = ["baseline", "stimulus", "recovery", "end"]
regime_colors: list[str] = ["purple", "firebrick", "dodgerblue", "black"]
mwe_data = pd.DataFrame(
    {"time": {"0":500,"1":161000,"2":777000,"3":1001000},
     "regimes":{"0":"baseline","1":"stimulus","2":"recovery","3":"end"},
     "onsets":{"0":0.5,"1":161.0,"2":777.0,"3":1001.0}
    }
)

mwe = (alt.Chart(mwe_data, name="RegimeChart").mark_rule(strokeWidth=2, strokeDash=(4, 2), tooltip=True)
        .encode(x="time:Q",color=alt.Color("regimes:N", scale=alt.Scale(domain=regime_names, range=regime_colors))
            .legend(title="Regime Onset")).interactive()
)

mwe.show()
mwe.save("mwe.html")

Working as expected

Outcome in Jupyter:
Image

Outcome as an exported HTML:
Image

Unexpected

When I change the encoding like so

mwe2 = (alt.Chart(mwe_data, name="RegimeChart").mark_rule(strokeWidth=2, strokeDash=(4, 2), tooltip=True)
        .encode(x="time:T",color=alt.Color("regimes:N", scale=alt.Scale(domain=regime_names, range=regime_colors))
            .legend(title="Regime Onset")).interactive()
)
mwe2.show()
mwe2.save("mwe2.html")

Outcome in Jupyter:
Image

Outcome as an exported HTML:
Image

The view source output for mwe2 is as follows:

{
  "config": {"view": {"continuousWidth": 300, "continuousHeight": 300}},
  "data": {"name": "data-956a08c8102878568ca9a281f0307c88"},
  "mark": {
    "type": "rule",
    "strokeDash": [4, 2],
    "strokeWidth": 2,
    "tooltip": true
  },
  "encoding": {
    "color": {
      "field": "regimes",
      "legend": {"title": "Regime Onset"},
      "scale": {
        "domain": ["baseline", "stimulus", "recovery", "end"],
        "range": ["purple", "firebrick", "dodgerblue", "black"]
      },
      "type": "nominal"
    },
    "x": {"field": "time", "type": "temporal"}
  },
  "name": "RegimeChart",
  "params": [
    {
      "name": "param_33",
      "select": {"type": "interval", "encodings": ["x", "y"]},
      "bind": "scales"
    }
  ],
  "$schema": "https://vega.github.io/schema/vega-lite/v5.20.1.json",
  "datasets": {
    "data-956a08c8102878568ca9a281f0307c88": [
      {"time": 500, "regimes": "baseline", "onsets": 0.5},
      {"time": 161000, "regimes": "stimulus", "onsets": 161},
      {"time": 777000, "regimes": "recovery", "onsets": 777},
      {"time": 1001000, "regimes": "end", "onsets": 1001}
    ]
  }
}

The output of pprint.pprint(mwe2.to_dict()) is as follows:

{'$schema': 'https://vega.github.io/schema/vega-lite/v5.20.1.json',
 'config': {'view': {'continuousHeight': 300, 'continuousWidth': 300}},
 'data': {'url': 'vegafusion+dataset://table_37cd13be_5355_4d25_b050_1a9a3ba36b45'},
 'encoding': {'color': {'field': 'regimes',
                        'legend': {'title': 'Regime Onset'},
                        'scale': {'domain': ['baseline',
                                             'stimulus',
                                             'recovery',
                                             'end'],
                                  'range': ['purple',
                                            'firebrick',
                                            'dodgerblue',
                                            'black']},
                        'type': 'nominal'},
              'x': {'field': 'time', 'type': 'temporal'}},
 'mark': {'strokeDash': [4, 2],
          'strokeWidth': 2,
          'tooltip': True,
          'type': 'rule'},
 'name': 'RegimeChart',
 'params': [{'bind': 'scales',
             'name': 'param_33',
             'select': {'encodings': ['x', 'y'], 'type': 'interval'}}]}

What would you like to happen instead?

I would expect that the in-notebook and in-browser view should be consonant.

The browser being used to visualize the exported HTML file is Edge Version 131.0.2903.70 (Official build) (64-bit).
I'm using
altair 5.4.1
jupyterlab 4.2.3
python 3.12.2

Which version of Altair are you using?

5.4.1

@TSSlade TSSlade added the bug label Dec 11, 2024
@joelostblom
Copy link
Contributor

Do you see this behavior without the vegafusion transformer enabled? There are some minor differences in how vegafusion handles :T as per vega/vegafusion#402

@TSSlade
Copy link
Author

TSSlade commented Dec 11, 2024

TLDR: no, I do not see the behavior without vegafusion enabled.


With vegafusion transformer disabled:

Image

Added a new cell, enabled vegafusion, executed: got a ValueError:
cell:

alt.data_transformers.enable("vegafusion")
mwe.show()
mwe.save("mwe.html")

output:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File [~/miniconda3/envs/ttemp/lib/python3.12/site-packages/IPython/core/formatters.py:977](http://localhost:8888/lab/tree/~/miniconda3/envs/ttemp/lib/python3.12/site-packages/IPython/core/formatters.py#line=976), in MimeBundleFormatter.__call__(self, obj, include, exclude)
    974     method = get_real_method(obj, self.print_method)
    976     if method is not None:
--> 977         return method(include=include, exclude=exclude)
    978     return None
    979 else:

File [~/miniconda3/envs/ttemp/lib/python3.12/site-packages/altair/vegalite/v5/api.py:3417](http://localhost:8888/lab/tree/~/miniconda3/envs/ttemp/lib/python3.12/site-packages/altair/vegalite/v5/api.py#line=3416), in TopLevelMixin._repr_mimebundle_(self, *args, **kwds)
   3415 else:
   3416     if renderer := renderers.get():
-> 3417         return renderer(dct)

File [~/miniconda3/envs/ttemp/lib/python3.12/site-packages/vegafusion/renderer.py:16](http://localhost:8888/lab/tree/~/miniconda3/envs/ttemp/lib/python3.12/site-packages/vegafusion/renderer.py#line=15), in vegafusion_mime_renderer(spec, mimetype, row_limit, embed_options)
     15 def vegafusion_mime_renderer(spec, mimetype="html", row_limit=None, embed_options=None):
---> 16     return spec_to_mime_bundle(
     17         spec,
     18         mimetype=mimetype,
     19         row_limit=row_limit,
     20         embed_options=embed_options
     21     )

File [~/miniconda3/envs/ttemp/lib/python3.12/site-packages/vegafusion/renderer.py:38](http://localhost:8888/lab/tree/~/miniconda3/envs/ttemp/lib/python3.12/site-packages/vegafusion/renderer.py#line=37), in spec_to_mime_bundle(spec, mimetype, row_limit, embed_options, html_template, full_html, scale)
     35 vega_spec = vegalite_compilers.get()(spec)
     37 inline_datasets = transformer.get_inline_datasets_for_spec(vega_spec)
---> 38 tx_vega_spec, warnings = runtime.pre_transform_spec(
     39     vega_spec,
     40     local_tz.get_local_tz(),
     41     row_limit=row_limit,
     42     inline_datasets=inline_datasets
     43 )
     45 for warning in warnings:
     46     if warning.get("type", "") == "RowLimitExceeded":

File [~/miniconda3/envs/ttemp/lib/python3.12/site-packages/vegafusion/runtime.py:371](http://localhost:8888/lab/tree/~/miniconda3/envs/ttemp/lib/python3.12/site-packages/vegafusion/runtime.py#line=370), in VegaFusionRuntime.pre_transform_spec(self, spec, local_tz, default_input_tz, row_limit, preserve_interactivity, inline_datasets, keep_signals, keep_datasets, data_encoding_threshold, data_encoding_format)
    369 try:
    370     if data_encoding_threshold is None:
--> 371         new_spec, warnings = self.embedded_runtime.pre_transform_spec(
    372             spec,
    373             local_tz=local_tz,
    374             default_input_tz=default_input_tz,
    375             row_limit=row_limit,
    376             preserve_interactivity=preserve_interactivity,
    377             inline_datasets=imported_inline_dataset,
    378             keep_signals=keep_signals,
    379             keep_datasets=keep_datasets,
    380         )
    381     else:
    382         # Use pre_transform_extract to extract large datasets
    383         new_spec, datasets, warnings = self.embedded_runtime.pre_transform_extract(
    384             spec,
    385             local_tz=local_tz,
   (...)
    392             keep_datasets=keep_datasets,
    393         )

ValueError: Internal error: No inline dataset named table_a85e2134_c103_4ce3_969a_7e6172bd81ff
    Context[0]: Failed to get node value

restarted kernel, enabled vegafusion transformer at the top, re-executed the code that generated that initial screenshot:

Image

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

2 participants