In Bokeh, is it possible to have the marker alpha vary with values in a specified field?
For example, to vary color and marker by a field:
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.transform import factor_cmap,factor_mark
df = some dataframe
palette = ['#440154', '#404387', '#29788E', '#22A784', '#79D151', '#FDE724']
bok_sym = ['circle','asterisk','square_x','circle_x','diamond','hex']
cat_lst = list(df['cat_field'].unique())
df_cds = ColumnDataSource(data=df)
fig = figure(some kwargs)
fig.scatter(x='x',y='y',
source = df_cds,
marker = factor_mark('cat_field',bok_sym,cat_lst)
fill_color = factor_cmap('cat_field',palette,cat_lst),
)
show(fig)
There does not appear to be a similarly explicit function for alpha, but bokeh.transform does provide a generic transform
https://docs.bokeh.org/en/latest/docs/reference/transform.html
Which I've attempted with the following additions:
from bokeh.transform import transform
alph_lst = [0.2,0.9,0.9,0.9,0.9,0.9]
fig.scatter(x='x',y='y',
source = df_cds,
marker = factor_mark('cat_field',bok_sym,cat_lst)
fill_color = factor_cmap('cat_field',palette,cat_lst),
fill_alpha = transform('cat_field',dict(zip(cat_lst,alph_lst))),
)
But without success.
Cheers
EDIT:
I'll note that I've already (unsuccessfully) tried to pass transparency as part of the hex code:
palette = ['#44015433', '#404387E6', '#29788EE6', '#22A784E6', '#79D151E6', '#FDE724E6']
As you stated in your question you can definitely achieve this with a custom Transform. They're a little cumbersome to wrap your head around (in my opinion) but hopefully this code should clear it up.
Essentially you need 3 steps create a custom javascript function and apply it to a field name in bokeh:
CustomJSTransform
object, feeding it the necessary arguments as a dictionary. In this case I built the factor -> alpha
mappings in python and passed it into javascript. We used the vfunc
argument because we want to apply this transformation to each instance of a category in our data.p.scatter
use the transform
function to tie your specified column name to your custom transformation. In this case alpha = transform("cat_field", categorical_alpha_transformer)
import pandas as pd
import numpy as np
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.transform import factor_cmap, factor_mark, transform
from bokeh.models.transforms import CustomJSTransform
from bokeh.io import output_notebook, show
output_notebook()
v_func = """
var new_xs = new Array(xs.length)
for(var i = 0; i < xs.length; i++) {
new_xs[i] = alpha_map[xs[i]]
}
return new_xs
"""
df = pd.DataFrame({
"cat_field": list("abcdef"),
"x": list(range(6)),
"y": list(range(6)),
})
palette = ['#440154', '#404387', '#29788E', '#22A784', '#79D151', '#FDE724']
bok_sym = ['circle','asterisk','square_x','circle_x','diamond','hex']
alphas = [.1, .25, .4, .55, .7, .85, 1]
cat_lst = list(df['cat_field'].unique())
alpha_map = dict(zip(cat_lst, alphas)) # {"a": .1, "b": .25, ... "f": 1}
categorical_alpha_transformer = CustomJSTransform(args={"alpha_map": alpha_map}, v_func=v_func)
df_cds = ColumnDataSource(data=df)
fig = figure(width=250, height=250)
fig.scatter(
x='x',
y='y',
source = df_cds,
marker = factor_mark('cat_field', bok_sym, cat_lst),
fill_color = factor_cmap('cat_field', palette, cat_lst),
size=20,
alpha = transform("cat_field", categorical_alpha_transformer)
)
show(fig)