Creating labelled horizontal lines on a plot - matplotlib

I'm trying to reproduce this diagram:
but I'm having trouble creating the horizontal lines with bars. I've tried annotate and hlines but they don't quite give the effect I'm after.
import matplotlib.pyplot as plt
plt.grid(which = 'both')
plt.xticks(fontsize = 16)
plt.yticks(fontsize = 16)
plt.xlim(-0.5,8)
plt.ylim(-0.5,10)
plt.xlabel('Redshift, z', fontsize = 16)
plt.hlines(8, 0, .3)
plt.annotate(r'H$\alpha$', fontsize = 16, xy = (0,8), xycoords='data', xytext=(0,8),
textcoords='data',
arrowprops=dict(arrowstyle='<|-|>', connectionstyle='arc3', color = 'k', lw=2))
fig = plt.gcf()
width, height = 15,35 # inches
fig.set_size_inches(width, height, forward = True)
plt.show()
What's the best way to produce the bars like this?

I would use annotate directly, but for more flexibility, I would separate the drawing of the horizontal bars and the corresponding text
plt.figure()
plt.grid(which = 'both')
plt.xticks(fontsize = 16)
plt.yticks(fontsize = 16)
plt.xlim(-0.5,8)
plt.ylim(-0.5,10)
plt.xlabel('Redshift, z', fontsize = 16)
bar_ys = [8,4]
bar_xs = [[0,6],[3,5]]
bar_texts = [r'H$\alpha$',r'H$\beta$']
bar_color = ['k','orange']
for y,xs,t,c in zip(bar_ys,bar_xs,bar_texts,bar_color):
plt.annotate('', xy = (xs[0],y), xycoords='data', xytext=(xs[1],y),
arrowprops=dict(arrowstyle='|-|', color=c, lw=2, shrinkA=0, shrinkB=0))
plt.annotate(t, xy = (xs[1],y), xycoords='data', xytext=(-5,5), textcoords='offset points',
fontsize = 16, va='baseline', ha='right', color=c)
plt.show()

The accepted answer works perfectly, thank you.
In addition, I automated the colours thus:
colors = iter(cm.tab10(np.linspace(0,0.8,13)))
colour = 'k'
for y,xs,t in zip(bar_ys,bar_xs,bar_texts):
plt.annotate('', xy = (xs[0],y), xycoords='data', xytext=(xs[1],y),
arrowprops=dict(arrowstyle='|-|', color=colour, lw=2, shrinkA=0, shrinkB=0))
plt.annotate(t, xy = (xs[1],y), xycoords='data', xytext=(-5,5), textcoords='offset points',
fontsize = 16, va='baseline', ha='right', color=colour)
colour = next(colors)

Related

Grid lines not aligned with cells when defining minor ticks in Matplotlib Heatmap

I am using the code below to generate this heatmap:
dim = np.arange(1, 32, 1)
fig, ax = plt.subplots(figsize=(7,9))
heatmap = ax.imshow(h.T, cmap=plt.cm.get_cmap('Blues', 4), clim=[1,144])
cbaxes = fig.add_axes([.8, .35, .04, .3])
cbar = fig.colorbar(heatmap, ticks = [1, 36, 72, 108, 144], label = 'Number of valid records per day', cax = cbaxes)
ax.set_ylabel("Days", fontsize=15)
ax.set_xlabel("Months", fontsize=15)
ax.set_title("Number of valid records per day", fontsize=20)
ax.set_yticks(range(0,31))
ax.set_yticklabels(dim, ha='center', minor=False, fontsize=12)
ax.set_xticks(range(0,13,1))
ax.set_xticklabels(ylabel, rotation = 45, ha = 'right')
ax.set_facecolor('gray')
cbar.set_label('Number of valid records')
ax.xaxis.set_minor_locator(MultipleLocator(0.5))
ax.yaxis.set_minor_locator(MultipleLocator(0.5))
ax.tick_params(axis='y', which='major', pad=10)
ax.grid(which = 'minor', color = 'w')
fig.show()
As you can see there is a slight offset of the gridlines with respect to the heat map cells. Why is that? How can I fix it?
Thanks to the comment left by Jody Klymak, I added the following line of code at the beginning of my notebook and it solved the problem:
matplotlib.rcParams['figure.dpi'] = 300

