pythonpandasmatplotlibbar-chart

How to change bar label color based on background


I'm new to using python for data, but by putting together answers found here and on other sites, I've managed to plot my dataset into a stacked bar chart with bar labels.

However, I would like the bar labels to have different text colors based on the bar color, to aid in visibility (I'm restricted to grayscale colors for the bars) I've found some examples that manage this but they interfere with other parts of my code and I'm not sure how to reconcile this. Can anyone help?

import pandas as pd
import matplotlib.pyplot as plt

plt.style.use('ggplot')
ax = df_new2.plot(kind='barh', stacked=True, figsize=(12, 10), color = ['black','dimgray','darkgray','silver'])

for c in ax.containers:
    
    # customize the label to account for cases when there might not be a bar section
    labels = [f'{w:.0f}%' if (w := v.get_width()) > 0.49 else '' for v in c ] 
    
    # set the bar label
    
    ax.bar_label(c, labels=labels, label_type='center',color='snow',padding=3,fontsize=8)


ax.legend(bbox_to_anchor=(1.025, 1), loc='upper left', borderaxespad=0.)
ax.set_xlim(right=103)

update: out of luck with this one still. please let me know if anyone can help.

enter image description here


Solution

  • You can create a list of desired label colors, enumerate the ax.containers, and index the label_colors inside the loop as below. In this case, I have chosen a label color of white to show on a background of black and dimgray while a label color of black shows on a background of darkgray and silver, but you can chose any colors you want.

    plt.style.use('ggplot')
    ax = df_new2.plot(kind='barh', stacked=True, figsize=(12, 10), color=['black', 'dimgray', 'darkgray', 'silver'])
    
    label_colors = ['white', 'white', 'black', 'black']
    
    for i, c in enumerate(ax.containers):
        labels = [f'{w:.0f}%' if (w := v.get_width()) > 0.49 else '' for v in c]
        ax.bar_label(c, labels=labels, label_type='center', color= label_colors[i], padding=3, fontsize=8)
    
    ax.legend(bbox_to_anchor=(1.025, 1), loc='upper left', borderaxespad=0.)
    ax.set_xlim(right=103)
    plt.show()