1

I'm trying desperately to create nice graphics with Matplot, but it's no easy task. To contextualize, I have two series (serie1, serie2). For each

I have 3 Groups (Group1, Group2 and Group3). For each group, I have some theme and values. Each series describes the behaviour of several individuals (G1, G2, G3) through different variables (Theme). The code is :

import pandas as pd
d = {"ThemeA": [25,34,75], "ThemeB": [0,71,18], "ThemeC": [2,0,0], "ThemeD":[1,14,0] }
serie1 = pd.DataFrame(data = d, index=["Groupe 1", "Groupe 2", "Groupe 3"] )
serie1= serie1.loc[:,:].div(serie1.sum(1), axis=0) * 100

d = {"ThemeA": [145,10,3], "ThemeB": [10,1,70], "ThemeC": [34,1,2], "ThemeD":[3,17,27]}
serie2= pd.DataFrame(data = d, index=["Groupe 1", "Groupe 2", "Groupe 3"])
serie2= serie2.loc[:,:].div(serie2.sum(1), axis=0) * 100

Now I would like to make a graph to display the user data :

ax = fig.add_subplot(111) 
ax = serie1.plot(kind='barh', ax=ax, width=0.2, stacked=True, position=0, sharex=True, 
             sharey=True, legend=True, figsize = (6,2))

serie2.plot(kind='barh', ax=ax, width=0.2, stacked=True, position=1.6, 
               sharex=True, sharey=True, legend=False)
ax.grid(False)
plt.ylim([-0.5, 2.5])

I was able to get the following graph:

Badddd

But I would like to move the legend to the bottom. If I try to do this,

ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05), 
           fancybox=True, shadow=True, ncol=5)

I get the following output, which has too many labels.

Arrrrrrrrggg

Of course I would like to see each label exactly once in the legend.
If someone has a miracle solution, I'm a taker! Thanks in advance.

6
  • Your problem is not reproducible. Your code produces NameError: name 'categories_events' is not defined. Please provide a piece of code that can be run. Commented Jan 30, 2018 at 18:05
  • I just fixed the error in the message. Commented Jan 30, 2018 at 18:15
  • 1
    you don't say what code you used to move the legend to the bottom Commented Jan 30, 2018 at 18:18
  • @tom ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05), fancybox=True, shadow=True, ncol=5) Commented Jan 30, 2018 at 18:20
  • That works for me, and doesn't reproduce your issue of too many labels. Commented Jan 30, 2018 at 18:21

2 Answers 2

1

You can use an xaxis longer than needed to have empty space for the legends

# calculate the size of the longer column (max of row sums)
max_col = serie2.sum(axis=1).max()
# increase the size of the x axis a factor of 1.4
xlim(0, max_col*1.4)

If you want the legends at bottom, when you call legend you actually are drawing the labels from the two plots. You need to remove duplicate labels. For this you use a dictionary.

from collections import OrderedDict

fig = figure()
figsize(6,2)
ax = fig.add_subplot(111) 

serie1.plot(kind='barh', ax=ax, width=0.2, stacked=True, position=0,
            sharex=True, sharey=True)

serie2.plot(kind='barh', ax=ax, width=0.2, stacked=True, position=1.6, 
            sharex=True, sharey=True)

handles, labels = gca().get_legend_handles_labels()
my_labels = OrderedDict(zip(labels, handles))
legend(my_labels.values(), my_labels.keys(), loc='upper center',
       bbox_to_anchor=(0.5, -0.1), fancybox=True, shadow=True, ncol=5)

ax.grid(False)
ylim([-0.5, 2.5])

Then you get:

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

What I'd like is to be able to put the legend down, but especially that it doesn't change from 4 to 8 labels.
This s not clear in your question. You do not provide the code used to put the legend at bottom
Yes my bad. ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05), fancybox=True, shadow=True, ncol=5)
Thanks a lot for your help !
1

A one-line hack which works in this case is to add the line

serie2.columns= ["_" + col for col in serie2.columns]

before you plot the second dataframe. This will replace all column names with an underscore, followed by the original name. Since names starting with underscore ("_") are not shown in the legend, this leaves you only with the legend entries of the first dataframe.
This solution requires to have the same order of columns in both dataframes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.