set a common color bar for subplot

I am going to plot a numpy array by plt module. I have no idea how I can just have a one shared color bar for my subplot. Or even if it plots with two, I would like to clim it to just plot in the range of (-0.4-0.4) for both colorbars.
Thanks all
Here is my codes:
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig,(ax1,ax2) = plt.subplots(1,2,figsize=(10, 10))
im1 = ax1.imshow(input[:,:,141],cmap = 'Spectral_r')
divider = make_axes_locatable(ax1)
cax = divider.append_axes("right", size="5%", pad=0.05)
fig.colorbar(im1, cax=cax, orientation='vertical')
im2 = ax2.imshow(out_put[:,:,141], cmap = 'Spectral_r')
divider = make_axes_locatable(ax2)
cax = divider.append_axes("right", size="5%", pad=0.05)
fig.colorbar(im2, cax=cax, orientation='vertical')
ax1.set_title('Input', fontsize = 16)
ax2.set_title('Output of model', fontsize = 16)
ax2.set_axis_off()
ax1.set_axis_off()
plt.show()
You can just call colorbar once, and pass both ax1,ax2 to ax. For the clim option, my matplotlib says that cb.set_clim is deprecated. It might be better to pass vmin,vmax to imshow:
fig,(ax1,ax2) = plt.subplots(1,2,figsize=(13, 5))
im1 = ax1.imshow(np.random.uniform(-1,1, size=(10,10)),
vmin=-0.4, vmax=0.4,
cmap = 'Spectral_r')
im2 = ax2.imshow(np.random.uniform(-1,1, size=(10,10)),
vmin=-0.4, vmax=0.4,
cmap = 'Spectral_r')
cb = fig.colorbar(im2, ax=(ax1, ax2), orientation='vertical')
cb.set_clim(-0.4,0.4)
Output:

Add vertical line to pandas df.plot of timeseries

I have a time series plot and I would like to add a vertical line to it at event time. If I use this code:
event_time = pd.to_datetime('10/12/2016 06:21:00')
ax = df_stats_t.plot(x = 't', y='t_TI_var_pwr', linestyle='none',...
color = 'black', marker = 'o')
ax1 = ax.twinx()
ax1.axvline(event_time, color='red', linestyle='-')
df_stats_t.plot(x='t',y='t_TI_var_ws',ax=ax1, linestyle='none', ...
color = 'green', marker = 'o')
It takes a subset of the time series starting at event_time and doesn't produce a vertical line.
If I move ax1.axvline(event_time, color='red', linestyle='-') to the bottom, I get the plot I want but the vertical line is still missing.
event_time = pd.to_datetime('10/12/2016 06:21:00')
ax = df_stats_t.plot(x = 't', y='t_TI_var_pwr', linestyle='none',...
color = 'black', marker = 'o')
ax1 = ax.twinx()
df_stats_t.plot(x='t',y='t_TI_var_ws',ax=ax1, linestyle='none',...
color = 'green', marker = 'o')
ax1.axvline(event_time, color='red', linestyle='-')
How can I get the vertical line to discplay at x = event_time for all y values?
works with plt
ax = df_stats_t.plot(x = 't', y='t_TI_var_pwr', linestyle='none', color = 'black', marker = 'o')
ax1 = ax.twinx()
df_stats_t.plot(x='t',y='t_TI_var_ws',ax=ax1, linestyle='none', color = 'green', marker = 'o')
plt.axvline(event_time, color='red', linestyle='-')

Issue with margins around a map in geopandas

