Adding a stacked plot as a subplot in python - python

Please I need help with a plot. I am making a 3x3 dimension figure containing 7 subplots. I want two(2) of the subplots (ax6 and ax7) to be stacked plots. Does anyone have an idea how I can make this work? I used the code below to make the grid.
fig = plt.figure()
fig.set_figheight(8)
fig.set_figwidth(10)
gs = gridspec.GridSpec(3, 3)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, -2])
ax3 = plt.subplot(gs[0, -1])
ax4 = plt.subplot(gs[1, 0])
ax5 = plt.subplot(gs[-1, 0])
ax6 = plt.subplot(gs[1:, -2])
ax7 = plt.subplot(gs[1:, -1])
I tried making the stacked plot for ax6 using the code below
ax6[0].plot(s[['xa']], s[['ac1']], label = "Data")
ax6[0].plot(s[['xa']], s[['ac2']], label = "C-C")
ax6[0].plot(s[['xa']], s[['ac3']], label = "C-O")
ax6[0].plot(s[['xa']], s[['ac4']], label = "C=C")
ax6[0].plot(s[['xa']], s[['ea1']], label = "Envelope")
ax6[0].text(0.08, 0.70, 'C', ha='center', va='baseline', wrap=True, fontsize= 10, fontweight='bold', color='darkgreen', transform=ax6[0].transAxes)
ax6[1].plot(s[['xb']], s[['bc1']], label = "Data")
ax6[1].plot(s[['xb']], s[['bc2']], label = "C-C")
ax6[1].plot(s[['xb']], s[['bc3']], label = "C-O")
ax6[1].plot(s[['xb']], s[['bc4']], label = "C=C")
ax6[1].plot(s[['xb']], s[['be1']], label = "Envelope")
ax6[1].text(0.08, 0.70, 'm.C', ha='center', va='baseline', wrap=True, fontsize= 10, fontweight='bold', color='darkgreen', transform=ax6[1].transAxes)

Please look at the comments in the code:
import matplotlib.pyplot as plt
from matplotlib import gridspec
import numpy as np
fig = plt.figure(figsize=(10, 8))
g = gridspec.GridSpec(3, 3)
ax1 = plt.subplot(g[0, 0])
ax2 = plt.subplot(g[0, 1])
ax3 = plt.subplot(g[0, 2])
ax4 = plt.subplot(g[1, 0])
ax5 = plt.subplot(g[2, 0])
# Create another grid
g2 = gridspec.GridSpec(3, 3)
g2.update(hspace=0.00)
# Generate data for three subplots in g2
x = np.linspace(0, 2 * np.pi, 400)
ya = np.sin(x)
yb = np.cos(x)
y7 = np.sin(x) ** 2
# Get three different Axes objects
ax6a = plt.subplot(g2[1, 1])
ax6b = plt.subplot(g2[2, 1], sharex=ax6a)
ax7 = plt.subplot(g2[1:, -1])
# Hide the xticklabels of top subplot in the shared plots
plt.setp(ax6a.get_xticklabels(), visible=False)
# Set xticks for lower subplots in the shared plots
ax6b.set_xticks(np.pi * np.array([0, 1/2, 1, 3/2, 2]))
# Try plotting
ax6a.plot(x, ya)
ax6b.plot(x, yb, 'g')
ax7.plot(x, y7, 'r')
plt.tight_layout()
plt.show()
This gives:
This answer was motivated by this answer and examples from older documentation of matplotlib.
If you want ax7 (red color subplot here) represented in to two separate subplots, either create a new Gridspec or use g depending on attributes you want to assign them e.g. in the code above:
# ax7 = plt.subplot(g2[1:, -1])
# ax7.plot(x, y7, 'r')
ax7a = plt.subplot(g[1, 2])
ax7b = plt.subplot(g[2, 2])
ax7a.plot(x, y7, 'r')
ax7b.plot(x, y7, 'r')
This gives:

Related

Draw a line on a figure with multiple axes

