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

Enable single legend for NdLayout (instead of multiple identical legends in each subplot) #332

Closed
maxalbert opened this issue Dec 4, 2015 · 5 comments

Comments

@maxalbert
Copy link
Contributor

The code snippet below creates an NdLayout object with multiple sub-panels, each of which is displayed with its own legend (see screenshot). However, all these legends are identical. It would be great to be able to attach a single legend to the top-level NdLayout instead of multiple ones to the individual sub-plots. This probably also applies to GridSpace, although I can't get legends to work with GridSpace at the moment so don't know how it handles them.

I wonder whether it would make sense to also allow "sharing" of other properties (such as axis labels/ticks, etc.) between sub-plots. However, I'm not sure whether this is covered by the existence of GridSpace because I don't fully understand the relation and difference between NdLayout and GridSpace.

sample_ndlayout

%reload_ext holoviews.ipython
from holoviews import *

import holoviews as hv
import numpy as np
import itertools

phases = np.linspace(0, np.pi, 5)
freqs = [10, 15]
shifts = [0, 0.5]

def make_curve(phase, freq, shift):
    x = np.linspace(-0.5, 0.5, 202)
    y = np.sin((freq * x + phase)) + shift
    return Curve(zip(x, y))

keys = itertools.product(phases, freqs, shifts)
items = [(key, make_curve(*key)) for key in keys]
hmap = HoloMap(items, kdims=['Phase', 'Frequency', 'Shift'])
layout = hmap.overlay('Shift').layout(['Frequency', 'Phase']).cols(5)
@jlstevens
Copy link
Contributor

Thanks for the nice example!

I've occasionally wondered if Legend couldn't be a separate table like element so you could disable the legends on the individual plots and add a Legend element into the layout. This could work technically, but breaks the philosophy of holoviews as the elements themselves are supposed to be about data, not visualization.

Unless you can argue that information held by a legend contains meaningful information? Maybe if the legend held the association between a descriptive label (about the data) and keys (also about a data) in a way that doesn't refer to what the elements look like?

Just a thought.

@maxalbert
Copy link
Contributor Author

I would definitely agree that there is valuable information contained in a legend that is independent of the visualisation (as this example shows). I like the idea of having a Legend element which abstracts this information that can then be added at different levels in the hierarchy of a nested object.

However, it feels like such a Legend element would behave slightly differently to other elements because the user wouldn't normally create and add it explicitly but rather still specify the option show_legend=True and Holoviews would then automatically add either a single legend or multiple ones to the correct containers.

@maxalbert
Copy link
Contributor Author

Is there currently any workaround for adding such a single legend manually to the matplotlib figure created by Holoviews? I don't mind if the solution is kludgy, but it would be a pity if I had to reproduce the figure from scratch entirely in matplotlib without being able to leverage Holoviews to get me most of the way. (Although it probably wouldn't be a huge effort so it's not a showstopper if it isn't currently possible.)

@philippjfr
Copy link
Member

I played with the idea of dynamically generated Legend and Colorbar elements, which could potentially make some of the plotting code cleaner in that regard but that's more of an implementation detail anyway because the user shouldn't really handle these directly anyway.

There are a few workarounds to get this to work already, but it should definitely be more convenient. Here's what I came up with, I ended up using GridSpace again because it's more convenient for this kind of parameter exploration but it should work with NdLayout as well:

legend_opts = {'NdOverlay': dict(show_legend=True, legend_position='right')}
grid[phases[-1], 10] = grid[phases[-1], 10](plot=legend_opts)
grid

image

You asked about the difference between GridSpace and NdLayout before and it's basically that GridSpace only supports two key dimensions and these two dimensions map directly onto the x- and y-axis of the grid, in terms of plotting it also automatically handles shared axes.

@philippjfr
Copy link
Member

I just pushed a commit (4ac3c4b) that fixes the show_legend plot option on GridSpaces, you should now get something similar to the above when you enable it. For NdLayouts you would still have to manually enable the legend on the plot where you want it again highlighting the difference between the two. To summarize:

  • NdLayout is useful for laying out plots indexed by arbitrary numbers of dimensions. Doesn't handle any of the shared axes or legends for you, just generates titles for each plot.
  • GridSpace is for parameter spaces of at most two dimensions but generates a nice gridded plot with shared axes and legends.

@philippjfr philippjfr added this to the v1.4.0 milestone Dec 4, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants