How to change linewidth when saving PDF figure - python

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')

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

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.

Matplotlib savefig to eps ignores visibility=False

I'm trying to save a streamline plot as an EPS, and then convert it to PDF using epstopdf, since this gives a much smaller filesize.
I use multiple subplots that share their x and y axis. I add one overarching subplot, so I can easily add a xlabel and ylabel. I set frameon=False, so it doesn't appear. Afterward, I set the spines and ticks of this axis off. When the figure is displayed, I do not see anything from the big axis. So far, so good.
The problem appears when I save the figure. Saving to EPS and then converting to PDF makes the ticklabels appear, and interfere with my text. Removing the ticklabels outright is also no good, since the spacing then places the labels among the ticklabels of the plots that I do want to see. Curiously, saving to pdf does not have this problem, but the file size is 11 times greater.
Does anyone know what I'm doing wrong, or what is going on?
Working example:
import matplotlib.pyplot as plt
import numpy as np
import subprocess
fig, ax = plt.subplots(2, 2, sharex=True, sharey=True)
ax = ax.flatten()
ax = np.append(ax, fig.add_subplot(1, 1, 1, frameon=False))
ax[-1].spines['top'].set_color('none')
ax[-1].spines['bottom'].set_color('none')
ax[-1].spines['left'].set_color('none')
ax[-1].spines['right'].set_color('none')
ax[-1].tick_params(
labelcolor='none', top='off', bottom='off', left='off', right='off')
ax[-1].set_xlabel('$u$', fontsize=14)
ax[-1].set_ylabel('$v$', fontsize=14)
plt.setp(ax[-1].get_xticklabels(), visible=False)
fig.savefig('TestPdf.pdf')
fig.savefig('TestEps.eps')
subprocess.check_call(['epstopdf', 'TestEps.eps'])
plt.show()
You can try some other backends. For examle pgf backend (available in matplotlib 1.3+) is also capable of producing pdf:
import matplotlib
matplotlib.use("pgf")
You can get a list of backends available with:
matplotlib.rcsetup.all_backends
And You can check wither backend supports eps or pdf with:
import matplotlib
matplotlib.use("BACKEND")
import matplotlib.pyplot as plt
fig = plt.figure()
print fig.canvas.get_supported_filetypes()

Matplotlib: Change math font size and then go back to default

I've learned from this question Matplotlib: Change math font size how to change the default size of math text in matplotlib. What I do is:
from matplotlib import rcParams
rcParams['mathtext.default'] = 'regular'
which effectively makes the LaTeX font the same size as the regular font.
What I don't know is how to reset this back to the default behaviour, ie: the LaTeX font looking a bit smaller than the regular font.
I need this because I want the LaTeX font to look the same size as the regular font only on one plot, not on all plots in my figure that use LaTex math formatting.
Here's a MWE of how I create my figure:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
# Generate random data.
x = np.random.randn(60)
y = np.random.randn(60)
fig = plt.figure(figsize=(5, 10)) # create the top-level container
gs = gridspec.GridSpec(6, 4) # create a GridSpec object
ax0 = plt.subplot(gs[0:2, 0:4])
plt.scatter(x, y, s=20, label='aaa$_{subaaa}$')
handles, labels = ax0.get_legend_handles_labels()
ax0.legend(handles, labels, loc='upper right', numpoints=1, fontsize=14)
plt.ylabel('A$_y$', fontsize=16)
plt.xlabel('A$_x$', fontsize=16)
ax1 = plt.subplot(gs[2:4, 0:4])
# I want equal sized LaTeX fonts only on this plot.
from matplotlib import rcParams
rcParams['mathtext.default'] = 'regular'
plt.scatter(x, y, s=20, label='bbb$_{subbbb}$')
handles, labels = ax1.get_legend_handles_labels()
ax1.legend(handles, labels, loc='upper right', numpoints=1, fontsize=14)
plt.ylabel('B$_y$', fontsize=16)
plt.xlabel('B$_x$', fontsize=16)
# If I set either option below the line that sets the LaTeX font as 'regular'
# is overruled in the entire plot.
#rcParams['mathtext.default'] = 'it'
#plt.rcdefaults()
ax2 = plt.subplot(gs[4:6, 0:4])
plt.scatter(x, y, s=20, label='ccc$_{subccc}$')
handles, labels = ax2.get_legend_handles_labels()
ax2.legend(handles, labels, loc='upper right', numpoints=1, fontsize=14)
plt.ylabel('C$_y$', fontsize=16)
plt.xlabel('C$_x$', fontsize=16)
fig.tight_layout()
out_png = 'test_fig.png'
plt.savefig(out_png, dpi=150)
plt.close()
I think this is because the mathtext.default setting is used when the Axes object is drawn, not when it's created. To walk around the problem we need the change the setting just before the Axes object is drawn, here is a demo:
# your plot code here
def wrap_rcparams(f, params):
def _f(*args, **kw):
backup = {key:plt.rcParams[key] for key in params}
plt.rcParams.update(params)
f(*args, **kw)
plt.rcParams.update(backup)
return _f
plt.rcParams['mathtext.default'] = 'it'
ax1.draw = wrap_rcparams(ax1.draw, {"mathtext.default":'regular'})
# save the figure here
Here is the output:
Another solution is to change the rcParams settings to force matplotlib to use tex for all the text (I'll not try to explain it because I only have a vague understanding of this setting). The idea is that by setting
mpl.rcParams['text.usetex']=True
You can pass string literals to any (or most of them?) text defining functions that will be passed to tex, so you can use most of its (dark) magic. For this case, it is enough to use the \tiny, \small, \normalsize, \large, \Large, \LARGE, \huge and \Huge font size size commands
In your MWE case it would be enough to change the second scatter line to
plt.scatter(x, y, s=20, label=r'bbb{\Huge$_{subbbb}$}')
to get a larger subscript font in the legend only in that case. All the other cases are handled correctly straight away
Here is a nice pythonic way to temporarily change rcParams and automatically change them back again. First we define a context manager that can be used with with:
from contextlib import contextmanager
#contextmanager
def temp_rcParams(params):
backup = {key:plt.rcParams[key] for key in params} # Backup old rcParams
plt.rcParams.update(params) # Change rcParams as desired by user
try:
yield None
finally:
plt.rcParams.update(backup) # Restore previous rcParams
Then we can use that to temporarily change the rcParams for one specific plot:
with temp_rcParams({'text.usetex': True, 'mathtext.default': 'regular'}):
plt.figure()
plt.plot([1,2,3,4], [1,4,9,16])
plt.show()
# Another plot with unchanged options here
plt.figure()
plt.plot([1,2,3,4], [1,4,9,16])
plt.show()
Result:

Categories