Matplolib subplots what is the purpose of the fig and axs variables - matplotlib

Consider the following code:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(19680801)
data = np.random.randn(2, 100)
fig, axs = plt.subplots(2, 2, figsize=(5, 5))
axs[0, 0].hist(data[0])
axs[1, 0].scatter(data[0], data[1])
axs[0, 1].plot(data[0], data[1])
axs[1, 1].hist2d(data[0], data[1])
plt.show()
I am aware that in order to create subplots you ned to write the line:
fig, axs = plt.subplots(2, 2, figsize=(5, 5)
However my question is concerning the meaning of this line, as in what does it actually achieve by producing the variables fig and axs, and why later on we use ax[0,0] as opposed to fig[0,0]

fig describes the figure as a whole, but the axs in this case refers to all the subplots within the figure. Since you defined 2 rows and 2 columns of subplots, you call each subplot with axs[0,0] for the top left and axs[1,1] for the bottom right subplot. In order to change the size of a subplot you have to change the size of the overall figure in which the subplots are embedded.
The difference is subtle, but multiple subplots or just one subplot can be found in a figure. So to plot a line you would do this on the subplot axes and not on the figure.

Related

after groupby, set subplots into plots next to each-other rather than in one plot

After doing groupby in a pandas data-frame I wanna set subplots into different plots stacked next to eachother however, the module puts all of them in one plot
df.groupby('week')['label'].plot(kind='density', legend=True)
Consider looping through the groupby object and plot to corresponding axes:
import matplotlib.pyplot as plt
...
week_grps = df.groupby('week')
fig, axs = plt.subplots(nrows=1, ncols=len(week_grps), figsize=(15,5))
for ax,(i, sub) in zip(axs, week_grps):
sub['label'].plot(kind='density', legend=True, title=i, ax=ax)
plt.tight_layout()
plt.show()
plt.clf()
plt.close()
I think you want to do
df.groupby('week')['label'].plot(kind='density', legend=True, subplots=True)

keep the area of a subplot unchanged while changing the ticklabels size

In matplotlib, if I increase the size of the ticklabels, the size of the subplots will keep unchanged but the whole figure will expand itself.
For example:
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1)
ax2.set_yticklabels(['a long and big label'], fontsize=26)
I want to know how to set the subplots to automatically adjust their size to accommodate the ticklabels so that the whole area a subplot takes will keep unchanged, like the following:
First one should note that the total area a subplot takes is unchanged by default. The code from the question result in
where the label is simply cut. When the code is run in jupyter notebook with the inline backend, the resulting png image is however expanded to contain everything, even parts initially outside the figure.
You can automatically adjust all the subplots to nicely fit into the figure via constrained_layout or tight_layout(). E.g.
fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True)
# ...
or
fig, (ax1, ax2) = plt.subplots(2, 1)
ax2.set_yticklabels(['a long and big label'], fontsize=26)
fig.tight_layout()
There is however no automatic way to only adjust one of the subplots independent of the others. For such case one would need to manually calculate the space needed.
import matplotlib.pyplot as plt
fig = plt.figure()
gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0,:])
ax2 = fig.add_subplot(gs[1,1])
ax2.set_yticklabels(['a long and big label'], fontsize=26)
fig.canvas.draw()
bb = ax2.yaxis.get_tightbbox(fig.canvas.get_renderer())
labelwidth = fig.transFigure.inverted().transform([bb.width, 0])[0]
axwidth = ax1.get_position().width
gs.set_width_ratios([labelwidth, axwidth-labelwidth] )
ax2.set_position(gs[1,1].get_position(fig))
plt.show()

changing the size of subplots with matplotlib

I am trying to plot multiple rgb images with matplotlib
the code I am using is:
import numpy as np
import matplotlib.pyplot as plt
for i in range(0, images):
test = np.random.rand(1080, 720,3)
plt.subplot(images,2,i+1)
plt.imshow(test, interpolation='none')
the subplots appear tiny though as thumbnails
How can I make them bigger?
I have seen solutions using
fig, ax = plt.subplots()
syntax before but not with plt.subplot ?
plt.subplots initiates a subplot grid, while plt.subplot adds a subplot. So the difference is whether you want to initiate you plot right away or fill it over time. Since it seems, that you know how many images to plot beforehand, I would also recommend going with subplots.
Also notice, that the way you use plt.subplot you generate empy subplots in between the ones you are actually using, which is another reason they are so small.
import numpy as np
import matplotlib.pyplot as plt
images = 4
fig, axes = plt.subplots(images, 1, # Puts subplots in the axes variable
figsize=(4, 10), # Use figsize to set the size of the whole plot
dpi=200, # Further refine size with dpi setting
tight_layout=True) # Makes enough room between plots for labels
for i, ax in enumerate(axes):
y = np.random.randn(512, 512)
ax.imshow(y)
ax.set_title(str(i), fontweight='bold')

Matplotlib Subplots -- Get Rid of Tick Labels Altogether

Is there a way to get rid of tick labels altogether when creating an array of subplots in Matplotlib? I am currently needing to specify each plot based on the row and column of a larger data set to which the plot corresponds. I've attempted to use the ax.set_xticks([]) and the similar y-axis command, to no avail.
I recognize that it's probably an unusual request to want to make a plot with no axis data whatsoever, but that's what I need. And I need it to automatically apply to all of the subplots in the array.
You have the right method. Maybe you are not applying the set_xticks to the correct axes.
An example:
import matplotlib.pyplot as plt
import numpy as np
ncols = 5
nrows = 3
# create the plots
fig = plt.figure()
axes = [ fig.add_subplot(nrows, ncols, r * ncols + c) for r in range(0, nrows) for c in range(0, ncols) ]
# add some data
for ax in axes:
ax.plot(np.random.random(10), np.random.random(10), '.')
# remove the x and y ticks
for ax in axes:
ax.set_xticks([])
ax.set_yticks([])
This gives:
Note that each axis instance is stored in a list (axes) and then they can be easily manipulated. As usual, there are several ways of doing this, this is just an example.
Even more concise than #DrV 's answer, remixing #mwaskom's comment, a complete and total one-liner to get rid of all axes in all subplots:
# do some plotting...
plt.subplot(121),plt.imshow(image1)
plt.subplot(122),plt.imshow(image2)
# ....
# one liner to remove *all axes in all subplots*
plt.setp(plt.gcf().get_axes(), xticks=[], yticks=[]);
Note: this must be called before any calls to plt.show()
The commands are the same for subplots
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.plot([1,2])
ax1.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off' # labels along the bottom edge are off)
)
plt.draw()
You can get rid of the default subplot x and y ticks with simply running the following codes:
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(plt.NullLocator())
ax.yaxis.set_major_locator(plt.NullLocator())
for i in range(3):
ax = fig.add_subplot(3, 1, i+1)
...
Just by adding the 2 aforementioned lines just after fig, ax = plt.subplots() you can remove the default ticks.
One can remove the xticks or yticks by
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
If you want to turn off also the spines, so having no axis at all, you can use:
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
And if you want to turn everything off at once, use:
ax.axis("off")

Copying axis limits from one subplot ('equal' aspect) to another

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)