I have created a figure with multiple axes and added AnchoredText to some of these axes. This AnchoredText is like a label which connects the axis in which it resides to an outer axis, like this:
[Example image]
I have tried using AnnnotationBbox like this:
from matplotlib import pyplot as plt
from matplotlib.offsetbox import AnchoredText, AnnotationBbox, TextArea
fig_width = 16
fig_height = 9
fig = plt.figure(figsize=(fig_width, fig_height))
fig.add_axes(
(0, 0, 1, 1),
alpha=1,
xticks=[],
yticks=[],
)
fig.get_axes()[-1].set_xlim(0, fig_width)
fig.get_axes()[-1].set_ylim(0, fig_height)
fig.add_axes(
(0.66, 0.225, 0.075, 0.025),
label="L1",
alpha=1,
xticks=[],
yticks=[],
)
axis = fig.get_axes()[-1]
axis.set_xlim(
fig_width * 0.66, fig_width * (0.66 + .075)
)
axis.set_ylim(
fig_height * .225, fig_height * (.225 + .025)
)
circle = AnchoredText(
s="L1",
frameon=False,
loc="upper left",
prop=dict(bbox=dict(boxstyle="circle")),
)
axis.add_artist(circle)
fig.add_axes(
(0.62, 0.28, 0.03, 0.04),
label="L2",
alpha=1,
xticks=[],
yticks=[],
)
axis = fig.get_axes()[-1]
axis.set_xlim(
fig_width * 0.62, fig_width * (0.62 + .03)
)
axis.set_ylim(
fig_height * .28, fig_height * (.28 + .04)
)
def get_axes(fig, name):
for ax in fig.axes:
label = ax.get_label()
if label == name:
return ax
l1 = get_axes(fig, "L1") # Gets axis with given label
l2 = get_axes(fig, "L2")
offsetbox = TextArea("Test")
# Get the top outer limit of the l2 axis
xlim = (l2.get_xlim()[0] + l2.get_xlim()[1]) / 2
ylim = l2.get_ylim()[1]
xy = [xlim, ylim]
# Approximating the coordinates of AnchoredText
xlim2 = l1.get_xlim()[0] * 0.25
ylim2 = l1.get_ylim()[0] * 0.75
xy2 = [xlim2, ylim2]
ab = AnnotationBbox(
offsetbox,
xy2,
xybox=xy,
xycoords="data",
boxcoords=("axes fraction", "data"),
box_alignment=(0.0, 0.5),
arrowprops=dict(arrowstyle="-"),
)
l1.add_artist(ab)
plt.show()
Running the code above does not add the line in my figure and I'm not sure why. Is there a better way to approach this problem? How can I get the limit of the AnchoredText, if at all?
Here is stab at it. The trick is to plot the subplots in one general blank (and transparent) axes and then plot a line connecting the subplots.
from matplotlib import pyplot as plt
from matplotlib.offsetbox import AnchoredText
# Initialize figure
fig, ax_canvas = plt.subplots(figsize=(12, 6), constrained_layout=True)
# Set order of drawing, Ax canvas will be the last one so that the line connecting the other plots is on top
ax_canvas.set_zorder(10)
# Hide the main axis to set our blank canvas
ax_canvas.set_axis_off()
# Make background transparent so that it does not cover other plots when we draw the connecting line
ax_canvas.patch.set_facecolor('None')
ax_canvas.patch.set_visible(False)
# Set dimensions so it's easier to plot the line correctly
ax_canvas.set_xlim([0, 1])
ax_canvas.set_ylim([0, 1])
# First subplot axes in the canvas
left1, bottom1, width1, height1 = [0.1, 0.5, 0.5, 0.4]
ax1 = fig.add_axes([left1, bottom1, width1, height1])
ax1.set_zorder(0)
ax1.set_xlim([0, 1])
ax1.set_ylim([0, 1])
ax1.text(0.5, 0.5, 'Ax 1')
# Add the circle
circle = AnchoredText(
s="L1",
frameon=False,
loc="upper left",
prop=dict(bbox=dict(boxstyle="circle")),
)
ax1.add_artist(circle)
# Second axes in the canvas
left2, bottom2, width2, height2 = [0.45, 0.15, 0.3, 0.2]
ax2 = fig.add_axes([left2, bottom2, width2, height2])
ax1.set_zorder(0)
ax2.set_xlim([0, 1])
ax2.set_ylim([0, 1])
ax2.text(0.5, 0.5, 'Ax 2')
# Line connecting Ax1 and Ax2 in the Ax canvas
# The values below are eyeballed based on the ax_canvas having x and y limits 0 to 1
# Roughly where the circle is in Ax1, x middle of Ax2
x_line = [0.125, 0.6]
# Roughly where the circle is in Ax1, height of Ax2
y_line = [0.87, 0.35]
ax_canvas.plot(x_line, y_line, c='k', ls='--')
plt.show()

Set size of matplotlib subplots

