I am trying to plot a set of graphs overlaid with a plot showing the % difference between them.
The code I have for plotting:
%%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
The result looks like this:
The flat blue graphs are actually supposed to look like this:
I need to plot the two graphs on two scales. I seem to be able to add a scale but not attach any of the plots to the scale.
Your solution is almost complete. To actually use the range & axis you create in your hook, you need to access underlying bokeh glyph and set its y_range_name.
A general example would look like this:
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
For further details, please refer to original github issue here: https://github.com/pyviz/holoviews/issues/396
EDITED - An Advanced example
Its been some time since the original question was answered, and as thread at github advances, i'd like to cross post a more advanced example taking care of some inconsistencies shown in original answer. I still encourage you to to through the thread at github to dig in the details, as multiple axis plots in holoviews currently require understanding of how bokeh handes multiple axis.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With