Python Forum
Stacked Barchart from Counter using matplotlib
Thread Rating:
  • 2 Vote(s) - 2 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Stacked Barchart from Counter using matplotlib
#1
I have a large csv file. This file is read and a

Counter
is returned every predefined amount of lines. As an Example:
counter = [Counter({(0, 1): 9, (1, 2): 8}), Counter({(1, 2): 99, (0, 1): 99}), Counter({(1, 2): 256, (0, 1): 189}), Counter({(1, 5): 473, (0, 1): 301})]
This is the script i used.
import matplotlib.pyplot as plt
import numpy
from collections import Counter
counter = [Counter({(0, 1): 9, (1, 2): 8}), Counter({(1, 2): 99, (0, 1): 99}), Counter({(1, 2): 256, (0, 1): 189}), Counter({(1, 5): 473, (0, 1): 301})]

fig = plt.figure()
ax1 = fig.add_subplot(111)

N = len(counter)
ind = numpy.arange(N)

j = 0
while j in range(0, len(counter)):
    a, i = 0, 0
    frequencies = counter[j].values()
    names = counter[j].keys()
    while i in range(0, len(frequencies)):
        if i == 0:
            ax1.bar(ind, frequencies[i], label=names[i], width=0.25)
            a = frequencies[i]
        else:
            ax1.bar(ind, frequencies[i], label=names[i], width=0.25, bottom=a)
            a += frequencies[i]
        i += 1
    j += 1
labels = ["%s to %s" % (200, 202)]
ax1.set_xticks(numpy.arange(N))
ax1.set_xticklabels(labels)
ax1.set_ylabel("Frequency")
ax1.set_xlabel("Material Contact")
ax1.legend()

plt. show()
However, it returns as error message:
Quote:ValueError: incompatible sizes: argument 'height' must be length 4 or scalar

To overcome this, i change the ind to ind[j], but ended up wit ha lot of colors. And these colors do not relate to their respective bins.
Reply
#2
pyplot.bar needs heights with same length as left array (for vertically orientated barplot), so for your data with left = ind with length 4 you need frequency[i] to be array/list with length 4.

Its not clear what you want to plot, it seems that you are trying to plot much more plots than you have data for ... Perhaps you have there one more loop than its necessary (and maybe you need to "reshape" your list/counters)

You should use for to loop over a range instead of while. And when that range is used just to iterate over list or dictionary, you should iterate directly over list or dictionary. Examples:

Output:
>>> for item in [1,2,3]: ...     print(item) ... 1 2 3 >>> d = {'a':[1,2], 'b':[3,4]} >>> for key, value in d.items(): ...    print(key, value) ... a [1, 2] b [3, 4] >>> for idx, (key, value) in enumerate(d.items()):  # if you need index position, use enumerate ...     print(idx, key, value) ... 0 a [1, 2] 1 b [3, 4]
its python3, but with change of print it works with python2 too
Reply
#3
@zivoni, thank you for that.


Quote:so for your data with left = ind with length 4 you need frequency[i] to be array/list with length 4

Thank you for that, however, it does not solve the problem. I have attached images, maybe this may make it clearer.

Current Results
[Image: Espc90.png]
Expected Result

[Image: pmEjcwwgp]

This is the new code i wrote after your explanation above, the issue i think lies in it read the I'th value of the counter list list then the internal Counter Collection, but when it goes out to the next i'th value, it loose the track of which bin it is in.


import matplotlib.pyplot as plt
import numpy
from collections import Counter
counter = [Counter({(0, 1): 9, (1, 2): 8}), Counter({(1, 2): 99, (0, 1): 99}), Counter({(1, 2): 256, (0, 1): 189}), Counter({(1, 5): 473, (0, 1): 301})]

fig = plt.figure()
ax1 = fig.add_subplot(111)

for idx, bins in enumerate(counter):
    stacker = 0
    for names, frequencies in (bins.iteritems()):
        if idx == 0:
            ax1.bar(idx, frequencies, label=names, width=0.25)
            stacker = frequencies
        else:
            ax1.bar(idx, frequencies, label=names, width=0.25, bottom=stacker)
            stacker += frequencies
labels = ["%s to %s" % (200, 202)]
ax1.set_xticks(numpy.arange(idx))
ax1.set_xticklabels(labels)
ax1.set_ylabel("Frequency")
ax1.set_xlabel("Material Contact")
ax1.legend()

plt.suptitle("Time Window %s to %s" % (200, 202))
plt.savefig("histogram.png")

plt. show()
Reply
#4
(Mar-31-2017, 04:56 PM)alicarlos13 Wrote: the issue i think lies in it read the I'th value of the counter list list then the internal Counter Collection, but when it goes out to the next i'th value, it loose the track of which bin it is in.

Indeed it does, it plots bars for entire serie, not "piecewise" one by one (technically you can do it by zero-filling remaining bins ...). That was what i meant by one more loop and likely need to reshape your data (but i wasnt sure what do you want).

You should have all data for (0, 1) in one serie/list, data for (1,2) in another and so on. Something like:
Output:
{(0, 1): [9, 99, 189, 301], (1, 2): [8, 99, 256, 0], (1, 5): [0, 0, 0, 473]}
 

You just need to take your "keys/labels", and for each key construct list of values  - for  each Counter in your counter list check if key is in Counter and append either its value or zero. You dont need need to store it in dict, but you should have lists of values and its keys/labels. With dict you dont have fixed order, so if you want specific order of values, you need to sort keys somehow.

I quickly converted your data and plotted it without any effort to beautify it:
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter

counter = [Counter({(0, 1): 9, (1, 2): 8}), Counter({(1, 2): 99, (0, 1): 99}), 
           Counter({(1, 2): 256, (0, 1): 189}), Counter({(1, 5): 473, (0, 1): 301})]
N = len(counter)
 
series = {}
for key in {key for keys in counter for key in keys}:
    series[key] = [(0 if key not in item else item[key]) for item in counter]

fig, ax = plt.subplots()
bottom, x = np.zeros(N), range(N)

for key in series:
    ax.bar(x, series[key], label=key, bottom=bottom)
    bottom += np.array(series[key])
plt.legend()
plt.savefig('boo.png', dpi=200)
[Image: aCwpGUT.png]
Reply
#5
Thank You Cool .

Guess i did not understand your previous post. Either that, or i can not get over the fact that to create a graph and i have to reassemble the Collections Counter again, because that to me (at least) defies the concept of the Counter in the first place Wall Wall .

Again, Thank You.

Final Result


[Image: a70Z0N.png]
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Enhance my stacked barplot title, and flip legend Clunk_Head 0 1,702 Jul-08-2019, 03:30 AM
Last Post: Clunk_Head
  stacked autoencoder training JohnMarie 0 2,644 Feb-24-2019, 12:23 AM
Last Post: JohnMarie
  [Plot a stacked bar graph using plotly offline mode] niks250891 1 5,199 Apr-22-2018, 02:11 PM
Last Post: niks250891

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020