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

feat: default Vega-Lite-based renderer (#342) #438

Merged
merged 50 commits into from Dec 14, 2022
Merged

Conversation

peter-gy
Copy link
Collaborator

@peter-gy peter-gy commented Nov 1, 2022

Goals

  • Create a default renderer based on Vega-Lite
    • Used Altair to get automatic VL schema validation
    • The renderer returns an alt.VConcatChart | alt.HConcatChart | alt.FacetChart | alt.Chart, ready to be displayed, exported, customized, etc.
  • Define the schema of the Draco specification language using Pydantic
    • Using raw dicts one has to fight a lot with the type checkers
    • Having typed access to the schema makes its traversal and processing more readable

closes #342
spawns #449
spawns #455

@peter-gy peter-gy added the enhancement New feature or request label Nov 1, 2022
@peter-gy peter-gy changed the title feat: default Vega-Lite-based renderer (#342) feat: default Vega-Lite-based renderer Nov 1, 2022
@peter-gy peter-gy changed the title feat: default Vega-Lite-based renderer feat: default Vega-Lite-based renderer (#342) Nov 1, 2022
We can delete this before merging, but the nb should be useful for discussions
@peter-gy
Copy link
Collaborator Author

peter-gy commented Nov 1, 2022

Vega-Lite vs. Draco spec

Below I list my takeaways in a nutshell from implementing all the example charts from the docs (https://dig.cmu.edu/draco2/facts/examples.html) in Vega-Lite using Altair.

  • VL mostly covers all our rendering needs, in most cases the mapping from our schema to the VL schema is straightforward ✓
  • VL has no explicit support for toggling between cartesian and polar coordinate systems
    • However, we can use an arc mark & the encodings theta, radius to overcome this ✓
  • As far as I can tell, VL does not allow for nested faceting / repeating / concatenation. This is problematic since Draco can produce multi-view recommendations and each view might or might not be faceted 🚫

Questions regarding Draco's spec

  • What do we consider as a strictly valid spec? Defining this would be important so that the renderer method can validate the input before trying to convert it into VL. For example, the following spec (Pie Chart Example) does not define a field for its encoding on the y channel:
{'field': [{'name': 'temperature', 'type': 'number'},
           {'name': 'condition', 'type': 'string'}],
 'number_rows': 100,
 'view': [{'coordinates': 'polar',
           'mark': [{'encoding': [{'aggregate': 'count',
                                   'channel': 'y',
                                   'stack': 'zero'},
                                  {'channel': 'color', 'field': 'condition'}],
                     'type': 'bar'}],
           'scale': [{'channel': 'y', 'type': 'linear', 'zero': 'true'},
                     {'channel': 'color', 'type': 'categorical'}]}]}

While it is easy to infer that the temperature field should be used here, is the renderer expected to handle this inferring?

  • What do you think, would it make sense to define a Draco spec strictly? We could use Pydantic for this & the implementation would mean relatively little effort. It has an awesome DX when it comes to defining schemas (see https://pydantic-docs.helpmanual.io/usage/schema/) and we already depend on the package.

@JunranY, @Zehua-Zeng, @domoritz

  • do you know a workaround to concatenate faceted views in VL?
  • what do you think about introducing a Pydantic-based schema validation?
  • in general, what kind of development roadmap would you prefer for this feature? Should we...
    • merge an MVP as soon as possible that is capable of rendering simpler recs (basically any non-multi-view rec in a cartesian polar system) and work on further refinements iteratively, or
    • create a more elaborate issue from this handling all specs & create a rendering base-class to support multiple renderer implementations?

@lgtm-com
Copy link
Contributor

lgtm-com bot commented Nov 8, 2022

This pull request introduces 1 alert when merging 932ed30 into 0bf09e5 - view on LGTM.com

new alerts:

  • 1 for Wrong name for an argument in a call

@peter-gy
Copy link
Collaborator Author

peter-gy commented Nov 8, 2022

Hey @domoritz! While the code I pushed is indeed not really LGTM (as it is partial work full of quick & dirty experimenting), I believe the LGTM.com report above is not right:

Screenshot 2022-11-08 at 17 37 17

kw_only is a totally valid parameter name since Python 3.10. Is it possible that the Python version based on which LGTM.com checks our code is not in sync with the version we actually use? I did not update it in 06144c4, as I have no access to the job's config as far as I know.

Renders all the example charts
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show resolved Hide resolved
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
draco/renderer/altair_renderer.py Fixed Show fixed Hide fixed
@peter-gy
Copy link
Collaborator Author

@domoritz could you please take a look at this PR?

@@ -1,4 +1,4 @@
name: "CodeQL"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this is in the diff. Do you need to rebase or merge main?

represented as an `Altair <https://altair-viz.github.io/>`_ chart object.
"""

def __init__(self, concat_mode: Literal["hconcat", "vconcat"] | None = None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to move the concat mode into the Draco spec itself. This would simplify and generalize the logic here.

draco/renderer/altair_renderer.py Fixed Show resolved Hide resolved
case "bar":
encodes_x_and_y = all([e.channel in ["x", "y"] for e in encodings])
if encodes_x_and_y:
return chart.mark_arc(stroke="#ffffff") + chart.mark_text(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we set the stroke here?

Copy link
Collaborator Author

@peter-gy peter-gy Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mental notes I took when writing this piece of code was the following:

When rendering a bar chart in a polar coordinate system, we are essentially rendering a classical radial chart in a cartesian coordinate system, using theta and radius as the channels instead of the polar x and y channels.

TLDR: I added a stroke here so there is a visual separation between the slices, just as we would have gaps between bars in a bar chart.

Without Stroke

Open the Chart in the Vega Editor

With Stroke

Open the Chart in the Vega Editor

f"Unknown channel: {encoding.channel}"
) # pragma: no cover

def __get_field_type(self, fields: list[Field], field_name: FieldName) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this logic is correct. We should look at the scale type instead. The data type could be number but the scale could be categorical or continuous.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meeting notes:

Scale type provides a cleaner mapping, as it makes it possible to differentiate between different number types such as numerical, ordinal, and categorical.

draco/types.py Outdated
import pydantic.main as pydantic_main
import pydantic.types as pydantic_types

"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, this file is Vega specific and should be moved into the Vega renderer.

@peter-gy
Copy link
Collaborator Author

peter-gy commented Dec 7, 2022

The failing workflows are most likely related to actions/setup-python#162.

Attempts to fix "Explicit returns mixed with implicit (fall through) returns"
A spec has either top-level- or view-level scales or both.
Adds support for determining Vega-Lite field type
based on a combination of the scale type and the field's
raw data type
Copy link
Member

@domoritz domoritz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one last comment.

"datetime": "temporal",
"linear": {
"number": "quantitative",
"string": "nominal",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string and boolean are not supported for linear, no?

},
"log": {
"number": "quantitative",
"string": "nominal",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

@peter-gy
Copy link
Collaborator Author

@domoritz, I added the discussed changes in bd7ca3b

draco/renderer/altair/altair_renderer.py Outdated Show resolved Hide resolved
@peter-gy peter-gy merged commit dc03ecc into main Dec 14, 2022
@peter-gy peter-gy deleted the feat/342-default-renderer branch December 14, 2022 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a renderer for visualization specifications
2 participants