Python Charts

Python plotting and visualization demystified

How to Color Individual Bars in a Matplotlib Bar Chart Based on Value

Color each bar in a Matplotlib bar chart individually — by threshold, colormap gradient, or to highlight a specific value.

TL;DR

Pass a list of colors — one per bar — to the color argument:

colors = ['#2A9D8F' if v >= 0 else '#E76F51' for v in values]
ax.bar(categories, values, color=colors)

Bar chart with green positive bars and red negative bars

Approach 1 — Threshold coloring

The simplest pattern: a list comprehension maps each value to a color based on a condition.

import matplotlib.pyplot as plt
from matplotlib.patches import Patch

categories = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
values = [12, -4, 8, 21, 35, 42, 39, 36, 18, -2, -8, 5]

colors = ['#2A9D8F' if v >= 0 else '#E76F51' for v in values]

fig, ax = plt.subplots(figsize=(8, 4))
ax.bar(categories, values, color=colors, edgecolor='white', linewidth=0.6)
ax.axhline(0, color='#2d3436', linewidth=0.8, linestyle='--')

# Manual legend since bars share the same artist
legend_elements = [
    Patch(facecolor='#2A9D8F', label='Positive'),
    Patch(facecolor='#E76F51', label='Negative'),
]
ax.legend(handles=legend_elements)
plt.tight_layout()
plt.show()

Note that ax.bar() does not automatically generate legend entries when you pass a color list, so you need to create Patch handles manually.

Approach 2 — Colormap gradient

Map values to a continuous colormap using matplotlib.colors.Normalize and a colormap like RdYlGn. This works well when the magnitude matters, not just the sign.

import matplotlib.cm as cm
import matplotlib.colors as mcolors

norm = mcolors.Normalize(vmin=min(values), vmax=max(values))
cmap = cm.RdYlGn
colors = [cmap(norm(v)) for v in values]

fig, ax = plt.subplots(figsize=(8, 4))
ax.bar(categories, values, color=colors, edgecolor='white', linewidth=0.6)

# Add a colorbar for reference
sm = cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
plt.colorbar(sm, ax=ax, label='Return (%)', pad=0.02)

plt.tight_layout()
plt.show()

ScalarMappable is needed to attach a colorbar to bars — unlike imshow or scatter, bar() does not return a mappable object directly.

Bar chart with a red-yellow-green colormap gradient and colorbar

Approach 3 — Highlight a single bar

Sometimes you just want to call out one bar — the maximum, a target month, a specific category — and mute the rest.

max_idx = values.index(max(values))

colors = [
    '#E9C46A' if i == max_idx else '#b2bec3'
    for i in range(len(values))
]

fig, ax = plt.subplots(figsize=(8, 4))
ax.bar(categories, values, color=colors, edgecolor='white', linewidth=0.6)

# Label the peak bar directly
ax.text(
    max_idx, values[max_idx] + 1,
    f'{values[max_idx]}%',
    ha='center', va='bottom',
    fontsize=11, fontweight='bold', color='#E9C46A'
)
plt.tight_layout()
plt.show()

Bar chart with all bars grey except the peak which is highlighted in amber

Changing colors after the fact

If you already have a bar chart and want to update colors without redrawing, iterate over the bar container:

bars = ax.bar(categories, values)

for bar, val in zip(bars, values):
    bar.set_color('#2A9D8F' if val >= 0 else '#E76F51')