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

Is there an established pattern for importing Vega themes into Altair #1333

Closed
palewire opened this issue Feb 13, 2019 · 16 comments
Closed

Is there an established pattern for importing Vega themes into Altair #1333

palewire opened this issue Feb 13, 2019 · 16 comments

Comments

@palewire
Copy link
Contributor

I have drafted a Los Angeles Times theme, which has been merged into vega/vega-themes.

Is there an established pattern for importing that into Altair? I'd prefer to do that than to recreate the configuration in Python.

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 13, 2019

No, I haven't seen anything like that. If you figure something out it would be worth documenting 😄

@palewire
Copy link
Contributor Author

palewire commented Feb 24, 2019

I gave this a little thought. I can conjure three distinctly different patterns to consider. I suspect there are other strategies out there to be dreamed up, but here's what came to my mind.

  1. The themes are packaged inside the core altair library, extending the very simple file that now exists.
  2. A 1:1 Python port of the vega/vega-themes library is created in the same style as altair/vega_datasets and packaged separately.
  3. No effort is made to centrally package Altair themes and users are on their own.

Option 1 would be the easiest for end users, especially newbies, but it would add yet more responsibilities for core maintainers and potentially bloat the library.

Option 2's similarity to the solution crafted for vega_datasets, which I view as successful, is appealing. I initially pursued this option, but I was not smart enough to figure out a way to automatically translate the existing vega-themes into Python. I suspect we would need some technique for compiling the JavaScript there into a neutral JSON format before translation to Python. If anyone knows how to do that, or can think of an alternative technique, please educate me.

Option 3 is probably the easiest for the maintainers, but it does put more burden on users. It could also potentially lead to package pollution if different developers are developing inconsistent patterns for maintaining and releasing themes. As an experiment, I took a stab at creating a Python package with my draft Los Angeles Times theme. I manually translated the code from my Vega theme into Python, and released the result on PyPI. You can find the code here. Here's how it works:

Install from PyPI.

$ pip install altair-latimes

Import with Altair.

import altair as alt
import altair_latimes as lat

Register and enable the theme.

alt.themes.register('latimes', lat.theme)
alt.themes.enable('latimes')

Make a chart.

from vega_datasets import data
source = data.iowa_electricity()

alt.Chart(source, title="Iowa's renewable energy boom").mark_area().encode(
    x=alt.X(
        "year:T",
        title="Year"
    ),
    y=alt.Y(
        "net_generation:Q",
        stack="normalize",
        title="Share of net generation",
        axis=alt.Axis(format=".0%"),
    ),
    color=alt.Color(
        "source:N",
        legend=alt.Legend(title="Electricity source"),
    )
)

example

Those are my thoughts, as I can best collect them now. I'd be interested to hear what you think, @jakevdp. I'd also bet that @domoritz has an opinion here as well.

@domoritz
Copy link
Member

I would prefer a 4th option. Vega themes already ships with Vega Embed, and vega-Embed is used in ipyvega and JupyterLab. I'd prefer if Altair could tell embed to use a particular theme.

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 24, 2019

I'd prefer if Altair could tell embed to use a particular theme.

Ah, I didn't realize it was built into vega-embed by default. It turns out you can set themes now via the embed_options argument of the renderer, e.g.

import altair as alt
from vega_datasets import data
cars = data.cars()

alt.renderers.enable(embed_options={'theme': 'quartz'})

alt.Chart(cars).mark_point().encode(
  x='Horsepower',
  y='Miles_per_Gallon',
  color='Origin'
)

visualization 42

It would be nice to make that more discoverable.

@domoritz
Copy link
Member

Oh perfect. I think there may be some confusion between the default themes in Vega-Embed and the themes Altair provides (alt.theme). Do you think it would make sense to set alt.renderers.enable(embed_options={'theme': 'quartz'}) for the core themes that https://github.com/vega/vega-themes provides and still let people define their own themes?

Also see #781

@palewire
Copy link
Contributor Author

palewire commented Feb 24, 2019 via email

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 24, 2019

Well, the core issue is that vega-embed introduced themes after Altair did 😄

But yes, I think there should be one way to set themes. Implementation-wise that's a bit difficult becuase currently Altair themes are set via a theme plugin, while vega themes are set via an argument to the renderer plugin, and there's no real obvious mechanism for cross-talk between plugin settings at the moment.

@palewire
Copy link
Contributor Author

Got it. Am I correct to assume that such "cross talk" would require either some way to translate Vega themes into Python dictionaries, or some forked system inside of altair.theme and its registry that would trigger a different set of actions using "embed_options" when a Vega theme like quartz was "enabled?"

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 24, 2019

I don't know... the problem is that the term "theme" in Altair is overloaded now. On the Altair side, we use it to mean a set of specifications embedded into the configuration. On the Vega-Lite side, it's a deeper property of the renderer itself. It's not clear to me how to best resolve that.

Maybe alt.themes.enable() could be used for Altair themes, and alt.renderers.set_theme() could be used for vega themes?

But that's a bit confusing as to why there are two ways to do theming.

We could make something like alt.themes.enable('vega.quartz') actually set the embedding properties of the renderer, but then if you do something like this:

alt.themes.enable('vega.quartz')
alt.renderer.enable('notebook')

the theme setting would be overwritten in a way that's not clear to the user (this is what I mean by "cross-talk").

Or perhaps we could rename altair themes to distinguish them from vega themes? I'm just not sure. Let me know if you have ideas.

@palewire
Copy link
Contributor Author

Hmm. This complication is what makes option two above appealing to me. I am far from a JavaScript wizard, but surely there must be some way to serialize out the config objects from vega_themes as vanilla JSON that can be packaged for Python. Am I nuts, @domoritz?

@domoritz
Copy link
Member

On the Vega-Lite side, it's a deeper property of the renderer itself.

Themes are still just configurations but we have a predefined set of configurations that embed knows about.

I am far from a JavaScript wizard, but surely there must be some way to serialize out the config objects from vega_themes as vanilla JSON that can be packaged for Python. Am I nuts, @domoritz?

Yes, we could do that. Someone just has to make sure the python package stays up to date.

I'd prefer if what Jake describes worked (alt.themes.enable('vega.quartz') or even just alt.themes.enable('quartz')). How would alt.renderer.enable('notebook') cause problems here?

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 24, 2019

How would alt.renderer.enable('notebook') cause problems here?

Because vega themes are specified as embed options in the renderer. So if we make it so that alt.themes.enable works by silently updating renderer options, then changing the renderer after setting the theme would remove the theme setting.

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 24, 2019

The other option is to make alt.renderers aware of the theme so that at render time it checks the state of alt.themes and possibly updates the theme in embed_options... this approach breaks the encapsulization of the plugin model, but it might be the best way forward here.

@jakevdp
Copy link
Collaborator

jakevdp commented Feb 24, 2019

A thought: the difficulty here would all be alleviated if the theme were actually part of the specification, rather than an extra argument to the renderer. In my mind this would make a lot more sense: if the vega-lite spec is meant to be a full specification of the chart, a recipe to generate what's on the screen given a dataset, would that recipe not include the theme as well?

@domoritz
Copy link
Member

I agree in principle. We have not included it in the spec until now because most of the themes are not that well designed yet and will evolve independently of vega or Vega-Lite releases.

@jakevdp
Copy link
Collaborator

jakevdp commented May 30, 2019

Vega themes are supported via the normal mechanism in #1539

e.g.

import altair as alt
alt.themes.enable('latimes')

from vega_datasets import data
cars = data.cars()

alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin',
).interactive()

visualization - 2019-05-29T225835 754

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

No branches or pull requests

3 participants