I created two subplots on a MPL figure, but i'm having an hard time setting the size on them. I want the space to be splitted between the two charts, so each chart needs to have 50% of the total width of the figure, and i want them to have the same height of the figure, here is how i initialized the subplots:
fig = plt.figure(facecolor='#131722',dpi=155, figsize=(10, 3))
ax1 = plt.subplot2grid((3,3), (2,0), facecolor='#131722')
ax2 = plt.subplot2grid((5,3), (2,2), colspan=5, rowspan=4, facecolor='#131722')
Colors = [['#0400ff', '#FF0000'], ['#09ff00', '#ff8c00']]
for x in List:
Index = List.index(x)
rate_buy = []
total_buy = []
rate_sell = []
total_sell = []
for y in x['data']['asks']:
rate_sell.append(y[0])
total_sell.append(y[1])
for y in x['data']['bids']:
rate_buy.append(y[0])
total_buy.append(y[1])
rBuys = pd.DataFrame({'buy': rate_buy})
rSells = pd.DataFrame({'sell': rate_sell})
tBuys = pd.DataFrame({'total': total_buy})
tSells = pd.DataFrame({'total': total_sell})
ax1.plot(rBuys.buy, tBuys.total, color=Colors[Index][0], linewidth=0.5, alpha=1, label='test')
ax2.plot(rSells.sell, tSells.total, color=Colors[Index][1],alpha=0.5, linewidth=1, label=x['exchange'])
ax1.fill_between(rBuys.buy, 0, tBuys.total, facecolor=Colors[Index][0], alpha=0.4)
ax2.fill_between(rSells.sell, 0, tSells.total, facecolor=Colors[Index][1], alpha=0.4)
And this is what i'm getting:
use plt.tight_layout() before calling plt.show().

matplotlib remove the ticks (axis) from the colorbar

I want to remove the (ticks) axis with numbers to the right of the colorbar. I am using matplotlib with python as follows:
f = plt.figure()
ax = f.add_subplot(1,1,1)
i = ax.imshow(mat, cmap= 'gray')
cbar = f.colorbar(i)
If you just want to remove the ticks but keep the ticklabels, you can set the size of the ticks to be 0 as following
f = plt.figure()
ax = f.add_subplot(1,1,1)
mat = np.arange(100).reshape((10, 10))
i = ax.imshow(mat, cmap= 'viridis')
cbar = f.colorbar(i)
cbar.ax.tick_params(size=0)
If you want to remove both, the ticks and the labels, you can use set_ticks([]) by passing an empty list.
cbar.set_ticks([])
Another option is to provided a formatter or locator. Here two combinations of:
a formatter which sets any value to an empty sting ('')
a locator that doesn't place a tick.
See the official matplotlib docs for more formatters or locators.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig, ax = plt.subplots(ncols=1)
mat = np.arange(100).reshape((10, 10))
cs = ax.imshow(mat, cmap= 'viridis')
divider = make_axes_locatable(ax)
dvider_kwargs = dict(position="right", size="15%", pad=0.5)
fig.colorbar(cs,
cax=divider.append_axes(**dvider_kwargs),
format = matplotlib.ticker.FuncFormatter(lambda x, pos: ''),
ticks = matplotlib.ticker.FixedLocator([]))
fig.colorbar(cs,
cax=divider.append_axes(**dvider_kwargs),
format = matplotlib.ticker.FuncFormatter(lambda x, pos: ''))
fig.colorbar(cs,
cax=divider.append_axes(**dvider_kwargs))
plt.tight_layout()
With make_axes_locatable and cax=divider.append_axes the colorbars have all the same size.
Another example
# gen data
n = 100000
bins = np.arange(-10, 10, .1)
value = np.random.normal(loc=20.0, scale=10.0, size=n)
samples0 = np.random.multivariate_normal([-2, 0], [[1, 0], [0, 1]], n)
samples1 = np.random.multivariate_normal([4, 4], [[1, -.9], [-.9, 1]], n)
samples2 = np.random.multivariate_normal([4, -4], [[1, .6], [.6, 1]], n)
h0, e = np.histogramdd(samples0, bins=[bins, bins], density=True)
h1, e = np.histogramdd(samples1, bins=[bins, bins], density=True)
h2, e = np.histogramdd(samples2, bins=[bins, bins], density=True)
# create figure
fig, ax = plt.subplots(ncols=1, figsize=(3,2))
kwargs = dict(vmin=0, vmax=.3)
cs0 = plt.pcolormesh(e[0][:-1], e[1][:-1], np.ma.masked_equal(h0, 0), cmap='Blues', **kwargs)
cs1 = plt.pcolormesh(e[0][:-1], e[1][:-1], np.ma.masked_equal(h1, 0), cmap='Greens', **kwargs)
cs2 = plt.pcolormesh(e[0][:-1], e[1][:-1], np.ma.masked_equal(h2, 0), cmap='Reds', **kwargs)
# create colorbars
divider = make_axes_locatable(ax)
divider_kwargs = dict(position="right", size="5%", pad=0.1)
fig.colorbar(cs0, extend='max',
cax=divider.append_axes(**divider_kwargs),
format = matplotlib.ticker.FuncFormatter(lambda x, pos: ''))
fig.colorbar(cs1, extend='max',
cax=divider.append_axes(**divider_kwargs),
format = matplotlib.ticker.FuncFormatter(lambda x, pos: ''))
fig.colorbar(cs2, extend='max',
cax=divider.append_axes(**divider_kwargs),
label='PDF')
# tune plot
ax.set_aspect('equal')
# ax.grid()
plt.tight_layout()

