That’s kinda more a pandas-related question → You’re right that linear_cmap (which is just a transform application of LinearColorMapper) is the route. The nuance of your question is that you want the color to be scaled based on the min/max of each particular category (i.e. the day).
My pandas-side solution is to just pre calculate the percentile of each loc using pandas’ groupby-agg. Instead of pointing linear_cmap to the loc field, you pre-calculated each loc’s relative value based on the min/max of the day it belongs to, then pass that to the linear cmap instead:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
from bokeh.sampledata.commits import data
from bokeh.transform import jitter
import numpy as np
from bokeh.transform import linear_cmap
from bokeh.palettes import Greens
DAYS = ['Sun', 'Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon']
data['loc'] = np.random.randint(20, 301, size=len(data))
#scale loc based on it's percentile per day
gb = data.groupby('day').agg({'loc':['min','max']}) #get min and max for each day
#pandas reindexing stuff
gb.columns = ['min','max']
gb = gb.reset_index()
#merge back to data
data = data.merge(gb,how='inner',on='day')
#calculate percentile
data['p'] = (data['loc']-data['min'])/(data['max']-data['min'])
source = ColumnDataSource(data)
p = figure(width=800, height=300, y_range=DAYS, x_axis_type='datetime',
title="Commits by Time of Day (US/Central) 2012-2016")
#creates a transform that will transform field_name p into a hex color using the Greens palette
cmap = linear_cmap(field_name='p',low=0,high=1,palette=Greens[9])
p.scatter(x='time', y=jitter('day', width=0.6, range=p.y_range)
, fill_color=cmap #assign the transform to the fill_color
,source=source, alpha=0.3)
p.xaxis.formatter.days = ['%Hh']
p.x_range.range_padding = 0
p.ygrid.grid_line_color = None
show(p)
There are definitely means of doing this on the JS-side using a CustomJSTransform but would be considerably more involved.