Hide legend in bokeh plot - python

LS,
Bokeh plot automatically generates a legend for a plot.
How can I hide (not show at all) the legend in a Bokeh plot ?
I tried: legend = 'none'. But no success.
Thanks

If I can just expand this a little - legend=False is the correct way to make the Bokeh legend invisible, but it's used within the creation of the plot itself, rather than being called as an attribute of the plot object. By which I mean, write
from bokeh.charts import Scatter
myPlot = Scatter(foo, bar, legend=False)
rather than
from bokeh.charts import Scatter
myPlot = Scatter(foo, bar)
myPlot.legend=False.

p1.line(x=data['col'].astype(str), y=data['col'],
color='black',legend_label='legend')
p1.legend.visible=False
The last line hides the legend.

Related

Matplotlib - plt.show() on chart object

I've got a really simple example here. I'm making a plot from an ax object returned from a function:
import matplotlib.pyplot as plt
x = ['a', 'b', 'c']
y = [1, 2, 3]
def make_plot():
fig, ax = plt.subplots()
ax.set_title('test bar chart')
return ax
chart = make_plot()
chart.bar(x, y)
plt.show() #why does this show the chart?
My question is about plt.show() at the end. Obviously, this shows the output of the chart. But why? To me it does not seem like plt.show() is tied to anything. I have my chart object, which contains my chart with all its parameters and whatnot. But how does plt.show() know to interact with this? It would seem more intuitive to have something like chart.show(), as chart is the class instance.
Good question! chart is an Axes created when you call plt.subplots(). Pyplot is what's called a "stateful" API. When you make calls to plt.* functions, it changes the internal state of pyplot and that can affect future calls. When you call plt.subplots(), it knows that there is a newly created Figure with newly created subplot Axes inside it.
Similarly, if you call plt.plot(), it has a stored record of what the most recent axes are, and plots the data on that plot.
Take a look at the pyplot sourcecode and you can see how it has a global manager variable that it uses to store the current plot, and updates that manager when you create a plot.
Additionally, there is Figure.show(). If you want to work around pyplot, it looks something like this

using sns.relplot.set to control aesthetics in seaborn

From How to set some xlim and ylim in Seaborn lmplot facetgrid I've find out that you can specify xlim by using
g = sns.relplot(data=df_weekday,
x="date", y="battery/min", kind="scatter", hue="version")
g.set(xlim=('2019-07-16','2019-11-06'))
This works perfectly however can I add more parameters inside,
instead of calling plt.xticks(rotation=90) and other controls on each line I want to include all inside g.set().
Furthermore when I try to spread my graph by setting height=10, aspect=2 however it makes all the fonts too small and having graph without height, aspect makes my graph too squished.
I want to add height, aspect and change all fonts also inside g.set() if possible.

Seaborn pairplot legend - how to control position