matplotlib text is not on top of patches even with a higher zorder

I've plotted data for females on one axes, and males on another axes. Each plot was made with zorder=0, but with position=1 and position=2 respectively. I label the bars with text with zorder=1, but as you can see, the bars overlap the text. Is it because they are on separate axes? In which case, how can I have text in one axes be higher than the highest zorder in another axes?
def get_ages():
df = pd.read_csv('surveydata.csv', low_memory=False)
fems = df.loc[df['gender'] == 1]
males = df.loc[df['gender'] == 2]
fdata = fems['age'].value_counts()
mdata = males['age'].value_counts()
fdata.sort_index(inplace=True)
mdata.sort_index(inplace=True)
print(fdata)
print(mdata)
fdata2 = fdata[0:14]
mdata2 = mdata[0:14]
fdata2['>31'] = sum(fdata[14:])
mdata2['>31'] = sum(mdata[14:])
fig = plt.figure() # Create matplotlib figure
ax = fig.add_subplot(111) # Create matplotlib axes
ax2 = ax.twinx() # Create another axes that shares the same x-axis as ax.
fdata2.plot(kind='bar', figsize=(10, 5.7), width=.4, color='pink', position=0, ax=ax,zorder=0)
mdata2.plot(kind='bar', figsize=(10, 5.7), width=.4, color='lightskyblue', position=1, ax=ax2, zorder=0)
ax.set_title("Ages", fontsize=18)
ax.set_ylabel("Occurrence", fontsize=18)
ax.set_facecolor('snow')
ax.set_xlim(ax.patches[0].get_x() - 1, ax.patches[-1].get_x() + 1)
ax2.set_yticks([])
totals = []
for i in ax.patches:
totals.append(i.get_height())
total = sum(totals)
for i in ax.patches:
ax.text(i.get_x() , i.get_height() + .5,
str(round((i.get_height() / total) * 100, 2)) + '%', fontsize=8,
color='black', horizontalalignment='left', zorder=9)
totals = []
for i in ax2.patches:
totals.append(i.get_height())
total = sum(totals)
for i in ax2.patches:
t = ax2.text(i.get_x()+ i.get_width(), i.get_height() + .5,
str(round((i.get_height() / total) * 100, 1)) + '%', fontsize=8,
color='black', horizontalalignment='right', zorder=10)
for x in ax.texts: #Shifts text up and down in case they overlap.
bb2 = x.get_window_extent(ax.get_figure().canvas.get_renderer())
bb = t.get_window_extent(ax.get_figure().canvas.get_renderer())
while bb2.overlaps(bb):
t.set_y(a._y - .01)
bb2 = x.get_window_extent(ax.get_figure().canvas.get_renderer())
bb = t.get_window_extent(ax.get_figure().canvas.get_renderer())

Make a frame with subplots in Matplotlib

