I want to add labels to my plot which consists of sub subplots. Here is what I want (I added the outer labels with GIMP)
And this is what I actually get:
Here is the code that produces the last plot:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
plots = 16
subplots = 9
fig = plt.figure(figsize=(8, 8))
wh_plots = int(np.sqrt(plots))
wh_subplots = int(np.sqrt(subplots))
outer_grid = gridspec.GridSpec(wh_plots, wh_plots, wspace=0.1, hspace=0.1)
for p in range(plots):
inner_grid = gridspec.GridSpecFromSubplotSpec(wh_subplots, wh_subplots, subplot_spec=outer_grid[p], wspace=0.05, hspace=0.05)
for s in range(subplots):
ax = plt.Subplot(fig, inner_grid[s])
ax.imshow(np.random.rand(10,10), cmap="magma", interpolation="none")
ax.set_xticks([])
ax.set_yticks([])
fig.add_subplot(ax)
if (p+1) > 12 and s == 7:
ax.set_xlabel("sub_xlabel")
if (p) % 4 == 0 and s == 3:
ax.set_ylabel("sub_ylabel")
all_axes = fig.get_axes()
plt.show()
My questions:
How can I get the "xlabel" and "ylabel" as seen in the first plot?
Is there a better way to label the subplots (sub_xlabel / sub_ylabel)
compared to what I did?
if (p+1) > 12 and s == 7:
ax.set_xlabel("sub_xlabel")
if (p) % 4 == 0 and s == 3:
ax.set_ylabel("sub_ylabel")
It works, but it doesn't look right.
You can add these lines before plt.show():
fig.text(0.5, 0.04, 'xlabel', ha='center', fontsize=18)
fig.text(0.04, 0.5, 'ylabel', va='center', rotation='vertical', fontsize=18)
Related
I created a scatter plot using matplotlib but I am somehow unable to get the labels to center into the boxes within the colorbar..
This is the code I have so far:
cMap = ListedColormap(['Orange', 'Purple', 'Blue','Red','Green'])
fig, ax = plt.subplots()
plt.figure(figsize=(12,12),dpi = 80)
#data
dist = np.random.rand(1900,1900)
#legend
cbar = plt.colorbar(scatter)
cbar.ax.get_yaxis().set_ticks([])
for j, lab in enumerate(['$Training$','$None$','$GS$','$ML$','$Both$']):
cbar.ax.text( .5, j - .985, lab, ha='left', va='center', rotation = 270)
cbar.ax.get_yaxis().labelpad = 15
cbar.ax.set_ylabel('Outliers', rotation=270)
indices = np.where(outlier_label != -2)[0]
plt.scatter(dist[indices, 0], dist[indices, 1], c=outlier_label[indices], cmap=cMap, s=20)
plt.gca().set_aspect('equal', 'datalim')
plt.title('Projection of the data', fontsize=24)
Thanks!
In line cbar.ax.text( .5, j - .985, lab, ha='left', va='center', rotation = 270) you have to work and change with '.985' with try and error to get better results.
You can extract the y limits of the colorbar to know its top and bottom. Dividing that area into 11 equally spaced positions, will have the 5 centers at the odd positions of that list. Similarly, you can extract the x limits to find the horizontal center.
Some remarks:
If you already called plt.subplots(), then plt.figure() will create a new figure, leaving the first plot empty. You can set the figsize directly via plt.subplots(figsize=...)
You are mixing matplotlib's "object-oriented interface" with the pyplot interface. This can lead to a lot of confusion. It is best to stick to one or the other. (The object-oriented interface is preferred, especially when you are creating non-trivial plots.)
You set dist = np.random.rand(1900,1900) of dimensions 1900x1900 while you are only using dimensions 1900x2.
The code nor the text give an indication of the values inside outlier_label. The code below assumes they are 5 equally-spaced numbers, and that both the lowest and the highest value are present in the data.
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import numpy as np
colors = ['Orange', 'Purple', 'Blue', 'Red', 'Green']
cmap = ListedColormap(colors)
fig, ax = plt.subplots(figsize=(12, 12), dpi=80)
# data
dist = np.random.randn(1900, 2).cumsum(axis=0)
outlier_label = np.repeat(np.arange(5), 1900 // 5)
indices = outlier_label != -2
scatter = ax.scatter(dist[indices, 0], dist[indices, 1], c=outlier_label[indices], cmap=cmap, s=20)
# legend
cbar = plt.colorbar(scatter, ax=ax)
cbar.ax.get_yaxis().set_ticks([])
cb_xmin, cb_xmax = cbar.ax.get_xlim()
cb_ymin, cb_ymax = cbar.ax.get_ylim()
num_colors = len(colors)
for j, lab in zip(np.linspace(cb_ymin, cb_ymax, 2 * num_colors + 1)[1::2],
['$Training$', '$None$', '$GS$', '$ML$', '$Both$']):
cbar.ax.text((cb_xmin + cb_xmax) / 2, j, lab, ha='center', va='center', rotation=270, color='white', fontsize=16)
cbar.ax.get_yaxis().labelpad = 25
cbar.ax.set_ylabel('Outliers', rotation=270, fontsize=18)
ax.set_aspect('equal', 'datalim')
ax.set_title('Projection of the data', fontsize=24)
plt.show()
I am trying to create an axis plot. I was trying to loop over it as I am plotting the same variable for two different categories. Currently, I have written code two times but I am looking for a smarter way with looping, if possible. Any other suggestion will also be helpful.
zone = ['AB','CD']
plt.style.use('default')
fig,(ax0,ax1) = plt.subplots(2,1, figsize = (18,18), sharex = False)
i = 0
while i < len(zone):
if zone[i] == zone[0]:
ax0.plot(df0['datetime'], df0['pnl1'], color='k', linewidth=1, label ='PnL1')
ax0.plot(df0['datetime'], df0['pnl2'], color='m', linewidth=1, label ='PnL2')
ax00 = ax0.twinx()
ax00.bar(df0['datetime'], df0['qty'], width = 1/96, color='g', align = 'edge', alpha = 0.5, label ='Qty')
elif zone[i] == zone[1]:
ax1.plot(df0['datetime'], df0['pnl1'], color='k', linewidth=1, label ='PnL1')
ax1.plot(df0['datetime'], df0['pnl2'], color='m', linewidth=1, label ='PnL2')
ax01 = ax1.twinx()
ax01.bar(df0['datetime'], df0['hedge'], width = 1/96, color='g', align = 'edge', alpha = 0.5, label ='Qty')
i = i + 1
I want to check if something like below can be done with axis plots or not.
zone = ['AB','CD']
plt.style.use('default')
fig,(ax0,ax1) = plt.subplots(2,1, figsize = (18,18), sharex = False)
i = 0
while i < len(zone):
ax{''}.format(i).plot(df0['datetime'], df0['pnl1'], color='k', linewidth=1, label ='PnL1')
ax{''}.format(i).plot(df0['datetime'], df0['pnl2'], color='m', linewidth=1, label ='PnL2')
ax0{''}.format(i) = ax{''}.format(i).twinx()
ax0{''}.format(i).bar(df0['datetime'], df0['qty'], width = 1/96, color='g', align = 'edge', alpha = 0.5, label ='Qty')
It did not work for me. Any leads to execute axis plot with loop will be helpful.
Here are some ways:
Simply loop over the list of axes
import matplotlib.pyplot as plt
import numpy as np
fig,axes = plt.subplots(2,1)
x = np.linspace(0,5,21)
for ax in axes:
ax.plot(x,np.sin(x))
plt.show()
Works also with index:
for i in range(len(axes)):
axes[i].plot(x,np.sin(x))
For a grid of plot you can use a similar approach:
import matplotlib.pyplot as plt
import numpy as np
fig,axes = plt.subplots(2,2)
x = np.linspace(0,5,21)
for i in range(len(axes)):
for j in range(len(axes[0])):
axes[i][j].plot(x,np.sin(x))
plt.show()
If you don't like double-loops, you can flatten the array with np.ravel()
fig,axes = plt.subplots(2,2)
x = np.linspace(0,5,21)
for ax in np.ravel(axes):
ax.plot(x,np.sin(x))
plt.show()
This question already has answers here:
How to have one colorbar for all subplots
(13 answers)
Closed 4 years ago.
i am getting multiple colobar for each subplot,i want one for all.
for i in range(6):
plot.subplot(2,3,i)
im=plot.contourf(xlon[:],xlat[:],rain[i,:,:])
plot.colorbar(im)
plot.show()
You can do this by adding the colorbar on its own axis. This can be done by manually creating an additional axis and shifting the existing plots as needed using subplots_adjust() and add_axes() e.g.
import matplotlib.pyplot as plt
import numpy as np
import random
fig, ax = plt.subplots(figsize=(10, 6), dpi=300)
for i in range(1,7):
# This simply creates some random data to populate with
a = np.arange(10)
x, y = np.meshgrid(a, a)
z = np.random.randint(0, 7, (10, 10))
plt.subplot(2,3,i)
im=plt.contourf(x, y, z)
# Tight layout is optional
fig.tight_layout()
fig.subplots_adjust(right=0.825)
cax = fig.add_axes([0.85, 0.06, 0.035, 0.91])
fig.colorbar(im, cax=cax)
plt.show()
The arguments for add_axes() in this case are [left, bottom, width, height]. This will produce something like
Edit
To remove the inter-plot axis labels, tick marks, etc requires a somewhat non-trivial modification from the above method wherein plt.subplots() is used to populate a 2x3 array of subplot objects over which we then iterate. E.g.
import matplotlib.pyplot as plt
import numpy as np
import random
nrows = 2
ncols = 3
# Create the subplot array
fig, (axes) = plt.subplots(nrows=nrows, ncols=ncols, figsize=(10, 6),
dpi=300, sharex=True, sharey=True)
for i in range(nrows):
for j in range(ncols):
a = np.arange(10)
x, y = np.meshgrid(a, a)
z = np.random.randint(0, 7, (10, 10))
im = axes[i][j].contourf(x, y, z)
# Remove the tick marks but leave the superleft and superbottom alone
if i != nrows-1:
if j != 0:
axes[i][j].tick_params(axis='both', which='both',
left=False, bottom=False, top=False)
else:
axes[i][j].tick_params(axis='both', which='both', bottom=False, top=False)
else:
if j != 0:
axes[i][j].tick_params(axis='both', which='both', left=False, top=False)
fig.tight_layout()
# Some additional whitespace adjustment is needed
fig.subplots_adjust(right=0.825, hspace=0.025, wspace=0.025)
cax = fig.add_axes([0.85, 0.06, 0.035, 0.91])
fig.colorbar(im, cax=cax)
plt.show()
I am new to data visualization world and has been assigned to create a
GUI(graphical user interface) to visualize data and have some fancy controls
in it.
The problem is that I am using subplots using gridspecs and I need to have
equal size buttons in "ax5 subplot" as shown in the figure.
I can't seem to find a solution to access that "ax5" and then divide this
area into 6 equal buttons.
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
def make_ticklabels_invisible(fig):
for i, ax in enumerate(fig.axes):
ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
ax.tick_params(labelbottom=False, labelleft=False)
# demo 3 : gridspec with subplotpars set.
fig = plt.figure(facecolor = '#0F0F0F')
fig.suptitle("GridSpec w/ different subplotpars", color= '#e21f1f')
gs1 = GridSpec(3, 3)
gs1.update(left=0.01, right=0.49, wspace=0.05, hspace=0.05)
ax1 = plt.subplot(gs1[:-1, :-1])
ax2 = plt.subplot(gs1[:-1, -1:])
ax3 = plt.subplot(gs1[-1, :])
gs2 = GridSpec(3, 3)
gs2.update(left=0.50, right=0.98, hspace=0.05, wspace=0.05)
ax4 = plt.subplot(gs2[:-2, :])
# need to have six buttons in 5th subplot
ax5 = plt.subplot(gs2[-2:-1, :])
ax6 = plt.subplot(gs2[-1, :])
make_ticklabels_invisible(fig)
pos5 = ax5.get_position()
print(pos5)
plt.show()
I am experimenting with matplotlib at the moment. Some time ago I used Excel VBA code to produce images such as the one attached.
You will notice it is not presented in a scientific/research style but rather as if produced by a school-student on graph paper - with three different grid-line styles.
Is there a fairly straightforward way to achieve this sort of thing with matplotlib?
Yes, you can use spines for this.
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import numpy as np
fig = plt.figure(1)
ax = fig.add_subplot(111)
# set up axis
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# draw curve
x = np.arange(-2.5,2.5,0.01)
line, = ax.plot(x, x**2)
#set bounds
ax.set_ybound(-1,7)
# create grid
#ax.xaxis.set_major_locator(MultipleLocator(1))
#ax.xaxis.set_minor_locator(MultipleLocator(0.2))
#ax.yaxis.set_major_locator(MultipleLocator(1))
#ax.yaxis.set_minor_locator(MultipleLocator(0.2))
#ax.xaxis.grid(True,'minor')
#ax.yaxis.grid(True,'minor')
#ax.xaxis.grid(True,'major',linewidth=2)
#ax.yaxis.grid(True,'major',linewidth=2)
#adjust grid on the 2s
#for idx,loc in enumerate(ax.xaxis.get_majorticklocs()):
#if loc !=0 and loc % 2 == 0: ax.get_xgridlines()[idx].set_c('r')
#for idx,loc in enumerate(ax.yaxis.get_majorticklocs()):
#if loc !=0 and loc % 2 == 0: ax.get_ygridlines()[idx].set_c('r')
## THIS IS THE EDIT
ax.xaxis.set_minor_locator(MultipleLocator(0.2))
ax.yaxis.set_minor_locator(MultipleLocator(0.2))
ax.xaxis.grid(True,'minor',linewidth=2)
ax.yaxis.grid(True,'minor',linewidth=2)
minor_grid_lines = [tick.gridline for tick in ax.xaxis.get_minor_ticks()]
for idx,loc in enumerate(ax.xaxis.get_minorticklocs()):
if loc % 2.0 == 0: minor_grid_lines[idx].set_c('r' )
elif loc % 1.0 == 0: minor_grid_lines[idx].set_c('g' )
else: minor_grid_lines[idx].set_c( 'b' )
plt.show()
This is a modified version of the accepted answer above.
Maybe somebody will find this helpful
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import numpy as np
from matplotlib.ticker import FormatStrFormatter
_fontsize_legend = 10
_fontsize = 15
DP = 2
fig = plt.figure(figsize=(12, 12), dpi=100, facecolor='w', edgecolor='k')
##fig = plt.figure()
fig.canvas.draw()
ax = plt.gca()
# set up axis
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# draw curve
x = np.arange(-2.5,2.5,0.01)
line, = ax.plot(x, x**2)
#set bounds
ax.set_ybound(-1,7)
## THIS IS THE EDIT
ax.xaxis.set_major_locator(MultipleLocator(1/4))
ax.yaxis.set_major_locator(MultipleLocator(1/4))
ax.xaxis.grid(True,'major',linewidth=2/DP,linestyle='-',color='#d7d7d7',zorder=0)
ax.yaxis.grid(True,'major',linewidth=2/DP,linestyle='-',color='#d7d7d7')
ax.xaxis.set_minor_locator(MultipleLocator( (1/4) / 5 ))
ax.yaxis.set_minor_locator(MultipleLocator( (1/4) / 5 ))
ax.xaxis.grid(True,'minor',linewidth=0.5/DP,linestyle='-',color='#d7d7d7')
ax.yaxis.grid(True,'minor',linewidth=0.5/DP,linestyle='-',color='#d7d7d7')
ax.set_axisbelow(True)
ax.set_aspect('equal')
##ax.axhline(linewidth=0)
##ax.axvline(linewidth=0)
ax.xaxis.set_major_formatter(FormatStrFormatter('%i'))
xticks = ax.xaxis.get_major_ticks()
for i,l in enumerate(xticks):
if not (i - 1) % 4 == 0:
xticks[i].label1.set_visible(False)
else:
xticks[i].label1.set_fontsize(_fontsize)
ax.yaxis.set_major_formatter(FormatStrFormatter('%i'))
yticks = ax.yaxis.get_major_ticks()
for i,l in enumerate(yticks):
if not (i - 1) % 4 == 0:
yticks[i].label1.set_visible(False)
else:
yticks[i].label1.set_fontsize(_fontsize)
figManager = plt.get_current_fig_manager()
figManager.window.showMaximized()
plt.show()
Just another thought - I have also tried to do it all with the minor gridlines (apart from anything else it will help my understanding), but it's not enumerating properly, no doubt due to the get_minorticklocs and ax.get_xgridlines. Sorry, and thanks in advance...
Geddes
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import numpy as np
fig = plt.figure(1)
ax = fig.add_subplot(111)
# set up axis
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# draw curve
x = np.arange(-2.5,2.5,0.01)
line, = ax.plot(x, x**2)
#set bounds
ax.set_ybound(-1,7)
# create grid
ax.xaxis.set_minor_locator(MultipleLocator(0.2))
ax.yaxis.set_minor_locator(MultipleLocator(0.2))
ax.xaxis.grid(True,'minor',linewidth=2)
ax.yaxis.grid(True,'minor',linewidth=2)
#adjust grid on the 2s
for idx,loc in enumerate(ax.xaxis.get_minorticklocs()):
if loc % 2 == 0: ax.get_xgridlines()[idx].set_color('r')
if loc % 1 == 0: ax.get_xgridlines()[idx].set_color('g')
if loc % 0.2 == 0: ax.get_xgridlines()[idx].set_color('b')
for idx,loc in enumerate(ax.yaxis.get_majorticklocs()):
if loc % 2 == 0: ax.get_ygridlines()[idx].set_c('b')
plt.savefig('spines3.png',dpi=300)