Matplotlib savefig to eps ignores visibility=False - python

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

Related

Matplotlib in VsCode displays graphs using 1e6 and integers rather than floats needed for my current exercise. (Python Crash Course)

I've got an issue with matplotlib and the way it displays graphs.
In my Python Crash Course coursebook, one of early graphs is meant to display up to 1000 on the x axis, and up to 1,000,000 on the y axis. Instead it displays a float of up to 2.0, and 1e6 at the top.
I use VSCode. I worry I haven't properly configured it. When displaying the course materials made by the developer, I have the same problem.
Here's the graph I want.
Here's the graph I've got.
And here's my code.
import matplotlib.pyplot as plt
x_values = range(1, 1001)
y_values = [x**2 for x in x_values]
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues, s=10)
# Set chart title and label axes.
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# Set size of tick labels.
ax.tick_params(axis='both', which='major', labelsize=14)
# Set the range for each axis.
ax.axis([0, 1100, 0, 1100000])
plt.show()
If anyone has any experience with this, please let me know. I'm happy to change to another IDE that displays this properly, any recommendations would be welcome.
This is default matplotlib behaviour. You can turn this off by creating a custom ScalarFormatter object and turning scientific notation off. For more details, see the matplotlib documentation pages on tick formatters and on ScalarFormatter.
# additional import statement at the top
import matplotlib.pyplot as plt
from matplotlib import ticker
# additional code before plt.show()
formatter = ticker.ScalarFormatter()
formatter.set_scientific(False)
ax.yaxis.set_major_formatter(formatter)
Note that, most likely, the axis label will be slightly cut off. One way to fix this is by adding fig.tight_layout() before plt.show().
Responding to an old question in case it helps someone, but place the following before "plt.show()"
ax.ticklabel_format(style='plain')

about .show() of matplotlib

I am working with matplotlib to generate some graphs but I do not know the difference between these two ways of showing an image. I already read some documentation about it but I do not understand yet.
First way:
import matplotlib.pyplot as plt
plt.figure()
plt.plot(x, y)
plt.show()
Second way:
import matplotlib.pyplot as plt
graph = plt.figure()
plt.plot(x, y)
graph.show()
I think this two ways do not do the same thing but it is not clear to me.
Could someone explain it step by step for the two ways?
Simplified, plt.show() will start an event loop and create a graphical representation for each figure that is active inside the pyplot state.
In contrast, fig.show(), where fig is a figure instance, would show only this figure. Since it would also not block, it is (only) useful in interactive sessions; else the figure would be closed directly after showing it due to the script exiting.
In the usual case you would hence prefer plt.show(). This does not prevent you from using the object-oriented interface. A recommended way of creating and showing a figure is hence,
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
For two windows you can just repeat the plotting,
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots()
ax1.plot(x1, y1)
fig2, ax2 = plt.subplots()
ax2.plot(x2, y2)
plt.show()
Matplotlib has two styles of API implemented. One is object based (graph.show()) and the other is procedural (plt.show()) and looks a lot like the Matlab plotting API.
The procedural API works on the current figure and/or set of axes. You can always getting the current figure with plt.gcf() and the current axes with plt.gca().
There are occasionally some slight differences in syntax here and there. For example, if you want to set the x axis limits:
plt.xlim([0, 10])
or
ax = plt.gca()
ax.set_xlim([0, 10])
plt.figure returns an object that is assigned with graph = plt.figure() to graph . this is used when specific characteristics of this object ( the plot ) are intended to be changed, now the object can be refered to by its instance graph ( object-based plotting )
you use this i.e. if you want to access the axes of the graph or labels, subplots, ...
see https://python4mpia.github.io/plotting/advanced.html for object-based plotting
to manipulate the plot object you have to get a reference to it ( handle ) and this is done by graph = plt.figure() ( cf Object-Oriented Programming )

use of tight_layout() in matplotlib with incomplete axis array:

I am creating PDFs of an array of axes. Sometimes the page is not full, ie not all axes have data. In this case, I want the unused axes not to show on the PDF. But I want the layout to be the same as if they were being used. I'm using tight_layout() to get non-overlapping axes and ylabels.
The following code shows first the case where the axes are used, then what happens if I delete the unused ones (tight_layout does not work properly), and then, if I instead just set them not to be visible, tight_layout() fails with a
AttributeError: 'NoneType' object has no attribute 'is_bbox'
error.
import numpy as np
import matplotlib.pyplot as plt
def prep_figure():
plt.close('all')
fig, axs = plt.subplots(4,3, figsize=(11,8.5))
axs=np.concatenate(axs)
for ii in range(5):
axs[ii].plot([1,2,3],[-10,-1,-10])
axs[ii].set_ylabel('ylabel')
axs[ii].set_xlabel('xlabel')
return fig,axs
fig,axs=prep_figure()
plt.tight_layout()
plt.show()
plt.savefig('tmp.pdf', )
# Try deleting extra axes
fig,axs=prep_figure()
for ii in range(5,12):
fig.delaxes(axs[ii])
plt.tight_layout()
plt.show()
plt.savefig('tmpd.pdf', )
# Try hiding extra axes
fig,axs=prep_figure()
for ii in range(5,12):
axs[ii].set_visible(False)
plt.tight_layout()
plt.show()
plt.savefig('tmph.pdf', )
I want the layout of the first version, but without the extra axes visible.
You could create the axes independently of the figure. I would also recommend this method because you have more control over the axes, for example you could have different shaped axes.
Code:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
for ii in range(5):
ax = fig.add_subplot(4,3,ii+1)
ax.scatter(np.random.random(5),np.random.random(5))
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')
fig.tight_layout()
fig.show()
Result:
The second case of deleting the axes works fine if it is used on its own (without the code from the first case executed) and if the figure is first saved and then shown,
fig,axs=prep_figure()
for ii in range(5,12):
fig.delaxes(axs[ii])
plt.tight_layout()
plt.savefig('tmpd.pdf', )
plt.show()
The third case works fine if again, the figure is saved before showing it, and instead of making it invisible, turning the axes off via ax.axis("off").
fig,axs=prep_figure()
for ii in range(5,12):
axs[ii].axis("off")
plt.tight_layout()
plt.savefig('tmph.pdf', )
plt.show()
The created pdf is the same in both cases:

