Вторичная ось для графика Холовьев (Боке)
Я пытаюсь построить набор графиков, наложенных на график, показывающий разницу между ними в%.
Код, который я имею для построения:
%%output size = 200
%%opts Curve[height=200, width=400,show_grid=True,tools=['hover','box_select'], xrotation=90]
%%opts Curve(line_width=1)
from bokeh.models import Range1d, LinearAxis
new_df = new_df.astype('float')
percent_diff_df = percent_diff_df.astype('float')
def twinx(plot, element):
# Setting the second y axis range name and range
start, end = (element.range(1))
label = element.dimensions()[1].pprint_label
plot.state.extra_y_ranges = {"foo": Range1d(start=0, end=150)}
# Adding the second axis to the plot.
linaxis = LinearAxis(axis_label='% Difference', y_range_name='foo')
plot.state.add_layout(linaxis, 'right')
wavelength = hv.Dimension('wavelength', label = 'Wavelength', unit = 'nm')
radiance = hv.Dimension('radiance', label = 'Radiance', unit = 'W/m^2/sr/nm')
curve = hv.Curve((new_df['Wave'], new_df['Level_9']), wavelength, radiance,label = 'Level_9', group = 'Requirements')*\
hv.Curve((new_df['gcal_wave'],new_df['gcal_9']),wavelength, radiance,label = 'GCAL_9', group = 'Requirements')
curve2 = hv.Curve((percent_diff_df['pdiff_wave'],percent_diff_df['pdiff_9']), label = '% Difference', group = 'Percentage Difference').opts(plot=dict(finalize_hooks=[twinx]), style=dict(color='purple'))
curve * curve2
Плоские синие графики на самом деле должны выглядеть так:
Мне нужно построить два графика на двух шкалах. Кажется, я могу добавить шкалу, но не прикрепить к ней ни один из графиков.
0 ответов
Ваше решение почти готово. Чтобы на самом деле использовать диапазон и ось, которые вы создаете в своем крючке, вам необходимо получить доступ к базовому глифу боке и установить его y_range_name.
Общий пример будет выглядеть так:
import pandas as pd
import holoviews as hv
from bokeh.models.renderers import GlyphRenderer
hv.extension('bokeh')
def apply_formatter(plot, element):
p = plot.state
# create secondary range and axis
p.extra_y_ranges = {"twiny": Range1d(start=0, end=35)}
p.add_layout(LinearAxis(y_range_name="twiny"), 'right')
# set glyph y_range_name to the one we've just created
glyph = p.select(dict(type=GlyphRenderer))[0]
glyph.y_range_name = 'twiny'
dts = pd.date_range('2015-01-01', end='2015-01-10').values
c_def = hv.Curve((dts, np.arange(10)), name='default_axis').options(color='red', width=300)
c_sec = hv.Curve((dts, np.arange(10)), name='secondary_axis').options(color='blue',width=300, hooks=[apply_formatter])
c_def + c_def * c_sec + c_sec
Дополнительные сведения см. В исходной проблеме github здесь: https://github.com/pyviz/holoviews/issues/396
EDITED - расширенный пример
Прошло некоторое время с тех пор, как был дан ответ на исходный вопрос, и по мере развития темы на github я хотел бы опубликовать более сложный пример, позаботившись о некоторых несоответствиях, показанных в исходном ответе. Я по-прежнему рекомендую вам пройти через ветку на github, чтобы покопаться в деталях, так как многоосевые графики в головных обзорах в настоящее время требуют понимания того, как боке взаимодействует с несколькими осями.
import pandas as pd
import streamz
import streamz.dataframe
import holoviews as hv
from holoviews import opts
from holoviews.streams import Buffer
from bokeh.models import Range1d, LinearAxis
hv.extension('bokeh')
def plot_secondary(plot, element):
'''
A hook to put data on secondary axis
'''
p = plot.state
# create secondary range and axis
if 'twiny' not in [t for t in p.extra_y_ranges]:
# you need to manually recreate primary axis to avoid weird behavior if you are going to
# use secondary_axis in your plots. From what i know this also relates to the way axis
# behave in bokeh and unfortunately cannot be modified from hv unless you are
# willing to rewrite quite a bit of code
p.y_range = Range1d(start=0, end=10)
p.y_range.name = 'default'
p.extra_y_ranges = {"twiny": Range1d(start=0, end=10)}
p.add_layout(LinearAxis(y_range_name="twiny"), 'right')
# set glyph y_range_name to the one we've just created
glyph = p.renderers[-1]
glyph.y_range_name = 'twiny'
# set proper range
glyph = p.renderers[-1]
vals = glyph.data_source.data['y'] # ugly hardcoded solution, see notes below
p.extra_y_ranges["twiny"].start = vals.min()* 0.99
p.extra_y_ranges["twiny"].end = vals.max()* 1.01
# define two streamz random dfs to sim data for primary and secondary plots
simple_sdf = streamz.dataframe.Random(freq='10ms', interval='100ms')
secondary_sdf = streamz.dataframe.Random(freq='10ms', interval='100ms')
# do some transformation
pdf = (simple_sdf-0.5).cumsum()
sdf = (secondary_sdf-0.5).cumsum()
# create streams for holoviews from these dfs
prim_stream = Buffer(pdf.y)
sec_stream = Buffer(sdf.y)
# create dynamic maps to plot streaming data
primary = hv.DynamicMap(hv.Curve, streams=[prim_stream]).opts(width=400, show_grid=True, framewise=True)
secondary = hv.DynamicMap(hv.Curve, streams=[sec_stream]).opts(width=400, color='red', show_grid=True, framewise=True, hooks=[plot_secondary])
secondary_2 = hv.DynamicMap(hv.Curve, streams=[prim_stream]).opts(width=400, color='yellow', show_grid=True, framewise=True, hooks=[plot_secondary])
# plot these maps on the same figure
primary * secondary * secondary_2