Вторичная ось для графика Холовьев (Боке)

Я пытаюсь построить набор графиков, наложенных на график, показывающий разницу между ними в%.

Код, который я имею для построения:

%%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
Другие вопросы по тегам