inset imshow within figure matplotlib

I'm trying to have an imshow plot inset into another in matplotlib.
I have a figure that has an plot on top and an imshow on the bottom:
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
plt.plot()
ax2 = fig.add_subplot(2,1,2)
plt.imshow()
and then I have another figure, which also is comprised of a plot on top and an imshow below. I want this second figure to be inset into the top plot on the first figure.
I tried follow this example. The code ran without an error but I had an empty axis in the position I wanted it.
My problem is just that I'm not sure where to put the plt.setp() command If that's what I am supposed to use.
First, I don't think you can put a figure into a figure in matplotlib. You will have to arrange your Axes objects (subplots) to achieve the look you want.
The example you provided uses absolute positioning to do that. setp there is not related to positioning, though — it just removes axis ticks from insets. An example of code that does what you want:
import numpy
import matplotlib.pyplot as plt
x = numpy.linspace(0, 1)
xx, yy = numpy.meshgrid(x, x)
im = numpy.sin(xx) + numpy.cos(yy)**2
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(x, x**2)
ax2 = fig.add_subplot(2,1,2)
ax2.imshow(im)
inset1 = fig.add_axes([.15, .72, .15, .15])
inset1.plot(x, x**2)
plt.setp(inset1, xticks=[], yticks=[])
inset2 = fig.add_axes([0.15, 0.55, .15, .15])
inset2.imshow(im)
plt.setp(inset2, xticks=[], yticks=[])
fig.savefig('tt.png')
Here the insets use explicit positioning with coordinates given in "figure units" (the whole figure has size 1 by 1).
Now, of course, there's plenty of room for improvement. You probably want the widths of your plots to be equal, so you'll have to:
specify the positioning of all subplots explicitly; or
play with aspect ratios; or
use two GridSpec objects (this way you'll have the least amount of magic numbers and manual adjustment)

matplotlib scatter plot change distance in x-axis

I want to plot some Data with Matplotlib scatter plot.
I used the following code to plot the Data as a scatter with using the same axes for the different subplots.
import numpy as np
import matplotlib.pyplot as plt
epsilon= np.array([1,2,3,4,5])
f, (ax1, ax2, ax3, ax4) = plt.subplots(4, sharex= True, sharey=True)
ax1.scatter(epsilon, mean_percent_100_0, color='r', label='Totaldehnung= 0.000')
ax1.scatter(epsilon, mean_percent_100_03, color='g',label='Totaldehnung= 0.003')
ax1.scatter(epsilon, mean_percent_100_05, color='b',label='Totaldehnung= 0.005')
ax1.set_title('TOR_R')
ax2.scatter(epsilon, mean_percent_111_0,color='r')
ax2.scatter(epsilon, mean_percent_111_03,color='g')
ax2.scatter(epsilon, mean_percent_111_05,color='b')
ax3.scatter(epsilon, mean_percent_110_0,color='r')
ax3.scatter(epsilon, mean_percent_110_03,color='g')
ax3.scatter(epsilon, mean_percent_110_05,color='b')
ax4.scatter(epsilon, mean_percent_234_0,color='r')
ax4.scatter(epsilon, mean_percent_234_03,color='g')
ax4.scatter(epsilon, mean_percent_234_05,color='b')
# Fine-tune figure; make subplots close to each other and hide x ticks for
# all but bottom plot.
f.subplots_adjust(hspace=0.13)
plt.setp([a.get_xticklabels() for a in f.axes[:-1]], visible=False)
plt.locator_params(axis = 'y', nbins = 4)
ax1.grid()
ax2.grid()
ax3.grid()
ax4.grid()
plt.show()
Now i want to have a x-axis with smaller space between each point. I tried to change the range but it was not working. Can someone help me?
To make the x ticks come closer you might have to set the dimensions of the figure.
Since, in your case, the figure is already created, Set the size of the plot using set_size_inches method of the figure object.
This question contains a few other ways to do the same.
Adding the following line before the plt.show()
fig.set_size_inches(2,8)
Gives me this :
Which I hope is what you are trying to do.

Categories