我对如何使用HoloViews样式自定义图并在后端实现一致外观感到迷惑。 HoloViews被标为一个程序包,它为多个后端(尤其是Bokeh和Matplotlib)提供抽象层,但是我完全失败了尝试使使用这些后端生成的图看起来相同。一个后端中的设置会被另一个后端忽略,每个后端都缺少许多(大多数)格式化选项,因此有必要突破抽象来直接对后端进行较低级别的调用。
我怀疑我只是缺少了一些东西或未能找到合适的文档。
例如,下面的代码(使用不会尝试产生相同外观的设置,但是暴露了一些问题)导致Matplotlib图形(右)显示
此外,还有两个我无法为其设置的后端图(例如网格线,框架颜色)还有许多其他自定义设置。
如何在HoloViews中设置样式,以实现对Bokeh和Matplotlib生成的图的完全一致的控制?
import numpy as np
import pandas as pd
import holoviews as hv
hv.extension('bokeh', 'matplotlib')
ds = hv.Dataset({'x': np.random.randn(100), 'y1': np.random.randn(100), 'y2': np.random.randn(100), 'y3': np.random.randn(100)},
['x'],['y1', 'y2', 'y3'])
def mpl_style_hook(plot, element):
# Settings required here are neither complete, nor do they correspond directly to the backend's naming
# Where is the correspondence between handles and the backend's names documented?
pass
def bok_style_hook(plot, element):
# Such a small set of abstractions is provided, it is almost always necessary to resort to hooks
plot.state.title.align = "center"
plot.handles['xaxis'].axis_label_text_color = 'red'
plot.handles['yaxis'].axis_label_text_color = 'green'
plot.handles['xaxis'].axis_label_text_font_style = "normal"
plot.handles['yaxis'].axis_label_text_font_style = "normal"
# Attempt to set options that apply to both backends; but ignored by Matplotlib
hv.opts.defaults(hv.opts.Scatter(color='green'), hv.opts.Histogram(fill_color='yellow'))
# Explictily set backend to avoid warnings (`backend=` isn't sufficient)
hv.Store.current_backend = 'bokeh'
hv.opts.defaults(
hv.opts.Scatter(line_color='orange', size=6, fill_alpha=1.0, hooks=[bok_style_hook]),
hv.opts.Histogram(fill_color='cyan', fill_alpha=0.9, line_width=1, line_color='gray', hooks=[bok_style_hook]),
backend='bokeh')
hv.Store.current_backend = 'matplotlib'
hv.opts.defaults(
hv.opts.Scatter(hooks=[mpl_style_hook]),
# Histogram color ignored
hv.opts.Histogram(color='orange', hooks=[mpl_style_hook]),
backend='matplotlib')
hv.Store.current_backend = 'bokeh'
s1 = hv.Scatter(ds, 'x', 'y1').opts(hv.opts.Scatter(labelled=[None, 'y'])).hist(num_bins=51, dimension=['x','y1'])
s2 = hv.Scatter(ds, 'x', 'y2').opts(hv.opts.Scatter(labelled=[None, 'y'])).hist(num_bins=51, dimension='y2')
s3 = hv.Scatter(ds, 'x', 'y3').hist(num_bins=51, dimension='y3')
p = (s1 + s2 + s3).opts(hv.opts.Histogram(labelled=[None, None]), hv.opts.Layout(shared_axes=True)).cols(1)
hv.save(p, '_testHV.html', backend='bokeh')
hv.save(p, '_testHV.png', backend='matplotlib')
p
答案 0 :(得分:3)
在实际的软件支持方面,我认为您不会丢失任何东西;您所缺少的是,HoloViews绝不保证将从不同后端绘制的绘图简化为相同。这些图旨在以大致相同的方式显示相同的数据,但是每个后端以不同的方式工作,而其中的一些差异实际上是选择该特定后端而不是另一个后端的原因。
HoloViews当然可以通过多种方式从抽象的样式概念映射到如何在不同的后端中完成操作的细节,但这令人惊讶地棘手。很少有用户要求这样做;大多数人会选择他们喜欢的后端并仅使用它,而宁愿我们将有限的开发时间用于其他功能。
也就是说,如果后端可以生成类似的图,则您应该能够计算出与HoloViews一起使用的设置,这些设置将以匹配的形式生成它们。为此,您需要一次设置一个后端的设置,然后在每个后端应用它们。例如。 .opts(line_width=3, backend='bokeh').opts(linewidth=4.5, backend='matplotlib')
,并在每个后端显示该对象时使用适当的选项。这两个选项的名称仅相差一个字符,但是它们对于这种看似简单的线宽概念却工作方式却大不相同:matplotlib接受以“点”为单位的宽度(取决于dpi并知道以英寸为单位的绝对大小),而bokeh则接受屏幕空间中的像素。它们都是宽度,但不一定有直接比较这两个值的方法,因为这取决于您可能对dpi和fig_size进行的单独设置。您应该能够通过足够的努力使它看起来相似,但是要一直在所有地块上实现这一目标是一项艰巨的任务,需要一些单独的资金和开发商才能实现!尽管如此,在HoloViews中执行此操作已经比在本地完全重写Matplotlib和Bokeh之间的绘图要容易得多,因此HoloViews仍然可以提供很大帮助,只是不能为您解决所有问题...