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

Vega spec. converted from vega-lite spec which includes selection_point with selection_interval not working on VegaFusionWidget #495

Open
cosmicfarmers opened this issue Jul 7, 2024 · 7 comments
Labels
bug Something isn't working

Comments

@cosmicfarmers
Copy link

cosmicfarmers commented Jul 7, 2024

Hello,

I can't use combination of selection_point and selection_interval on VegaFusionWidget ONLY WHEN I convert vega-lite spec. to vega spec and input the vega spec to VegaFusionWidget.

Please refer to the below. (jupyter notebook cells converted to markdown. ipynb file is not uploadable.)

import altair as alt
import vegafusion as vf
import vl_convert as vlc
import pandas as pd
alt.__version__, vf.__version__, vlc.__version__, pd.__version__
('5.3.0', '1.6.9', '1.5.0', '2.1.2')
buttons_pdf = pd.DataFrame({'greeting':['hello','world']})

values_pdf = pd.DataFrame({'vals':[1,2,3,4,5]})

buttons_picker = alt.selection_point(fields=['greeting'], name='buttons_picker')
buttons_ap = (
    alt.Chart(buttons_pdf)
        .encode(
            y='greeting:N',
            opacity=alt.condition(buttons_picker, alt.value(1), alt.value(0.2))
        )
        .mark_circle(size=200, color='crimson')
        .add_params(buttons_picker)
)


values_brush = alt.selection_interval(encodings=['x'], name='values_brush')
values_ap = (
    alt.Chart(values_pdf)
        .encode(
            x='vals:O',
            opacity=alt.condition(values_brush, alt.value(1), alt.value(0.2))
        )
        .mark_tick(thickness=5, color='royalblue')
        .add_params(values_brush)
)

Working: Altair (vega-lite)

final_ap = alt.hconcat(buttons_ap, values_ap)
final_ap

altair

Working: Altair on VegaFusionWidget

vf.jupyter.VegaFusionWidget(final_ap)

altair_vf

Not Working: Vega Spec. on VegaFusionWidget

Only selection_interval is working; selection_point is not clickable.

vl_spec = final_ap.to_json()
vg_spec = vlc.vegalite_to_vega(vl_spec)

# I want to add some more vega spec to the vega spec. converted from the vega-lite spec. created from altair api.
# Here, I didn't add any vega spec yet but selection_point is already not working.

vf.jupyter.VegaFusionWidget(vg_spec, verbose=True)
# greeting button is not clickable. no relative error messages on web console and jupyterlab console.

vg_spec_vf

