Holoviews: NdOverlay legends: Style by dim transform association with each of NdOverlay's kdim

Created on 28 Feb 2019  路  6Comments  路  Source: holoviz/holoviews

When doing e.g. an NdOverlay, the legend has an entry for each Element (see below). For 2 or more key dimensions, this quickly becomes a lot. It would be really nice if instead of listing every single Element, we could style the NdOverlay by setting different Cycle options along the different key dimensions, e.g. along kdim1 we vary line width, along kdim2 the color, along kdim3 the opacity, ... As far as styling the Element goes, this would mostly be a convenience, but it could make the associated legends much clearer.

E.g. this is what it currently looks like:
screen shot 2019-02-28 at 2 19 31 pm

What the legend could instead look like is that there's a thick line (no colour) for "measured", a thin one for "representative", a blue patch for "overturning", an orange one for "perennial" (no variation in thickness), and so on.

Or is this too complicated to generalize?

Also, unsure if this is related: #1161

discussion feature wishlist

Most helpful comment

This is now possible by using style mappers, e.g.:

df = pd.DataFrame({
    'Time': np.tile(np.arange(100), 9),
    'y': np.random.randn(900).cumsum(axis=0),
    'gridsize': np.repeat([0, 1, 2, 0, 1, 2, 0, 1, 2], 100),
    'turbulence': np.repeat([0, 0, 0, 1, 1, 1, 2, 2, 2], 100)})

df['label'] = ['Gridsize: %s, Turbulence: %s' % (r.gridsize, r.turbulence) for i, r in df.iterrows()]

curves = hv.Dataset(df, ['Time']).to(hv.Curve, 'Time', ['y', 'gridsize','turbulence'], 'label')
curves.overlay('label').opts(
    opts.Curve(color=dim('gridsize').categorize({0: 'red', 1: 'blue', 2: 'green'}),
               line_dash=dim('turbulence').categorize({0: 'solid', 1: 'dashed', 2: 'dotdash'})),
    opts.NdOverlay(width=900, legend_position='right'))

Screen Shot 2019-03-25 at 7 00 20 PM

I'd like to add a cycle method on dim though so you don't have to provide an explicit mapping.

All 6 comments

It would be great if we could have a simple way to construct arbitrary legends as needed. E.g. for datashader, we constructed this legend by adding fake invisible points, just to be able to show the color code:

image

Being able to do that more explicitly but conveniently would be a great help. I don't have any immediate API suggestions other than that it would take some ordered dictionary of some visible item and a text description.

The code snippet shared in #3026 for adding a legend manually to a GridMatrix may also be useful here.

Come to think of it, styling along kdims can probably be achieved pretty easily by just wrapping it into a cartesian product. Should put that into a cookbook somewhere...
Remains adding a custom legend cycling through each of the lists separately, choosing a a fixed entry from all the others.

styles = itertools.product(['blue', 'orange'], ['<', 'o', 's'])
a, b = zip(*styles)
opts.Scatter(color=hv.Cycle(list(a)), marker=hv.Cycle(list(b)))

This is now possible by using style mappers, e.g.:

df = pd.DataFrame({
    'Time': np.tile(np.arange(100), 9),
    'y': np.random.randn(900).cumsum(axis=0),
    'gridsize': np.repeat([0, 1, 2, 0, 1, 2, 0, 1, 2], 100),
    'turbulence': np.repeat([0, 0, 0, 1, 1, 1, 2, 2, 2], 100)})

df['label'] = ['Gridsize: %s, Turbulence: %s' % (r.gridsize, r.turbulence) for i, r in df.iterrows()]

curves = hv.Dataset(df, ['Time']).to(hv.Curve, 'Time', ['y', 'gridsize','turbulence'], 'label')
curves.overlay('label').opts(
    opts.Curve(color=dim('gridsize').categorize({0: 'red', 1: 'blue', 2: 'green'}),
               line_dash=dim('turbulence').categorize({0: 'solid', 1: 'dashed', 2: 'dotdash'})),
    opts.NdOverlay(width=900, legend_position='right'))

Screen Shot 2019-03-25 at 7 00 20 PM

I'd like to add a cycle method on dim though so you don't have to provide an explicit mapping.

I'd like to add a cycle method on dim though so you don't have to provide an explicit mapping.

Sounds like a great idea.

Thanks @philippjfr, I learned about dim().categorize a bit later (and .cycle would be even better!). The problem of mapping this into a less bloated legend would remain though.

Just to clarify Philipp's code snippet, it may look like you have to pass extra vdims to do the dim transform, but you can even use dim transforms on kdims of NdOverlays! https://nbviewer.jupyter.org/github/poplarShift/pyviz-recipes/blob/master/notebooks/styling_by_attribute.ipynb

Changing title to reflect that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jlstevens picture jlstevens  路  6Comments

ceh-creare picture ceh-creare  路  5Comments

mansenfranzen picture mansenfranzen  路  5Comments

asmith26 picture asmith26  路  4Comments

claresloggett picture claresloggett  路  5Comments