matplotlib share xaxis with yaxis from another plot - python

If I want to tie the x and y axis of two separate axes together so that they zoom together I usually do something like this:
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122,sharex=ax1, sharey=ax1)
But I don't know how to share the xaxis of one plot with the yaxis of another plot. For example the xaxis of one plot is 'time' and I want to share that with the yaxis of another plot which also represents 'time'. Something like this (which doesn't work...):
ax2 = fig.add_subplot(122,sharex=ax1.yaxis, sharey=ax1.xaxis)
Thanks

I would be doing something like this,
fig, axs = plt.subplots(3, sharex=True, sharey=True) //Here
fig.suptitle('Sharing both axes')
axs[0].plot(x, y ** 2)
axs[1].plot(x, 0.3 * y, 'o')
axs[2].plot(x, y, '+')
##As per matplotlib docs and works fine for me too
Another way is you can superimpose too.

Related

Matplotlib: Subplot heights with squared imshow

I want to plot a three-panel figure, with an imshow on the top and two other plots below. My imshow is has the same x and y dimensions, ie. is squared. The two other plots have the same x dimension, they can therefore share the x axis. However, how do I specify that all three panels have the same width?
This is my code
import matplotlib.pyplot as plt
x = np.linspace(0,100,20)
y = np.sin(x)
z = np.random.rand(100,100)
fig, (ax, ax2, ax3) = plt.subplots(nrows=3, sharex=True, figsize=(10,10), gridspec_kw={"height_ratios": [2,1,1]})
# "main" plot
ax.imshow(z)
# accessory plot #1
ax2.plot(x, y)
ax2.set_xlim(0,99)
# accessory plot #2
ax3.plot(x*2, y)
plt.show()
I don't understand the behavior here, why does my imshow not have the correct width like the two panels below?
I found a solution here, but as far as I understand this can only create one additional axis per side?
This is my current output:
And I would like it to look like this:
Layout is defnitely something matplotlib could use some work on. What is happening is that you have a square figure, with height_ratios=[2, 1, 1]: the first axis (where you plot the image) has double the height of the other two. But the image you are plotting is also square, so it is using all the available height, while it has "white" space on the horizontal direction.
Try to play with figsize and maybe the height_ratios, something like this:
fig, (ax, ax2, ax3) = plt.subplots(nrows=3, sharex=True, figsize=(6,12), gridspec_kw={"height_ratios": [4,1,1]})

Python: Draw a second y-axis on pyplot figure

I am trying to create a subplot that consists of two figures. Each of the figures shows some data plotted vs a time axis. And for each figure I want to have two y axes corresponding to two different graphs shown within the same figure.
Let's start with the data corresponding to one of the y-axes. This data is the same for each of the two figures and is generated as follows (it is fairly ugly code and if you have any suggestions as on how to improve it, please let me know!):
pwm_len = len(Time)/6
pwm_max = 255
pwm_min = 150
pwm_mid = 200
pwm_zero = 0
pwm1 = np.repeat(pwm_max, pwm_len)
pwm2 = np.repeat(pwm_min, pwm_len)
pwm3 = np.repeat(pwm_max, pwm_len)
pwm4 = np.repeat(pwm_mid, pwm_len)
pwm5 = np.repeat(pwm_max, pwm_len)
pwm6 = np.repeat(pwm_zero, pwm_len)
pwm = pwm1 + pwm2 + pwm3 + pwm4 + pwm5 + pwm6
To create the figure, I am using the following code (please note that it is not working right now, due to some wrong usage of twinx() ):
fig, axs = plt.subplots(2, sharex=True, sharey=True)
plt.subplots_adjust(hspace=0.5)
axs_pwm = axs.twinx()
axs[0].plot(Time, velocity, 'b-')
axs_pwm[0].plot(Time, pwm, 'r-')
axs[0].set_ylabel('[mm/s]')
axs_pwm[0].set_ylabel('PWM')
axs[0].grid(True)
axs[1].plot(Time, velocity_filtered, 'b-')
axs_pwm[1].plot(Time, pwm, 'r-')
axs[1].set_ylabel('[mm/s]')
axs_pwm[1]-set_ylabel('PWM')
axs[1].grid(True)
plt.show()
apparently I am using the twinx() function in a wrong way. But what is a different way to draw the second y axis?
Extending upon ImportanceOfBeingErnest's's suggestion, you need the following:
Create the twin axis for each subplot using the index 0 and 1 while using twinx()
Use the respective twin axis' object to plot data and set y-axis labels
fig, axs = plt.subplots(2, sharex=True, sharey=True)
plt.subplots_adjust(hspace=0.5)
axs_pwm1 = axs[0].twinx() # Create twin axis for the first subplot
axs[0].plot(Time, velocity, 'b-')
axs_pwm1.plot(Time, pwm, 'r-')
axs[0].set_ylabel('[mm/s]')
axs_pwm1.set_ylabel('PWM')
axs[0].grid(True)
axs_pwm2 = axs[1].twinx() # Create twin axis for the second subplot
axs[1].plot(Time, velocity_filtered, 'b-')
axs_pwm2.plot(Time, pwm, 'r-')
axs[1].set_ylabel('[mm/s]')
axs_pwm2.set_ylabel('PWM')
axs[1].grid(True)
plt.show()
Or as suggested by #SpghttCd in the comments, you can predefine all the twin axis and then use index as
ax2 = [ax.twinx() for ax in axs]
ax2[0].plot(...)
ax2[1].plot(...)

