4

here is some sample data from the dataframe

Country      restricted  V1%      V2%
0   Algeria  True    39.575812    60.424188
1   Angola   True    56.931682    43.068318
2   Argent   False    15.555556   84.4444

I am using a stacked bar chart in order to show 3 values. The V1 and V2 have been normalised as %ges so it is simple to create the basic chart like this:

xx=df.plot(kind="bar", x='Country',stacked=True,figsize=(20,10);

having previously ordered the dataframe

df=conjoint_ag_df.sort_values(['restricted',.Country'], ascending=[False,True])

so as to display it by both country and the true / false value.

That gives me a basic display like this.

enter image description here

What I am trying to do is to arrange for all of the columns with a true value in restricted to have one color pair and those with false to have another color pair - as I am sorting by restricted first this means that the color would change from the former to the latter along the x axis of the graph. I can do this for simple bar charts following this example

Color matplotlib bar chart based on value

and for a single pair of colors using this

xx=_df.plot(kind="bar", x='Country',stacked=True,figsize=(20,10),color=['r','b'])

(Hideous) but I can't work out how to apply a differentiator to the columns to change two color values to two different values based on the True False values.

BigBen
  • 46,229
  • 7
  • 24
  • 40
SimonN
  • 383
  • 1
  • 2
  • 13

2 Answers2

2

There may be a better approach, but here is one using a loop:

for idx, row in df.iterrows():
    color = ['tab:blue', 'tab:orange'] if row['restricted'] else ['tab:blue', 'tab:red']
    plt.bar(row['Country'], row['V1%'], color=color[0])
    plt.bar(row['Country'], row['V2%'], bottom=row['V1%'], color=color[1])

Output:

enter image description here

BigBen
  • 46,229
  • 7
  • 24
  • 40
1

Probably this is not the most beautiful approach, but it works. I'm using the keyword argument color from the plot method. As argument, we can you a dictionary based on the 'restricted' condition. I've use some buint-in colors defined in matplotlib colormaps, but you can use the colors of your choice, of course:

from matplotlib import cm

palette1 = cm.get_cmap('autumn', 2)
palette2 = cm.get_cmap('winter', 2)

colors = {
    'V1': [palette1(0) if restricted else palette2(0) for restricted in df['restricted']],
    'V2': [palette1(1) if restricted else palette2(1) for restricted in df['restricted']]
}

xx = df.plot(kind="bar", x='Country', stacked=True, color=colors, legend=False, figsize=(20, 10))

Since this messes up the default legend, I've tried making one (not the prettiest, though):

from matplotlib.lines import Line2D

custom_legend = [
    Line2D([0], [0], color=palette1(0), lw=3),
    Line2D([0], [0], color=palette1(1), lw=3),
    Line2D([0], [0], color=palette2(0), lw=3),
    Line2D([0], [0], color=palette2(1), lw=3),
]

plt.legend(custom_legend, ['V1 (Restricted)', 
                           'V2 (Restricted)', 
                           'V1 (Non-Restricted)', 
                           'V2 (Non-Restricted'])

plt.show()

This gives us the following plot as output:

enter image description here

Ralubrusto
  • 1,394
  • 2
  • 11
  • 24
  • 2
    I actually used your code for the legend - along with customizing the labels but the original answer was more convenient with the code I had hence the solution tick. – SimonN Dec 04 '20 at 23:00