Search code examples
pythonpython-3.xdatatablebokehstyling

Bokeh DataTable color cells based on different range for each cell


I have a bokeh data table with this format that shows the concentration measured for 3 different QC samples (low concentration, medium concentration and high concentration).

Unnamed: 0      run     9Be      45Sc   51V   52Cr  55Mn........
QC Low Mean  11/14/19   1.16    0.845   1.2   2.5   9.6
QC Med Mean  11/14/19   2.37    0.865   2.0   5.6   10.0
QC Hi Mean   11/14/19   15.28   0.894   12.7  32.6  23.9

Each of these values have an acceptable range they can be. The range depends on the sample (low, med, high) AND the element. If they fall outside that range I want to color the cell red.

For example:

         if QC Low 9Be was < 1.0 or >1.4 the cell would be colored red.
         if QC Med 9Be was <2.2 or >2.7 the cell would be colored red
         if QC Hi  9Be was <14.5 or >16.9 the cell would be red
         if QC Low 51V was <0.9 or >1.4 the cell would be red
         etc

I have all these ranges stored in the columndata source as separate columns (for example Min9Be and Max9Be, etc)

I know you can set a template and use the htmlformatter to format the cells similar to this

template="""
            <div style="background:<%= 
                (function colorfromint(){
                    if(some logic ){
                        return("red")}
                    }()) %>; 
                color: black"> 
            <%= value %>
            </div>
            """
formatter =  HTMLTemplateFormatter(template=template)

But from what I've seen this only works when comparing entire columns? Or only applies one rule for the entire column?

Is it possible to do it cell by cell? Is there a way to achieve what I want?


Solution

  • It's possible. AFAIK there are two ways to achieve this in Underscore.js templates:

    from bokeh.io import show
    from bokeh.models import DataTable, TableColumn, ColumnDataSource, HTMLTemplateFormatter
    
    ds = ColumnDataSource(dict(x=list(range(10))))
    template_it = """\
    <div style="background: <%
        if (value % 2) {
            %>red<%
        } else {
            %>green<%
        } %>; color: black;">
        <%- value %>
    </div>
    """
    
    template_js = """\
    <%
    var bc;
    if (value < 3) bc = 'red';
    else if (value < 6) bc = 'green';
    else bc = 'blue';
    %>
    <div style="background: <%- bc %>; color: black;">
        <%- value %>
    </div>
    """
    dt = DataTable(columns=[TableColumn(field='x', name='X with inline template',
                                        formatter=HTMLTemplateFormatter(template=template_it)),
                            TableColumn(field='x', name='X with a JS block',
                                        formatter=HTMLTemplateFormatter(template=template_js))],
                   source=ds)
    
    show(dt)