I've two frames as you can see on the picture. I want to automatically center this two figures and put in a photo. But I failed in the first part. My code is:
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(nrows = 2, ncols = 2)
# Axes 1
ax1 = plt.subplot2grid((3, 3), (0, 0))
#ax1.set_title[['[0,0]']
# Axes 2
ax2 = plt.subplot2grid((3, 3), (0, 2))
I've two other plots, but they don't have to be considered. How to I center this according the whole fig? I couldn't find anything on the matplot site.
Thanks.
Assuming that you don't want the ratio of the displayed length of x- and y-axes of the subplots in the first row of the figure to change, we can use plt.subplot to get the expected result by creating a finer resolution of row by having more columns accessible to plot a particular subplot:
import matplotlib.pyplot as plt
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(nrows = 2, ncols = 2)
# Creates a 3 * 13 grid on the figure
ax1 = plt.subplot(3, 13, (4, 6))
ax2 = plt.subplot(3, 13, (8, 10))
plt.show()
This gives:
Related
This question already has answers here:
How to make an axes occupy multiple subplots with pyplot
(7 answers)
Closed 24 days ago.
I need to create 4 plots, where there are two large ones and two stacked smaller ones to the right (the sum is the same as the large plots). Picture attached.
So far I managed only to create two separate figures:
fig, axs = plt.subplots(1, 3, figsize=(12, 4))
#plotting
fig, axs_right = plt.subplots(2, 1, figsize=(4, 8))
#plotting
The difficulty is that I need to plot with seaborn and have control over axes to change the way they look.
This is a relatively simple layout you can achieve with plt.subplot2grid():
grid = (2, 3)
fig = plt.figure(figsize=(8, 6)) # or whatever
ax = plt.subplot2grid(grid, (0, 0), rowspan=2) # left column plot
...
ax = plt.subplot2grid(grid, (0, 1), rowspan=2) # middle column plot
...
ax = plt.subplot2grid(grid, (0, 2)) # right column upper plot
...
ax = plt.subplot2grid(grid, (1, 2)) # right column lower plot
...
Result:
There are multiple ways of achieving that, it is described in the "Arranging multiple Axes in a Figure" part of the matplotlib documentation.
Below is a basic recopy for your case:
import matplotlib.pyplot as plt
figsize = (7, 4)
# Method 1
fig, axd = plt.subplot_mosaic(
[
["1", "2", "3"],
["1", "2", "4"],
],
figsize=figsize,
layout="constrained",
)
# Method 2
fig = plt.figure(figsize=figsize, layout="constrained")
spec = fig.add_gridspec(ncols=3, nrows=2)
ax1 = fig.add_subplot(spec[:, 0])
ax2 = fig.add_subplot(spec[:, 1])
ax3 = fig.add_subplot(spec[0, 2])
ax4 = fig.add_subplot(spec[1, 2])
# Method 3
fig = plt.figure(figsize=figsize, layout="constrained")
spec0 = fig.add_gridspec(ncols=2, nrows=1, width_ratios=[2, 1])
spec01 = spec0[0].subgridspec(ncols=2, nrows=1)
spec02 = spec0[1].subgridspec(ncols=1, nrows=2)
ax1 = fig.add_subplot(spec01[0])
ax2 = fig.add_subplot(spec01[1])
ax3 = fig.add_subplot(spec02[0])
ax4 = fig.add_subplot(spec02[1])
plt.show()
I'm using code much like:
import matplotlib.pyplot as plt
labels = ['AAA', 'BBBBBBBBBBBBB', 'CCCCCC', 'DDDDDDDDDD']
values = [0, 2, 2, 5]
fig, ax = plt.subplots(figsize=(8, 0.07 + 0.25 * len(values)))
bars = ax.barh(labels, values, color=colors)
to generate horizontal bar plots as separate figures, one after another:
How can I make the left spines (i.e. the black bars) align when the width of labels varies between plots? (Aside from just aligning the rendered images to the right.)
I think the left margin/padding/space should be fixed, or the bar width should be fixed, but I can't quite figure how to do it.
In these cases, I just add empty axes at the left edge of each figure. I'm sure there are more sophisticated ways, but I find this to be simplest:
fig1 with blank axes at left location
fig1, ax = plt.subplots(figsize=(8, 1))
ax.barh(['AAA', 'BBBBBBBBBBBBB', 'CCCCCC', 'DDDDDDDDDD'], [0, 2, 2, 5])
# add empty axes at `left` location (unit: fraction of figure width)
left = -0.05 # requires manual adjustment
fig1.add_axes([left, 0, 0, 0.01]).axis('off')
plt.show()
fig2 with blank axes at same left location as fig1
fig2, ax = plt.subplots(figsize=(8, 1))
ax.barh(['AAaaaA', 'BBBB', 'CCCCCC', 'DDDDD'], [2, 8, 7, 1])
# add empty axes at same `left` location as fig1
fig2.add_axes([left, 0, 0, 0.01]).axis('off')
plt.show()
Output of fig1 and fig2:
A similar approach would be to annotate a space character at the left of each figure:
ax.annotate(' ', (left, 0), xycoords='axes fraction', annotation_clip=False)
I'm testing out different way of displaying figures. I have one figure which is made up of 12 subplots split into two columns. Something like...
fig, ax = plt.subplots(6, 2, figsize= (20,26))
I have another code which splits the 12 subplots into 3 different figures based on categorical data. Something like
figA, ax = plt.subplots(5, 1, figsize= (10,23))
figB, ax = plt.subplots(3, 1, figsize= (10,17))
fig2, ax = plt.subplots(4, 1, figsize= (10,20))
Is there a way to ensure all the subplots in every figure have the same x and y axis length?
Answer turns out to be simple. Use a variable that can be scaled by the number of plots in the figure. So, a figure with more plots will have a higher figsize yet equal plot sizes. Something like...
ps = 5 #indicates plot size
figA, ax = plt.subplots(5, 1, figsize= (10, 5*ps))
figB, ax = plt.subplots(3, 1, figsize= (10, 3*ps))
fig2, ax = plt.subplots(4, 1, figsize= (10, 4*ps))
So I have the following code:
## BAR PLOTS
#expected value vs probability of choosing option1
fig, ax = plt.subplots(1, 2, dpi=320)
data.plot(kind='bar', y='value_1', ax=ax[0], color ='red')
data.plot(kind='bar', y='p_1', ax=ax[1], color ='blue')
#ax.set_xlabel("Trials")
#ax.set_ylabel("Value 1 / P_1")
#plt.xticks(np.arange(0, len('value_1')+1, 5), np.arange(0, len('value_1')+1, 5) )
#ticks = range(0, 500, 5)
#labels = ticks
#plt.xticks(ticks, labels)
plt.xticks(np.arange(0, len(data.value_1)+1, 5), np.arange(0, len(data.value_1)+1, 5) )
#ax.legend(["Value 1, P_1"])
plt.title('Bar plots Practice , Expected Vs. Probability')
fig.savefig("figure.pdf")
plt.show()
See now The graph is fine, and showing everything that I would want it to show; however, as you can see in this picture below, there is a problem with reading the x-axis / having the tickers spaced out correctly. I've tried to fix it several different ways but have not been able to make it look clean like an excel graph.
Try increasing the figure size, plt.figure(figsize = (15,15))
I am searching for an elegant solution to placing a figure label (A, B, C) above the corner of each subplot using matplotlib- but have each be the exact same distance from the subplot axes corner. The problem I am having is that typical labeling solutions take advantage of the axes fraction- so it is easy to place A,B,C in the same place relative to each plot.
import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2,2, figsize = (10,10))
texts = ['A', 'B', 'C', 'D']
axes = fig.get_axes()
for a,l in zip(axes, texts):
a.annotate(l, xy=(-0.1, 1.1), xycoords="axes fraction", fontsize=15, weight = 'bold')
plt.suptitle('Label_Distance Consistent', fontsize = 20)
however, if the plots are different sizes you will get labels that are variably far from the corners of the plots (due to aspect ratio). See Label A and C for example. I am looking for a good way to ensure proportional distance of labels from axes corners for panels containing multiple sizes/aspect ratio subplots, and/or to explicitly set text a specific distance (in inches or maybe figure coordinate units) from axes corners.
In the past I have placed same sized square axes at the corner of each subplot axes in the panel, made those invisible, and scaled text to these, but it is a convoluted approach.
fig, ax = plt.subplots(2,2, figsize = (10,10))
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))
axes = fig.get_axes()
texts = ['A', 'B', 'C', 'D', 'E']
for a,l in zip(axes, texts):
a.annotate(l, xy=(-0.1, 1.1), xycoords="axes fraction", fontsize=15, weight = 'bold')
plt.suptitle('Label Distance Inconsistent', fontsize = 20)
You could use axes pixels istead of axes fraction as reference and add the individual heights to compensate the reference origin lower left corner:
Here for brevity only the changed line within your loop, rest of your code untouched:
a.annotate(l, xy=(-40, 10 + a.bbox.height), xycoords="axes pixels", fontsize=15, weight = 'bold')
You can get the position of each subplot and add text on figure-level, so that offsets are in fractions of the figure size - which is equal for all subplots:
X = a.get_position().x0
Y = a.get_position().y1
fig.text(X - .04, Y + .015, l, size=15, weight='bold')
Result:
What about simply adding the title of the subplots?
(Again only the line to be replaced within your loop:)
a.set_title(l, size=15, weight='bold', loc='left')
Result:
While both of the above solutions work (and are more simple/meet most peoples needs), I thought I'd post a solution I pieced together allowing you to place text/objects with exact inches/points of offset from a particular axes point. This means that regardless of figure panel size/dpi, the text could be placed the same distance from corners- useful if making multiple figures of different dimensions and want them to be consistent (eg for publication). Apparently the matplotlib.transforms.offset_copy() function was designed for this purpose allowing specification of inches, points (1/72 inch) or dots as offset.
https://matplotlib.org/examples/pylab_examples/transoffset.html
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
fig, ax = plt.subplots(2,2, figsize = (10,7), dpi = 500)
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))
axes = fig.get_axes()
texts = ['A', 'B', 'C', 'D', 'E']
for a,l in zip(axes, texts):
a.set_xlim(0,10)
inv = a.transData.inverted()
# specify the number of points or inches or dots to transform by
# places an axes transform here for the axes on which you want to pick an anchor point on
offsetter = mpl.transforms.offset_copy(a.transAxes, fig=fig, x= -2, y= 6, units = 'points')
# offset an anchor point - this will return display coordinates
corner_offset = offsetter.transform([0,1])
# convert display coordinate to axes fraction
axes_frac = inv.transform(corner_offset)
a.annotate(l, xy=(axes_frac[0],axes_frac[1]), xycoords="axes fraction", fontsize=15, weight = 'bold', color='blue')
or with figsize/dpi changed