I want to plot some data I have (square wave signals) in a subplot but I want to remove the axis for better visualization. This results in not having a ylabel. I thought I could add a simple text() so I could manually insert the text I want, but I can't seem to be able to use negative values for the y axis (as I could without a subplot). The code I thought would work was:
fig, (ax1, ax2, ax3, ax4, ax5, ax6)= plt.subplots(6,1)
#plot
ax1.plot(PathClockGeneration_4.q2bar_x,PathClockGeneration_4.clk_y, linewidth=2, color='black')
ax2.plot(PathClockGeneration_4.q2bar_x,PathClockGeneration_4.clkbar_y, linewidth=2, color='black')
ax3.plot(PathClockGeneration_4.q2bar_x,PathClockGeneration_4.q1_y, linewidth=2, color='C0')
ax4.plot(PathClockGeneration_4.q2bar_x,PathClockGeneration_4.q2_y, linewidth=2, color='C1')
ax5.plot(PathClockGeneration_4.q2bar_x,PathClockGeneration_4.q1bar_y, linewidth=2, color='C2')
ax6.plot(PathClockGeneration_4.q2bar_x,PathClockGeneration_4.q2bar_y, linewidth=2, color='C3')
#axis
ax1.axis('off')
ax2.axis('off')
ax3.axis('off')
ax4.axis('off')
ax5.axis('off')
ax6.axis('off')
#text
ax1.text(-1.5, 2, 'MyText')
If i try the last line as ax1.text(0, 2, 'MyText') it works fine, but the placement of the text is not the one I want. I suppose this comes from the size my plot is allowed to have and I would need to change it, how to do so?
EDIT
This is what I obtain hiding the axis manually (which can allow me to insert a ylabel). This is what I really want as plot obtained from the coded posted above by commenting ax1.text(-1.5, 2, 'MyText')
You can use fig instead of the ax1 to place your text. The arguments 0.05, 0.6 are the x and y coordinates in relative scale. You can choose them as per your taste.
Complete answer
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2, ax3, ax4, ax5, ax6) = plt.subplots(6,1)
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)
ax1.plot(x, y, linewidth=2, color='black')
ax2.plot(x, y, linewidth=2, color='black')
ax3.plot(x, y, linewidth=2, color='C0')
ax4.plot(x, y, linewidth=2, color='C1')
ax5.plot(x, y, linewidth=2, color='C2')
ax6.plot(x, y, linewidth=2, color='C3')
# Hiding axis
for ax in [ax1, ax2, ax3, ax4, ax5, ax6]:
ax.axis('off')
fig.text(0.05, 0.6, 'MyText', rotation=90, fontsize=20)
plt.show()
Related
I would like to do subplots of tricontourf. I tried this but it does not work, I obtain the error message: RuntimeError: No mappable was found to use for colorbar creation. First define a mappable such as an image (with imshow) or a contour set (with contourf).
fig, (ax1, ax2, ax3) = plt.subplots(1, 3)
fig = plt.figure()
# background_field
levels=np.linspace(0,max(max_ls, lc),5)
levels=np.r_[levels,[lc_smooth_min]]
levels=np.sort(levels)
print(levels)
ax1.tricontourf(X, Y, background_field(X,Y,Z), levels, cmap ='inferno')
plt.colorbar(boundaries=levels)
plt.xlim([0, 3])
plt.ylim([0, 0.5])
plt.xlabel("X")
plt.ylabel("Y")
# echelle_kolmogorov
ax2.tricontourf(X, Y, echelle_1(X,Y,Z), levels, cmap ='inferno')
plt.colorbar(boundaries=levels)
plt.xlim([0, 3])
plt.ylim([0, 0.5])
plt.xlabel("X")
plt.ylabel("Y")
# echelle_HTLES
ax3.tricontourf(X, Y, echelle_2(X,Y,Z), levels, cmap ='inferno')
plt.colorbar(boundaries=levels)
plt.xlim([0, 3])
plt.ylim([0, 0.5])
plt.xlabel("X")
plt.ylabel("Y")
plt.show()
Is it possible to perform subplots of tricontourf without using imshow?
Can someone explain me how should I do it?
Thanks a lot !
I have such a piece of code for plotting:
sns.set_style("darkgrid")
fig, ax = plt.subplots(1, 1)
x = np.arange(10)
ax.plot(x, x)
And it gives me:
How the number of grid lines can be increased in seaborn, to make it denser?
Based on this question : add minor gridlines to matplotlib plot using seaborn, you can do it like that.
sns.set_style("darkgrid")
fig, ax = plt.subplots(1, 1)
x = np.arange(10)
ax.plot(x, x)
ax.get_xaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())
ax.get_yaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())
ax.grid(b=True, which='major', color='w', linewidth=1.0)
ax.grid(b=True, which='minor', color='w', linewidth=0.5)
You obtain this figure :
My goal is to create plot with four subplots, where the bottom two are really just empty boxes where I will display some text. Unfortunately, all of my efforts to remove the y and x axis tick marks and labels have failed. I'm still new to matplotlib so I'm sure there's something simple that I'm missing. Here's what I'm trying and what I get:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, sharex=False, sharey=True, figsize=(6,6))
fig.add_subplot(111, frameon=False)
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.title('Neuron Length')
plt.xlabel('Strain')
plt.ylabel('Neuron Length (um)')
aIP = fig.add_subplot(223, frameon=False)
aIP.annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5),
xycoords='axes fraction', va='center')
# First approach
aIP.axes.xaxis.set_ticks([])
aIP.axes.yaxis.set_ticks([])
# Second approach
ax = plt.gca()
ax.axes.yaxis.set_visible(False)
plt.show()
This is achieved by using plt.subplots() to draw four of them and remove the bottom left frame.
import matplotlib.pyplot as plt
import numpy as np
t = np.linspace(-np.pi, np.pi, 1000)
x1 = np.sin(2*t)
x2 = np.cos(2*t)
x3 = x1 + x2
fig,axes = plt.subplots(nrows=2,ncols=2,figsize=(6,6), sharex=True, sharey=True)
axes[0,0].plot(t, x1, linewidth=2)
axes[0,1].plot(t, x2, linewidth=2)
axes[1,1].plot(t, x3, linewidth=2)
axes[1,0].axis('off') # off
axes[1,0].annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5), xycoords='axes fraction', va='center')
fig.suptitle('Neuron Length')
for ax in axes.flat:
ax.set(xlabel='Strain', ylabel='Neuron Length (um)')
plt.show()
I create two scatterplots with matplotlib in python with this code, the data for the code is here:
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
fig = plt.figure(figsize=(20,12))
ax1 = fig.add_subplot(111)
ax3 = ax1.twinx()
norm = Normalize(vmin=0.95*min(arr), vmax=1.05*max(arr))
ax1.scatter(x, y1, s=20, c=arr, cmap='Blues_r', norm=norm, marker='x', label='bla1')
ax3.scatter(x, y2, s=(20*(1.1-arr))**3.5, c=arr, cmap='Reds_r', norm=norm, marker='^', label='bla1')
The created fig. looks like this:
So, the dot size (in ax3) and the dot colour (in ax1 and ax3) are taken from arrays containing floats with all kinds of values in the range [0,1]. My question: How do I create a legend that displays the corresponding y-values for, let's say 5 different dot sizes and 5 different colour nuances?
I would like the legend to look like in the figure below (source here), but with the colour bar and size bar put into a single legend, if possible. Thanks for suggestions and code!
# using your data in dataframe df
# create s2
df['s2'] = (20*(1.1-df.arr))**3.5
fig = plt.figure(figsize=(20,12))
ax1 = fig.add_subplot(111)
ax3 = ax1.twinx()
norm = Normalize(vmin=0.95*min(df.arr), vmax=1.05*max(df.arr))
p1 = ax1.scatter(df.x, df.y1, s=20, c=df.arr, cmap='Blues_r', norm=norm, marker='x')
fig.colorbar(p1, label='arr')
p2 = ax3.scatter(df.x, df.y2, s=df.s2, c=df.arr, cmap='Reds_r', norm=norm, marker='^')
fig.colorbar(p2, label='arr')
# create the size legend for red
for x in [15, 80, 150]:
plt.scatter([], [], c='r', alpha=1, s=x, label=str(x), marker='^')
plt.legend(loc='upper center', bbox_to_anchor=(1.23, 1), ncol=1, fancybox=True, shadow=True, title='s2')
plt.show()
There's no legend for p1 because the size is static.
I think this would be better as two separate plots
I used Customizing Plot Legends: Legend for Size of Points
Separate
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(20, 10))
norm = Normalize(vmin=0.95*min(df.arr), vmax=1.05*max(df.arr))
p1 = ax1.scatter(df.x, df.y1, s=20, c=df.arr, cmap='Blues_r', norm=norm, marker='x')
fig.colorbar(p1, ax=ax1, label='arr')
p2 = ax2.scatter(df.x, df.y2, s=df.s2, c=df.arr, cmap='Reds_r', norm=norm, marker='^')
fig.colorbar(p2, ax=ax2, label='arr')
# create the size legend for red
for x in [15, 80, 150]:
plt.scatter([], [], c='r', alpha=1, s=x, label=str(x), marker='^')
plt.legend(loc='upper center', bbox_to_anchor=(1.2, 1), ncol=1, fancybox=True, shadow=True, title='s2')
plt.show()
I have a figure with two subplots as 2 rows and 1 column. I can add a nice looking figure legend with
fig.legend((l1, l2), ['2011', '2012'], loc="lower center",
ncol=2, fancybox=True, shadow=True, prop={'size':'small'})
However, this legend is positioned at the center of the figure and not below the center of the axes as I would like to have it. Now, I can obtain my axes coordinates with
axbox = ax[1].get_position()
and in theory I should be able to position the legend by specifying the loc keyword with a tuple:
fig.legend(..., loc=(axbox.x0+0.5*axbox.width, axbox.y0-0.08), ...)
This works, except that the legend is left aligned so that loc specifies the left edge/corner of the legend box and not the center. I searched for keywords such as align, horizontalalignment, etc., but couldn't find any. I also tried to obtain the "legend position", but legend doesn't have a *get_position()* method. I read about *bbox_to_anchor* but cannot make sense of it when applied to a figure legend. This seems to be made for axes legends.
Or: should I use a shifted axes legend instead? But then, why are there figure legends in the first place? And somehow it must be possible to "center align" a figure legend, because loc="lower center" does it too.
Thanks for any help,
Martin
In this case, you can either use axes for figure legend methods. In either case, bbox_to_anchor is the key. As you've already noticed bbox_to_anchor specifies a tuple of coordinates (or a box) to place the legend at. When you're using bbox_to_anchor think of the location kwarg as controlling the horizontal and vertical alignment.
The difference is just whether the tuple of coordinates is interpreted as axes or figure coordinates.
As an example of using a figure legend:
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
# The key to the position is bbox_to_anchor: Place it at x=0.5, y=0.5
# in figure coordinates.
# "center" is basically saying center horizontal alignment and
# center vertical alignment in this case
fig.legend([line1, line2], ['yep', 'nope'], bbox_to_anchor=[0.5, 0.5],
loc='center', ncol=2)
plt.show()
As an example of using the axes method, try something like this:
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
# The key to the position is bbox_to_anchor: Place it at x=0.5, y=0
# in axes coordinates.
# "upper center" is basically saying center horizontal alignment and
# top vertical alignment in this case
ax1.legend([line1, line2], ['yep', 'nope'], bbox_to_anchor=[0.5, 0],
loc='upper center', ncol=2, borderaxespad=0.25)
plt.show()
This is a very good question and the accepted answer indicates the key (i.e. loc denotes alignment and bbox_to_anchor denotes position). I have also tried some codes and would like to stress the importance of bbox_transform property that may sometimes needs to be explicitly specified to achieve desired effects. Below I will show you my findings on fig.legend. ax.legend should be very similar as loc and bbox_to_anchor works the same way.
When using the default setting, we will have the following.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,4), sharex=True)
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)
This is basically satisfactory. But it could be easily found that the legend overlays with the x-axis ticklabels of ax2. This is the problem that will become even severe when figsize and/or dpi of the figure changes, see the following.
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)
So you see there are big gaps between ax2 and the legend. That's not what we want. Like the questioner, we would like to manually control the location of the legend. First, I will use the 2-number style of bbox_to_anchor like the answer did.
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
axbox = ax2.get_position()
# to place center point of the legend specified by loc at the position specified by bbox_to_anchor.
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05])
Almost there! But it is totally wrong as the center of the legend is not at the center of what we really mean! The key to solving this is that we need to explicitly inform the bbox_transform as fig.transFigure. By default None, the Axes' transAxes transform will be used. This is understandable as most of the time we will use ax.legend().
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
axbox = ax2.get_position()
# to place center point of the legend specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05], bbox_transform=fig.transFigure)
As an alternative, we can also use a 4-number style bbox_to_anchor for loc. This is essentially specify a real box for the legend and loc really denotes alignment! The default bbox_to_anchor should just be [0,0,1,1], meaning the entire figure box! The four numbers represent x0,y0,width,height, respectively. It is very similar to specifying a cax for a shared colorbar! Hence you can easily change the y0 just a little bit lower than axbox.y0 and adjust loc accordingly.
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
axbox = ax2.get_position()
# to place center point specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2,
bbox_to_anchor=[0, axbox.y0-0.05,1,1], bbox_transform=fig.transFigure)