I have a Quad plot displaying 2 data-sets. I would like to add a legend to the plot, however I am not sure how to do this with the Quad glyph.
Previous examples have used 'legend' however this is now deprecated, and I've tried using
'legend_label' however this is does not work.
My ultimate goal is to use the legend to interactively display both datasets
# Convert dataframe to column data source
src1 = ColumnDataSource(Merged_Bins)
src2 = ColumnDataSource(Merged_Bins)
#------------------------------------------------------------------------------------------------
# Plot Histogram using Bokeh plotting library
#------------------------------------------------------------------------------------------------
plot = figure(y_range=Range1d(start=0, end=Max_Histogram_Value),sizing_mode="scale_width",width=3000,height= 600,
title= "Histogram Plot",
x_axis_label="Time (ms)",
y_axis_label="Count",toolbar_location = "below")
plot.yaxis.ticker = FixedTicker(ticks=list(tick_vals))
glyph1=Quad(bottom=0, top='Delay1', left='left1',
right='right1', fill_color='#FF7F00',
line_color='black', fill_alpha=0.7,line_alpha=0.5,name="Option 2")
glyph1_plot=plot.add_glyph(src1, glyph1)
glyph2=Quad(bottom=0, top='Delay2', left='left2',
right='right2', fill_color='#616261',
line_color='#616261',line_alpha=0.1, fill_alpha=0.1,name="Original Design")
plot.add_glyph(src2, glyph2)
# Add hover tool for when mouse is over data
hover1 = HoverTool(tooltips=[('Delay Envelope', '#Bin_interval'),('Count', '#Delay1'),('Count Original', '#Delay2')],mode='vline',renderers=[glyph1_plot])
plot.add_tools(hover1)
plot.legend.location = "top_left"
plot.legend.click_policy="hide"
# Set autohide to true to only show the toolbar when mouse is over plot
plot.toolbar.autohide = True
script, div = components(plot)
show(plot)
It works just fine if you use the Figure.quad method instead of manually calling Figure.add_glyph with an explicitly created instance of Quad. All legen_* arguments are parsed by glyph methods of the Figure class - the glyph classes themselves do not use them at all.
from bokeh.io import show
from bokeh.plotting import figure
p = figure()
p.quad(-1, 1, 1, -1, legend_label='Hello')
p.quad(1, 3, 3, 1, color='green', legend_label='there')
show(p)
Alternatively, if you really need the manual approach for some reason, you can also create a legend manually by creating an instance of the Legend class and by adding it to the figure with Figure.add_layout.
Also, on an unrelated note - your plot looks like it was created with vbar instead of quad because all bars seem to have the same width. If so, perhaps using vbar would be simpler in your case.
Related
How to change the font size of the axis markers and make that bold for the following figure ? Figure is plotted with the bokeh library in python. Is there a way to get the attributes of the images like gcf where we can append the new values ?
You need the properties of the axis that have names starting with major_label_text_. The documentation: https://docs.bokeh.org/en/latest/docs/reference/models/axes.html#bokeh.models.axes.Axis.major_label_standoff
Some example:
from bokeh.plotting import figure, show
p1 = figure()
p1.xaxis.major_label_text_color = 'red'
p1.xaxis.major_label_text_font_size = '20px'
p1.line([0, 1], [0, 1])
show(p1)
How do I add y-limits (ylim) to a plot created using Holoviews Datashader?
I have tried the hv.Dimension function and also adding ylim=() parameters but it would either be the Holoview that rejects it or the Datashader function that doesn't understand the parameter.
plot_Z1 = datashade(hv.Curve(df).redim(y=hv.Dimension('y', range=(-50,50))))
plot_Z2 = datashade(hv.Curve(df).redim(y=hv.Dimension('y', range=(-50,50))))
plot_Z1.options(width=500) + plot_Z2.options(width=500)
ylim isn't recognized and hv.Dimension has no effect
It would appear that I cannot use ylim and shared_axes together with Holoview datashading. At least not in the sense where shared_axes works properly that it will zoom in/out on all subplots together. If I stick to just Holoviews either it won't apply the ylim or the shared_axes won't zoom in/out on all subplots (only one plot with zoom while others stay still).
The only way I found to get shared_axes working properly together with a ylim parameter is using HVPLOT instead.
plot_1 = df.hvplot(y='Something', width=200, datashade=True)
plot_2 = df.hvplot(y='Something Else', width=200, ylim=(-50, 50), datashade=True)
plot = (plot_1 + plot_2.options(shared_axes=True)).cols(1)
plot
For python relplot, how to control the location of legend and add a plot title? I tried plt.title('title') but it doesn't work.
import seaborn as sns
dots = sns.load_dataset("dots")
# Plot the lines on two facets
sns.relplot(x="time", y="firing_rate",
hue="coherence", size="choice", col="align",
size_order=["T1", "T2"],
height=5, aspect=.75, facet_kws=dict(sharex=False),
kind="line", legend="full", data=dots)
A typical way of changing the location of a legend in matplotlib is to use the arguments loc and bbox_to_anchor.
In Seaborn's relplot a FacetGrid object is returned. In order to get the legend object we can use _legend. We can then set the loc and bbox_to_anchor:
g = sns.relplot(...)
leg = g._legend
leg.set_bbox_to_anchor([0.5, 0.5]) # coordinates of lower left of bounding box
leg._loc = 2 # if required you can set the loc
To understand the arguments of bbox_to_anchor see What does a 4-element tuple argument for 'bbox_to_anchor' mean in matplotlib?
The same can be applied to the title. The matplotlib argument is suptitle. But we need the figure object. So we can use
g.fig.suptitle("My Title")
Putting this all together:
import seaborn as sns
dots = sns.load_dataset("dots")
# Plot the lines on two facets
g = sns.relplot(x="time", y="firing_rate",
hue="coherence", size="choice", col="align",
size_order=["T1", "T2"],
height=5, aspect=.75, facet_kws=dict(sharex=False),
kind="line", legend="full", data=dots)
g.fig.suptitle("My Title")
leg = g._legend
leg.set_bbox_to_anchor([1,0.7]) # change the values here to move the legend box
# I am not using loc in this example
Update
You can change the position of the title by providing the x and y coordinates (figure coordinates) so that it doesn't overlap the subplot titles
g.fig.suptitle("My Title", x=0.4, y=0.98)
Although I would probably move your subplots down slightly and leave the figure title where it is using:
plt.subplots_adjust(top=0.85)
I'm running the code from the top answer to this question in an IPython/Jupyter notebook. The first time I run it, it displays properly:
If I change any parameter, no matter how inconsequential (e.g. a single quote to a double quote), when I run the cell again, or when I run the same code in another cell, the following appears:
It looks like it's recursively putting the desired 2x1 grid of subplots inside a new 2x1 grid of subplots. I tried adding bk_plotting.reset_output(), but it had no effect.
Here's the cell code:
import numpy as np
import bokeh.plotting as bk_plotting
import bokeh.models as bk_models
# for the ipython notebook
bk_plotting.output_notebook()
# a random dataset
data = bk_models.ColumnDataSource(data=dict(x=np.arange(10),
y1=np.random.randn(10),
y2=np.random.randn(10)))
# defining the range (I tried with start and end instead of sources and couldn't make it work)
x_range = bk_models.DataRange1d(sources=[data.columns('x')])
y_range = bk_models.DataRange1d(sources=[data.columns('y1', 'y2')])
# create the first plot, and add a the line plot of the column y1
p1 = bk_models.Plot(x_range=x_range,
y_range=y_range,
title="",
min_border=2,
plot_width=250,
plot_height=250)
p1.add_glyph(data,
bk_models.glyphs.Line(x='x',
y='y1',
line_color='black',
line_width=2))
# add the axes
xaxis = bk_models.LinearAxis()
p1.add_layout(xaxis, 'below')
yaxis = bk_models.LinearAxis()
p1.add_layout(yaxis, 'left')
# add the grid
p1.add_layout(bk_models.Grid(dimension=1, ticker=xaxis.ticker))
p1.add_layout(bk_models.Grid(dimension=0, ticker=yaxis.ticker))
# add the tools
p1.add_tools(bk_models.PreviewSaveTool())
# create the second plot, and add a the line plot of the column y2
p2 = bk_models.Plot(x_range=x_range,
y_range=y_range,
title="",
min_border=2,
plot_width=250,
plot_height=250)
p2.add_glyph(data,
bk_models.glyphs.Line(x='x',
y='y2',
line_color='black',
line_width=2))
# add the x axis
xaxis = bk_models.LinearAxis()
p2.add_layout(xaxis, 'below')
# add the grid
p2.add_layout(bk_models.Grid(dimension=1, ticker=xaxis.ticker))
p2.add_layout(bk_models.Grid(dimension=0, ticker=yaxis.ticker))
# add the tools again (it's only displayed if added to each chart)
p2.add_tools(bk_models.PreviewSaveTool())
# display both
gp = bk_plotting.GridPlot(children=[[p1, p2]])
bk_plotting.show(gp)
It is important to specify versions in reports such as this. This bug was noted in this GitHub issue (detailed comment), with a fix recorded as being in put in place in this Pull Request before the 0.8.2 release. I cannot reproduce the problem in a clean Bokeh 0.8.2 conda environent (python3, jupiter/ipython 3.10, OSX+safari) with the code above. If you can still reproduce this problem with Bokeh >= 0.8.2, please file a bug report on Bokeh issue tracker with as much information above versions, platform, etc. as possible.
I use the matplotlib library for plotting data in python. In my figure I also have some text to distinguish the data. The problem is that the text goes over the border in the figure window. Is it possible to make the border of the plot cut off the text at the corresponding position and only when I pan inside the plot the the rest of the text gets visible (but only when inside plot area). I use the text() function to display the text
[EDIT:]
The code looks like this:
fig = plt.figure()
ax = fig.add_subplot(111)
# ...
txt = ax.text(x, y, n, fontsize=10)
txt.set_clip_on(False) # I added this due to the answer from tcaswell
I think that your text goes over the border because you didn't set the limits of your plot.
Why don't you try this?
fig=figure()
ax=fig.add_subplot(1,1,1)
text(0.1, 0.85,'dummy text',horizontalalignment='left',verticalalignment='center',transform = ax.transAxes)
This way your text will always be inside the plot and its left corner will be at point (0.1,0.85) in units of your plot.
You just need to tell the text artists to not clip:
txt = ax.text(...)
txt.set_clip_on(False) # this will turn clipping off (always visible)
# txt.set_clip_on(True) # this will turn clipping on (only visible when text in data range)
However, there is a bug matplotlib (https://github.com/matplotlib/matplotlib/pull/1885 now fixed) which makes this not work. The other way to do this (as mentioned in the comments) is
to use
txt = ax.text(..., clip_on=True)