I have a number of subplots in a figure fig1, created via
ax = fig1.add_subplot(221)
I then plot stuff in each of the subplots via
im=ax.plot(x,y)
and add some axis labels via
ax.set_xlabel('xlabel')
I would then like to clear a specific subplot completely, as described in When to use cla(), clf() or close() for clearing a plot in matplotlib?. However the problem is that ax.cla()and ax.clear() seem to only clear the data from the plot, without removing the axes, axis tick labels etc. On the other hand plt.clf() clears the entire figure. Is there something in between? A clf-like command that clears everything in a subplot, including axis labels? Or have I simply used the commands in a wrong way?
ax.clear() clears the axes. That is, it removes all settings and data from the axes such that you are left with an axes, just as it had been just created.
ax.axis("off") turns the axes off, such that all axes spines and ticklabels are hidden.
ax.set_visible(False) turns the complete axes invisible, including the data that is in it.
ax.remove() removes the axes from the figure.
Complete example:
import matplotlib.pyplot as plt
fig,axes = plt.subplots(2,3)
for ax in axes.flat:
ax.plot([2,3,1])
axes[0,1].clear()
axes[1,0].axis("off")
axes[1,1].set_visible(False)
axes[0,2].remove()
plt.show()
Related
I'm using matplotlib and grispec to plot 4 axes, three of which I want tight to one another and the last one I want spaced a little bit away from the above three. Reason being is that I want the top three to share the same x-axis units, and the bottom one to have different x-axis units. I've tried using gs.update right after the third axis, but this spaced out all the axis from one another, instead of the bottom fourth axis from the top three.
Is there a simple gridspec/matplotlib command that I'm missing, or do I have to hack around this somehow?
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(13,12))
gs1=fig.add_gridspec(nrows=4, ncols=2, hspace=0.0)
ax2=fig.add_subplot(gs1[1,:])
ax1=fig.add_subplot(gs1[0,:], sharey=ax2)
ax3=fig.add_subplot(gs1[2,:], sharey=ax2)
ax4=fig.add_subplot(gs1[3,:]) #<- want this spaced farther down than the above three axes
plt.show()
This is one solution -- here I first added a gridspec for the first three plots. This I specified should finish at 0.35 from the bottom (0 being the very bottom, 1 the top). Then I added another gridspec that starts at 0.3 and so has a gap of 0.05. Obviously you can play around with the numbers/placement.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(13,12))
gs1=fig.add_gridspec(nrows=3, ncols=1, hspace=0, bottom=0.35)
ax1=fig.add_subplot(gs1[0,0])
ax2=fig.add_subplot(gs1[1,0], sharey=ax1)
ax3=fig.add_subplot(gs1[2,0], sharey=ax1)
gs2=fig.add_gridspec(nrows=1, ncols=1, top=0.3)
ax4=fig.add_subplot(gs2[0,0])
for ax in fig.axes:
ax.set_yticks([])
ax.set_xticks([])
plt.show()
Result:
I've drawn a plot that looks something like the following:
It was created using the following code:
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
# 1. Plot a figure consisting of 3 separate axes
# ==============================================
plotNames = ['Plot1','Plot2','Plot3']
figure, axisList = plt.subplots(len(plotNames), sharex=True, sharey=True)
tempDF = pd.DataFrame()
tempDF['date'] = pd.date_range('2015-01-01','2015-12-31',freq='D')
tempDF['value'] = np.random.randn(tempDF['date'].size)
tempDF['value2'] = np.random.randn(tempDF['date'].size)
for i in range(len(plotNames)):
axisList[i].plot_date(tempDF['date'],tempDF['value'],'b-',xdate=True)
# 2. Create a new single axis in the figure. This new axis sits over
# the top of the axes drawn previously. Make all the components of
# the new single axis invisibe except for the x and y labels.
big_ax = figure.add_subplot(111)
big_ax.set_axis_bgcolor('none')
big_ax.set_xlabel('Date',fontweight='bold')
big_ax.set_ylabel('Random normal',fontweight='bold')
big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
big_ax.spines['right'].set_visible(False)
big_ax.spines['top'].set_visible(False)
big_ax.spines['left'].set_visible(False)
big_ax.spines['bottom'].set_visible(False)
# 3. Plot a separate figure
# =========================
figure2,ax2 = plt.subplots()
ax2.plot_date(tempDF['date'],tempDF['value2'],'-',xdate=True,color='green')
ax2.set_xlabel('Date',fontweight='bold')
ax2.set_ylabel('Random normal',fontweight='bold')
# Save plot
# =========
plt.savefig('tempPlot.png',dpi=300)
Basically, the rationale for plotting the whole picture is as follows:
Create the first figure and plot 3 separate axes using a loop
Plot a single axis in the same figure to sit on top of the graphs
drawn previously. Label the x and y axes. Make all other aspects of
this axis invisible.
Create a second figure and plot data on a single axis.
The plot displays just as I want when using jupyter-notebook but when the plot is saved, the file contains only the second figure.
I was under the impression that plots could have multiple figures and that figures could have multiple axes. However, I suspect I have a fundamental misunderstanding of the differences between plots, subplots, figures and axes. Can someone please explain what I'm doing wrong and explain how to get the whole image to save to a single file.
Matplotlib does not have "plots". In that sense,
plots are figures
subplots are axes
During runtime of a script you can have as many figures as you wish. Calling plt.save() will save the currently active figure, i.e. the figure you would get by calling plt.gcf().
You can save any other figure either by providing a figure number num:
plt.figure(num)
plt.savefig("output.png")
or by having a refence to the figure object fig1
fig1.savefig("output.png")
In order to save several figures into one file, one could go the way detailed here: Python saving multiple figures into one PDF file.
Another option would be not to create several figures, but a single one, using subplots,
fig = plt.figure()
ax = plt.add_subplot(611)
ax2 = plt.add_subplot(612)
ax3 = plt.add_subplot(613)
ax4 = plt.add_subplot(212)
and then plot the respective graphs to those axes using
ax.plot(x,y)
or in the case of a pandas dataframe df
df.plot(x="column1", y="column2", ax=ax)
This second option can of course be generalized to arbitrary axes positions using subplots on grids. This is detailed in the matplotlib user's guide Customizing Location of Subplot Using GridSpec
Furthermore, it is possible to position an axes (a subplot so to speak) at any position in the figure using fig.add_axes([left, bottom, width, height]) (where left, bottom, width, height are in figure coordinates, ranging from 0 to 1).
Normally when you plot a list of points and axis. Only a part of point will be shown for those intersecting with the axis line, see the first point in this png for an example. How to make sure the whole point circle in shown above the axis line?
You can turn off the clipping by using the parameter clip_on of the plotting functions:
plt.plot(range(10), marker='o', ms=20, clip_on=False)
You can turn off clipping for the resulting plot artist(s) by setting clip_on=False in your call to plot or scatter. Note that you can also modify the clipping box by hand if you have a reference to the artist.
import matplotlib.pyplot as plt
plt.plot([0,1,2], [0,1,2], 'bo', clip_on=False)
produces:
In a figure with 2x2 subplots, I need both the subplots on the right to share the x-axis, but the ones on the left not to share their axis. In addition, I need the subplot that is determining the x-axis limits to have 'equal' aspect ratio. I tried this:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2, 2, figsize=(12, 9))
# Subplot [0,1]
ax[0,1].axis('equal')
ax[0,1].plot(...)
[xmin01, xmax01, ymin01, ymax01] = self.ax[0,1].axis()
# Subplot [1,1]
ax[1,1].plot(...)
ax[1,1].set_xlim(left=xmin01, right=xmax01)
This is not working: the limits of the x-axis returned by axis() are near the data limits and are not the real limits shown in the graphed subplot. Changing the position of ax[0,1].axis('equal') after the plot command has no effect. Any idea?
Looking into the pyplot source code I discovered that axis('equal') is calling the method set_aspect(). This latter method is modifying the variable self._aspect but it is not further updating anything related! Then, I looked for and found the method that is really updating the aspect ratio: it is named apply_aspect(). So, it doesn't seem very elegant, but at least my problem is solved as shown:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2, 2, figsize=(12, 9))
# Subplot [0,1]
ax[0,1].axis('equal')
ax[0,1].plot(...)
ax[0,1].apply_aspect()
[xmin01, xmax01, ymin01, ymax01] = self.ax[0,1].axis()
# Subplot [1,1]
ax[1,1].plot(...)
ax[1,1].set_xlim(left=xmin01, right=xmax01)
I recently found the function subplots, which seems to be a more elegant way of setting up multiple subplots than subplot. However, I don't seem to be able to be able to change the properties of the axes for each subplot.
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as npx = np.linspace(0, 20, 100)
fig, axes = plt.subplots(nrows=2)
for i in range(10):
axes[0].plot(x, i * (x - 10)**2)
plt.ylabel('plot 1')
for i in range(10):
axes[1].plot(x, i * np.cos(x))
plt.ylabel('plot 2')
plt.show()
Only the ylabel for the last plot is shown. The same happens for xlabel, xlim and ylim.
I realise that the point of using subplots is to create common layouts of subplots, but if sharex and sharey are set to false, then shouldn't I be able to change some parameters?
One solution would be to use the subplot function instead, but do I need to do this?
Yes you probably want to use the individual subplot instances.
As you've found, plt.ylabel sets the ylabel of the last active plot. To change the parameters of an individual Axes, i.e. subplot, you can use any one of the available methods. To change the ylabel, you can use axes[0].set_ylabel('plot 1').
pyplot, or plt as you've defined it, is a helper module for quickly accessing Axes and Figure methods without needing to store these objects in variables. As the documentation states:
[Pyplot p]rovides a MATLAB-like plotting framework.
You can still use this interface, but you will need to adjust which Axes is the currently active Axes. To do this, pyplot has an axes(h) method, where h is an instance of an Axes. So in you're example, you would call plt.axes(axes[0]) to set the first subplot active, then plt.axes(axes[1]) to set the other.