I would like to move the Seaborn pairplot legend outside the scatter plot matrix. The official docs don't give a keyword legend. I am trying to plot the legend outside the plot in 3 different locations simultaneously: bottom center, top center, right center
I have tried this:
import matplotlib.pyplot as plt
import seaborn as sns
iris = sns.load_dataset("iris")
g = sns.pairplot(iris,hue='species', palette='husl', markers='d', size=2.5, plot_kws=
{
"s":40,
"alpha":1.0,
'lw':0.5,
'edgecolor':'k'
})
plt.legend(loc='upper center', bbox_to_anchor=(1.10, 1.0), ncol=1) #vertical legend
plt.legend(loc='lower center', bbox_to_anchor=(0.0, -0.15), ncol=3) #horizontal legend bottom
plt.legend(loc='upper left', bbox_to_anchor=(0.0, 1.15), ncol=3) #horizontal legend top
g.savefig('Test.png', bbox_inches='tight')
The output of the above code is this file: .
All 3 legends are being printed incorrectly. I am not sure if it is doing the positioning that I requested. I am not sure what is happening on the right - it seems that 2 vertical legends are appearing and for some reason and they are fully overlapping each other. One of the two horizontal legends is not appearing at all.
Is there a way to avoid overlapping the vertical legends and also place the legend outside the plot in 3 locations - top center, bottom center, right center?
pairplot already adds a legend outside the plot matrix and it is not clear where you want to move it. The other legends are probably being positioned as you requested even if that might be not what you actually want.
plt.legend will attach a legend to the current axes and there can be only one legend per axes so in your code only the last legend is drawn. But you can attach several legends to a figure and IIUC you want to position the legends relative to the figure so figure legends seems like the best option.
In order to plot figure legends you need to explicitly pass the handlers and labels. I had to use the private attribute PairPlot._legend_data for that, I did not find a way to do it using the public API.
Unfortunately matplotlib won't automatically make room to acomodate these legends and they will overlap with the subplots unless you make some adjustments. I will just use subplot_adjust with some hardcoded values that work for me in this case because calculations are tricky.
So this is the code that hopefully does what you want:
g = sns.pairplot(iris, hue='species', palette='husl', markers='d', size=2.5, plot_kws=
{
"s":40,
"alpha":1.0,
'lw':0.5,
'edgecolor':'k'
})
handles = g._legend_data.values()
labels = g._legend_data.keys()
g.fig.legend(handles=handles, labels=labels, loc='upper center', ncol=1)
g.fig.legend(handles=handles, labels=labels, loc='lower center', ncol=3)
g.fig.legend(handles=handles, labels=labels, loc='upper left', ncol=3)
g.fig.subplots_adjust(top=0.92, bottom=0.08)
The OP asked in a comment whether this can be found in the seaborn documentation. Of course part of this is just pure matplotlib, not specific to seaborn. But I also realized that I had to rely on a couple of undocumented features.
The fact that PairGrid has a fig attribute referencing the Figure instance is not documented. Anyway that was an easy guess and I could have used fig = plt.gcf() instead.
Getting the labels and handles of the legend is trickier. I learnt about the _legend_data attribute by looking at the docstring of PairGrid.add_legend, but the docstring itself is a bit hidden (it does not appear in the web) and the attribute is underscored as if it were private, so using it feels uncomfortable. I find it inconsistent that a private attribute is mentioned in the docstring of a public method, probably we should have legend_data as a public attribute, but I digress.
Alternatively you could try to extract the labels and handles from the subplots. You could guess that the subplots have this information but there are no actual guaranties so this means relying on undocumented behaviour. It turns out that the non-diagonal subplots have it (but the diagonal ones don't, so if you just looked at the first subplot you would be misguided) and you can do handles, labels = fig.get_axes()[1].get_legend_handles_labels(). But as I said this is undocumented behaviour even if you are using only documented API.
You could also create your own handles but it would be cumbersome, that's why I looked for shortcuts, even if undocumented.
To control the position of the default pairplot legend:
g._legend.set_bbox_to_anchor((0.5, 0.5))
this helped me, it removes the default legend and there are a lot of settings here.
g = sns.pairplot(data, hue="Group")
sns.move_legend(g, "lower center",bbox_to_anchor=(0.5, -0.035), labels=labels, ncol=5, title='Group', frameon=False,)
This is actually a lot easier than you'd think it is. It's simply a default setting in the actual matplot output. Just go to the configure subplot button at the top of your output window, and lower your right border upper limit a little and it'll be fixed.

How to retrieve colorbar instance from figure in matplotlib

all. I want to update the colorbar of a figure when the imagedata is changed. So something like:
img = misc.lena()
fig = plt.figure()
ax = plt.imshow(im)
plt.colorbar(ax)
newimg = img+10*np.randn(512,512)
def update_colorbar(fig,ax,newimg):
cbar = fig.axes[1]
ax.set_data(newimg)
cbar.update_normal(ax)
plt.draw()
but it seems that returned results from fig.axes() does not have the colorbar instance like I expected. I can probably just pass the colorbar instance as an argument to the update function, but I thought just passing one fig parameter may be good enough. Can anyone explain a little bit on how to retrieve the colorbar from the figure? Or why 'fig.axes()' doesn't return the AxesImage or Colobar instance but just the Axes or AxesSubplot? I think I just need more understanding of the Axes/Figure stuff.Thank you!
Sometimes it can be useful to retrieve a colorbar even if it was not held in a variable.
In this case, it is possible to retrieve the colorbar from the plot with:
# Create an example image and colourbar
img = np.arange(20).reshape(5,4)
plt.imshow(img)
plt.colorbar()
# Get the current axis
ax = plt.gca()
# Get the images on an axis
im = ax.images
# Assume colorbar was plotted last one plotted last
cb = im[-1].colorbar
# Do any actions on the colorbar object (e.g. remove it)
cb.remove()
EDIT:
or, equivalently, the one liner:
plt.gca().images[-1].colorbar.remove()
N.B.: see also comments for the use of ax.collections[-1] instead of ax.images[-1]. For me it always worked only the first way, I don't know what depends on, maybe the type of data or plot.
Now you can operate on cb as if it were stored using commands described in the colorbar API. For instance you could change xlim or call update as explained in other comments. You could remove it with cb.remove() and recreate it with plt.colorbar().
plt.draw() or show should be called after to update plot.
As the image is the mappable associated to the colorbar and can be obtained with cb.mappable.
First off, I think you're getting a bit confused between the axes (basically, the plot), the figure, the scalar mappable (the image, in this case), and the colorbar instance.
The figure is the window that the plot is in. It's the top-level container.
Each figure usually has one or more axes. These are the plots/subplots.
Colorbars are also inside the figure. Adding a colorbar creates a new axes (unless you specify otherwise) for the colorbar to be displayed in. (It can't normally be displayed in the same axes as the image, because the colorbar needs to have its own x and y limits, etc.)
Some of your confusion is due to the fact that you're mixing the state-machine interface and the OO interface. It's fine to do this, but you need to understand the OO interface.
fig.axes[1] isn't the colorbar instance. It's the axes that the colorbar is plotted in. (Also, fig.axes[1] is just the second axes in the figure. It happens to be the axes that the colorbar is in for a figure with one subplot and one colorbar, but that won't generally be the case.)
If you want to update the colorbar, you'll need to hold on to the colorbar instance that colorbar returns.
Here's an example of how you'd normally approach things:
import matplotlib.pyplot as plt
import numpy as np
data = np.random.random((10,10)) # Generate some random data to plot
fig, ax = plt.subplots() # Create a figure with a single axes.
im = ax.imshow(data) # Display the image data
cbar = fig.colorbar(im) # Add a colorbar to the figure based on the image
If you're going to use update_normal to update the colorbar, it expects a ScalarMappable (e.g. an image created by imshow, the collection that scatter creates, the ContourSet that contour creates, etc) to be passed in. (There are other ways to do it, as well. Often you just want to update the limits, rather than the whole thing.) In the case of the code above, you'd call cbar.update_normal(im).
However, you haven't created a new AxesImage, you've just changed it's data. Therefore, you probably just want to do:
cbar.set_clim(newimg.min(), newimg.max())

Setting spines in matplotlibrc

For a strange reason I cannot find the way to specify spines configuration in Python's matplotlibrc file. Any idea on how to cause matplotlib not to draw upper and right spines by default?
(source: sourceforge.net)
More about info about spines in matplotlib is here
Thank you
In order to hide the right and top spines of a subplot, you need to both set the colour of the relevant spines to 'none', as well as set the tick position to 'left' for the xtick, and 'bottom' for the ytick (in order to hide the tick marks as well as the spines).
Unfortunately, none of these are currently accessible via matplotlibrc. The parameters specified in matplotlibrc are validated, and then stored in a dict called rcParams. It is then up to the individual modules to check for a key in this dict whose value will act as their default. If they don't check it for one of their options, that option is not alterable via the rc file.
Due to the nature of the rc system, and the way that spines are written, altering the code to allow for this would not be straightforward:
Spines currently obtain their colour through the same rc parameter used to define axis colours; you cannot set it to 'none' without hiding all of your axis drawing. They are also agnostic towards whether they are top, right, left, or bottom — these are really just four separate spines stored in a dict. The individual spine objects do not know what side of the plot they compose, so you cannot just add new rc params and assign the proper one during spine initialization.
self.set_edgecolor( rcParams['axes.edgecolor'] )
(./matplotlib/lib/matplotlib/spines.py, __init__(), line 54)
If you have a large amount of existing code, such that adding the axis parameters manually to each one would be too burdensome, you could alternately use a helper function to iterate through all of the Axis objects and set the values for you.
Here's an example:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import show
# Set up a default, sample figure.
fig = plt.figure()
x = np.linspace(-np.pi,np.pi,100)
y = 2*np.sin(x)
ax = fig.add_subplot(1,2,2)
ax.plot(x,y)
ax.set_title('Normal Spines')
def hide_spines():
"""Hides the top and rightmost axis spines from view for all active
figures and their respective axes."""
# Retrieve a list of all current figures.
figures = [x for x in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
for figure in figures:
# Get all Axis instances related to the figure.
for ax in figure.canvas.figure.get_axes():
# Disable spines.
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
# Disable ticks.
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
hide_spines()
show()
Just call hide_spines() before show(), and it will hide them in all of the figures that show() displays. I cannot think of a simpler way to alter a large number of figures, outside of spending the time to patch matplotlib and add in rc support for the needed options.
To make matplotlib not to draw upper and right spines, one can set the following in matplotlibrc file
axes.spines.right : False
axes.spines.top : False
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)

Categories