How to stack the graphs in such a way that the share a common scale along x-axis - matplotlib

The following code is for generating the 3 subplots. And on all the 3 subplots scale is mentioned. I want to stack them in such a way that x-axis and y-axis scale appear once like this. Can I get this plot with plt.subplot() or fig.add_axes is compulsory for this? I actually want to do this with subplots because in fig.add_subplot I havve to specify the width and height of each plot that I don't want.
`fig,axes = plt.figure(nrow=3, ncolmn=1)
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)
ind1 =[1,2,3]
ind2 = [4,5,6]
for i in range(len(3)):
data1=np.load(..)
data2=np.load(..)
axes[i].plot(data1, data2)`

Here is one solution using subplots_adjust where you put the space between two plots to 0 using hspace. Also, use sharex=True to have a shared x-axis
fig, axes = plt.subplots(nrows=3, ncols=1,sharex=True)
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
for i, ax in enumerate(axes.ravel()): # or axes.flatten() or axes.flat
ax.plot(x, y, label='File %d' %i)
ax.legend()
fig.text(0.5, 0.01, 'X-label', ha='center')
fig.text(0.01, 0.5, 'Y-label', va='center', rotation='vertical')
plt.tight_layout() # To get a better spacing between the subplots
plt.subplots_adjust(hspace=.0)

Related

Ytick overlaps with Matplotlib figure space in violinplot

The violin plots I am making with Matplotlib have one of the plot intersecting with ytick label. See the picture below:
The code I am using as follow:
alp = 1
fig, axes = plt.subplots(nrows=10, ncols=1, figsize=(8, 6), dpi=100)#, sharex=True)
for i,t in enumerate(top_features):
# print(i)
alp = alp - 0.08
parts = axes[i].violinplot(data[i], vert=False, widths=0.9,
showmeans=True, showextrema=True, showmedians=False,
bw_method='scott')#, points=200)
for pc in parts['bodies']:
pc.set_facecolor('#D43F3A')
pc.set_edgecolor('black')
pc.set_alpha(alp)
axes[i].set_ylabel('{:.3f}'.format(top_features[i]), rotation=0)
# axes[i].axis('off')
axes[i].set_xlim(-0.0010, 0.0030)
axes[i].spines['top'].set_visible(False)
axes[i].spines['right'].set_visible(False)
axes[i].spines['bottom'].set_visible(False)
axes[i].spines['left'].set_visible(False)
for i, ax in enumerate(axes):
if i !=9:
ax.set_xticks([])
ax.set_yticks([])
fig.tight_layout()
fig.subplots_adjust(left=0.01,right= 1.1, hspace=0.14, wspace=0.3)
ax.set_yticklabels(top_features)
How can move the figure to the right with the shared X-axis ticks and all the other violinplots?
That moment when you find an answer to your own question! -_-
axes[i].set_ylabel('{:.3f}'.format(top_features[i]), rotation=0, labelpad=30)
Adjusting the labelpad value solves!
Thanks all!

Fill between two horizontal lines thresholds in matplotlib

