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(...)
Related
Matplotlib madness...
dfin = pd.read_csv(inputfilename, sep=";", encoding='ISO-8859-1')
# create a return column
dfin['return'] = dfin['close'].shift(9) / dfin['close'].shift(12)
# create a cumulative sum column
dfin['return_cum'] = dfin['return'].cumsum()
close = dfin.iloc[:-1]['close']
test = dfin.iloc[:-1]['close'] * dfin.iloc[:-1]['return']
fig, axs = plt.subplots(figsize=(20, 10), sharex=True, sharey=True)
axs.plot(close, color='black')
axs.plot(test, color='blue')
plt.show()
plt.close()
However, when I try to run a cumulative plot of any kind, MPL flattens the first plot and plots the second relative to it:
test = dfin.iloc[:-1]['close'] * dfin.iloc[:-1]['return_cum']
I'm doing stock analysis, and trying to plot returns relative to the existing closing price. I don't understand why MPL is flatting the first plot - or how to make it stop.
Thanks for any help.
It's not flattening it per se. But the scale of the second line/plot is much bigger than the first that it shows like it's flattened.
You will need to use multiple scales (multiple y axis).
Check out this example from the matplotlib documentation.
Basically, you will need to do something like this:
...
fig, axs = plt.subplots(figsize=(20, 10), sharex=True, sharey=True)
axs.plot(close, color='black')
// same code as before above
// changed code below
ax2 = axs.twinx()
ax2.plot(test, color='blue')
fig.tight_layout()
plt.show()
plt.close()
I'm attempting to plot two bar charts using matplotlib.pyplot.subplots. I've created subplots within a function, but when I output the subplots they are too long in height and not long enough in width.
Here's the function that I wrote:
def corr_bar(data1, data2, method='pearson'):
# Basic configuration.
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(7, 7))
ax1, ax2 = axes
corr_matrix1 = data1.corr(method=method)
corr_matrix2 = data2.corr(method=method)
cmap = cm.get_cmap('coolwarm')
major_ticks = np.arange(0, 1.1, 0.1)
minor_ticks = np.arange(0, 1.1, 0.05)
# Values for plotting.
x1 = corr_matrix1['price'].sort_values(ascending=False).index
x2 = corr_matrix2['price'].sort_values(ascending=False).index
values1 = corr_matrix1['price'].sort_values(ascending=False).values
values2 = corr_matrix2['price'].sort_values(ascending=False).values
im1 = ax1.bar(x1, values1, color=cmap(values1))
im2 = ax2.bar(x2, values2, color=cmap(values2))
# Formatting for plot 1.
ax1.set_yticks(major_ticks)
ax1.set_yticks(minor_ticks, minor=True)
plt.setp(ax1.get_xticklabels(), rotation=45, ha='right', rotation_mode='anchor')
ax1.grid(which='both')
ax1.grid(which='minor', alpha=0.4)
ax1.grid(which='major', alpha=0.7)
ax1.xaxis.grid(False)
# Formatting for plot 2.
ax2.set_yticks(major_ticks)
ax2.set_yticks(minor_ticks, minor=True)
plt.setp(ax2.get_xticklabels(), rotation=45, ha='right', rotation_mode='anchor')
ax2.grid(which='both')
ax2.grid(which='minor', alpha=0.4)
ax2.grid(which='major', alpha=0.7)
ax2.xaxis.grid(False)
fig.tight_layout()
plt.show()
This function (when run with two Pandas DataFrames) outputs an image like the following:
I purposely captured the blank right side of the image as well in an attempt to better depict my predicament. What I want is for the bar charts to be appropriately sized in height and width as to take up the entire space, rather than be elongated and pushed to the left.
I've tried to use the ax.set(aspect='equal') method but it "scrunches up" the bar chart. Would anybody happen to know what I could do to solve this issue?
Thank you.
When you define figsize=(7,7) you are setting the size of the entire figure and not the subplots. So your entire figure must be a square in this case. You should change it to figsize=(14,7) or use a number larger than 14 to get a little bit of extra space.
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.
I'm trying to add a subplot onto the bottom of a figure of subplots. The issue I've got is that all of the first set of subplots want to share their x axis, but not the bottom one.
channels are the subplots that want to share the x axis.
So how can I add a subplot that does not share the x axis?
This is my code:
def plot(reader, tdata):
'''function to plot the channels'''
channels=[]
for i in reader:
channels.append(i)
fig, ax = plt.subplots(len(channels)+1, sharex=False, figsize=(30,16), squeeze=False)
plot=0
#where j is the channel name
for i, j in enumerate(reader):
y=reader["%s" % j]
ylim=np.ceil(np.nanmax(y))
x=range(len((reader["%s" % j])))
ax[plot,0].plot(y, lw=1, color='b')
ax[plot,0].set_title("%s" % j)
ax[plot,0].set_xlabel('Time / s')
ax[plot,0].set_ylabel('%s' % units[i])
ax[plot,0].set_ylim([np.nanmin(y), ylim+(ylim/100)*10])
plot=plot+1
###here is the new subplot that doesn't want to share the x axis###
ax[plot, 0].plot()
plt.tight_layout()
plt.show()
This code does NOT work because they are shareing the x-axis of the last subplot. The length of channels changes dependant onn what I specify earlier in the code.
Is it a valid option to use add_subplot somehow, ever though I don't have a constant number of channels?
thanks so much for any help
EDIT
Image for Joe:
It's easiest to use fig.add_subplot directly in this case.
As a quick example:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(6, 8))
# Axes that share the x-axis
ax = fig.add_subplot(4, 1, 1)
axes = [ax] + [fig.add_subplot(4, 1, i, sharex=ax) for i in range(2, 4)]
# The bottom independent axes
axes.append(fig.add_subplot(4, 1, 4))
# Let's hide the tick labels for all but the last shared-x axes
for ax in axes[:2]:
plt.setp(ax.get_xticklabels(), visible=False)
# And plot on the first subplot just to demonstrate that the axes are shared
axes[0].plot(range(21), color='lightblue', lw=3)
plt.show()
These two graphs have exactly the same x axis value of each point, is it possible to display the box whisker on top of the first graph?
I tried this:
fig1 = plt.figure()
ax = fig1.add_subplot(211)
ax.set_xscale('log')
ax.plot(x7,y7,'c+-')
ax.plot(x8,y8,'m+-')
ax.plot(x9,y9,'g+-')
ax.boxplot(dataset)
xtickNames = plt.setp(ax, xticklabels=boxx)
plt.setp(xtickNames)
The results only display the box whisker graph without the other three lines, so, I tried this instead:
fig1 = plt.figure()
ax = fig1.add_subplot(211)
ax2 = fig1.add_subplot(212)
ax.set_xscale('log')
ax.plot(x7,y7,'c+-')
ax.plot(x8,y8,'m+-')
ax.plot(x9,y9,'g+-')
ax2.set_xscale('log')
ax2.boxplot(dataset)
xtickNames = plt.setp(ax2, xticklabels=boxx)
plt.setp(xtickNames)
But I want them to be shown in the same graph, is that possible?
If you want two graphs with comparable X and Y ranges to appear one on top of the other, you can try "Hold". For example:
import pylab
pylab.plot([1,2,3,4],[4,3,2,1])
pylab.hold(True)
pylab.plot([1,2,3,4],[1,2,3,4])