Legend in a multibars chart

Hi,

I'm plotting two subplots using bar charts, the first one contains one dataset (one bar for each abscissa value), the second 3 dataset (3 bars for each abscissa value, each one with its own color). The legend of the multibars chart is not correct, it shows the color of the first dataset for all the bars legend.
Note that it works correctly with matplotlib.pyplot.plot instead of matplotlib.pyplot.bar.
Any idea to fix it?
Thanks,

A.Frances

CODE OF MULTIBARS

···

#-------------------------------------------------------------------------------
# Name: multibars
# Purpose:
#
# Author: alf
#
# Created: 05-01-2011
# Copyright: (c) alf 2010
# Licence: <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import matplotlib.pyplot as plt
from numpy import array
import random
strTitle = 'Bars plot - multi data and legend'
x = array(range(10))
y1 = []
y2 = [[],[],[]]
for i in range(len(x)):
    y1.append(1.0 + random.random())
    for j in range(len(y2)):
        y2[j].append(0.5 + random.random())
lbl_y1 = []
lbl_y1.append('bar 1')
lbl_y2 = 'bar 2'
lbl_type = ['red', 'green', 'blue']
colors_y2 = ([1,0,0],[0,1,0],[0,0,1])
lbls_y2 = []
for i in range(len(lbl_type)):
    lbls_y2.append(lbl_y2 + " " + lbl_type[i])
fig = plt.figure()
ax1=fig.add_subplot(2,1,1)
ax1.set_title("Single data")
plt.bar(x, y1, color='purple', linewidth = 0, align='edge', width = 0.8)
plt.legend(lbl_y1, loc= 0)
plt.xticks(x)
ax2=fig.add_subplot(2,1,2, sharex=ax1)
ax2.set_title("Multi data")
for i in range(len(y2)):
    plt.bar(x+(0.8*float(i)/len(y2)),y2[i],color = colors_y2[i], linewidth=0, align='edge', width = (0.8/len(y2)))
plt.legend(lbls_y2, loc=0)
plt.xticks(x)
plt.subplots_adjust(left=0.05, bottom=0.1, right=0.95, top=0.95, wspace=0.1, hspace=0.15)
plt.show()
##############################

CODE OF MULTILINES
#-------------------------------------------------------------------------------
# Name: multilines
# Purpose:
#
# Author: alf
#
# Created: 05-01-2011
# Copyright: (c) alf 2010
# Licence: <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import matplotlib.pyplot as plt
from numpy import array
import random
strTitle = 'Lines plot - multi data and legend'
x = array(range(10))
y1 = []
y2 = [[],[],[]]
for i in range(len(x)):
    y1.append(1.0 + random.random())
    for j in range(len(y2)):
        y2[j].append(0.5 + random.random())
lbl_y1 = []
lbl_y1.append('line 1')
lbl_y2 = 'line 2'
lbl_type = ['red', 'green', 'blue']
colors_y2 = ([1,0,0],[0,1,0],[0,0,1])
lbls_y2 = []
for i in range(len(lbl_type)):
    lbls_y2.append(lbl_y2 + " " + lbl_type[i])
fig = plt.figure()
ax1=fig.add_subplot(2,1,1)
ax1.set_title("Single data")
plt.plot(x, y1, color='purple')
plt.legend(lbl_y1, loc= 0)
plt.xticks(x)
ax2=fig.add_subplot(2,1,2, sharex=ax1)
ax2.set_title("Multi data")
for i in range(len(y2)):
    plt.plot(x,y2[i],color = colors_y2[i])
plt.legend(lbls_y2, loc=0)
plt.xticks(x)
plt.subplots_adjust(left=0.05, bottom=0.1, right=0.95, top=0.95, wspace=0.1, hspace=0.15)
plt.show()
##############################

I can confirm the problem, and I have a few suspects as to the cause. Most notably that the legend code probably assumes that it is looking for line objects, not patch objects and starts using its own color cycler when it can’t get a list of colors to correspond with its list of labels.

As a work-around, you can add a label keyword to the call to bar() and not bother giving legend() a list of labels.

Here is a cleaned-up version of your first code. Note I also took a moment to take advantage of python syntax and better numpy/matplotlib coding styles. The only issue seems to be that there might be a bug with respect to positioning the legend:

import matplotlib.pyplot as plt
import numpy as np

strTitle = ‘Bars plot - multi data and legend’
x = np.arange(10)
y1 = 1.0 + np.random.random(x.shape)
y2 = 0.5 + np.random.random((3, len(x)))

lbl_y1 = ‘bar 1’
lbl_type = [‘red’, ‘green’, ‘blue’]
colors_y2 = ([1,0,0],[0,1,0],[0,0,1])
lbls_y2 = ["bar 2 " + lbl for lbl in lbl_type]

fig = plt.figure()

