Adjust padding inside matplotlib annotation box - python

I'm using the annotate method on an Axes object to add an arrow with text to a plot. For example:
ax.annotate('hello world,
xy=(1, 1),
xycoords='data',
textcoords='data',
fontsize=12,
backgroundcolor='w',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3")
This works well but I want to decrease the padding on the inside of the annotation box. Essentially, I want to make the box 'squeeze' tighter around the text. Is there any way to do this via the arrowprops or bbox_props kwargs?
I'm looking for something like borderpad that's available on legends, similar to what's discussed on this answer.

Yes, but you'll need to switch to a slightly different way of specifying the box. The "basic" box doesn't support it, so you need to have annotate make a FancyBboxPatch associated with the text object. (The same syntax for a "fancy" box would work text placed with ax.text as well, for what it's worth.)
Also, before we go much farther, there are a couple of rather thorny bugs that affect this in the current version of matplotlib (1.4.3). (e.g. https://github.com/matplotlib/matplotlib/issues/4139 and https://github.com/matplotlib/matplotlib/issues/4140)
If you're seeing things like this:
Instead of this:
You might consider downgrading to matplotlib 1.4.2 until the issue is fixed.
Let's take your example as a starting point. I've changed the background color to red and put it in the center of the figure to make it a touch easier to see. I'm also going to leave off the arrow (avoiding the bug above) and just use ax.text instead of annotate.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
a = ax.text(0.5, 0.5, 'hello world',
fontsize=12,
backgroundcolor='red')
plt.show()
To be able to change the padding, you'll need to use the bbox kwarg to text (or annotate). This makes the text use a FancyBboxPatch, which supports padding (along with several other things).
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
a = ax.text(0.5, 0.5, 'hello world', fontsize=12,
bbox=dict(boxstyle='square', fc='red', ec='none'))
plt.show()
The default padding is pad=0.3. (If I recall correctly, the units are fractions of the height/width of the text's extent.) If you'd like to increase it, use boxstyle='square,pad=<something_larger>':
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
a = ax.text(0.5, 0.5, 'hello world', fontsize=12,
bbox=dict(boxstyle='square,pad=1', fc='red', ec='none'))
plt.show()
Or you can decrease it by putting in 0 or a negative number to shrink it farther:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
a = ax.text(0.5, 0.5, 'hello world', fontsize=12,
bbox=dict(boxstyle='square,pad=-0.3', fc='red', ec='none'))
plt.show()

Related

How i can delete xlabel of plot? [duplicate]

I'm trying to plot a figure without tickmarks or numbers on either of the axes (I use axes in the traditional sense, not the matplotlib nomenclature!). An issue I have come across is where matplotlib adjusts the x(y)ticklabels by subtracting a value N, then adds N at the end of the axis.
This may be vague, but the following simplified example highlights the issue, with '6.18' being the offending value of N:
import matplotlib.pyplot as plt
import random
prefix = 6.18
rx = [prefix+(0.001*random.random()) for i in arange(100)]
ry = [prefix+(0.001*random.random()) for i in arange(100)]
plt.plot(rx,ry,'ko')
frame1 = plt.gca()
for xlabel_i in frame1.axes.get_xticklabels():
xlabel_i.set_visible(False)
xlabel_i.set_fontsize(0.0)
for xlabel_i in frame1.axes.get_yticklabels():
xlabel_i.set_fontsize(0.0)
xlabel_i.set_visible(False)
for tick in frame1.axes.get_xticklines():
tick.set_visible(False)
for tick in frame1.axes.get_yticklines():
tick.set_visible(False)
plt.show()
The three things I would like to know are:
How to turn off this behaviour in the first place (although in most cases it is useful, it is not always!) I have looked through matplotlib.axis.XAxis and cannot find anything appropriate
How can I make N disappear (i.e. X.set_visible(False))
Is there a better way to do the above anyway? My final plot would be 4x4 subplots in a figure, if that is relevant.
Instead of hiding each element, you can hide the whole axis:
frame1.axes.get_xaxis().set_visible(False)
frame1.axes.get_yaxis().set_visible(False)
Or, you can set the ticks to an empty list:
frame1.axes.get_xaxis().set_ticks([])
frame1.axes.get_yaxis().set_ticks([])
In this second option, you can still use plt.xlabel() and plt.ylabel() to add labels to the axes.
If you want to hide just the axis text keeping the grid lines:
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
Doing set_visible(False) or set_ticks([]) will also hide the grid lines.
If you are like me and don't always retrieve the axes, ax, when plotting the figure, then a simple solution would be to do
plt.xticks([])
plt.yticks([])
I've colour coded this figure to ease the process.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
You can have full control over the figure using these commands, to complete the answer I've add also the control over the spines:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# X AXIS -BORDER
ax.spines['bottom'].set_visible(False)
# BLUE
ax.set_xticklabels([])
# RED
ax.set_xticks([])
# RED AND BLUE TOGETHER
ax.axes.get_xaxis().set_visible(False)
# Y AXIS -BORDER
ax.spines['left'].set_visible(False)
# YELLOW
ax.set_yticklabels([])
# GREEN
ax.set_yticks([])
# YELLOW AND GREEN TOGHETHER
ax.axes.get_yaxis().set_visible(False)
I was not actually able to render an image without borders or axis data based on any of the code snippets here (even the one accepted at the answer). After digging through some API documentation, I landed on this code to render my image
plt.axis('off')
plt.tick_params(axis='both', left=False, top=False, right=False, bottom=False, labelleft=False, labeltop=False, labelright=False, labelbottom=False)
plt.savefig('foo.png', dpi=100, bbox_inches='tight', pad_inches=0.0)
I used the tick_params call to basically shut down any extra information that might be rendered and I have a perfect graph in my output file.
Somewhat of an old thread but, this seems to be a faster method using the latest version of matplotlib:
set the major formatter for the x-axis
ax.xaxis.set_major_formatter(plt.NullFormatter())
One trick could be setting the color of tick labels as white to hide it!
plt.xticks(color='w')
plt.yticks(color='w')
or to be more generalized (#Armin Okić), you can set it as "None".
When using the object oriented API, the Axes object has two useful methods for removing the axis text, set_xticklabels() and set_xticks().
Say you create a plot using
fig, ax = plt.subplots(1)
ax.plot(x, y)
If you simply want to remove the tick labels, you could use
ax.set_xticklabels([])
or to remove the ticks completely, you could use
ax.set_xticks([])
These methods are useful for specifying exactly where you want the ticks and how you want them labeled. Passing an empty list results in no ticks, or no labels, respectively.
You could simply set xlabel to None, straight in your axis. Below an working example using seaborn
from matplotlib import pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set(xlabel=None)
plt.show()
Just do this in case you have subplots
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
ax[0].set_yticklabels([]) # x-axis
ax[0].set_xticklabels([]) # y-axis

Remove text from figure when using dataframe.boxplot(by=...) [duplicate]

I'm trying to plot a figure without tickmarks or numbers on either of the axes (I use axes in the traditional sense, not the matplotlib nomenclature!). An issue I have come across is where matplotlib adjusts the x(y)ticklabels by subtracting a value N, then adds N at the end of the axis.
This may be vague, but the following simplified example highlights the issue, with '6.18' being the offending value of N:
import matplotlib.pyplot as plt
import random
prefix = 6.18
rx = [prefix+(0.001*random.random()) for i in arange(100)]
ry = [prefix+(0.001*random.random()) for i in arange(100)]
plt.plot(rx,ry,'ko')
frame1 = plt.gca()
for xlabel_i in frame1.axes.get_xticklabels():
xlabel_i.set_visible(False)
xlabel_i.set_fontsize(0.0)
for xlabel_i in frame1.axes.get_yticklabels():
xlabel_i.set_fontsize(0.0)
xlabel_i.set_visible(False)
for tick in frame1.axes.get_xticklines():
tick.set_visible(False)
for tick in frame1.axes.get_yticklines():
tick.set_visible(False)
plt.show()
The three things I would like to know are:
How to turn off this behaviour in the first place (although in most cases it is useful, it is not always!) I have looked through matplotlib.axis.XAxis and cannot find anything appropriate
How can I make N disappear (i.e. X.set_visible(False))
Is there a better way to do the above anyway? My final plot would be 4x4 subplots in a figure, if that is relevant.
Instead of hiding each element, you can hide the whole axis:
frame1.axes.get_xaxis().set_visible(False)
frame1.axes.get_yaxis().set_visible(False)
Or, you can set the ticks to an empty list:
frame1.axes.get_xaxis().set_ticks([])
frame1.axes.get_yaxis().set_ticks([])
In this second option, you can still use plt.xlabel() and plt.ylabel() to add labels to the axes.
If you want to hide just the axis text keeping the grid lines:
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
Doing set_visible(False) or set_ticks([]) will also hide the grid lines.
If you are like me and don't always retrieve the axes, ax, when plotting the figure, then a simple solution would be to do
plt.xticks([])
plt.yticks([])
I've colour coded this figure to ease the process.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
You can have full control over the figure using these commands, to complete the answer I've add also the control over the spines:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# X AXIS -BORDER
ax.spines['bottom'].set_visible(False)
# BLUE
ax.set_xticklabels([])
# RED
ax.set_xticks([])
# RED AND BLUE TOGETHER
ax.axes.get_xaxis().set_visible(False)
# Y AXIS -BORDER
ax.spines['left'].set_visible(False)
# YELLOW
ax.set_yticklabels([])
# GREEN
ax.set_yticks([])
# YELLOW AND GREEN TOGHETHER
ax.axes.get_yaxis().set_visible(False)
I was not actually able to render an image without borders or axis data based on any of the code snippets here (even the one accepted at the answer). After digging through some API documentation, I landed on this code to render my image
plt.axis('off')
plt.tick_params(axis='both', left=False, top=False, right=False, bottom=False, labelleft=False, labeltop=False, labelright=False, labelbottom=False)
plt.savefig('foo.png', dpi=100, bbox_inches='tight', pad_inches=0.0)
I used the tick_params call to basically shut down any extra information that might be rendered and I have a perfect graph in my output file.
Somewhat of an old thread but, this seems to be a faster method using the latest version of matplotlib:
set the major formatter for the x-axis
ax.xaxis.set_major_formatter(plt.NullFormatter())
One trick could be setting the color of tick labels as white to hide it!
plt.xticks(color='w')
plt.yticks(color='w')
or to be more generalized (#Armin Okić), you can set it as "None".
When using the object oriented API, the Axes object has two useful methods for removing the axis text, set_xticklabels() and set_xticks().
Say you create a plot using
fig, ax = plt.subplots(1)
ax.plot(x, y)
If you simply want to remove the tick labels, you could use
ax.set_xticklabels([])
or to remove the ticks completely, you could use
ax.set_xticks([])
These methods are useful for specifying exactly where you want the ticks and how you want them labeled. Passing an empty list results in no ticks, or no labels, respectively.
You could simply set xlabel to None, straight in your axis. Below an working example using seaborn
from matplotlib import pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set(xlabel=None)
plt.show()
Just do this in case you have subplots
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
ax[0].set_yticklabels([]) # x-axis
ax[0].set_xticklabels([]) # y-axis

How to change linewidth when saving PDF figure

I need to generate a figure in PDF format for better quality (eps would still be good). In the figure, I need an oscillatory background, which is the actual signal, with a thin linewidth and a thicker (3 times) moving average filter line in the middle.
If I save the image in .png the desired linewidths are kept, the same does not apply for .pdf, .eps and .pgf, for which there seems to be a minimum linewidth, even though I didn't find anything in this sense on the docs. I'm running on Spyder with Python 3.7.3, updated matplotlib. I tried both to specify the linewidth into the ax.plot() call and also from rcParams but the behavior does not change: only .png keeps the correct format.
I attach the function which does the actual plot:
import matplotlib.pyplot as plt
import numpy as np
def data_plot(xaxis, sequences, xlabel, ylabel, legend, widths,
alphas, colors, fil):
n = len(sequences)
fig, ax = plt.subplots(figsize=(10,6))
for i in range(n):
ax.plot(xaxis, sequences[i], linewidth=widths[i], alpha=alphas[i],
color=colors[i])
ax.plot(xaxis[int(fil/2)-1:len(xaxis)-int(fil/2)],
np.convolve(sequences[i],np.ones(fil)/fil,mode='valid'),
linewidth=4*widths[i], alpha=2*alphas[i], color=colors[i],
label=legend[i])
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.legend(loc='upper right', prop={'size': 12})
plt.show()
seq = [np.random.uniform(12,14,500), np.random.uniform(11,12.5,500),
np.random.uniform(12.7,15,500)]
x = np.linspace(1,100,500)
data_plot(x,seq,'x','y',['A','B','C'],
[0.2,0.2,0.2],[0.5,0.4,0.3],['C0','C1','C2'],10)
plt.savefig('figure.png')
plt.savefig('figure.pdf')
In case others land here, I had the same issue and it turned out it was due to the PDF viewer. Specifically, with Foxit Reader Version 11.0.0.49893; see here. After uninstalling and downgrading to 10.1.3 linewidths display properly. So please check with another viewer.
However, this is not the problem with OP's script. Running it saves blank images. This is because of the plt.show called before plt.savefig; see here for explanation.
You could, for example, move the savefig inside the function, or return the fig. The linewidths will then show correctly on the saved PDF/PNG files: saved plot
Pasting code below. *Increased linewidth from '4' to '20' for emphasis.
import matplotlib.pyplot as plt
import numpy as np
def data_plot(xaxis, sequences, xlabel, ylabel, legend, widths,
alphas, colors, fil):
n = len(sequences)
fig, ax = plt.subplots(figsize=(10,6))
for i in range(n):
ax.plot(xaxis, sequences[i], linewidth=widths[i], alpha=alphas[i],
color=colors[i])
ax.plot(xaxis[int(fil/2)-1:len(xaxis)-int(fil/2)],
np.convolve(sequences[i],np.ones(fil)/fil,mode='valid'),
linewidth=20*widths[i], alpha=2*alphas[i], color=colors[i],
label=legend[i])
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.legend(loc='upper right', prop={'size': 12})
plt.show()
return fig
seq = [np.random.uniform(12,14,500), np.random.uniform(11,12.5,500),
np.random.uniform(12.7,15,500)]
x = np.linspace(1,100,500)
fig = data_plot(x,seq,'x','y',['A','B','C'],
[0.2,0.2,0.2],[0.5,0.4,0.3],['C0','C1','C2'],10)
fig.savefig('figure.png')
fig.savefig('figure.pdf')

Add text on matplotlib graph in the loc = "best" fashion (not in data coordinate) [duplicate]

Is there a way of telling pyplot.text() a location like you can with pyplot.legend()?
Something like the legend argument would be excellent:
plt.legend(loc="upper left")
I am trying to label subplots with different axes using letters (e.g. "A","B"). I figure there's got to be a better way than manually estimating the position.
Thanks
Just use annotate and specify axis coordinates. For example, "upper left" would be:
plt.annotate('Something', xy=(0.05, 0.95), xycoords='axes fraction')
You could also get fancier and specify a constant offset in points:
plt.annotate('Something', xy=(0, 1), xytext=(12, -12), va='top'
xycoords='axes fraction', textcoords='offset points')
For more explanation see the examples here and the more detailed examples here.
I'm not sure if this was available when I originally posted the question but using the loc parameter can now actually be used. Below is an example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnchoredText
# make some data
x = np.arange(10)
y = x
# set up figure and axes
f, ax = plt.subplots(1,1)
# loc works the same as it does with figures (though best doesn't work)
# pad=5 will increase the size of padding between the border and text
# borderpad=5 will increase the distance between the border and the axes
# frameon=False will remove the box around the text
anchored_text = AnchoredText("Test", loc=2)
ax.plot(x,y)
ax.add_artist(anchored_text)
plt.show()
The question is quite old but as there is no general solution to the problem till now (2019) according to Add loc=best kwarg to pyplot.text(), I'm using legend() and the following workaround to obtain auto-placement for simple text boxes:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpl_patches
x = np.linspace(-1,1)
fig, ax = plt.subplots()
ax.plot(x, x*x)
# create a list with two empty handles (or more if needed)
handles = [mpl_patches.Rectangle((0, 0), 1, 1, fc="white", ec="white",
lw=0, alpha=0)] * 2
# create the corresponding number of labels (= the text you want to display)
labels = []
labels.append("pi = {0:.4g}".format(np.pi))
labels.append("root(2) = {0:.4g}".format(np.sqrt(2)))
# create the legend, supressing the blank space of the empty line symbol and the
# padding between symbol and label by setting handlelenght and handletextpad
ax.legend(handles, labels, loc='best', fontsize='small',
fancybox=True, framealpha=0.7,
handlelength=0, handletextpad=0)
plt.show()
The general idea is to create a legend with a blank line symbol and to remove the resulting empty space afterwards. How to adjust the size of matplotlib legend box? helped me with the legend formatting.

Matplotlib text transparency

I was wondering if it is possible to change the transparency of a text in Matplotlib. set_alpha does not function and in the documentation I couldn't find anything relevant. Are there may be any workarounds?
I want to connect it to a pick_event.
EDIT:
I was actually trying to change the transparency of a legend-text. Although I tried to solve the issue with set_alpha, I have overseen that I was trying to modify the transparency of a list and hence I couldn't succeed. To sum up, as can be seen from answers, the transparency can be modified with set_alpha
You can set alpha when using annotate to add the text to your figure.
Before:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.annotate("TESTING", xy=(.5, .5), xytext=(.5, .5))
plt.show()
After:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
text = ax.annotate("TESTING", xy=(.5, .5), xytext=(.5, .5))
text.set_alpha(.4)
plt.show()
If you want to set alpha on the legend text, you should have said so:
ax.plot([1,2,3], [4,5,6], label='Null')
leg = ax.legend()
# print dir(leg) # inspection
for _txt in leg.texts:
_txt.set_alpha(0.3)
Side note: Because I can never remember where exactly to find things in the mpl docs, I inspected the legend object. Attribute texts sounded useful.

Categories