I need to recreate a chart similar to the one below created in Excel.  I was hoping to use matplotlib, but can't seem to find any examples or reference for how to do a chart like this.  I need to have bars colored based on a performance threshold, and also display the threshold.  Can anyone point me in the right direction?  I do need to be able to do this with Python, though.
            Asked
            
        
        
            Active
            
        
            Viewed 8,375 times
        
    16
            
            
         
    
    
        Dave_750
        
- 1,225
- 1
- 13
- 28
- 
                    1Also see [Plot a horizontal line using matplotlib](https://stackoverflow.com/q/33382619/7758804) & [Adding value labels on a matplotlib bar chart](https://stackoverflow.com/q/28931224/7758804) & [Format y axis as percent](https://stackoverflow.com/q/31357611/7758804) – Trenton McKinney Aug 09 '21 at 05:08
2 Answers
26
            I gotta run, but here's something to get you started:
import numpy as np
import matplotlib
matplotlib.rcParams['text.usetex'] = False
import matplotlib.pyplot as plt
import pandas
df = pandas.DataFrame(np.random.uniform(size=37)*100, columns=['A'])
threshold = 75
fig, ax = plt.subplots(figsize=(8,3))
good = df['A'][df['A'] >= threshold]
bad = df['A'][df['A'] < threshold]
ax.bar(left=good.index, height=good, align='center', color='ForestGreen', zorder=5)
ax.bar(left=bad.index, height=bad, align='center', color='Firebrick', zorder=5)
ax.axhline(y=threshold, linewidth=2, color='ForestGreen', zorder=0)
ax.set_xticks(df.index)
ax.set_xlim(left=df.index[0]-0.75, right=df.index[-1]+0.75)
def annotateBars(row, ax=ax):
    if row['A'] < 20:
        color = 'black'
        vertalign = 'bottom'
        vertpad = 2
    else:
        color = 'white'
        vertalign = 'top'
        vertpad = -2
    ax.text(row.name, row['A'] + vertpad, "{:.1f}%".format(row['A']),
            zorder=10, rotation=90, color=color,
            horizontalalignment='center',
            verticalalignment=vertalign,
            fontsize=8, weight='heavy')
junk = df.apply(annotateBars, ax=ax, axis=1)
And that gives me:

 
    
    
        Paul H
        
- 65,268
- 20
- 159
- 136
3
            
            
        This can now be plotted much more concisely:
- Axes.bar_labelautomatically labels bars (requires matplotlib 3.4.0+)
- Axes.barhas a- colorparam that can accept an array of colors (e.g. via- numpy.where)
So now it only takes a handful of lines, e.g. using Paul's sample df = pd.DataFrame({'A': np.random.uniform(size=35) * 100}):
fig, ax = plt.subplots(figsize=(9, 3))
threshold = 75
# plot bars as blue if A > threshold, else red
color = np.where(df.A > threshold, 'blue', 'red')
ax.bar(x=df.index, height=df.A, color=color)
# add bar labels
ax.bar_label(ax.containers[0], fmt='%.1f%%')
# add threshold line
ax.axhline(threshold, alpha=0.5, zorder=0)
Or for multiple thresholds, just update color as desired (e.g. via numpy.select):
upper, lower = 75, 25
color = np.select([df.A > upper, df.A < lower], ['blue', 'red'], default='gray')
Note that a color array can also be passed into other bar plot helpers:
- 
df.plot.bar(y='A', color=color, ax=ax)
- 
df.A.plot.bar(color=color, ax=ax)
- seaborn.barplot(as- palette):- sns.barplot(x=df.index, y=df.A, palette=color, ax=ax)
 
    
    
        tdy
        
- 36,675
- 19
- 86
- 83