VegaFusionWidget(spec='{\n  "$schema": "https://vega.github.io/schema/vega/v5.json",\n  "background": "white",…
# I also tested vega spec converted from https://vega.github.io/editor/ on VegaFusionWidget, which is not working.
# Pasting the following output to https://vega.github.io/editor/ works.
import json
print(json.dumps(vg_spec))
{"$schema": "https://vega.github.io/schema/vega/v5.json", "background": "white", "padding": 5, "data": [{"name": "buttons_picker_store"}, {"name": "values_brush_store"}, {"name": "data-608367a698199bd17d43a4fe7ad087c2", "values": [{"greeting": "hello"}, {"greeting": "world"}]}, {"name": "data-48acafe826497c8a0e7547b6ac2f1594", "values": [{"vals": 1}, {"vals": 2}, {"vals": 3}, {"vals": 4}, {"vals": 5}]}], "signals": [{"name": "view_1_width", "value": 20}, {"name": "view_1_y_step", "value": 20}, {"name": "view_1_height", "update": "bandspace(domain('view_1_y').length, 1, 0.5) * view_1_y_step"}, {"name": "view_2_x_step", "value": 20}, {"name": "view_2_width", "update": "bandspace(domain('view_2_x').length, 1, 0.5) * view_2_x_step"}, {"name": "view_2_height", "value": 20}, {"name": "unit", "value": {}, "on": [{"events": "pointermove", "update": "isTuple(group()) ? group() : unit"}]}, {"name": "buttons_picker", "update": "vlSelectionResolve(\"buttons_picker_store\", \"union\", true, true)"}, {"name": "values_brush", "update": "vlSelectionResolve(\"values_brush_store\", \"union\")"}], "layout": {"padding": 20, "bounds": "full", "align": "each"}, "marks": [{"type": "group", "name": "view_1_group", "style": "cell", "encode": {"update": {"width": {"signal": "view_1_width"}, "height": {"signal": "view_1_height"}}}, "signals": [{"name": "width", "update": "view_1_width"}, {"name": "buttons_picker_tuple", "on": [{"events": [{"source": "scope", "type": "click"}], "update": "datum && item().mark.marktype !== 'group' && indexof(item().mark.role, 'legend') < 0 ? {unit: \"view_1\", fields: buttons_picker_tuple_fields, values: [(item().isVoronoi ? datum.datum : datum)[\"greeting\"]]} : null", "force": true}, {"events": [{"source": "view", "type": "dblclick"}], "update": "null"}]}, {"name": "buttons_picker_tuple_fields", "value": [{"type": "E", "field": "greeting"}]}, {"name": "buttons_picker_toggle", "value": false, "on": [{"events": [{"source": "scope", "type": "click"}], "update": "event.shiftKey"}, {"events": [{"source": "view", "type": "dblclick"}], "update": "false"}]}, {"name": "buttons_picker_modify", "on": [{"events": {"signal": "buttons_picker_tuple"}, "update": "modify(\"buttons_picker_store\", buttons_picker_toggle ? null : buttons_picker_tuple, buttons_picker_toggle ? null : true, buttons_picker_toggle ? buttons_picker_tuple : null)"}]}], "marks": [{"name": "view_1_marks", "type": "symbol", "style": ["circle"], "interactive": true, "from": {"data": "data-608367a698199bd17d43a4fe7ad087c2"}, "encode": {"update": {"opacity": [{"test": "!length(data(\"buttons_picker_store\")) || vlSelectionTest(\"buttons_picker_store\", datum)", "value": 1}, {"value": 0.2}], "size": {"value": 200}, "fill": {"value": "crimson"}, "ariaRoleDescription": {"value": "circle"}, "description": {"signal": "\"greeting: \" + (isValid(datum[\"greeting\"]) ? datum[\"greeting\"] : \"\"+datum[\"greeting\"])"}, "x": {"signal": "view_1_width", "mult": 0.5}, "y": {"scale": "view_1_y", "field": "greeting"}, "shape": {"value": "circle"}}}}], "axes": [{"scale": "view_1_y", "orient": "left", "grid": false, "title": "greeting", "zindex": 0}]}, {"type": "group", "name": "view_2_group", "style": "cell", "encode": {"update": {"width": {"signal": "view_2_width"}, "height": {"signal": "view_2_height"}}}, "signals": [{"name": "height", "update": "view_2_height"}, {"name": "values_brush_x", "value": [], "on": [{"events": {"source": "scope", "type": "pointerdown", "filter": ["!event.item || event.item.mark.name !== \"values_brush_brush\""]}, "update": "[x(unit), x(unit)]"}, {"events": {"source": "window", "type": "pointermove", "consume": true, "between": [{"source": "scope", "type": "pointerdown", "filter": ["!event.item || event.item.mark.name !== \"values_brush_brush\""]}, {"source": "window", "type": "pointerup"}]}, "update": "[values_brush_x[0], clamp(x(unit), 0, view_2_width)]"}, {"events": {"signal": "values_brush_scale_trigger"}, "update": "[0, 0]"}, {"events": [{"source": "view", "type": "dblclick"}], "update": "[0, 0]"}, {"events": {"signal": "values_brush_translate_delta"}, "update": "clampRange(panLinear(values_brush_translate_anchor.extent_x, values_brush_translate_delta.x / span(values_brush_translate_anchor.extent_x)), 0, view_2_width)"}, {"events": {"signal": "values_brush_zoom_delta"}, "update": "clampRange(zoomLinear(values_brush_x, values_brush_zoom_anchor.x, values_brush_zoom_delta), 0, view_2_width)"}]}, {"name": "values_brush_vals", "on": [{"events": {"signal": "values_brush_x"}, "update": "values_brush_x[0] === values_brush_x[1] ? null : invert(\"view_2_x\", values_brush_x)"}]}, {"name": "values_brush_scale_trigger", "value": {}, "on": [{"events": [{"scale": "view_2_x"}], "update": "(!isArray(values_brush_vals) || (invert(\"view_2_x\", values_brush_x)[0] === values_brush_vals[0] && invert(\"view_2_x\", values_brush_x)[1] === values_brush_vals[1])) ? values_brush_scale_trigger : {}"}]}, {"name": "values_brush_tuple", "on": [{"events": [{"signal": "values_brush_vals"}], "update": "values_brush_vals ? {unit: \"view_2\", fields: values_brush_tuple_fields, values: [values_brush_vals]} : null"}]}, {"name": "values_brush_tuple_fields", "value": [{"field": "vals", "channel": "x", "type": "E"}]}, {"name": "values_brush_translate_anchor", "value": {}, "on": [{"events": [{"source": "scope", "type": "pointerdown", "markname": "values_brush_brush"}], "update": "{x: x(unit), y: y(unit), extent_x: slice(values_brush_x)}"}]}, {"name": "values_brush_translate_delta", "value": {}, "on": [{"events": [{"source": "window", "type": "pointermove", "consume": true, "between": [{"source": "scope", "type": "pointerdown", "markname": "values_brush_brush"}, {"source": "window", "type": "pointerup"}]}], "update": "{x: values_brush_translate_anchor.x - x(unit), y: values_brush_translate_anchor.y - y(unit)}"}]}, {"name": "values_brush_zoom_anchor", "on": [{"events": [{"source": "scope", "type": "wheel", "consume": true, "markname": "values_brush_brush"}], "update": "{x: x(unit), y: y(unit)}"}]}, {"name": "values_brush_zoom_delta", "on": [{"events": [{"source": "scope", "type": "wheel", "consume": true, "markname": "values_brush_brush"}], "force": true, "update": "pow(1.001, event.deltaY * pow(16, event.deltaMode))"}]}, {"name": "values_brush_modify", "on": [{"events": {"signal": "values_brush_tuple"}, "update": "modify(\"values_brush_store\", values_brush_tuple, true)"}]}], "marks": [{"name": "values_brush_brush_bg", "type": "rect", "clip": true, "encode": {"enter": {"fill": {"value": "#333"}, "fillOpacity": {"value": 0.125}}, "update": {"x": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "signal": "values_brush_x[0]"}, {"value": 0}], "y": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "value": 0}, {"value": 0}], "x2": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "signal": "values_brush_x[1]"}, {"value": 0}], "y2": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "field": {"group": "height"}}, {"value": 0}]}}}, {"name": "view_2_marks", "type": "rect", "style": ["tick"], "interactive": true, "from": {"data": "data-48acafe826497c8a0e7547b6ac2f1594"}, "encode": {"update": {"opacity": [{"test": "!length(data(\"values_brush_store\")) || vlSelectionTest(\"values_brush_store\", datum)", "value": 1}, {"value": 0.2}], "fill": {"value": "royalblue"}, "ariaRoleDescription": {"value": "tick"}, "description": {"signal": "\"vals: \" + (isValid(datum[\"vals\"]) ? datum[\"vals\"] : \"\"+datum[\"vals\"])"}, "xc": {"scale": "view_2_x", "field": "vals"}, "yc": {"signal": "view_2_height", "mult": 0.5}, "height": {"value": 15}, "width": {"value": 5}}}}, {"name": "values_brush_brush", "type": "rect", "clip": true, "encode": {"enter": {"cursor": {"value": "move"}, "fill": {"value": "transparent"}}, "update": {"x": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "signal": "values_brush_x[0]"}, {"value": 0}], "y": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "value": 0}, {"value": 0}], "x2": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "signal": "values_brush_x[1]"}, {"value": 0}], "y2": [{"test": "data(\"values_brush_store\").length && data(\"values_brush_store\")[0].unit === \"view_2\"", "field": {"group": "height"}}, {"value": 0}], "stroke": [{"test": "values_brush_x[0] !== values_brush_x[1]", "value": "white"}, {"value": null}]}}}], "axes": [{"scale": "view_2_x", "orient": "bottom", "grid": false, "title": "vals", "labelAlign": "right", "labelAngle": 270, "labelBaseline": "middle", "zindex": 0}]}], "scales": [{"name": "view_1_y", "type": "point", "domain": {"data": "data-608367a698199bd17d43a4fe7ad087c2", "field": "greeting", "sort": true}, "range": {"step": {"signal": "view_1_y_step"}}, "padding": 0.5}, {"name": "view_2_x", "type": "point", "domain": {"data": "data-48acafe826497c8a0e7547b6ac2f1594", "field": "vals", "sort": true}, "range": {"step": {"signal": "view_2_x_step"}}, "padding": 0.5}]}
@cosmicfarmers cosmicfarmers changed the title Vega spec. converted from alt.selection_point not working with alt.selection_interval on VegaFusionWidget Vega spec. converted from vega-lite spec which includes alt.selection_point with alt.selection_interval not working on VegaFusionWidget Jul 7, 2024
@cosmicfarmers cosmicfarmers changed the title Vega spec. converted from vega-lite spec which includes alt.selection_point with alt.selection_interval not working on VegaFusionWidget Vega spec. converted from vega-lite spec which includes selection_point with selection_interval not working on VegaFusionWidget Jul 7, 2024
@jonmmease
Copy link
Collaborator

