13

I am making a plot following the example found here

Unfortunately, I have 17 curves I need to display, and the legend overlaps them. I know I can create a legend object that can be displayed outside the plot area like here, but I have 17 curves so using a loop is much more convenient.

Do you know how to combine both methods?

Despe1990
  • 595
  • 1
  • 3
  • 21

4 Answers4

18

Ok, I found the solution. See the code below where I have just modified the interactive legend example:

import pandas as pd
from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG
from bokeh.models import Legend
from bokeh.io import output_notebook

output_notebook()

p = figure(plot_width=800, plot_height=250, x_axis_type="datetime", toolbar_location='above')
p.title.text = 'Click on legend entries to mute the corresponding lines'

legend_it = []

for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
    df = pd.DataFrame(data)
    df['date'] = pd.to_datetime(df['date'])
    c = p.line(df['date'], df['close'], line_width=2, color=color, alpha=0.8,
           muted_color=color, muted_alpha=0.2)
    legend_it.append((name, [c]))


legend = Legend(items=legend_it)
legend.click_policy="mute"

p.add_layout(legend, 'right')

show(p)
joelostblom
  • 43,590
  • 17
  • 150
  • 159
Despe1990
  • 595
  • 1
  • 3
  • 21
  • 3
    after some attempts I ended up with the exact same logic as @Despe1990 ... how can it be this the "best" way of putting the legend box outside the plot frame? I would have imagined that a negative position coordinate would have made the trick, but it does not. it just makes disappear the legend from the output ... that's something bokeh should would on! – Rho Phi Apr 29 '18 at 01:02
  • I found out using location=(10, 60) works better for me. I use my figures in a column layout and (0, -60) made the legend to be partially visible. – Vahid S. Bokharaie Sep 20 '18 at 17:02
16

I'd like to expand on joelostbloms answer. It is also possible to pull out the legend from an existing plot and add it somewhere else after the plot has been created.

from bokeh.palettes import Category10
from bokeh.plotting import figure, show
from bokeh.sampledata.iris import flowers


# add a column with colors to the data
colors = dict(zip(flowers['species'].unique(), Category10[10]))
flowers["color"] = [colors[species] for species in flowers["species"]]

# make plot
p = figure(height=350, width=500)
p.circle("petal_length", "petal_width", source=flowers, legend_group='species',
         color="color")
p.add_layout(p.legend[0], 'right')

show(p)
Sam De Meyer
  • 2,031
  • 1
  • 25
  • 32
10

It is also possible to place legends outside the plot areas for auto-grouped, indirectly created legends. The trick is to create an empty legend and use add_layout to place it outside the plot area before using the glyph legend_group parameter:

from bokeh.models import CategoricalColorMapper, Legend
from bokeh.palettes import Category10
from bokeh.plotting import figure, show
from bokeh.sampledata.iris import flowers


color_mapper = CategoricalColorMapper(
    factors=[x for x in flowers['species'].unique()], palette=Category10[10])
p = figure(height=350, width=500)
p.add_layout(Legend(), 'right')
p.circle("petal_length", "petal_width", source=flowers, legend_group='species',
         color=dict(field='species', transform=color_mapper))
show(p)

enter image description here

joelostblom
  • 43,590
  • 17
  • 150
  • 159
0

A note on visibility as the above answers, while useful, didn't see me successfully place the legend below the plot and others may come across this too.

Where the plot_height or height are set for the figure as so:

p = figure(height=400)

But the legend is created as in Despee1990's answer and then placed below the plot as so:

legend = Legend(items=legend_it)
p.add_layout(legend, 'below')

Then the legend is not displayed, nor the plot.

If the location is changed to the right:

p.add_layout(legend, 'right')

...then the legend is only displayed where the items fit within the figure plot height. I.e. if you have a plot height of 400 but the legend needs a height of 800 then you won't see the items that don't fit within the plot area.

To resolve this either remove the plot height from the figure entirely or specify a height sufficient to include the height of the legend items box.

i.e. either:

p = figure()

or if Legend required height = 800 and glyph required height is 400:

p = figure(plot_height=800)
p.add_layout(legend, 'below')
lrcto_1
  • 37
  • 5