如何使用 Bokeh(或其他库)在 python 中绘制等高线图?
How to make a contour plot in python using Bokeh (or other libs)?
我有兴趣在 Bokeh 中制作等高线图。到目前为止我还没有在网上找到任何东西。
提醒一下,这是等高线图:
如有任何帮助,我们将不胜感激。我也欢迎向其他库提出建议,但前提是允许 interactive/animated 图,而不仅仅是渲染静态输出(图像)。
Plotly 在过去一年 Python 中支持等高线图:
https://plot.ly/python/contour-plots/
https://plot.ly/pandas/contour-plots/
密度图(具有相邻直方图的等值线图):
https://plot.ly/python/2d-density-plots/
https://plot.ly/pandas/2d-density-plots/
Plotly Python 库在 pip 上,100% 免费和开源。
通过选择具有 10 个值的调色板,就像在您提供的示例中一样,可以在散景中使用图像(参见 Bokeh image example)来模拟等高线图。黑色的轮廓线和数字不见了,但颜色之间的边界实际上是轮廓线。另外,据我所知,Bokeh 不提供颜色条,但您可以将其编码为另一张图像 (更新:Bokeh 的最新版本确实提供了颜色条。):
from bokeh.io import output_file
from bokeh.plotting import gridplot,figure, show
from bokeh.models import ColumnDataSource,FixedTicker
import numpy as np
from matplotlib import cm,colors
output_file("contour.html")
cmap = cm.get_cmap("jet") #choose any matplotlib colormap here
num_slabs = 10 # number of color steps
jet_10 = [colors.rgb2hex(m) for m in cmap(np.arange(0,cmap.N,cmap.N/(num_slabs-1)))]
vmin = 0
vmax = 1550
N = 200
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = vmax * (1. + np.sin(xx)*np.cos(yy))
source = ColumnDataSource(data={'d': [d], 'xx': [x], 'yy': [y]})
p = figure(plot_width=400,plot_height=400,x_range=[0, 10], y_range=[0, 10],min_border_right=10)
p.image(image="d", x=[0], y=[0], dw=[10], dh=[10], palette=jet_10,source=source)
# The following code is for the colorbar:
pcb = figure(plot_width=80,plot_height=400,x_range=[0, 1], y_range=[0, vmax],min_border_right=10)
pcb.image(image=[np.linspace(vmin,vmax,100).reshape(100,1)],x=[0],y=[0],dw=[1],dh=[vmax-vmin], palette=jet_10)
pcb.xaxis.major_label_text_color = None
pcb.xaxis.major_tick_line_color = None
pcb.xaxis.minor_tick_line_color = None
pcb.yaxis[0].ticker=FixedTicker(ticks=np.linspace(vmin,vmax,num_slabs+1)) # 11 ticks
pgrid = gridplot([[p,pcb]]) # this places the colorbar next to the image
show(pgrid)
输出将如下所示:
您还可以使用 matplotlibs contour plot 计算等高线数据,然后使用 bokehs 绘制等高线 multi_line。我也在绘制文本标签(不幸的是有点难看)。
import numpy as np
import matplotlib.pyplot as plt
from bokeh.models import ColumnDataSource
from bokeh.io import output_file
from bokeh.plotting import gridplot,figure, show
def get_contour_data(X, Y, Z):
cs = plt.contour(X, Y, Z)
xs = []
ys = []
xt = []
yt = []
col = []
text = []
isolevelid = 0
for isolevel in cs.collections:
isocol = isolevel.get_color()[0]
thecol = 3 * [None]
theiso = str(cs.get_array()[isolevelid])
isolevelid += 1
for i in range(3):
thecol[i] = int(255 * isocol[i])
thecol = '#%02x%02x%02x' % (thecol[0], thecol[1], thecol[2])
for path in isolevel.get_paths():
v = path.vertices
x = v[:, 0]
y = v[:, 1]
xs.append(x.tolist())
ys.append(y.tolist())
xt.append(x[len(x) / 2])
yt.append(y[len(y) / 2])
text.append(theiso)
col.append(thecol)
source = ColumnDataSource(data={'xs': xs, 'ys': ys, 'line_color': col,'xt':xt,'yt':yt,'text':text})
return source
output_file("contour.html")
N = 400
x = np.linspace(-1, 1, N)
y = np.linspace(-1, 1, N)
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2
source = get_contour_data(X,Y,Z)
plot = figure(plot_width=400,plot_height=400,x_range=[-1,1], y_range=[-1,1])
plot.multi_line(xs='xs', ys='ys', line_color='line_color', source=source)
plot.text(x='xt',y='yt',text='text',source=source,text_baseline='middle',text_align='center')
show(plot)
这是输出:
我写了一个基于matplotlib输出的填充轮廓工具:
谢谢 Pablo,你的回答对我很有帮助。
使用您的示例,我制作了一个类似的图,该图使用 skimage 库添加了等高线。我还打开了您引用的 Bokeh image example 中的悬停工具。希望其他人出现并添加行标签。
请注意,以下代码适用于 Jupyter Notebook 中的 运行。
from skimage import measure
import numpy as np
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import (ColorBar,
FixedTicker,
LinearColorMapper,
PrintfTickFormatter)
output_notebook()
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
mapper = LinearColorMapper(palette='Spectral11', low=-1, high=1)
p = figure(x_range=(0, 10), y_range=(0, 10),
tooltips=[("x", "$x"), ("y", "$y"), ("value", "@image")])
# must give a vector of image data for image parameter
p.image(image=[d], x=0, y=0, dw=10, dh=10, palette='Spectral11')
levels = np.linspace(-1, 1, 12)
color_bar = ColorBar(color_mapper=mapper,
major_label_text_font_size="8pt",
ticker=FixedTicker(ticks=levels),
formatter=PrintfTickFormatter(format='%.2f'),
label_standoff=6,
border_line_color=None,
location=(0, 0))
p.add_layout(color_bar, 'right')
for level in levels:
contours = measure.find_contours(d, level)
for contour in contours:
x = contour[:,1]/50
y = contour[:,0]/50
p.line(x, y, color='grey', line_width=2)
show(p)
我有兴趣在 Bokeh 中制作等高线图。到目前为止我还没有在网上找到任何东西。
提醒一下,这是等高线图:
如有任何帮助,我们将不胜感激。我也欢迎向其他库提出建议,但前提是允许 interactive/animated 图,而不仅仅是渲染静态输出(图像)。
Plotly 在过去一年 Python 中支持等高线图:
https://plot.ly/python/contour-plots/
https://plot.ly/pandas/contour-plots/
密度图(具有相邻直方图的等值线图):
https://plot.ly/python/2d-density-plots/
https://plot.ly/pandas/2d-density-plots/
Plotly Python 库在 pip 上,100% 免费和开源。
通过选择具有 10 个值的调色板,就像在您提供的示例中一样,可以在散景中使用图像(参见 Bokeh image example)来模拟等高线图。黑色的轮廓线和数字不见了,但颜色之间的边界实际上是轮廓线。另外,据我所知,Bokeh 不提供颜色条,但您可以将其编码为另一张图像 (更新:Bokeh 的最新版本确实提供了颜色条。):
from bokeh.io import output_file
from bokeh.plotting import gridplot,figure, show
from bokeh.models import ColumnDataSource,FixedTicker
import numpy as np
from matplotlib import cm,colors
output_file("contour.html")
cmap = cm.get_cmap("jet") #choose any matplotlib colormap here
num_slabs = 10 # number of color steps
jet_10 = [colors.rgb2hex(m) for m in cmap(np.arange(0,cmap.N,cmap.N/(num_slabs-1)))]
vmin = 0
vmax = 1550
N = 200
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = vmax * (1. + np.sin(xx)*np.cos(yy))
source = ColumnDataSource(data={'d': [d], 'xx': [x], 'yy': [y]})
p = figure(plot_width=400,plot_height=400,x_range=[0, 10], y_range=[0, 10],min_border_right=10)
p.image(image="d", x=[0], y=[0], dw=[10], dh=[10], palette=jet_10,source=source)
# The following code is for the colorbar:
pcb = figure(plot_width=80,plot_height=400,x_range=[0, 1], y_range=[0, vmax],min_border_right=10)
pcb.image(image=[np.linspace(vmin,vmax,100).reshape(100,1)],x=[0],y=[0],dw=[1],dh=[vmax-vmin], palette=jet_10)
pcb.xaxis.major_label_text_color = None
pcb.xaxis.major_tick_line_color = None
pcb.xaxis.minor_tick_line_color = None
pcb.yaxis[0].ticker=FixedTicker(ticks=np.linspace(vmin,vmax,num_slabs+1)) # 11 ticks
pgrid = gridplot([[p,pcb]]) # this places the colorbar next to the image
show(pgrid)
输出将如下所示:
您还可以使用 matplotlibs contour plot 计算等高线数据,然后使用 bokehs 绘制等高线 multi_line。我也在绘制文本标签(不幸的是有点难看)。
import numpy as np
import matplotlib.pyplot as plt
from bokeh.models import ColumnDataSource
from bokeh.io import output_file
from bokeh.plotting import gridplot,figure, show
def get_contour_data(X, Y, Z):
cs = plt.contour(X, Y, Z)
xs = []
ys = []
xt = []
yt = []
col = []
text = []
isolevelid = 0
for isolevel in cs.collections:
isocol = isolevel.get_color()[0]
thecol = 3 * [None]
theiso = str(cs.get_array()[isolevelid])
isolevelid += 1
for i in range(3):
thecol[i] = int(255 * isocol[i])
thecol = '#%02x%02x%02x' % (thecol[0], thecol[1], thecol[2])
for path in isolevel.get_paths():
v = path.vertices
x = v[:, 0]
y = v[:, 1]
xs.append(x.tolist())
ys.append(y.tolist())
xt.append(x[len(x) / 2])
yt.append(y[len(y) / 2])
text.append(theiso)
col.append(thecol)
source = ColumnDataSource(data={'xs': xs, 'ys': ys, 'line_color': col,'xt':xt,'yt':yt,'text':text})
return source
output_file("contour.html")
N = 400
x = np.linspace(-1, 1, N)
y = np.linspace(-1, 1, N)
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2
source = get_contour_data(X,Y,Z)
plot = figure(plot_width=400,plot_height=400,x_range=[-1,1], y_range=[-1,1])
plot.multi_line(xs='xs', ys='ys', line_color='line_color', source=source)
plot.text(x='xt',y='yt',text='text',source=source,text_baseline='middle',text_align='center')
show(plot)
这是输出:
我写了一个基于matplotlib输出的填充轮廓工具:
谢谢 Pablo,你的回答对我很有帮助。
使用您的示例,我制作了一个类似的图,该图使用 skimage 库添加了等高线。我还打开了您引用的 Bokeh image example 中的悬停工具。希望其他人出现并添加行标签。
请注意,以下代码适用于 Jupyter Notebook 中的 运行。
from skimage import measure
import numpy as np
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import (ColorBar,
FixedTicker,
LinearColorMapper,
PrintfTickFormatter)
output_notebook()
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
mapper = LinearColorMapper(palette='Spectral11', low=-1, high=1)
p = figure(x_range=(0, 10), y_range=(0, 10),
tooltips=[("x", "$x"), ("y", "$y"), ("value", "@image")])
# must give a vector of image data for image parameter
p.image(image=[d], x=0, y=0, dw=10, dh=10, palette='Spectral11')
levels = np.linspace(-1, 1, 12)
color_bar = ColorBar(color_mapper=mapper,
major_label_text_font_size="8pt",
ticker=FixedTicker(ticks=levels),
formatter=PrintfTickFormatter(format='%.2f'),
label_standoff=6,
border_line_color=None,
location=(0, 0))
p.add_layout(color_bar, 'right')
for level in levels:
contours = measure.find_contours(d, level)
for contour in contours:
x = contour[:,1]/50
y = contour[:,0]/50
p.line(x, y, color='grey', line_width=2)
show(p)