Dears, I need to make a fill between two thresholds in my chart I have tried with my code but it does not display, could you please tell me what I am doing wrong? I would like my figure like contour red like figure 2
fig = plt.figure(figsize=(15, 6))
ax = fig.add_subplot(111)
ax = lacebita['Prob'].plot(figsize=(15, 7), )
xtime = np.linspace(1990,2021,384)
ax.plot(xtime, lacebita['Prob'], 'black', alpha=1.00, linewidth=2, label = 'Deciles')
ax.fill_between(xtime, 0., lacebita['Prob'], lacebita['Prob']< 30., color='red', alpha=.75)
ax.axhline(50, linestyle='--', color='black',label='Percentile 50')
ax.axhline(33, linestyle='--', color='orange', label='Percentile 33')
ax.set_xlim(1990, 2021)
ax.set_ylim(0, 100, 10)
plt.grid(True)
plt.legend(loc = "upper left")
#ax.autoscale_view()
ax.set_title('Deciles para 12-Meses La Cebita(1990-2020)', fontsize=16)
ax.set_xlim(lacebita.index.min(), lacebita.index.max())
plt.savefig('deciles_12_lacebita.jpg')
There are a couple of ways to go about it. One approach is to fill the space in between the two horizontal threshold lines:
# Make a fake function
t = 20
fs = 10
samples = np.linspace(0, t, int(fs*t), endpoint=False)
wave_y = np.sin(samples)
time_x = np.arange(0, len(wave_y))
# Set upper and lower thresholds where horizontal lines will go and fill in between
upper_th = 0.5
lower_th = -0.5
# Plot function
fig, ax = plt.subplots()
ax.plot(time_x, wave_y)
ax.grid()
ax.set_ylim([-1.25, 1.25])
ax.set_ylabel('y label')
ax.set_xlim([0, 125])
ax.set_xlabel('x label')
# Fill in area under the curve and the horizontal lines
ax.fill_between(x=time_x, y1=upper_th, y2=lower_th, color='red', interpolate=True, alpha=.75)
# Horizontal lines
ax.axhline(upper_th, linestyle='--', color='black', label="upper_th: 0.5")
ax.axhline(lower_th, linestyle='--', color='orange', label='lower_th: - 0.5')
ax.legend()
plt.show()
Or if you change y1 or y2, for example to y1=0, you can play around with where exactly the fill is.
Another method is to fill in between the curve and the horizontal dashed lines. To do that you could modify the original data so that the values that go above the upper threshold and below the lower threshold become the threshold values. In other words, we want to make a new y curve that includes the threshold points by eliminating the points that go above/below the threshold so that matplotlib understands that the horizontal lines are part of the y curve.
# Copy original data, we are now going to modify
new_wave_y = np.copy(wave_y)
# Change values outside thresholds to threshold value for fill in
new_wave_y[new_wave_y < lower_th] = lower_th
new_wave_y[new_wave_y > upper_th] = upper_th
This way we can use where in fill between to point out where exactly under the curve, including under/above the horizontal lines, matplotlib needs to fill in the area. The full script:
# Make a fake function
t = 20
fs = 10
samples = np.linspace(0, t, int(fs*t), endpoint=False)
wave_y = np.sin(samples)
time_x = np.arange(0, len(wave_y))
# Set upper and lower thresholds where horizontal lines will go and fill in between
upper_th = 0.5
lower_th = -0.5
# Copy original data, we are now going to modify
new_wave_y = np.copy(wave_y)
# Change values outside thresholds to threshold value for fill in
new_wave_y[new_wave_y < lower_th] = lower_th
new_wave_y[new_wave_y > upper_th] = upper_th
# Plot function
fig, ax = plt.subplots()
ax.plot(time_x, wave_y)
ax.grid()
ax.set_ylim([-1.25, 1.25])
ax.set_ylabel('y label')
ax.set_xlim([0, 125])
ax.set_xlabel('x label')
# Fill in area under the curve and the horizontal lines
ax.fill_between(x=time_x, y1=new_wave_y, where=(lower_th < new_wave_y), color='red', interpolate=True, alpha=.75)
ax.fill_between(x=time_x, y1=new_wave_y, where=(new_wave_y < upper_th), color='red', interpolate=True, alpha=.75)
# Horizontal lines
ax.axhline(upper_th, linestyle='--', color='black', label="upper_th: 0.5")
ax.axhline(lower_th, linestyle='--', color='orange', label='lower_th: - 0.5')
ax.legend()
plt.show()
You can get some more information in the Matplotlib fill between demo and the fill between docs.
Edit:
If you want to fill in below or above the threshold line, for example fill in below the lower threshold, you can modify the y curve so that the values above the threshold become the threshold value (same as before but reverse) and change the values in fill_between . The full script:
# Make a fake function
t = 20
fs = 10
samples = np.linspace(0, t, int(fs*t), endpoint=False)
wave_y = np.sin(samples)
time_x = np.arange(0, len(wave_y))
# Set upper and lower thresholds where horizontal lines will go and fill in between
upper_th = 0.5
lower_th = -0.5
# Copy original data, we are now going to modify
new_wave_y = np.copy(wave_y)
# Change values outside thresholds to threshold value for fill in
new_wave_y[new_wave_y > lower_th] = lower_th
# Plot function
fig, ax = plt.subplots()
ax.plot(time_x, wave_y)
ax.grid()
ax.set_ylim([-1.25, 1.25])
ax.set_ylabel('y label')
ax.set_xlim([0, 125])
ax.set_xlabel('x label')
# Fill in area under the curve and the horizontal lines
ax.fill_between(x=time_x, y1=new_wave_y, y2=lower_th, where=(new_wave_y < lower_th), color='red', interpolate=True, alpha=.75)
# Horizontal lines
ax.axhline(upper_th, linestyle='--', color='black', label="upper_th: 0.5")
ax.axhline(lower_th, linestyle='--', color='orange', label='lower_th: - 0.5')
ax.legend()
plt.show()

matplotlib bar plot spacings