Is there a way to add a second y axis to a matplotlib subplot?

(I think) I know about the twiny/x function in matplotlib, but I am really struggling to figure out how to use this function in a subplot context. I have a line plot like this showing rainfall data:
Generated by this code:
fig,ax = plt.subplots(3, figsize=(10,15),sharey=True)
ax[0].plot(YEAR1['pcp_1D_tot'], label='RG')
ax[0].plot(YEAR1['ppt_1D'], label='TRMM')
ax[0].set_title('Year 1',x=0.1,y=0.9)
ax[1].plot(YEAR2['pcp_1D_tot'], label='RG')
ax[1].plot(YEAR2['ppt_1D'], label='TRMM')
ax[1].set_title('Year 2',x=0.1,y=0.9)
ax[1].set_ylabel('Rainfall total (mm/day)')
ax[2].plot(YEAR3['pcp_1D_tot'], label='RG')
ax[2].plot(YEAR3['ppt_1D'], label='TRMM')
ax[2].set_title('Year 3',x=0.1,y=0.9)
ax[2].set_xlabel('Date')
fig.legend(loc=(0.8,0.9))
fig.tight_layout()
plt.show()
But I also have data on flood magnitude I would like to add which is in categories of 1, 2, and 3, stored in a column named, e.g.
YEAR1['Size']
I'd like to plot these as a scatter plot on top of the line plot to show their incidence relative to rainfall, and so I believe I need to add another y axis on the right, but I am very unclear how to do this.
Can anyone help?
**###########UPDATE############### **
Thanks to the contributions below, I managed to make the following, which is exactly what I was hoping for:
By using the following code:
x = YEAR1m.index #### these are referring to other data that has been filtered
y = YEAR2m.index
z = YEAR3m.index
fig,ax = plt.subplots(3, figsize=(10,15),sharey=True)
ax[0].plot(YEAR1['pcp_1D_tot'], label='RG')
ax[0].plot(YEAR1['ppt_1D'], label='TRMM')
ax[0].set_title('Year 1',x=0.1,y=0.9)
ax0 = ax[0].twinx()
ax0.scatter(x, YEAR1m['Size'], marker='*', color='r',s=100)
ax0.set_ylim([0,3.2])
ax0.set_yticklabels(['0',' ','1',' ','2',' ','3'])
ax[1].plot(YEAR2['pcp_1D_tot'], label='RG')
ax[1].plot(YEAR2['ppt_1D'], label='TRMM')
ax[1].set_title('Year 2',x=0.1,y=0.9)
ax[1].set_ylabel('Rainfall total (mm/day)')
ax1 = ax[1].twinx()
ax1.scatter(y, YEAR2m['Size'], marker='*', color='r',s=100)
ax1.set_ylim([0,3.2])
ax1.set_yticklabels(['0',' ','1',' ','2',' ','3'])
ax[2].plot(YEAR3['pcp_1D_tot'], label='RG')
ax[2].plot(YEAR3['ppt_1D'], label='TRMM')
ax[2].set_title('Year 3',x=0.1,y=0.9)
ax[2].set_xlabel('Date')
ax2 = ax[2].twinx()
ax2.scatter(z, YEAR3m['Size'], marker='*', color='r',s=100)
ax2.set_ylim([0,3.2])
ax2.set_yticklabels(['0',' ','1',' ','2',' ','3'])
# fig.legend(loc=(0.8,0.9))
fig.tight_layout()
plt.show()
In the absence of data, I can only provide a guess solution. The following should work. You will have to use individual subplot object to create a secondary y-axis using twinx
fig,ax = plt.subplots(3, figsize=(10,15),sharey=True)
ax[0].plot(YEAR1['pcp_1D_tot'], label='RG')
ax[0].plot(YEAR1['ppt_1D'], label='TRMM')
ax[0].set_title('Year 1',x=0.1,y=0.9)
ax0 = ax[0].twinx()
ax0.plot(YEAR1['Size'])
ax[1].plot(YEAR2['pcp_1D_tot'], label='RG')
ax[1].plot(YEAR2['ppt_1D'], label='TRMM')
ax[1].set_title('Year 2',x=0.1,y=0.9)
ax[1].set_ylabel('Rainfall total (mm/day)')
ax1 = ax[1].twinx()
ax1.plot(YEAR2['Size'])
ax[2].plot(YEAR3['pcp_1D_tot'], label='RG')
ax[2].plot(YEAR3['ppt_1D'], label='TRMM')
ax[2].set_title('Year 3',x=0.1,y=0.9)
ax[2].set_xlabel('Date')
ax2 = ax[2].twinx()
ax2.plot(YEAR3['Size'])
I have not your data, let's see if I understood your problem...
Usual stuff,
import matplotlib.pyplot as plt
import numpy as np
Generate some data
x = np.linspace(0, 1.57, 21)
y = np.sin(x)
z = 4*np.random.random(21)
Prepare the figure with two subplots — I'm going to use only the first one to avoid have you bored — add a parasitic axes not to the current plot but to a specific axes
fig, (ax0, ax1) = plt.subplots(2, 1)
ax01 = ax0.twinx()
Plot the curve(s), plot the scatter points using different colors (this is not dealt with automatically)
ax0.plot(x, y, color='blue')
ax01.scatter(x, z, color='red')
This is a bit of an unrequested final touch... (note, it's colors, not color)
ax0.tick_params(axis='y', colors='blue')
ax01.tick_params(axis='y', colors='red')
and finally plt.show() gives us
I'd like to add: thank you Rutger Kassies for your fine answer, where the reader of my answer can find further advice on customizing all the details of the two vertical spines.

How to use twinx and still get square plot

How do I show a plot with twin axes such that the aspect of the top and right axes are 'equal'. For example, the following code will produce a square plot
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
But this changes as soon as you use the twinx function.
ax2 = ax.twinx()
ax2.set_ylim([0,2])
ax3 = ax.twiny()
ax3.set_xlim([0,2])
Using set_aspect('equal') on ax2 and ax3 seems to force it the the aspect of ax, but set_aspect(0.5) doesn't seem to change anything either.
Put simply, I would like the plot to be square, the bottom and left axes to run from 0 to 1 and the top and right axes to run from 0 to 2.
Can you set the aspect between two twined axes? I've tried stacking the axes:
ax3 = ax2.twiny()
ax3.set_aspect('equal')
I've also tried using the adjustable keyword in set_aspect:
ax.set_aspect('equal', adjustable:'box-forced')
The closest I can get is:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal', adjustable='box-forced')
ax.plot([0,1],[0,1])
ax2=ax.twinx()
ax3 = ax2.twiny()
ax3.set_aspect(1, adjustable='box-forced')
ax2.set_ylim([0,2])
ax3.set_xlim([0,2])
ax.set_xlim([0,1])
ax.set_ylim([0,1])
Which produces:
I would like to remove the extra space to the right and left of the plot
It seems overly complicated to use two different twin axes to get two independent set of axes. If the aim is to create one square plot with one axis on each side of the plot, you may use two axes, both at the same position but with different scales. Both can then be set to have equal aspect ratios.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
ax2 = fig.add_axes(ax.get_position())
ax2.set_facecolor("None")
ax2.set_aspect('equal')
ax2.plot([2,0],[0,2], color="red")
ax2.tick_params(bottom=0, top=1, left=0, right=1,
labelbottom=0, labeltop=1, labelleft=0, labelright=1)
plt.show()

Use matplotlib: plot error bars on two y axes

I'd like to plot a series with x and y error bars, then plot a second series with x and y error bars on a second y axis all on the same subplot. Can this be done with matplotlib?
import matplotlib.pyplot as plt
plt.figure()
ax1 = plt.errorbar(voltage, dP, xerr=voltageU, yerr=dPU)
ax2 = plt.errorbar(voltage, current, xerr=voltageU, yerr=currentU)
plt.show()
Basically, I'd like to put ax2 on a second axis and have the scale on the right side.
Thanks!
twinx() is your friend for adding a secondary y-axis, e.g.:
import matplotlib.pyplot as pl
import numpy as np
pl.figure()
ax1 = pl.gca()
ax1.errorbar(np.arange(10), np.arange(10), xerr=np.random.random(10), yerr=np.random.random(10), color='g')
ax2 = ax1.twinx()
ax2.errorbar(np.arange(10), np.arange(10)+5, xerr=np.random.random(10), yerr=np.random.random(10), color='r')
There is not a lot of documentation except for:
matplotlib.pyplot.twinx(ax=None)
Make a second axes that shares the x-axis. The new axes will overlay ax (or the current axes if ax is None). The ticks for ax2 will be placed on the right, and the ax2 instance is returned.
I was struggling to share the x-axis, but thank you #Bart you saved me!
The simple solution is use twiny instead of twinx
ax1.errorbar(layers, scores_means[str(epoch)][h,:],np.array(scores_stds[str(epoch)][h,:]))
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_xlabel('depth', color='b')
ax1.tick_params('x', colors='b')
ax2 = ax1.twiny()
ax2.errorbar(hidden_dim, scores_means[str(epoch)][:,l], np.array(scores_stds[str(epoch)][:,l]))
ax2.set_xlabel('width', color='r')
ax2.tick_params('x', colors='r')
fig.tight_layout()
plt.show()

Categories