Search code examples
pythonbokeh

Bokeh: Unable to generate different line colours when using MultiLine glyph


I've used Bokeh to generate a multiline chart that updates using a slider. I cannot find a way to have each line drawn with a different colour. I've tried using itertools to iterate through a palette, and passing a range of palette colours.

Here's the itertools approach (full_source is there to support the slider interaction which uses CustomJS):

import itertools
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.palettes import Category20 as palette
from bokeh.models.glyphs import MultiLine
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.layouts import column, row
from bokeh.io import show

data={'xdata':[[0, 1, 2, 4, 5, 6, 10, 11, 12], [4, 8, 16, 0, 13, 21, -3, 9, 21]],
      'ydata':[[4, 8, 16, 0, 13, 21, -3, 9, 21], [0, 1, 2, 4, 5, 6, 10, 11, 12]]}
colors=itertools.cycle(palette[2])

source = ColumnDataSource(data)
full_source = ColumnDataSource(data)
glyph = MultiLine(xs='xdata', ys='ydata', line_color = next(colors))

p = figure(title = None, plot_width = 400, plot_height = 400, toolbar_location = None)
p.add_glyph(source, glyph)
print(glyph.line_color)
show(p)

This gives two lines, but both of the same colour. print(glyph.line_color) shows just one color passed - #1f77b4 (which is the first colour in the Category20 palette)

I've also tried using the example found here:

import itertools
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.palettes import Spectral11
from bokeh.models.glyphs import MultiLine
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.layouts import column, row
from bokeh.io import show

data={'xdata':[[0, 1, 2, 4, 5, 6, 10, 11, 12], [4, 8, 16, 0, 13, 21, -3, 9, 21]],
      'ydata':[[4, 8, 16, 0, 13, 21, -3, 9, 21], [0, 1, 2, 4, 5, 6, 10, 11, 12]]}
my_pallet = Spectral11[0:2]

source = ColumnDataSource(data)
full_source = ColumnDataSource(data)
glyph = MultiLine(xs='xdata', ys='ydata', line_color = my_pallet)

p = figure(title = None, plot_width = 400, plot_height = 400, toolbar_location = None)
p.add_glyph(source, glyph)
print(glyph.line_color)
show(p)

This gives: ValueError expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), Either(String, Instance(Transform), Instance(Expression), Color)) or Color, got ['#5e4fa2', '#3288bd', '#66c2a5']

How can I get multiple colours from a palette into a MultiLine graph?


Solution

  • Ok it looks like I'd not been using ColumnDataSource correctly. By passing the colours into the ColumnDataSource as an additional key:value pair in the data Dict, it works. I also could get rid of the MultiLine glyph object.

    Working code is:

    from bokeh.plotting import figure
    from bokeh.embed import components
    from bokeh.models import CustomJS, ColumnDataSource, Slider
    from bokeh.palettes import Category20 as palette
    from bokeh.models.widgets import DataTable, TableColumn
    from bokeh.layouts import column, row
    from bokeh.io import show
    
    data = {'xs':[[...,...,..,][...,...,...]],'ys':[[...,...,..,][...,...,...]]}
    length = len(data)
    colors = palette[length]
    
    #because Category20 has a minimum of 3 values, and length may be smaller
    while len(colors)>length:
        colors.pop()
    
    data['color'] = colors
    source = ColumnDataSource(data)
    full_source = ColumnDataSource(data)
    
    p = figure(title = None, plot_width = 400, plot_height = 400, toolbar_location = None)
    p.multi_line(xs='xs', ys='ys', source=source, line_color='color')