I'm trying to plot 3 bars per value of N. The data is:
N=['A','B','C','D','E','F']
SF = (0.87,9.4,49,110,820,1752)
IRT = (0.34,25,210,487,2861,5541)
IRTO = (0.5,33,271,568,3031,6314)
These numbers of w (width), y_pos and figsize are basically just trial and error until it looks decent enough to my eye...
w = 1
y_pos = 4*np.arange(len(N))
fig, ax = plt.subplots(figsize=(10,5))
rects1 = ax.bar(y_pos - w, SF, align='center', alpha=0.7, width=w, color="lightgrey", log=True)
rects2 = ax.bar(y_pos , IRT, align='center', alpha=1.0, width=w, color="grey", log=True)
rects3 = ax.bar(y_pos + w, IRTO, align='center', alpha=1.0, width=w, color="black", log=True)
plt.xticks(y_pos,N,fontsize=13)
legend = ax.legend((rects1[0], rects2[0], rects3[0]), ('SF', 'IRT', 'IRTO'), loc='upper left')
plt.show()
All that produces the following plot:
This is pretty good, unfortunately I like to to make everything as perfect as possible so I want to do basically 4 thins:
reduce the space between the left axis and the first bar (and the last bar and the right axis).
increase the size of the top axis so that the highest bar is not as close to it
remove the middle ticks over the lower axis
add the blue ticks (between the bars) - see figure below:
I really tried to do this, but I couldnt find a way to do it yet.
I was able to do number 3 alone, but not number 4.
Thanks!

how to add variable error bars to scatter plot points with shared axes in python matplotlib

I have generated a plot that shows a topographic profile with points along the profile that represent dated points. However, these dated points also have symmetric uncertainty values/error bars (that typically vary for each point).
In this example, I treat non-dated locations as 'np.nan'. I would like to add uncertainty values to the y2 axis (Mean Age) with defined uncertainty values as y2err.
Everytime I use the ax2.errorbar( ... ) line, my graph is squeezed and distorted.
import numpy as np
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
#Longitude = x; Elevation = y
x = (-110.75696,-110.75668,-110.75640,-110.75612,-110.75584,-110.75556,-110.75528)
y = (877,879,878,873,871,872,872)
ax1.plot(x, y)
ax1.set_xlabel('Longitude')
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_ylabel('Elevation', color='b')
ax1.tick_params('y', colors='b')
ax2 = ax1.twinx()
# Mean Age, np.nan = 0.0
y2 = (np.nan,20,np.nan,np.nan,np.nan,np.nan,np.nan)
y2err = (np.nan,5,np.nan,np.nan,np.nan,np.nan,np.nan)
ax2.scatter(x, y2, color='r')
#add error bars to scatter plot points
# (??????) ax2.errorbar(x, y, y2, y2err, capsize = 0, color='black')
ax2.set_ylim(10,30)
ax2.set_ylabel('Mean Age', color='r')
ax2.tick_params('y', colors='r')
fig.tight_layout()
plt.show()
If I do not apply the ax2.errorbar... line my plot looks like the first image, which is what I want but with the points showing uncertainty values (+/- equal on both side of point in the y-axis direction).
Plot of Elevation vs Age without error bars
When I use the ax2.errorbar line it looks like the second image:
Plot when using ax2.errorbar line
Thanks!

Manipulation on vertical space in matplotlib subplots

I want to reduce the verticalspacing between subplot. Surfing along the web I just have found how to reduce the horizontal spacing, something like
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=4, ncols=4)
fig.tight_layout() # Or equivalently, "plt.tight_layout()"
fig.subplots_adjust(hspace=0.5)
plt.show()
The hspace thing is the one that manipulates such behaviour, but apparently there's no vspace.
EDIT:
This does not reduce the space between the y-axis, that is what I want to manipulate.
As you said in your question hspace reduces the vertical spacing between subplots. The equivalent for horizontal spacing between subplots is wspace. Below is an example:
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(nrows=2, ncols=2)
fig.tight_layout()
ax1.plot(x, y)
ax2.scatter(x, y)
ax3.scatter(x, y)
ax4.scatter(x, y)
fig.subplots_adjust(wspace=0.2)
plt.show()
Using a value for 1 for wspace gives
Using 0.2 as the value of wspace gives
An alternative approach is to pass the gridspec_kw argument a dict with keys wspace / hspace:
Example
fig, axes = plt.subplots(nrows=2, ncols=2, gridspec_kw={'hspace': 0.2, 'wspace': 0.9})
plt.tight_layout()
for ax, color in zip(axes.ravel(), list('rgbk')):
ax.scatter(np.arange(100), np.random.randn(100), color=color)
If I understood your question correctly, you want to reduce the vertical spacing, which is not what I have seen in all of the answers above.
If I am correct, you should reduce the hspace from 0.5 to 0.2, for instance. That's because hspace does not stand for horizontal spacing, it stands for height spacing, which is what you need.