I want to make a figure which consist of a frame with 4 figures, but in each figure there are three subplots. I am using the current version of Matplotlib
I show my code in order to do each individual figure, the point as I comment before, is how put 4 of this plots together in order to make a single figure
filename1 = "file1.txt"
filename2 = "file2.txt"
filename3 = "file3.txt"
datalist1 = np.loadtxt(filename1)
datalist2 = np.loadtxt(filename2)
datalist3 = np.loadtxt(filename3)
f, (ax1, ax2, ax3) = plt.subplots(3, sharex=True, sharey=True)
#First subplot
ax1.plot(datalist1[:,0], datalist1[:,1], 'k-')
ax1.plot(datalist2[:,0], datalist2[:,1], 'b-')
ax1.plot(datalist2[:,0], datalist2[:,2], 'g-')
ax1.plot(datalist2[:,0], datalist2[:,3], 'r-')
ax1.plot(datalist3[:,0], datalist3[:,1], 'k--')
ax1.set_ylim(-1.2, 1.2)
ax1.set_xlim(0, 10)
major_ticks_x = np.arange(0.0, 11, 2.0)
minor_ticks_x = np.arange(0.0, 11, 1.0)
major_ticks_y = np.arange(-1, 1.05, 1.0)
minor_ticks_y = np.arange(-1, 1.05, 0.25)
ax1.set_yticks(major_ticks_y)
ax1.set_yticks(minor_ticks_y, minor=True)
#Second subplot
ax2.plot(datalist1[:,0], datalist1[:,2], 'k-')
ax2.plot(datalist2[:,0], datalist2[:,4], 'b-')
ax2.plot(datalist2[:,0], datalist2[:,5], 'g-')
ax2.plot(datalist2[:,0], datalist2[:,6], 'r-')
ax2.plot(datalist3[:,0], datalist3[:,1], 'k--')
ax2.set_ylim(-1.2, 1.2)
ax2.set_xlim(0, 10)
ax2.set_yticks(major_ticks_y)
ax2.set_yticks(minor_ticks_y, minor=True)
#Third subplot
ax3.plot(datalist1[:,0], datalist1[:,3], 'k-')
ax3.plot(datalist2[:,0], datalist2[:,7], 'b-')
ax3.plot(datalist2[:,0], datalist2[:,8], 'g-')
ax3.plot(datalist2[:,0], datalist2[:,9], 'r-')
ax3.plot(datalist3[:,0], datalist3[:,1], 'k--')
ax3.set_ylim(-1.2, 1.2)
ax3.set_xlim(0, 10)
ax3.set_yticks(major_ticks_y)
ax3.set_yticks(minor_ticks_y, minor=True)
ax3.set_xticks(major_ticks_x)
ax3.set_xticks(minor_ticks_x, minor=True)
ax3.set_xlabel(r"$t$")
f.subplots_adjust(hspace=0.0)
plt.setp([a.get_xticklabels() for a in f.axes[:-1]], visible=False)
The plot that I want to obtain is somtehing like this, in a single figure:
Somebody knows how can be do it?? Thanks for your attention.
OK, I'll bite. It is unclear what you want, but I assume you want 12 subplots (6 rows, 2 columns) grouped into 4 groups with shared x-axis.
As usual creating the subplots and plotting is easy. Sharing x-axis is straightforward as well, but requires some manual work. You can either set up the shared x-axis during the subplot creation or modify it after. I think modifying after is simpler.
Sorry for the manual part in the middle - it is possible to automate obviously.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
fig, axx = plt.subplots(6, 2, figsize=(10,14))
## merge axis
axx[0, 0].get_shared_x_axes().join(axx[0, 0], axx[2, 0])
axx[0, 0].set_xticklabels([])
axx[1, 0].get_shared_x_axes().join(axx[1, 0], axx[2, 0])
axx[1, 0].set_xticklabels([])
axx[0, 1].get_shared_x_axes().join(axx[0, 1], axx[2, 1])
axx[0, 1].set_xticklabels([])
axx[1, 1].get_shared_x_axes().join(axx[1, 1], axx[2, 1])
axx[1, 1].set_xticklabels([])
axx[3, 0].get_shared_x_axes().join(axx[3, 0], axx[5, 0])
axx[3, 0].set_xticklabels([])
axx[4, 0].get_shared_x_axes().join(axx[4, 0], axx[5, 0])
axx[4, 0].set_xticklabels([])
axx[3, 1].get_shared_x_axes().join(axx[3, 1], axx[5, 1])
axx[3, 1].set_xticklabels([])
axx[4, 1].get_shared_x_axes().join(axx[4, 1], axx[5, 1])
axx[4, 1].set_xticklabels([])
# plot some data
for i, row in enumerate(axx):
for j, cell in enumerate(row):
if i <= 2:
cell.plot(np.random.rand(100))
else:
cell.plot(np.random.rand(200))
Here is the result.

Categories