Hi @cosmicfarmers, thanks for opening an issue and apologies for the delay in getting back to you (I was on holiday and at a conference the past couple of weeks).

I can reproduce what you're seeing, and will try to dig in soon.

I had been planning to deprecate VegaFusionWidget and direct folks to use Altair's JupyterChart with the "vegafusion" data transformer. This has the same performance characteristics as VegaFusionWidget and supports accessing selections and params from Python. But the JupyterChart approach only works on Altair charts, not on Vega specs. So this issue is a good reminder of that use case. I might rework VegaFusionWidget to work like JupyterChart, but focus exclusively on Vega specs.

@jonmmease jonmmease added the bug Something isn't working label Jul 12, 2024
@cosmicfarmers
Copy link
Author

Hi @cosmicfarmers, thanks for opening an issue and apologies for the delay in getting back to you (I was on holiday and at a conference the past couple of weeks).

I can reproduce what you're seeing, and will try to dig in soon.

I had been planning to deprecate VegaFusionWidget and direct folks to use Altair's JupyterChart with the "vegafusion" data transformer. This has the same performance characteristics as VegaFusionWidget and supports accessing selections and params from Python. But the JupyterChart approach only works on Altair charts, not on Vega specs. So this issue is a good reminder of that use case. I might rework VegaFusionWidget to work like JupyterChart, but focus exclusively on Vega specs.

