0

I built a heatmap in Bokeh which works fine. I want it to be colored from bright green to bright red, continuously. The current code is:

colors = ["#66ff00", "#FFFFFF", "#FF0000"]
colors.reverse()
mapper = LinearColorMapper(palette=colors, low=-50, high=50)

p.rect(..., fill_color=transform('percentage', mapper))

The problem is that only 3 colors are appearing in the heatmap. I would expect all values between -50 and 50 to linearly interpolate the values between bright red to white to bright green and show up in the heatmap. How can I achieve this?

2 Answers2

1

The currently accepted answer is absolutely correct, I just thought anybody finding this could use a working solution for their problem.

Using matplotlib you can create a linear segmented colormap:

colors = ["darkorange", "gold", "lawngreen", "lightseagreen"]
cmap1 = matplotlib.colors.LinearSegmentedColormap.from_list("mycmap", colors)

If your colors shouldn't be uniformly spread along the range, you can define their position too:

colors = ["darkorange", "gold", "lawngreen", "lightseagreen"]
nodes = [0.0, 0.4, 0.8, 1.0]
cmap2 = matplotlib.colors.LinearSegmentedColormap.from_list("mycmap", list(zip(nodes, colors)))

From here you can treat this colormap the same way you would any built in linear segmented colormap, like copper. For example you can export a list of 192 colors from the range between 25%-90% of your colormap like this:

palette = [matplotlib.colors.rgb2hex(c) for c in cmap1(np.linspace(0.25, 0.9, 192))]

And then you can use this palette in whatever charting solution you prefer.

Edit: As I also often need the exact same functionality, I just created a function for this.

The code itself:

def get_palette(cmap='Greys', n=192, start=0, end=1):
  import matplotlib, numpy as np

  linspace = np.linspace(start, end, n)

  if isinstance(cmap, list):
    cmap = matplotlib.colors.LinearSegmentedColormap.from_list("customcmap", cmap)
    palette = cmap(linspace)
  elif isinstance(cmap, str):
    cmap = matplotlib.pyplot.cm.get_cmap(cmap)
    palette = cmap(linspace)
  else:
    palette = cmap(linspace)

  hex_palette = [matplotlib.colors.rgb2hex(c) for c in palette]
  return hex_palette
0

As of version 2.3.1, palettes are just lists of colors, and currently existing colormappers just use palettes, as given, without modifying them. If you want more colors you would need to interpolate the palette (list of colors) out to whatever size you want before passing it to Bokeh. The "Linear" in LinearColorMapper refers to how values are linearly mapped into the bins defined by a palette.

bigreddot
  • 33,642
  • 5
  • 69
  • 122