Bokeh: Legend outside plot in multi line chart

Question:

I have a multi line plot in Bokeh:

import pandas as pd
from bokeh.plotting import figure, show
from bokeh.palettes import Category20c_7
from bokeh.io import output_file
from bokeh.models import SingleIntervalTicker, LinearAxis, ColumnDataSource

output_file("conso_daily.html")

treatcriteria_daily_data = pd.read_csv("treatcriteria_evolution.csv", sep=';')

final_daily_data = treatcriteria_daily_data.groupby(['startdate_weekyear','startdate_dayweek'],as_index = False).sum().pivot('startdate_weekyear','startdate_dayweek').fillna(0)

# keep only integer values in x axis
def interval_integer(plot):
    ticker = SingleIntervalTicker(interval=1, num_minor_ticks=1)
    xaxis = LinearAxis(ticker=ticker)
    plot.add_layout(xaxis, 'below')
    
numlines = len(final_daily_data.columns)
palette = Category20c_7[0:numlines]

# remove the last week if there is not all the data
data_without_last_week = final_daily_data[(final_daily_data != 0).all(1)]

cpu_values_daily = data_without_last_week.values.T.tolist()

weeks = []
for i in range(0,len(data_without_last_week.columns)):
    weeks.append(data_without_last_week.index)

df = {'week': weeks, 
      'day': ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], 
      'color': ['red', 'orange', 'yellow', 'green', 'grey', 'pink', 'purple'],
      'HCPU': cpu_values_daily}

source = ColumnDataSource(df)

p = figure(width=800, height=500)
p.multi_line(xs='week', ys='HCPU', legend='day', color='color',
             line_width=5, line_alpha=0.6, hover_line_alpha=1.0,
             source=source)
p.xaxis.visible = False
p.left[0].formatter.use_scientific = False

interval_integer(p)

show(p)

enter image description here

I want to show legend outside the plot area because the top curve (Sunday) is hidden.
I try to follow this thread, but it’s for single lines and not for multiline: Create a two line legend in a bokeh plot

Using this code, I searched to show legend in right outside the plot area, but it doesn’t work:

legend = Legend(items=[
    ('Monday', [p[0]]),
    ('Tuesday', [p[1]]),
    ('Wednesday', [p[2]]),
    ('Thursday', [p[3]]),
    ('Friday', [p[4]]),
    ('Saturday', [p[5]]),
    ('Sunday', [p[6]]),
], location=(0, -30))


p.add_layout(legend, 'right')
TypeError: 'Figure' object is not subscriptable

Thank you.

Edit: Here is my data ‘final_daily_data’ if it’s useful:

                     mc_cpu_hours                                
startdate_dayweek               1              2              3   
startdate_weekyear                                                
27                  527644.000731  468053.338183  517548.838022   
28                  349896.850976  481313.693908  372385.568095   
29                  168595.113447  388117.184580  373894.548600   
30                  176007.786269  364379.872622  366155.953075   
31                  177517.591864       0.000000       0.000000   

                                                                                
startdate_dayweek               4              5              6              7  
startdate_weekyear                                                              
27                  573669.325129  515710.534260  511711.421986  841073.028107  
28                  378069.713821  385937.231788  385856.666340  842468.209151  
29                  343235.942227  376405.876236  400007.946715  662019.708660  
30                  375948.240935  366151.336263  395790.387672  700936.336812  
31                       0.000000       0.000000       0.000000  686023.780120
Asked By: Rinnosuke

||

Answers:

Your problem is in legend = Legend(items=[('Monday', [p[0]]), ...]) or even more precise in p[0], …, p[7]. The figure objet is not subscriptable, because it is not a list or dictionary and this raises the error. I think in your case it is enough to define the Legend()-class blank, without any further information.

Small Example

import pandas as pd
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import Legend
output_notebook()
source = pd.DataFrame({
     'xs':[[1,2,3,4],[1,2,3,4]],
     'ys':[[1,2,3,4],[4,3,2,1]],
     'label':['a','b'],
     'color':['blue','green']
    })

p = figure(width=400, height=300)
p.add_layout(Legend(),'right')
p.multi_line(xs='xs', ys='ys', legend_field ='label', color='color', source=source)

show(p)

Output

multi_line legend

Answered By: mosc9575

Look at this answer, in particular the comment from @Sam De Meyer. In short, you create the figure, and then you do:

p.add_layout(p.legend[0], 'right')
show(p)
Answered By: LorenzoPeve
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.