Hi Jon,

Thanks for getting back to me!

What I love about VegaFusion is that even though it doesn’t support all data transformations (like contour and kde2d), I can still use server-side support for a lot of transformations. This is super helpful when my pipeline includes both supported and unsupported operations.

I’m working on creating contoured shot frequency maps by pitch location from football games, which involves handling really large datasets. The process works smoothly because I first aggregate by location coordinates and then apply unsupported Vega transformations like kde2d and contour (which will work in web browsers if I understood correctly.). Real-time transformations are key since I offer a bunch of interactive options for viewing the contours.

Given all this, supporting Vega specs is essential for my work, even though you’re planning to deprecate VegaFusionWidget, which I currently rely on.

Employing JupyterChart will help users feel more accessible, but I really support this project supporting Vega specs as well. After all, the project name is not Vega-liteFusion. :)

By the way, currently, I am not combining selection_point and selection_interval as a workaround. This is an awesome project, and I've been using VegaFusion for almost all of my work recently.

Thanks so much!

P.S. one quick question: sometimes all of data transformations are sent to my browser. I haven't investigated deeply because now it works like separating heavy parts work in server-side fortunately. Is there a way that I can check what part will work in server-side and which part will work in client-side before rendering it?

@jonmmease
Copy link
Collaborator

Thanks for the kind words, it's great to hear that VegaFusion has been helpful for you!

Given all this, supporting Vega specs is essential for my work, even though you’re planning to deprecate VegaFusionWidget, which I currently rely on.

We'll make sure that there continues to be a widget solution for Vega specs. At this point I'm picturing a small rewrite of VegaFusionWidget that uses AnyWidget (so it's much easier to maintain), and removes support for Vega-Lite and Vega-Altair charts. (All of the dedicated Altair support in VegaFusion has been upstreamed to Altair itself, and Vega-Lite to Vega conversions are easy with vl-convert, so I'd like to remove the Vega-Lite and Altair dependencies from VegaFusion). We could also make it possible to access and set signals and datasets from Python.

What I love about VegaFusion is that even though it doesn’t support all data transformations (like contour and kde2d), I can still use server-side support for a lot of transformations. This is super helpful when my pipeline includes both supported and unsupported operations.

We'd like to support more Vega transforms over time (like contour and kde2d), but since this will take a while VegaFusion is designed to avoid extracting unsupported transforms (and all of their dependencies) to the server.

one quick question: sometimes all of data transformations are sent to my browser. I haven't investigated deeply because now it works like separating heavy parts work in server-side fortunately. Is there a way that I can check what part will work in server-side and which part will work in client-side before rendering it?

