Data and Imports
import matplotlib.pyplot as plt
import seaborn as sns
# create example data
data = {'x': ['A', 'B', 'C', 'D'],
'y': [10, 5, 8, 12]}
Option 1
# create bar plot
fig, ax = plt.subplots(figsize=(9, 6))
sns.barplot(y='x', x='y', data=data, color='tab:blue', width=0.4, ax=ax)
# extract x, y and text for each xtick label
ytls = [(v.get_position()[0], v.get_position()[1], v.get_text()) for v in ax.get_yticklabels()]
# set the targets
targets = {'A': 6, 'B': 4, 'C': 1, 'D': 2}
# iterate through the bar containers
for c in ax.containers:
# extract the value for the bottom edge of the bar patch
y0s = [p.get_y() for p in c]
# iterate through the values
for y0, (x, y, t) in zip(y0s, ytls):
diff = y - y0
ymin = y - diff
ymax = y + diff
# remove the print lines, they're just for demonstrating
print(f'Bottom Corner of Patch: {y0}')
print(f'x: {x} y: {y} text: {t}')
print(f'Patch diff from center: {diff} ymin: {ymin} ymax: {ymax}')
print('\n')
# add the lines
ax.vlines(x=targets[t], color='red', linestyle='-', linewidth=3, ymin=ymin, ymax=ymax)
printed output
Bottom Corner of Patch: -0.2
x: 0 y: 0 text: A
Patch diff from center: 0.2 ymin: -0.2 ymax: 0.2
Bottom Corner of Patch: 0.8
x: 0 y: 1 text: B
Patch diff from center: 0.19999999999999996 ymin: 0.8 ymax: 1.2
Bottom Corner of Patch: 1.8
x: 0 y: 2 text: C
Patch diff from center: 0.19999999999999996 ymin: 1.8 ymax: 2.2
Bottom Corner of Patch: 2.8
x: 0 y: 3 text: D
Patch diff from center: 0.20000000000000018 ymin: 2.8 ymax: 3.2
Option 2
- Since
width
of the bars is being set manually, you can simply divide by 2 to get the coordinates of the edges.
# create bar plot
width = 0.4
fig, ax = plt.subplots(figsize=(9, 6))
sns.barplot(y='x', x='y', data=data, color='tab:blue', width=width, ax=ax)
# set the targets
targets = {'A': 6, 'B': 4, 'C': 1, 'D': 2}
# extract y and text for each xtick label, calculate ymin and ymax and extract the target x
ytls = [(targets[v.get_text()], v.get_position()[1] - width/2, v.get_position()[1] + width/2) for v in ax.get_yticklabels()]
# iterate through the tuples in ytls
for x, ymin, ymax in ytls:
# add the vertical lines
ax.vlines(x=x, color='red', linestyle='-', linewidth=3, capstyle='butt', ymin=ymin, ymax=ymax)
Option 2 Consolidated
- Note that
.vlines
accepts a list or array of values for x
, ymin
, and ymax
.
# create bar plot
width = 0.4
fig, ax = plt.subplots(figsize=(9, 6))
sns.barplot(y='x', x='y', data=data, color='tab:blue', width=width, ax=ax)
# set the targets
targets = {'A': 6, 'B': 4, 'C': 1, 'D': 2}
# extract y and text for each xtick label, calculate ymin and ymax and extract the target x
x, ymins, ymaxs = list(zip(*[(targets[v.get_text()], v.get_position()[1] - width/2, v.get_position()[1] + width/2) for v in ax.get_yticklabels()]))
# add vertical lines
ax.vlines(x=x, color='red', linestyle='-', linewidth=3, capstyle='butt', ymin=ymins, ymax=ymaxs)
Plot Result
