Is there any simpler way to have this line (with two annotations) using matplotlib?
The idea is just draw a line showing a interval ([0,T]) and some points with annotations. This code is too big for such a small thing.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ylim(-.3,.3)
plt.xlim(0, 1)
xmin, xmax = ax.get_xlim()
# removing the default axis on all sides:
for side in ['bottom','right','top','left']:
ax.spines[side].set_visible(False)
# removing the axis ticks
plt.xticks([])
plt.yticks([])
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
# draw x and y axis
ax.arrow(xmin, 0, xmax-xmin, 0, fc='k', ec='k',
length_includes_head= True, clip_on = False)
esp=0.05
ax.text(0.5, esp, r'$\Delta t$', ha='center')
ax.text(0.45, 0, r'$|$', ha='center')
ax.text(0.45, -esp, r'$t_i$', ha='center')
ax.text(0.55, 0, r'$|$', ha='center')
ax.text(0.55, -esp, r'$t_{i+1}$', ha='center')
ax.text(0, 0, r'$|$', ha='center')
ax.text(0, -esp, r'$0$', ha='center')
ax.text(1, 0, r'$|$', ha='center')
ax.text(1, -esp, r'$T$', ha='center')
plt.show()
One could use the xaxis as the line to draw the intervals. That would allow to use its ticks and ticklabels for the annotations.
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
plt.subplots_adjust(bottom=0.5, top=0.7)
for side in ['right','top','left']:
ax.spines[side].set_visible(False)
ax.set_yticks([])
ticks=[0,.45,.55,1]
ticklabels=[0,r'$t_i$', r'$t_{i+1}$',r'$T$']
ax.set_xticks(ticks)
ax.set_xticklabels(ticklabels)
ax.tick_params(direction='inout', length=10,pad=7)
ax.text(0.5, 0.2, r'$\Delta t$', ha='center')
plt.show()
I found similar solution using the x-axis but it is a little more compact
plt.ylim(0,.3)
plt.xticks([0, 0.45, 0.55, 1],
('$0$', '$t_i$', '$t_{i+1}$', '$T$'),
size=20,
verticalalignment='top')
plt.tick_params(axis='x',
direction='inout',
length=20,
pad=15,
bottom=True,
top=False)
Another approach is drawing the ticks as scatterplot and annotating each, i.e.
for x, label in [(0, '$0$'), (0.45, '$t_i$'), (0.55, '$t_{i+1}$'), (1, '$T$')]:
plt.scatter(x, 0, s=50, marker='|')
plt.annotate(label, [x,-0.05], size=20)
Related
I'm plotting a bar graphic and horizontal spans with this code:
fig = plt.figure('Graphic', figsize=(20,15), dpi=400)
ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75])
data.plot('DATE',["PP"],kind='bar',color='black', fontsize = 15.0,ax=ax1,alpha=1)
data.plot('DATE',['PP'],kind='line',marker='*',style=['--'],linewidth=1,color='gray', ms=5,ax=ax1)
ax1.axhspan(0, 1, facecolor='lightyellow', alpha=1)
ax1.axhspan(1, 1.5, facecolor='yellow', alpha=1)
ax1.axhspan(1.5, 2, facecolor='lime', alpha=1)
ax1.axhspan(2, 3.5, facecolor='green', alpha=1)
ax1.axhspan(0, -1, facecolor='bisque', alpha=1)
ax1.axhspan(-1, -1.5, facecolor='orange', alpha=1)
ax1.axhspan(-1.5, -2, facecolor='pink', alpha=1)
ax1.axhspan(-2, -3.5, facecolor='red', alpha=1)
The issue is that spans are hiding the Bar graphic. I would like to be able to visualize the spans with the bar graphs. Both with alpha=1. I don't want to reduce the alpha values.
Is this possible?
Thanks in advance.
I am displaying the image with axhspans with alpha=1 covering the bar charts.
I noticed two things that needed to change.
When you use pandas line and bar plots with X-axis being dates, there was/is a bug. Refer to this link. The workaround used here is what was there. Using matplotlib plot instead of pandas helped resolve this.
Refer to zorder. You can specify the order of the various components (line plot, bar, spans) to tell it what will come on top of what. Higher the zorder, the higher the plot will be. I have used 1 for the spans zorder, 2 for the bar plot zorder and 2 for line plot.
Updated code is below. See if this helps.
fig = plt.figure('Graphic', figsize=(20,15), dpi=400)
ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75])
data.plot('DATE',["PP"],kind='bar',color='black', fontsize = 15.0,ax=ax1,alpha=1, zorder=2) ## Added zorder
# Changed to matplotlib, increased linewidth to 3 so you can see it and zorder=3
ax1.plot(data[['PP']], marker='*',ls='--',linewidth=3,color='gray', ms=5, zorder=3)
## All zorder = 0
ax1.axhspan(0, 1, facecolor='lightyellow', alpha=1, zorder=1)
ax1.axhspan(1, 1.5, facecolor='yellow', alpha=1, zorder=1)
ax1.axhspan(1.5, 2, facecolor='lime', alpha=1, zorder=1)
ax1.axhspan(2, 3.5, facecolor='green', alpha=1, zorder=1)
ax1.axhspan(0, -1, facecolor='bisque', alpha=1, zorder=1)
ax1.axhspan(-1, -1.5, facecolor='orange', alpha=1, zorder=1)
ax1.axhspan(-1.5, -2, facecolor='pink', alpha=1, zorder=1)
ax1.axhspan(-2, -3.5, facecolor='red', alpha=1, zorder=1)
Plot
The order of display in the pandas plot is not adjustable, so I guess we have to deal with it in matplotlib. ax is set up with a line chart and horizontal fill, and a bar chart is added as a second axis. Then I get the order of the line chart, add 1 to the value of the line chart, and set the display order to the bar chart. Since no data was provided, stock price data was used as a sample.
import yfinance as yf
import pandas as pd
data = yf.download("AAPL", start="2022-06-01", end="2022-09-01")
data.index = pd.to_datetime(data.index)
import matplotlib.pyplot as plt
fig = plt.figure('Graphic', figsize=(10,7.5), dpi=100)
ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75])
ax1.plot(data.index, data['Close'], marker='*', linestyle='--', linewidth=1, color='gray', ms=5)
ax1.axhspan(170, 180, facecolor='lightyellow', alpha=1)
ax1.axhspan(160, 170, facecolor='yellow', alpha=1)
ax1.axhspan(150, 160, facecolor='lime', alpha=1)
ax1.axhspan(145, 150, facecolor='green', alpha=1)
ax1.axhspan(140, 145, facecolor='bisque', alpha=1)
ax1.axhspan(135, 140, facecolor='orange', alpha=1)
ax1.axhspan(130, 135, facecolor='pink', alpha=1)
ax1.axhspan(120, 130, facecolor='red', alpha=1)
ax2 = ax1.twinx()
ax2.bar(x=data.index, height=data['Volume'], color='black')
ax2.set_zorder(ax1.get_zorder()+1)
ax2.set_frame_on(False)
ax1.set_ylim(120, 180)
plt.show()
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.
The following code returns the nice graph provided here:
.
However, when I add a line code such as ax.set_ylim(ymax=14) I get the following graph:
.
Clearly there is something wrong. Could anyone help me figure out what?
Thanks!
fig, ax = plt.subplots(figsize=(15, 10))
# Set bins for histograms:
bins = np.arange(0, 40+0.5, 1)
# set interval on this bins for curve fitting:
xplot = np.linspace(min(bins), max(bins), 100)
# -- make a histogram
ax.grid(axis='y', zorder=0)
plt.axvline(x=af_farm_w_speedb.mean(), color='black', linestyle='dashed', linewidth=2, label = "Mean speed at control")
plt.axvline(x=af_farm_w_speedw.mean(), color='red', linestyle='dashed', linewidth=2, label = "Mean speed at treatment")
ax.hist([af_farm_w_speedb, af_farm_w_speedw], bins=bins, density=True, alpha = 1, align='left', zorder=1, rwidth=0.8, color=['lightsteelblue','grey'], label = ['Records at control', 'Records at treatment'])
ax.tick_params(axis='both', which='major', labelsize=30)
ax.set_yticklabels([0, 2, 4, 6, 8, 10, 12])
ax.set_title('Post-farm wind speed distribution', fontsize=35)
(scale, a, shape, c) = stats.exponweib.fit(af_farm_w_speedw, f0=1, floc=0)
ax.plot(xplot, stats.exponweib.pdf(xplot, *stats.exponweib.fit(af_farm_w_speedb, 1, 1, scale=1, loc=0)), zorder=3, color = "black", linewidth=1.6, label="Weibull fit at control")
ax.plot(xplot, stats.exponweib.pdf(xplot, *stats.exponweib.fit(af_farm_w_speedw, 1, 1, scale=1, loc=0)), zorder=3, color = "red", linewidth=1.6, label="Weibull fit at treatment")
fig.text(0.6, 0.55, "Mean speed at treatment: {:.4g}".format(af_farm_w_speedw.mean()), fontsize=18)
fig.text(0.6, 0.6, "Mean speed at control: {:.4g}".format(af_farm_w_speedb.mean()), fontsize=18)
ax.legend(prop=dict(size=18))
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)
This is the sine and cosine plot I draw using matplotlib. But the tick labels are below the plot and can hardly seen.
My python code is:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6), dpi=96)
plt.subplot(111)
X = np.linspace(-np.pi, np.pi, 256, endpoint=True)
C,S = np.cos(X), np.sin(X)
plt.plot(X, C, color="blue", linewidth=2.5, linestyle="-", label="consine")
plt.plot(X, S, color="red", linewidth=2.5, linestyle="-", label="sine")
plt.xlim(X.min()*1.1, X.max()*1.1)
plt.ylim(C.min()*1.1, C.max()*1.1)
plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],
[r'$-\pi$', r'$-\frac{\pi}{2}$', r'$0$', r'$+\frac{\pi}{2}$', r'$+\pi$'])
plt.yticks([-1, 1],
[r'$-1$', r'$+1$'])
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
plt.legend(loc='upper left', frameon=False)
for label in ax.get_xticklabels()+ax.get_yticklabels():
label.set_fontsize(16)
label.set_bbox(dict(facecolor='green', edgecolor='None', alpha=0.2))
plt.savefig("figures/exercise10.png", dpi=120)
plt.show()
So, how should I set a tick label above a plot?
Thank you!
Possibly you want to set the labels and the axes spines on top of the lines. This can easily be achieved with the "axes.axisbelow" rcParam.
plt.rcParams["axes.axisbelow"] = False