Can I plot a colorbar for a bokeh heatmap?

前端 未结 6 1455
再見小時候
再見小時候 2020-12-30 06:26

Does bokeh have a simple way to plot the colorbar for a heatmap?

In this example it would be a strip illustrating how colors correspond to values.

6条回答
  •  别那么骄傲
    2020-12-30 07:13

    This is high on my wish list as well. It would also need to automatically adjust the range if the plotted data changed (e.g. moving through one dimension of a 3D data set). The code below does something which people might find useful. The trick is to add an extra axis to the colourbar which you can control through a data source when the data changes.

    import numpy
    
    from bokeh.plotting import Figure
    
    from bokeh.models import ColumnDataSource, Plot, LinearAxis
    from bokeh.models.mappers import LinearColorMapper
    from bokeh.models.ranges import Range1d
    from bokeh.models.widgets import Slider
    from bokeh.models.widgets.layouts import VBox
    
    from bokeh.core.properties import Instance
    
    from bokeh.palettes import RdYlBu11
    
    from bokeh.io import curdoc
    
    class Colourbar(VBox):
    
        plot = Instance(Plot)
        cbar = Instance(Plot)
    
        power = Instance(Slider)
    
        datasrc = Instance(ColumnDataSource)
        cbarrange = Instance(ColumnDataSource)
    
        cmap = Instance(LinearColorMapper)
    
        def __init__(self):
    
            self.__view_model__ = "VBox"
            self.__subtype__ = "MyApp"
    
            super(Colourbar,self).__init__()
    
            numslices = 6
            x = numpy.linspace(1,2,11)
            y = numpy.linspace(2,4,21)
            Z = numpy.ndarray([numslices,y.size,x.size])
            for i in range(numslices):
                for j in range(y.size):
                    for k in range(x.size):
                        Z[i,j,k] = (y[j]*x[k])**(i+1) + y[j]*x[k]
    
            self.power = Slider(title = 'Power',name = 'Power',start = 1,end = numslices,step = 1,
                                value = round(numslices/2))
            self.power.on_change('value',self.inputchange)
    
            z = Z[self.power.value]
            self.datasrc = ColumnDataSource(data={'x':x,'y':y,'z':[z],'Z':Z})
    
            self.cmap = LinearColorMapper(palette = RdYlBu11)
    
            r = Range1d(start = z.min(),end = z.max())        
            self.cbarrange = ColumnDataSource(data = {'range':[r]})
    
            self.plot = Figure(title="Colourmap plot",x_axis_label = 'x',y_axis_label = 'y',
                               x_range = [x[0],x[-1]],y_range=[y[0],y[-1]],
                               plot_height = 500,plot_width = 500)
    
            dx = x[1] - x[0]
            dy = y[1] - y[0]
    
            self.plot.image('z',source = self.datasrc,x = x[0]-dx/2, y = y[0]-dy/2,
                            dw = [x[-1]-x[0]+dx],dh = [y[-1]-y[0]+dy],
                            color_mapper = self.cmap)
    
            self.generate_colorbar()
    
            self.children.append(self.power)
            self.children.append(self.plot)
            self.children.append(self.cbar)
    
        def generate_colorbar(self,cbarlength = 500,cbarwidth = 50):
    
            pal = RdYlBu11
    
            minVal = self.datasrc.data['z'][0].min()
            maxVal = self.datasrc.data['z'][0].max()
            vals = numpy.linspace(minVal,maxVal,len(pal))
    
            self.cbar = Figure(tools = "",x_range = [minVal,maxVal],y_range = [0,1],
                               plot_width = cbarlength,plot_height = cbarwidth)
    
            self.cbar.toolbar_location = None 
            self.cbar.min_border_left = 10
            self.cbar.min_border_right = 10
            self.cbar.min_border_top = 0
            self.cbar.min_border_bottom = 0
            self.cbar.xaxis.visible = None
            self.cbar.yaxis.visible = None
            self.cbar.extra_x_ranges = {'xrange':self.cbarrange.data['range'][0]}
            self.cbar.add_layout(LinearAxis(x_range_name = 'xrange'),'below')
    
            for r in self.cbar.renderers:
                if type(r).__name__ == 'Grid':
                    r.grid_line_color = None
    
            self.cbar.rect(x = vals,y = 0.5,color = pal,width = vals[1]-vals[0],height = 1)
    
        def updatez(self):
    
            data = self.datasrc.data
            newdata = data
            z = data['z']
            z[0] = data['Z'][self.power.value - 1]
            newdata['z'] = z
            self.datasrc.trigger('data',data,newdata)
    
        def updatecbar(self):
    
            minVal = self.datasrc.data['z'][0].min()
            maxVal = self.datasrc.data['z'][0].max()
            self.cbarrange.data['range'][0].start = minVal
            self.cbarrange.data['range'][0].end = maxVal
    
        def inputchange(self,attrname,old,new):
    
            self.updatez()
            self.updatecbar()
    
    curdoc().add_root(Colourbar())
    

提交回复
热议问题