Python - matplotlib - differences between subplot() and subplots() - python

I'm kind of new in coding and thus in python so this may sound quite dumb, but what are the main differences between .subplot() and .subplots() methods from matplotlib in python?
I didn't find this explanation anywhere else and after reading the documentation from https://matplotlib.org/ I inferred that with both methods you can create as many figures and plots as you want...so for me both of them seem to be quite the same thing and they just differ the way you can handle plots, axes, etc...or am I wrong?
Btw, I am using python3 in jupyter notebook if it makes any difference.

1. matplotlib.pyplot.subplots()
From the documentation page on matplotlib.pyplot.subplots():
This utility wrapper makes it convenient to create common layouts of subplots, including the enclosing figure object, in a single call.
That means you can use this single function to create a figure with several subplots with only one line of code. For example, the code below will return both fig which is the figure object, and axes which is a 2x3 array of axes objects which allows you to easily access each subplot:
fig, axes = plt.subplots(nrows=2, ncols=3)
2. matplotlib.pyplot.subplot()
In contrast, matplotlib.pyplot.subplot() creates only a single subplot axes at a specified grid position. This means it will require several lines of code to achieve the same result as matplot.pyplot.subplots() did in a single line of code above:
# first you have to make the figure
fig = plt.figure(1)
# now you have to create each subplot individually
ax1 = plt.subplot(231)
ax2 = plt.subplot(232)
ax3 = plt.subplot(233)
ax4 = plt.subplot(234)
ax5 = plt.subplot(235)
ax6 = plt.subplot(236)
or you can also use built-in method of fig:
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax3 = fig.add_subplot(233)
ax4 = fig.add_subplot(234)
ax5 = fig.add_subplot(235)
ax6 = fig.add_subplot(236)
Conclusion
The code above can be condensed with a loop, but it is still considerably more tedious to use. I'd therefore recommend you use matplotlib.pyplot.subplots() since it is more concise and easy to use.

Related

Lineplot above clustermap [duplicate]

In a previous answer it was recommended to me to use add_subplot instead of add_axes to show axes correctly, but searching the documentation I couldn't understand when and why I should use either one of these functions.
Can anyone explain the differences?
Common grounds
Both, add_axes and add_subplot add an axes to a figure. They both return a (subclass of a) matplotlib.axes.Axes object.
However, the mechanism which is used to add the axes differs substantially.
add_axes
The calling signature of add_axes is add_axes(rect), where rect is a list [x0, y0, width, height] denoting the lower left point of the new axes in figure coodinates (x0,y0) and its width and height. So the axes is positionned in absolute coordinates on the canvas. E.g.
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
places a figure in the canvas that is exactly as large as the canvas itself.
add_subplot
The calling signature of add_subplot does not directly provide the option to place the axes at a predefined position. It rather allows to specify where the axes should be situated according to a subplot grid. The usual and easiest way to specify this position is the 3 integer notation,
fig = plt.figure()
ax = fig.add_subplot(231)
In this example a new axes is created at the first position (1) on a grid of 2 rows and 3 columns. To produce only a single axes, add_subplot(111) would be used (First plot on a 1 by 1 subplot grid). (In newer matplotlib versions, add_subplot() without any arguments is possible as well.)
The advantage of this method is that matplotlib takes care of the exact positioning. By default add_subplot(111) would produce an axes positioned at [0.125,0.11,0.775,0.77] or similar, which already leaves enough space around the axes for the title and the (tick)labels. However, this position may also change depending on other elements in the plot, titles set, etc.
It can also be adjusted using pyplot.subplots_adjust(...) or pyplot.tight_layout().
In most cases, add_subplot would be the prefered method to create axes for plots on a canvas. Only in cases where exact positioning matters, add_axes might be useful.
Example
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (5,3)
fig = plt.figure()
fig.add_subplot(241)
fig.add_subplot(242)
ax = fig.add_subplot(223)
ax.set_title("subplots")
fig.add_axes([0.77,.3,.2,.6])
ax2 =fig.add_axes([0.67,.5,.2,.3])
fig.add_axes([0.6,.1,.35,.3])
ax2.set_title("random axes")
plt.tight_layout()
plt.show()
Alternative
The easiest way to obtain one or more subplots together with their handles is plt.subplots(). For one axes, use
fig, ax = plt.subplots()
or, if more subplots are needed,
fig, axes = plt.subplots(nrows=3, ncols=4)
The initial question
In the initial question an axes was placed using fig.add_axes([0,0,1,1]), such that it sits tight to the figure boundaries. The disadvantage of this is of course that ticks, ticklabels, axes labels and titles are cut off. Therefore I suggested in one of the comments to the answer to use fig.add_subplot as this will automatically allow for enough space for those elements, and, if this is not enough, can be adjusted using pyplot.subplots_adjust(...) or pyplot.tight_layout().
The answer by #ImportanceOfBeingErnest is great.
Yet in that context usually one want to generate an axes for a plot and add_axes() has too much overhead.
So one trick is, as in the answer of #ImportanceOfBeingErnest, is to use add_subplot(111).
Yet more elegant alternative and simple would be:
hAx = plt.figure(figsize = (10, 10)).gca()
If you want 3D projection you can pass any axes property. For instance the projection:
hAx = plt.figure(figsize = (16, 10)).gca(projection = '3d')