The best way right now is to look at the server_vega_spec property of the VegaFusionWidget instance. This will contain a Vega spec that includes the data specifications that were eligible for evaluation on the server. client_vega_spec contains the Vega spec that is actually rendered on the client (with the extracted data transforms removed), and comm_plan contains the specification of which datasets will be transferred between the client and server to maintain interactivity. This is described in https://vegafusion.io/planner_results.html. We need to add a compatibility table to the docs, but you can look at https://github.com/vega/vegafusion/tree/main/vegafusion-runtime/src/transform to see the collection of transforms that are currently (at least partially) implemented.

Does that help?

@NikosAlexandris
Copy link

New into VegaFusion. Does this thread say that taking the JSON example from https://vega.github.io/editor/#/gist/9f120931575dd754c80f251cb51316af/spec.json and trying to use it along with VegaFusionWidget won't work ?

@jonmmease
Copy link
Collaborator

Hi @NikosAlexandris, VegaFusionWidget can accept a Vega specification directly. It will extract the transforms that is supports and pre-evaluate them in the Python kernel.

But from looking at this example, it's not obvious to me that this will be beneficial. What are you hoping to improve by using VegaFusion?

@NikosAlexandris
Copy link

Hi @NikosAlexandris, VegaFusionWidget can accept a Vega specification directly. It will extract the transforms that is supports and pre-evaluate them in the Python kernel.

But from looking at this example, it's not obvious to me that this will be beneficial. What are you hoping to improve by using VegaFusion?

Apologies for sort of cross-posting (see also : #433 (comment)). I guess I need to do some homework and better understand the Vega / Altair universe. I want to use Python and do some nice stuff, eventually with massive data, and embed them in technical documentation (thinking of a Material for MkDocs instance) either via some plugin that can run Python inside some Markdown page or maybe (easier?) as a Jupyter Notebook.

@NikosAlexandris
Copy link

New into VegaFusion. Does this thread say that taking the JSON example from https://vega.github.io/editor/#/gist/9f120931575dd754c80f251cb51316af/spec.json and trying to use it along with VegaFusionWidget won't work ?

I tried to use the "timeline" vega specification (JSON)

import vegafusion as vf
import altair as alt

and

vega_specification = r """
<source JSON from https://vega.github.io/editor/#/gist/9f120931575dd754c80f251cb51316af/spec.json>
"""

and finally

vf.jupyter.VegaFusionWidget(vega_specification)

yet I get this error

[Open Browser Console for more detailed log - Double click to close this message]
Failed to create view for 'VegaFusionView' from module 'vegafusion-jupyter' with model 'VegaFusionModel' from module 'vegafusion-jupyter'
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[3871]:0x5b1b2d
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[5402]:0x630c15
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[4566]:0x6197aa
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[4379]:0x612275
$@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/42.7f340c1b888746330122.js?v=7f340c1b888746330122:1:6634
h@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/439.66cc935afb7e9bf3bdb9.js?v=66cc935afb7e9bf3bdb9:1:613
value_changed@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/480.1bbfa387a4decef0a812.js?v=1bbfa387a4decef0a812:1:2733
render@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/480.1bbfa387a4decef0a812.js?v=1bbfa387a4decef0a812:1:1575

and then more from the Console

panicked at vegafusion-wasm/src/lib.rs:396:58:
called `Result::unwrap()` on an `Err` value: Error("missing field `fields`", line: 824, column: 8)

Stack:

wn@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/42.7f340c1b888746330122.js?v=7f340c1b888746330122:1:12017
__wbg_new_abda76e883ba8a5f@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/remoteEntry.309c24c3ed6d290e29ab.js:1:13199
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[6925]:0x64b19f
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[3871]:0x5b1b07
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[5402]:0x630c15
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[4566]:0x6197aa
@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/efe94fb19af9c243d85a.module.wasm:wasm-function[4379]:0x612275
$@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/42.7f340c1b888746330122.js?v=7f340c1b888746330122:1:6634
h@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/439.66cc935afb7e9bf3bdb9.js?v=66cc935afb7e9bf3bdb9:1:613
value_changed@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/480.1bbfa387a4decef0a812.js?v=1bbfa387a4decef0a812:1:2733
render@http://localhost:8888/lab/extensions/vegafusion-jupyter/static/480.1bbfa387a4decef0a812.js?v=1bbfa387a4decef0a812:1:1575


42.7f340c1b888746330122.js:1:12183

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants