9

I want to plot a bar chart or a histogram using matplotlib. I don't want a stacked bar plot, but a superimposed barplot of two lists of data, for instance I have the following two lists of data with me:

Some code to begin with :

import matplotlib.pyplot as plt
from numpy.random import normal, uniform

highPower   = [1184.53,1523.48,1521.05,1517.88,1519.88,1414.98,1419.34,
              1415.13,1182.70,1165.17]
lowPower    = [1000.95,1233.37, 1198.97,1198.01,1214.29,1130.86,1138.70,
               1104.12,1012.95,1000.36]

plt.hist(highPower, bins=10, histtype='stepfilled', normed=True,
         color='b', label='Max Power in mW')
plt.hist(lowPower, bins=10, histtype='stepfilled', normed=True,
         color='r', alpha=0.5, label='Min Power in mW')

I want to plot these two lists against the number of values in the two lists such that I am able to see the variation per reading.

Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
AnkitSablok
  • 3,021
  • 7
  • 35
  • 52

3 Answers3

19

You can produce a superimposed bar chart using plt.bar() with the alpha keyword as shown below.

The alpha controls the transparency of the bar.

N.B. when you have two overlapping bars, one with an alpha < 1, you will get a mixture of colours. As such the bar will appear purple even though the legend shows it as a light red. To alleviate this I have modified the width of one of the bars, this way even if your powers should change you will still be able to see both bars.

plt.xticks can be used to set the location and format of the x-ticks in your graph.

import matplotlib.pyplot as plt
import numpy as np

width = 0.8

highPower   = [1184.53,1523.48,1521.05,1517.88,1519.88,1414.98,
               1419.34,1415.13,1182.70,1165.17]
lowPower    = [1000.95,1233.37, 1198.97,1198.01,1214.29,1130.86,
               1138.70,1104.12,1012.95,1000.36]

indices = np.arange(len(highPower))

plt.bar(indices, highPower, width=width, 
        color='b', label='Max Power in mW')
plt.bar([i+0.25*width for i in indices], lowPower, 
        width=0.5*width, color='r', alpha=0.5, label='Min Power in mW')

plt.xticks(indices+width/2., 
           ['T{}'.format(i) for i in range(len(highPower))] )

plt.legend()

plt.show()

Plot

Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
  • There is no values in the list that exceeds 2000, then how does this bar chart show a value greater than 2000 and the labels on the x-axis have to be [1,2,3,4,.....,10] but it shows in the increments of 2 – AnkitSablok Apr 25 '14 at 12:35
  • I don't want them stacked on top of each other rather they should be superimposing each other that is lower value should occupy some region of the bar with a higher value – AnkitSablok Apr 25 '14 at 12:40
  • Great, this works, thankyou for the explanation too :) but how do I modify the ticks on the x axis to be T1,T2,T3 and so on – AnkitSablok Apr 25 '14 at 12:49
  • I have added an example of how to set the x-ticks to be "T1", "T2", etc. In the future however I would advise that you either explain your question fully at the beginning (not add more sub-questions on as you go) or simply ask a new question for your extended questions. – Ffisegydd Apr 25 '14 at 12:58
  • when I run this code, the red bars show on the right side of the blue bar, instead of the middle. Can you update the code? – Snow Oct 18 '18 at 19:35
  • How to get the legend colors to match with the actual colors? – tejasvi88 Aug 13 '20 at 06:15
6

Building on @Ffisegydd's answer, if your data is in a Pandas DataFrame, this should work nicely:

def overlapped_bar(df, show=False, width=0.9, alpha=.5,
                   title='', xlabel='', ylabel='', **plot_kwargs):
    """Like a stacked bar chart except bars on top of each other with transparency"""
    xlabel = xlabel or df.index.name
    N = len(df)
    M = len(df.columns)
    indices = np.arange(N)
    colors = ['steelblue', 'firebrick', 'darksage', 'goldenrod', 'gray'] * int(M / 5. + 1)
    for i, label, color in zip(range(M), df.columns, colors):
        kwargs = plot_kwargs
        kwargs.update({'color': color, 'label': label})
        plt.bar(indices, df[label], width=width, alpha=alpha if i else 1, **kwargs)
        plt.xticks(indices + .5 * width,
                   ['{}'.format(idx) for idx in df.index.values])
    plt.legend()
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    if show:
        plt.show()
    return plt.gcf()

And then in a python command line:

low = [1000.95, 1233.37, 1198.97, 1198.01, 1214.29, 1130.86, 1138.70, 1104.12, 1012.95, 1000.36]
high = [1184.53, 1523.48, 1521.05, 1517.88, 1519.88, 1414.98, 1419.34, 1415.13, 1182.70, 1165.17]
df = pd.DataFrame(np.matrix([high, low]).T, columns=['High', 'Low'],
                  index=pd.Index(['T%s' %i for i in range(len(high))],
                  name='Index'))
overlapped_bar(df, show=False)

overlapped bar chart in matplotlib

hobs
  • 18,473
  • 10
  • 83
  • 106
  • Trying this code and it displays the bar chart twice, Can the code be edited so it only displays the graph once? – Cathal Brady Mar 14 '20 at 15:33
  • How would you also change the fig size for this graph? I cannot seem to change it doing it the standard way – Cathal Brady Mar 14 '20 at 15:34
  • @CathalBrady are you using Spider? What is your python environment? This code will only work in a full python environment with matplotlib and its backend configured correctly for your OS or environment. You'll have to do something slightly different in a Jupyter Notebook. – hobs Mar 14 '20 at 22:16
  • Somehow there's no darksage in my environnement. I also tried to modify it to barh because I think vertical bars look better for my data, but my attempts break the index – jmd Jul 24 '21 at 12:40
6

It is actually simpler than the answers all over the internet make it appear.

a = range(1,10)
b = range(4,13)
ind = np.arange(len(a))

fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(x=ind, height=a, width=0.35,align='center')
ax.bar(x=ind, height=b, width=0.35/3,  align='center')

plt.xticks(ind, a)

plt.tight_layout()
plt.show()

enter image description here

tejasvi88
  • 635
  • 7
  • 15