Combining Dataframe plots into single figure

I am trying to merge an arbitrary number of line charts into a single image, and while there are many, many questions about this sort of thing, none of them seem applicable to the code I'm working with.
Unlike a large number of answers, I don't want to have the separate graphs displayed side by side, or above one another, in a single output, but rather, combined together.
For all of these graphs the value of the "y_x" column would be the same, but the "yhat_y" produced during each loop would be different.
Adding subplots = True to the plot method of a dataframe seems to change the return type to something that is no longer compatible with the code numpy.ndarray' object has no attribute 'get_figure'
#ax = plt.subplot(111) doesnt seem to do anything
for variable in range(max_num):
forecast = get_forecast(variable)
cmp1 = forecast.set_index("ds")[["yhat", "yhat_lower", "yhat_upper"]].join(
both.set_index("ds")
)
e.augmented_error[variable]= sklearn.metrics.mean_absolute_error(
cmp["y"].values, cmp1["yhat"].values
)
cmp2=cmp.merge(cmp1,on='ds')
plot = cmp2[['y_x', 'yhat_y']].plot(title =e)
fig1 = plot.get_figure()
plot.set_title("prediction")
plt.show()
fig1.savefig('output.pdf', format="pdf")
plt.close()
The most straightforward way would be to create a reusable ax handle outside the loop, then call ax.plot inside the loop:
fig, ax = plt.subplots() # create reusable `fig` and `ax` handles
for variable in range(max_num):
...
ax.plot(cmp2['y_x'], cmp2['yhat_y']) # use `ax.plot(cmp2...)` instead of `cmp2.plot()`
ax.set_title('predictions')
fig.savefig('output.pdf', format='pdf')

Using pyplot from Plots.jl. How to make several subplots have only one colobar?

I am using Plots.jl to make several plots in the same figure. When using the pyplot backend, each plot has it's own colorbar, which I don't want as they have the same data. I am trying to replicate the answer from this question, however I don't know in detail of the machinery under the Plots.jl API, so I haven't been able to replicate it. My plot is done as:
using Plots;pyplot()
p1 = plot(a,st=:contour,fill=true)
p2 = plot(b,st=:contour,fill=true)
p = plot(p1,p2)
And, the answer (which is in python) from the link is this:
fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(im, cax=cbar_ax)
plt.show()
As far as I understand, the code inside the for is actually making the plots in the axes created by plt.subplots (in my case this is done by Plots.jl
The next line makes the plots closer, and then the line fig.add_axes creates a new axis for the colorbar.
Finally, the line of fig.colorbar creates a colorbar in the new axis and uses the data from the last plot in the for loop.
My current code is:
cbar_ax = p.o[:add_axes]([0.85, 0.15, 0.05, 0.7]);
p.o[:colorbar](p.o[:axes][1][:contourf],cax=cbar_ax)
display(p)
And it doesn't work (I wouldn't expect it to work because I don't know what I'm doing.
The error I get is:
AttributeError("'function' object has no attribute 'autoscale_None'")
Which makes me think p.o:axes[:contourf] is not the way to summon what I am trying to.
Can anyone help out? Thanks
In general, if you want to use code on the PyPlot object it's better to just use PyPlot and forget about Plots. The mix rarely works in practice.
If you do want to use Plots you should be able to do
using Plots;pyplot()
lims = extrema([a;b])
p1 = plot(a,st=:contour,fill=true, colorbar = false)
p2 = plot(b,st=:contour,fill=true, colorbar = true, clims = lims)
p = plot(p1,p2)
One of the subplots will be much wider than the other - you probably need to adjust with #layout to get them the same width.

Avoid overlapping on seaborn plots

I'm making some EDA using pandas and seaborn, this is the code I have to plot the histograms of a group of features:
skewed_data = pd.DataFrame.skew(data)
skewed_features =skewed_data.index
fig, axs = plt.subplots(ncols=len(skewed_features))
plt.ticklabel_format(style='sci', axis='both', scilimits=(0,0))
for i,skewed_feature in enumerate(skewed_features):
g = sns.distplot(data[column])
sns.distplot(data[skewed_feature], ax=axs[i])
This is the result I'm getting:
Is not readable, how can I avoid that issue?
I know you are concerning about the layout of the figures. However, you need to first decide how to represent your data. Here are two choices for your case
(1) Multiple lines in one figure and
(2) Multiple subplots 2x2, each subplot draws one line.
I am not quite familiar with searborn, but the plotting of searborn is based on matplotlib. I could give you some basic ideas.
To archive (1), you can first declare the figure and ax, then add all line to this ax. Example codes:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# YOUR LOOP, use the ax parameter
for i in range(3)
sns.distplot(data[i], ax=ax)
To archive (2), same as above, but with different number subplots, and put your line in the different subplot.
# Four subplots, 2x2
fig, axarr = plt.subplots(2,2)
# YOUR LOOP, use different cell
You may check matplotlib subplots demo. To do a good visualization is a very tough work. There are so many documents to read. Check the gallery of matplotlib or seaborn is a good and quick way to understand how some kinds of visualization are implemented.
Thanks.

Purpose of 'ax' keyword in pandas scatter_matrix function

I'm puzzled by the meaning of the 'ax' keyword in the pandas scatter_matrix function:
pd.scatter_matrix(frame, alpha=0.5, figsize=None, ax=None, grid=False, diagonal='hist', marker='.', density_kwds={}, hist_kwds={}, **kwds)
The only clue given in the docstring for the ax keyword is too cryptic for me:
ax : Matplotlib axis object
I had a look in the pandas code for the scatter_matrix function, and the ax variable is incorporated in the following matplotlib subplots call:
fig, axes = plt.subplots(nrows=n, ncols=n, figsize=figsize, ax=ax,
squeeze=False)
But, for the life of me, I can't find any reference to an 'ax' keyword in matplotlib subplots!
Can anyone tell me what this ax keyword is for???
This is tricky here. When looking at the source of pandas scatter_matrix you will find this line right after the docstring:
fig, axes = _subplots(nrows=n, ncols=n, figsize=figsize, ax=ax, squeeze=False)
Hence, internally, a new figure, axes combination is created using the internal _subplots method. This is strongly related to the matplotlibs subplots command but slightly different. Here, the ax keyword is supplied as well. If you look at the corresponding source (pandas.tools.plotting._subplots) you will find these lines:
if ax is None:
fig = plt.figure(**fig_kw)
else:
fig = ax.get_figure()
fig.clear()
Hence, if you supply an axes object (e.g. created using matplotlibs subplots command), pandas scatter_matrix grabs the corresponding (matplolib) figure object and deletes its content. Afterwards a new subplots grid is created into this figure object.
All in all, the ax keyword allows to plot the scatter matrix into a given figure (even though IMHO in a slightly strange way).
In short, it targets a subplot within a grid.
If you have nrows=2 and ncols=2, for example, then ax allows you to plot on a specific axis by passing ax=axes[0,0] (top left) or ax=axes[1,1] (bottom right), etc.
When you create the subplots, you receive an axes variable. You can later plot (or subplot) with an element of that axes variable as above.
Take a look at the "Targeting different subplots" section of this page: http://pandas.pydata.org/pandas-docs/dev/visualization.html#targeting-different-subplots
I hope this helps.

Categories

Resources