How to Plot Min/Max Bars with a Bar Plot
I'd Like to Adapt My Plotting Code in Order to Show Min/Max Bar as Depicted in the Figure Below: My Code Is: from Datetime Import Datetime, Timedelta Import...
I'd like to adapt my plotting code in order to show min/max bar as depicted in the figure below:
My code is:
from datetime import datetime, timedelta
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("white")
sns.set_style('darkgrid',{"axes.facecolor": ".92"}) # (1)
sns.set_context('notebook')
Delay = ['S1', 'S2', 'S3', 'S4']
Time = [87, 66, 90, 55]
df = pd.DataFrame({'Delay':Delay,'Time':Time})
print("Accuracy")
display(df) # in jupyter
fig, ax = plt.subplots(figsize = (8,6))
x = Delay
y = Time
plt.xlabel("Delay", size=14)
plt.ylim(-0.3, 100)
width = 0.1
for i, j in zip(x,y):
ax.bar(i,j, edgecolor = "black",
error_kw=dict(lw=1, capsize=1, capthick=1))
ax.set(ylabel = 'Accuracy')
from matplotlib import ticker
ax.yaxis.set_major_locator(ticker.MultipleLocator(10))
plt.savefig("Try.png", dpi=300, bbox_inches='tight')
The code produce this figure:
The min/max I want to add is for:
87 (60-90)
66 (40-70)
90 (80-93)
55 (23-60)
Thanks in advance for help.
3 Answers
- This answer expands on the code from your previous question, by including examples for
seaborn.barplotandax.bar. - Also see Different ways of specifying error bars &
matplotlib.pyplot.errorbar - Tested in
python 3.11,pandas 1.5.3,matplotlib 3.7.0,seaborn 0.12.2
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# set edgecolor param (this is a global setting, so only set it once)
plt.rcParams["patch.force_edgecolor"] = True
# setup the dataframe
Delay = ['S1', 'S2', 'S3', 'S4']
Time = [87, 66, 90, 55]
df = pd.DataFrame({'Delay':Delay,'Time':Time})
# create a dict for the errors
error = {87: {'max': 90,'min': 60}, 66: {'max': 70,'min': 40}, 90: {'max': 93,'min': 80}, 55: {'max': 60,'min': 23}}
seaborn.barplot
seaborn.barplotwill add error bars automatically, as shown in the examples at the link. However, this is specific to using many data points. In this case, a value is being specified as the error, the error is not being determined from the data.- When error bars are added in this way, the
capsizeparameter can be specified, to add horizontal lines at the top and bottom of the error bar.
- When error bars are added in this way, the
# plot the figure
fig, ax = plt.subplots(figsize=(8, 6))
sns.barplot(x='Delay', y='Time', data=df, ax=ax)
# add the lines for the errors
for p in ax.patches:
x = p.get_x() # get the bottom left x corner of the bar
w = p.get_width() # get width of bar
h = p.get_height() # get height of bar
min_y = error[h]['min'] # use h to get min from dict z
max_y = error[h]['max'] # use h to get max from dict z
plt.vlines(x+w/2, min_y, max_y, color='k') # draw a vertical line
- As noted in the answer from gepcel, the
yerrparameter can be used to explicitly provide errors to the API.- However, the format of your errors is not correct for the parameter.
yerrexpects the values to be in relation to the top of the barS1is 87, withminof 60, andmaxof 90. Therefore,yminis 27, (87-60), andymaxis 3, (90-87).
- However, the format of your errors is not correct for the parameter.
- The
seaborn.barplotcapsizeparameter doesn't seem to work withyerr, so you must set thematplotlib'errorbar.capsize'rcParmas. See Matplotlib Errorbar Caps Missing
# set capsize param (this is a global setting, so only set it once)
plt.rcParams['errorbar.capsize'] = 10
# create dataframe as shown by gepcel
Delay = ['S1', 'S2', 'S3', 'S4']
Time = [87, 66, 90, 55]
_min = [60, 40, 80, 23]
_max = [90, 70, 93, 60]
df = pd.DataFrame({'Delay':Delay,'Time':Time, 'Min': _min, 'Max': _max})
# create ymin and ymax
df['ymin'] = df.Time - df.Min
df['ymax'] = df.Max - df.Time
# extract ymin and ymax into a (2, N) array as required by the yerr parameter
yerr = df[['ymin', 'ymax']].T.to_numpy()
# plot with error bars
fig, ax = plt.subplots(figsize=(8, 6))
sns.barplot(x='Delay', y='Time', data=df, yerr=yerr, ax=ax)
pandas.DataFrame.plot.bar
fig, ax = plt.subplots(figsize=(8, 6))
df.plot.bar(x='Delay', y='Time', ax=ax)
for p in ax.patches:
x = p.get_x() # get the bottom left x corner of the bar
w = p.get_width() # get width of bar
h = p.get_height() # get height of bar
min_y = error[h]['min'] # use h to get min from dict z
max_y = error[h]['max'] # use h to get max from dict z
plt.vlines(x+w/2, min_y, max_y, color='k') # draw a vertical line
ax.bar
fig, ax = plt.subplots(figsize=(8, 6))
ax.bar(x='Delay', height='Time', data=df)
for p in ax.patches:
x = p.get_x() # get the bottom left x corner of the bar
w = p.get_width() # get width of bar
h = p.get_height() # get height of bar
min_y = error[h]['min'] # use h to get min from dict z
max_y = error[h]['max'] # use h to get max from dict z
plt.vlines(x+w/2, min_y, max_y, color='k') # draw a vertical line
You can use yerr arg of plt.bar directly. Using @Trenton McKinney's code for an example:
import pandas as pd
import matplotlib.pyplot as plt
# setup the dataframe
Delay = ['S1', 'S2', 'S3', 'S4']
Time = [87, 66, 90, 55]
_min = [60, 40, 80, 23]
_max = [90, 70, 93, 60]
df = pd.DataFrame({'Delay':Delay,'Time':Time, 'Min': _min, 'Max': _max})
df = (df.assign(yerr_min = df.Time-df.Min)
.assign(yerr_max=df.Max-df.Time))
plt.figure(figsize=(8, 6))
plt.bar(x='Delay', height='Time', yerr=df[['yerr_min', 'yerr_max']].T.values, capsize=10, data=df)
plt.show()
Here's a solution using yerr and numpy. It has less boilerplate code than @gepcel's.
import matplotlib.pyplot as plt
import numpy as np
Delay = ['S1', 'S2', 'S3', 'S4'] # Categories
Time = [87, 66, 90, 55]
_min = [60, 40, 80, 23]
_max = [90, 70, 93, 60]
plt.figure(figsize=(8, 6))
yerr = [np.subtract(Time, _min), np.subtract(_max, Time)]
plt.bar(Delay, Time, yerr=yerr, capsize=10)
plt.show()