Bokeh Plot: Overlay a plot and a Div to get a gradient background using Bokeh

357 Views Asked by At

Initial Goal

I have a candlestick plot in Bokeh that is essentially a clone of the MSFT candlesticks example but with my custom data.

All I want is a grey-black gradient background behind that plot.

What I've Tried

Bokeh doesn't have background_gradient_fill option, so I tried using an image_url I found online of a gradient, that didn't work because firefox doesn't allow third party image loading. This option sucks anyway because it's slow and relies on some url off google.

I tried making my own gradient numpy array and them adding that as an image that didn't work because the image coords are basically hard-coded onto my plot, but my candlesticks are dynamic and are being loaded in live.

I finally thought I'd make a Gradient Div styled using CSS, set the candlestick plot fill to transparent, then overlay on top of each other. I tried doing that using gridplot, column, row and neither really allows you to control the offset of the elements within it from what I've seen. So now I don't really know what to do...

How do I squash the two together?

What I Have Now:

My code currently produces this: enter image description here

I just want both of these on top of each other basically. How do I do so?


Minimal Repliclable Example:

The MRE for this is the same as the MSFT candlesticks example but with the added div. To run, just place this code in a python file and run bokeh sampledata to download sample data then bokeh serve --show yourfilename.py

import pandas as pd
from bokeh.layouts import gridplot
from bokeh.models import Div
from bokeh.plotting import curdoc
from bokeh.plotting import figure
from bokeh.sampledata.stocks import MSFT
from math import pi

df = pd.DataFrame(MSFT)[:50]
df["date"] = pd.to_datetime(df["date"])

inc = df.close > df.open
dec = df.open > df.close
w = 12 * 60 * 60 * 1000  # half day in ms

TOOLS = "pan,wheel_zoom,box_zoom,reset,save"

p = figure(x_axis_type="datetime", tools=TOOLS, width=1000, title="MSFT Candlestick")
p.xaxis.major_label_orientation = pi / 4
p.grid.grid_line_alpha = 0.3

div = Div(style={"background": "linear-gradient(rgb(40,40,40), rgb(10,10,10))",
                 "height": "1000px", "width": "1000px"}, sizing_mode="scale_both")

p.segment(df.date, df.high, df.date, df.low, color="black")
p.vbar(df.date[inc], w, df.open[inc], df.close[inc], fill_color="#D5E1DD", line_color="black")
p.vbar(df.date[dec], w, df.open[dec], df.close[dec], fill_color="#F2583E", line_color="black")

curdoc().add_root(gridplot([[p, div]], toolbar_location="left"))
1

There are 1 best solutions below

0
On

If you want a gradient background, you can build it manually. You can generate an image based on the values of X and Y axis and than map these values to a Linear Colour Map.

from math import pi
import matplotlib.dates as mdates

import pandas as pd
import numpy as np

from bokeh.plotting import figure, show
from bokeh.models import LinearColorMapper
from bokeh.sampledata.stocks import MSFT

df = pd.DataFrame(MSFT)[:50]
df["date"] = pd.to_datetime(df["date"])

min_price = min(df.close.min(), df.open.min())
max_price = max(df.close.max(), df.open.max())

inc = df.close > df.open
dec = df.open > df.close
w = 12*60*60*1000 # half day in ms

TOOLS = "pan,wheel_zoom,box_zoom,reset,save"

p = figure(x_axis_type="datetime", tools=TOOLS, width=1000, title = "MSFT Candlestick", 
           x_range=[df.date.min(), df.date.max()], y_range=[min_price-10, max_price+10])
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha=0.3

# Gradient background
x = np.linspace(1, 0, 256)
y = np.linspace(0, 1, 256)

xArray, yArray = np.meshgrid(x, y)
plotArray = np.sqrt(xArray + yArray)

color_mapper = LinearColorMapper(palette="Greys256", low=0, high=1)

p.image(image=[plotArray], color_mapper=color_mapper, dw_units='screen', dw=1000, dh=max_price-min_price+20, x=[df.date.min()], 
        y=[min_price-10])

p.segment(df.date, df.high, df.date, df.low, color="black")
p.vbar(df.date[inc], w, df.open[inc], df.close[inc], fill_color="#D5E1DD", line_color="black")
p.vbar(df.date[dec], w, df.open[dec], df.close[dec], fill_color="#F2583E", line_color="black")

show(p)

The code above gives the following result: bokeh plot with gradient background