I'm trying to make my first plots in python using matplotlib, but i would like the text in the plot to be "outside" the plot i.e. next to the line instead of above it or under it.
plt.figure()
plt.scatter(mean, diff, color='k')
plt.axhline(md, color='black', linestyle='-', lw=3)
plt.axhline(md + 1.96*sd, color='black', linestyle='--')
plt.axhline(md - 1.96*sd, color='black', linestyle='--')
plt.axhline(0, color='black', linestyle='--')
plt.ylim(-max(diff)*2, max(diff)*2)
plt.xlabel('Mean')
plt.ylabel('Difference')
plt.title('Bland altman plot for ' + variable)
txt1=('+1.96 SD')
txt2=('-1.96 SD')
txt3 =('Mean')
x = max(mean)
plt.text(x, md+1.96*sd, txt1, horizontalalignment='left', verticalalignment='bottom', fontweight='bold')
plt.text(x, md-1.96*sd, txt2, horizontalalignment='left', verticalalignment='top', fontweight='bold')
plt.text(x, 0.1, txt3)
The result is:
The main ingredient you still need to position the labels at the edge of the axes, is the coordinate of the edge of the axes. You can get those via plt.gca().get_xlim(). The upper limit can then be used as x position of the text label.
import matplotlib.pyplot as plt
import numpy as np
X = np.random.normal(size=(12))
Y = np.random.normal(size=(12))
plt.figure()
plt.scatter(X, Y, color='k')
plt.axhline(Y.mean(), color='black', linestyle='-', lw=3)
plt.axhline(Y.mean() + 1.96*Y.std(), color='black', linestyle='--')
plt.axhline(Y.mean() - 1.96*Y.std(), color='black', linestyle='--')
plt.axhline(0, color='black', linestyle='--')
txt1=(' +1.96 SD')
txt2=(' -1.96 SD')
txt3 =(' Mean')
x0,x1 = plt.gca().get_xlim()
plt.text(x1, Y.mean() + 1.96*Y.std(), txt1, ha='left', va='center', fontweight='bold')
plt.text(x1, Y.mean() - 1.96*Y.std(), txt2, ha='left', va='center', fontweight='bold')
plt.text(x1, 0, txt3, ha='left', va='center',)
# make more space on right side to host the labels
plt.subplots_adjust(right=0.8)
plt.show()
Note that this is the very basic (but also quite understandable) version.
If you're comfortable with transformations, you can use the technique in this question:
Add a label to y-axis to show the value of y for a horizontal line in matplotlib
Related
I am trying to do EDA with the Kaggle dataset link
I made a plot with 3 subplots and have plotted 3 vertical lines on the basis of mean, median and mode. is there any way to show these 3 lines in a legend?
This is my code
def plott(data):
fig, axes = plt.subplots(3, sharex=True, figsize=(15, 15),gridspec_kw={"height_ratios": (1, 0.2, 0.6)})
fig.suptitle('Spread of Data for ' + data.name, fontsize=20, fontweight='bold')
sns.histplot(data, kde=True, binwidth=1, ax=axes[0])
sns.boxplot(x=data, orient='h', ax=axes[1])
sns.violinplot(x=data, ax=axes[2])
axes[0].set_xlabel('')
axes[1].set_xlabel('')
axes[2].set_xlabel('')
axes[0].axvline(data.mean(), color='r', linewidth=2, linestyle='solid')
axes[0].axvline(data.median(), color='r', linewidth=2, linestyle='dashed')
axes[0].axvline(data.mode()[0], color='r', linewidth=2, linestyle='dotted')
axes[1].axvline(data.mean(), color='r', linewidth=2, linestyle='solid')
axes[1].axvline(data.median(), color='r', linewidth=2, linestyle='dashed')
axes[1].axvline(data.mode()[0], color='r', linewidth=2, linestyle='dotted')
axes[2].axvline(data.mean(), color='r', linewidth=2, linestyle='solid')
axes[2].axvline(data.median(), color='r', linewidth=2, linestyle='dashed')
axes[2].axvline(data.mode()[0], color='r', linewidth=2, linestyle='dotted')
axes[0].tick_params(axis='both', which='both', labelsize=10, labelbottom=True)
axes[1].tick_params(axis='both', which='both', labelsize=10, labelbottom=True)
axes[2].tick_params(axis='both', which='both', labelsize=10, labelbottom=True)
plott(df['Age'])
This is the resulting plot
Is there a way to add the legend in here in accordance to the 3 vertical lines
like this with each line type denoting the value?
Also, how to add more values in x axis of all three graphs?
like make it interval of 5 or 2 years apart?
Thanks
Give the axvlines a "label" value, then call plt.legend after plotting it.
Example:
import matplotlib.pyplot as plt
plt.plot([1,2,3],[1,2,3],label="Test")
plt.axvline(x=0.22058956, label="Test2", color="red")
plt.legend()
Output:
I plotted an array in seaborn heatmap, and I want to add tick limits to the axis.
My code:
# plot
eixoz = numpy.linspace(0, Z)
eixor = numpy.linspace(ra, R, nr)
eixox = D
numpy.meshgrid(eixoz, eixor)
ax = seaborn.heatmap(eixox)
ax.invert_yaxis()
plt.xlabel("Eixo z", fontsize=20)
plt.ylabel("Eixo r", fontsize=20)
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
ax.collections[0].colorbar.set_label("Celsius", fontsize=20)
plt.show()
How can I add those limit ticks in blue? And also, how can I resize the color bar numbers?
The size of the colorbar tick labels can be changed via ax.collections[0].colorbar.ax.tick_params(labelsize=20).
Text at the start and end of the axes can be place using the axes transform, where 0 is the left (or bottom) and 1 is the right (or top) of the axes. Negative values (or values larger than 1) are proportionall outside the axes area. Horizontal and vertical lines can use the same transform, but unlike text need clip_on=False to be drawn outside the axes area.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
len_eixoz = 20
eixox = np.repeat(np.arange(37.55, 37.66, 0.02), len_eixoz).reshape(-1, len_eixoz)
ax = sns.heatmap(eixox)
ax.invert_yaxis()
ax.set_xlabel("Eixo z", fontsize=20)
ax.set_ylabel("Eixo r", fontsize=20)
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
ax.collections[0].colorbar.set_label("Celsius", fontsize=20)
cbar = ax.collections[0].colorbar.ax.tick_params(labelsize=20)
x0, x1 = 1, 2
y0, y1 = 0, 1
ax.text(0, -0.07, x0, ha='center', va='top', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.text(1, -0.07, x1, ha='center', va='top', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.text(-0.05, 0, y0, ha='right', va='center', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.text(-0.05, 1, y1, ha='right', va='center', fontsize=20, color='steelblue', transform=ax.transAxes)
ax.vlines([0, 1], [0, 0], [-0.06, -0.06], color='crimson', clip_on=False, transform=ax.transAxes)
ax.hlines([0, 1], [0, 0], [-0.04, -0.04], color='crimson', clip_on=False, transform=ax.transAxes)
plt.tight_layout()
plt.show()
Note that calling sns.set(font_scale=1.8) at the start would scale all fonts.
I'm trying to draw two rows with three columns of pcolormesh plots and a combined colorbar for all plots. So far it seems to work. However, I'm sure I'm not using the most elegant way...
The only problem I have, is that I can't decrease the horizontal spacing any further. The following line should set the horizontal spacing to zero:
fig.subplots_adjust(left=0.05, right=0.98, top=0.93, bottom=0.00, wspace=0, hspace=0.03)
But this does not work in conjunction with
ax.set_aspect('equal')
I've attached a small code snippet that creates the following figure:
Example figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
rows = 2
columns = 3
fig = plt.figure()
gs = gridspec.GridSpec(rows+1, columns)
lines = []
x = np.linspace(1,10,100)
y = x
X, Y = np.meshgrid(x,y)
Z = np.random.rand(100,100)
lines = []
for i in range(rows):
lines.append([])
for j in range(columns):
ax = fig.add_subplot(gs[i, j])
line = ax.pcolormesh(X, Y, Z, cmap=plt.cm.Reds)
lines[i].append(line)
ax.set_aspect('equal')
for tick in ax.get_xticklabels():
tick.set_rotation(45)
if i!=rows-1:
ax.set_xticklabels([])
if j!=0:
ax.set_yticklabels([])
#title
props = dict(boxstyle='round', facecolor='white', alpha=0.7)
ax.text(0.05, 0.95, "plot (%i, %i)" % (i,j), transform=ax.transAxes, fontsize=5,
verticalalignment='top', bbox=props)
ax.tick_params(labelsize=7)
cb_ax = fig.add_subplot(gs[-1,:])
cb_ax.set_aspect(0.05)
cbar = fig.colorbar(lines[0][0], cax=cb_ax, orientation='horizontal')
cb_ax.tick_params(labelsize=7)
fig.subplots_adjust(left=0.05, right=0.98, top=0.93, bottom=0.00, wspace=0, hspace=0.03)
#fig.tight_layout()
fig.text(0.5, 0.2, "x axis", ha='center', va='center')
fig.text(0.5, 0.97, "overall title", ha='center', va='center')
fig.text(0.02, 0.5, "y axis", ha='center', va='center', rotation='vertical')
fig.text(0.5, 0.02, "quantity [unit]", ha='center', va='center',)
plt.savefig("test.png", dpi=600)
I am plotting 5 sets of data onto a single graph. For each set of data, I need to plot the scatter graph of the points (which I do using seaborn.regplot) and a line function which was separately fitted to the points.
The problem is that I can add the lines to the legend, but I can't add the markers from the regplot since I don't have any object handle on it. Here is my code:
f, ax = plt.subplots()
#Plotting the scatter points onto the axes object ax
sns.regplot(x='logF', y='R', data=MeanParams[(0 < MeanParams.M) & (0.2 > MeanParams.M)],
fit_reg=False, color='g', marker='+', ax=ax)
sns.regplot(x='logF', y='R', data=MeanParams[(0.2 < MeanParams.M) & (0.5 > MeanParams.M)],
fit_reg=False, color='y', marker='x', ax=ax)
sns.regplot(x='logF', y='R', data=MeanParams[(0.5 < MeanParams.M) & (1.5 > MeanParams.M)],
fit_reg=False, color='r', marker='x', ax=ax)
sns.regplot(x='logF', y='R', data=MeanParams[(1.5 < MeanParams.M) & (3.5 > MeanParams.M)],
fit_reg=False, color='b', marker='x', ax=ax)
sns.regplot(x='logF', y='R', data=MeanParams[(3.5 < MeanParams.M)],
fit_reg=False, color='k', marker='+', ax=ax)
#plotting the lines onto the same axes object
line1, = ax.plot(x, y_0, 'k-', linewidth=2)
line2, = ax.plot(x, y_1, 'k--', linewidth=2)
line3, = ax.plot(x, y_2, 'k-.', linewidth=3)
line4, = ax.plot(x, y_3, 'k:', linewidth=3)
line5, = ax.plot(x, y_4, 'r--', linewidth=2)
#creating the legend
ax.legend((line1,line2,line3,line4,line5),
(r'0.0 - 0.2', r'0.2 - 0.5', r'0.5 - 1.5', r'1.5 - 3.5', r'3.5 + '),
title='Mass Range', loc='upper left')
As you can see, I have handles on the line objects produced by ax.plot() but since I did the scatter plot with seaborn.regplot() I don't have any handle on the markers.
One easy way to solve this would be to persumably just use the pyplot scatter instead. But I guess I'm just asking out of curiosity, is there a way to pull out the line/marker objects so that I can put them into a legend as well?
I need log scale x-axis. Here is my code:
plt.bar(critical_pressures_reversed, mercury_volume_scaled, bottom = 0, log = True, linewidth=0, align="center",width=.1)
plt.title("Mercury intrusion", fontsize=20)
plt.xlabel("Critical Pressure $P_c \, [kPa]$", fontsize=16)
plt.ylabel("Mercury volume $V_m \, [\mu m^3]$", fontsize=16)
plt.grid(b=True, which='major', color='black', linestyle='-', linewidth=1)
plt.grid(b=True, which='minor', color='gray', linestyle='-', linewidth=0.15)
frame = plt.gca()
figure = plt.gcf()
frame.set_xscale('log')
frame.set_axisbelow(True)
figure.set_size_inches(12, 6)
plt.savefig("intrusion_6n_press.png", dpi=300, bbox_inches='tight')
plt.close()
Resulting plot:
How to force pyplot to draw bars with constant width?
I am using matplotlib (1.4.2)
You could use plt.fill but the bar width should change based on the log. For instance, for a random dataset, the following lines:
import matplotlib.pyplot as plt
import numpy as np
x, y = np.random.randint(1,51,10), np.random.randint(1,51,10)
width = 1e-2
for i in range(len(x)):
plt.fill([10**(np.log10(x[i])-width), 10**(np.log10(x[i])-width), 10**(np.log10(x[i])+width), 10**(np.log10(x[i])+width)],[0, y[i], y[i], 0], 'r', alpha=0.4)
plt.bar(x,y, bottom = 0, log = True, linewidth=0, align="center",width=.1, alpha=0.4)
will produce the figure below. Everything you need to do is to choose a proper width parameter.