ax1 = fig.add_subplot(2,1,1)
ax1.set_title(“Single data”)
ax1.bar(x, y1, color=‘purple’, linewidth=0, align=‘edge’, width=0.8, label=lbl_y1)
ax1.legend(loc=0)
ax1.set_xticks(x)

ax2 = fig.add_subplot(2,1,2, sharex=ax1)
ax2.set_title(“Multi data”)
for i, (y, color, lbl) in enumerate(zip(y2, colors_y2, lbls_y2)) :
ax2.bar(x+(0.8*float(i)/len(y2)), y, color=color, label=lbl, linewidth=0, align=‘edge’, width=(0.8/len(y2)))

ax2.legend(loc=0)

fig.subplots_adjust(left=0.05, bottom=0.1, right=0.95, top=0.95, wspace=0.1, hspace=0.15)

plt.show()

I hope this helps!
Ben Root

···

On Wed, Jan 5, 2011 at 7:19 AM, Alain Pascal Frances <frances17404@…3388…> wrote:

Hi,

I’m plotting two subplots using bar charts, the first one contains one dataset (one bar for each abscissa value), the second 3 dataset (3 bars for each abscissa value, each one with its own color). The legend of the multibars chart is not correct, it shows the color of the first dataset for all the bars legend.

Note that it works correctly with matplotlib.pyplot.plot instead of matplotlib.pyplot.bar.

Any idea to fix it?

Thanks,

A.Frances

Hi Ben,

Thanks for your nicer and working code! It works perfectly and is indeed clean.

Up to now I didn’t noticed any problem with the positioning of the legend.

Best regards,

Alain

···

From: ben.v.root@…287… [mailto:ben.v.root@…287…] On Behalf Of Benjamin Root
Sent: Thursday, 06 January, 2011 04:43
To: Alain Pascal Frances
Cc: matplotlib-users@lists.sourceforge.net
Subject: Re: [Matplotlib-users] Legend in a multibars chart

On Wed, Jan 5, 2011 at 7:19 AM, Alain Pascal Frances <frances17404@…3388…> wrote:

Hi,

I’m plotting two subplots using bar charts, the first one contains one dataset (one bar for each abscissa value), the second 3 dataset (3 bars for each abscissa value, each one with its own color). The legend of the multibars chart is not correct, it shows the color of the first dataset for all the bars legend.
Note that it works correctly with matplotlib.pyplot.plot instead of matplotlib.pyplot.bar.
Any idea to fix it?
Thanks,

A.Frances

I can confirm the problem, and I have a few suspects as to the cause. Most notably that the legend code probably assumes that it is looking for line objects, not patch objects and starts using its own color cycler when it can’t get a list of colors to correspond with its list of labels.

As a work-around, you can add a label keyword to the call to bar() and not bother giving legend() a list of labels.

Here is a cleaned-up version of your first code. Note I also took a moment to take advantage of python syntax and better numpy/matplotlib coding styles. The only issue seems to be that there might be a bug with respect to positioning the legend:

import matplotlib.pyplot as plt
import numpy as np

strTitle = ‘Bars plot - multi data and legend’
x = np.arange(10)
y1 = 1.0 + np.random.random(x.shape)
y2 = 0.5 + np.random.random((3, len(x)))

lbl_y1 = ‘bar 1’
lbl_type = [‘red’, ‘green’, ‘blue’]
colors_y2 = ([1,0,0],[0,1,0],[0,0,1])
lbls_y2 = ["bar 2 " + lbl for lbl in lbl_type]

fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.set_title(“Single data”)
ax1.bar(x, y1, color=‘purple’, linewidth=0, align=‘edge’, width=0.8, label=lbl_y1)
ax1.legend(loc=0)
ax1.set_xticks(x)

ax2 = fig.add_subplot(2,1,2, sharex=ax1)
ax2.set_title(“Multi data”)
for i, (y, color, lbl) in enumerate(zip(y2, colors_y2, lbls_y2)) :
ax2.bar(x+(0.8*float(i)/len(y2)), y, color=color, label=lbl, linewidth=0, align=‘edge’, width=(0.8/len(y2)))
ax2.legend(loc=0)

fig.subplots_adjust(left=0.05, bottom=0.1, right=0.95, top=0.95, wspace=0.1, hspace=0.15)

plt.show()

I hope this helps!
Ben Root

The main issue here is that a single "bar" command creates multiple
patch artists. There is not much thing legend code can do. I guess
this is where documentation needs to be improved though.

Regards,

-JJ

···

On Thu, Jan 6, 2011 at 12:43 PM, Benjamin Root <ben.root@...1304...> wrote:

I can confirm the problem, and I have a few suspects as to the cause. Most
notably that the legend code probably assumes that it is looking for line
objects, not patch objects and starts using its own color cycler when it
can't get a list of colors to correspond with its list of labels.