我可以为散景热图绘制颜色条吗?

Posted

技术标签:

【中文标题】我可以为散景热图绘制颜色条吗?【英文标题】:Can I plot a colorbar for a bokeh heatmap? 【发布时间】:2015-12-13 09:57:48 【问题描述】:

散景是否有一种简单的方法来绘制热图的颜色条?

In this example 这将是一个条带,说明颜色如何与值对应。

在 matlab 中,它被称为“颜色条”,如下所示:

【问题讨论】:

【参考方案1】:

更新:现在要容易得多:请参阅

http://docs.bokeh.org/en/latest/docs/user_guide/annotations.html#color-bars


恐怕我没有一个很好的答案,这在 Bokeh 中应该更容易。但是我以前手动做过类似的事情。

因为我经常想把这些从我的情节中取出,所以我制作了一个新情节,然后将它与hplotgridplot 之类的东西组合在一起。

这里有一个例子:https://github.com/birdsarah/pycon_2015_bokeh_talk/blob/master/washmap/washmap/water_map.py#L179

在你的情况下,情节应该很简单。如果您制作了这样的数据源:

| value | color
| 1     | blue
.....
| 9     | red

然后你可以这样做:

legend = figure(tools=None)
legend.toolbar_location=None
legend.rect(x=0.5, y='value', fill_color='color', width=1, height=1, source=source)
layout = hplot(main, legend)
show(legend)

但是,这确实依赖于您知道您的值对应的颜色。您可以将调色板传递给您的热图图表调用 - 如下所示:http://docs.bokeh.org/en/latest/docs/gallery/cat_heatmap_chart.html 这样您就可以使用它来构建新的数据源。

我很确定至少有一个关于彩色地图的未解决问题。我知道我刚刚为场外传奇添加了一个。

【讨论】:

更新:这现在容易多了 - bokeh.pydata.org/en/latest/docs/user_guide/…【参考方案2】:

由于此处的其他答案似乎非常复杂,因此这里有一段易于理解的代码,可在散景热图上生成颜色条。

import numpy as np
from bokeh.plotting import figure, show
from bokeh.models import LinearColorMapper, BasicTicker, ColorBar


data = np.random.rand(10,10)

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

plot = figure(x_range=(0,1), y_range=(0,1))
plot.image(image=[data], color_mapper=color_mapper,
           dh=[1.0], dw=[1.0], x=[0], y=[0])

color_bar = ColorBar(color_mapper=color_mapper, ticker= BasicTicker(),
                     location=(0,0))

plot.add_layout(color_bar, 'right')

show(plot)

【讨论】:

【参考方案3】:

由于 0.12.3 版本 Bokeh 有 ColorBar。

这份文档对我非常有用:

http://docs.bokeh.org/en/dev/docs/user_guide/annotations.html#color-bars

【讨论】:

【参考方案4】:

为此,我和@birdsarah 做了同样的事情。作为一个额外的提示,如果你使用 rect 方法作为你的颜色图,那么在颜色栏中再次使用 rect 方法并使用相同的源。最终结果是您可以选择颜色条的各个部分,它也会在您的绘图中进行选择。

试试看:

http://simonbiggs.github.io/electronfactors

【讨论】:

【参考方案5】:

下面是一些基于birdsarah 生成颜色条响应的代码:

def generate_colorbar(palette, low=0, high=15, plot_height = 100, plot_width = 500, orientation = 'h'):

    y = np.linspace(low,high,len(palette))
    dy = y[1]-y[0]
    if orientation.lower()=='v':
        fig = bp.figure(tools="", x_range = [0, 1], y_range = [low, high], plot_width = plot_width, plot_height=plot_height)
        fig.toolbar_location=None
        fig.xaxis.visible = None
        fig.rect(x=0.5, y=y, color=palette, width=1, height = dy)
    elif orientation.lower()=='h':
        fig = bp.figure(tools="", y_range = [0, 1], x_range = [low, high],plot_width = plot_width, plot_height=plot_height)
        fig.toolbar_location=None
        fig.yaxis.visible = None
        fig.rect(x=y, y=0.5, color=palette, width=dy, height = 1)
    return fig

另外,如果您对模拟 matplot lib 颜色图感兴趣,请尝试使用:

import matplotlib as mpl
def return_bokeh_colormap(name):
    cm = mpl.cm.get_cmap(name)
    colormap = [rgb_to_hex(tuple((np.array(cm(x))*255).astype(np.int))) for x in range(0,cm.N)]
    return colormap
def rgb_to_hex(rgb):
    return '#%02x%02x%02x' % rgb[0:3]

【讨论】:

这应该是答案 - 可能需要注意绘图大小以确保它与您的主绘图相匹配,并且我需要垂直条的宽度大于 120正确显示。另外,要使用 bk OR mpl 调色板字符串,我只使用了palette = getattr(bk.palettes, palette) if hasattr(bk.palettes, palette) else return_bokeh_colormap(palette) @user2561747,我同意。这是对我有用的答案。【参考方案6】:

这在我的愿望清单上也很重要。如果绘制的数据发生变化(例如,在 3D 数据集的一维中移动),它还需要自动调整范围。下面的代码做了一些人们可能会觉得有用的事情。诀窍是在颜色条中添加一个额外的轴,当数据发生变化时,您可以通过数据源进行控制。

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())

【讨论】:

以上是关于我可以为散景热图绘制颜色条吗?的主要内容,如果未能解决你的问题,请参考以下文章

绘制排序的热图保持(x,y)值颜色

散景:如何在图例中添加图例和非固定颜色边界?

如何将数组绘制为热图时间序列

珍藏 史上最全热图绘制工具及操作流程(一)

如何将颜色条居中于 seaborn 热图的定义值?

Matplotlib风格与样式