I cannot seem to solve some margin/location issue between the GeoPandas map and added annotations. Annotations are added by functions addChartSignature and addTitle and they cause a terrible layout.
I have managed to sort some vertical margins issue with a hack, see comment ### to deal with vertical margin issue, however I cannot seem to deal with the margin on the left side: I would like the map, the signature and the title to be aligned with the left side with a very small margin (like on the right side).
I added the variable righthspace to move annotations right, but it doesn't help and it doesn't feel right anyway.
Below a code sample which summarize the issue
from __future__ import division
import matplotlib.pyplot as plt
import geopandas
from mpl_toolkits.axes_grid1 import make_axes_locatable
def addChartSignature(ax, vspace=0, righthspace=0):
ax.annotate('',
xy=(0.97, 0.05 + vspace),
xycoords='figure fraction',
xytext=(0.03 + righthspace,0.05 + vspace),
textcoords='figure fraction',
arrowprops=dict(arrowstyle="-",
linewidth=0.7,
facecolor='grey',
alpha=.7,
edgecolor='grey'),
horizontalalignment = 'center',
verticalalignment='bottom')
ax.annotate(u" ©myCompany",
xy=(0.5, 0.5),
xycoords='figure fraction',
xytext= (0.03 + righthspace,0.01+vspace),
textcoords='figure fraction',
ha="left",
va="bottom",
color = 'grey',
alpha = .7,
fontsize = 11)
ax.annotate(u"Source: Internal",
xy=(0.5, 0.5),
xycoords='figure fraction',
xytext=(0.97,0.01+vspace),
textcoords='figure fraction',
ha="right",
va="bottom",
color = 'grey',
alpha = .7,
fontsize = 11)
def addTitle(ax, vspace=0, righthspace=0):
ax.annotate("My Chart Title",
xy=(0.5, 0.5),
xycoords='figure fraction',
xytext=(0.01+righthspace, 0.985+vspace),
textcoords='figure fraction',
ha="left",
va="top",
color = 'black',
alpha = .75,
fontsize = 19,
weight = 'bold')
path = geopandas.datasets.get_path('naturalearth_lowres')
world = geopandas.read_file(path)
world = world[(world.pop_est>0) & (world.name!="Antarctica")]
plt.style.use('fivethirtyeight')
fig = plt.figure(figsize=(8, 6))
ax = fig.add_axes([0., 0., 1, 1])
ax = world.plot(color="lightgrey", ax=ax)
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size="3%", pad=-1.3)
world.dropna().plot(
column='pop_est',
ax=ax,
legend=True,
cax=cax,
cmap='RdYlGn',
)
ax.grid(color='#F8F8F8')
ax.set_xticklabels([])
ax.set_yticklabels([])
### to deal with vertical margin issue
ax.set_aspect(aspect=4./3)
ax.margins(0)
ax.apply_aspect()
bbox = ax.get_window_extent().inverse_transformed(fig.transFigure)
w,h = fig.get_size_inches()
fig.set_size_inches(w*bbox.width, h*bbox.height)
addChartSignature(ax, righthspace = 0.05)
addTitle(ax, righthspace = 0.05)

Adding edges to bars in matplotlib

I have been following the example provided in:
https://matplotlib.org/examples/api/barchart_demo.html
My problem is that I want to add edges to the bars. But when I set the
linewidth=1, edgecolor='black'
parameters, the edges are only applied to the first pair of bars, leaving the remaining pairs unchanged.
"""
========
Barchart
========
A bar plot with errorbars and height labels on individual bars
"""
import numpy as np
import matplotlib.pyplot as plt
N = 5
men_means = (20, 35, 30, 35, 27)
men_std = (2, 3, 4, 1, 2)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men_means, width, color='r', yerr=men_std,linewidth=1, edgecolor='black')
women_means = (25, 32, 34, 20, 25)
women_std = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind + width, women_means, width, color='y', yerr=women_std, linewidth=1, edgecolor='black')
# add some text for labels, title and axes ticks
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5'))
ax.legend((rects1[0], rects2[0]), ('Men', 'Women'))
def autolabel(rects):
"""
Attach a text label above each bar displaying its height
"""
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width()/2., 1.05*height,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1)
autolabel(rects2)
plt.show()
Thanks for your help.
David.