How to fix the max value of a stack bar chart's using matplotlib?

I'm trying to create a stack bar chart from 2 lists:

counts_start = [tensor(0.),
 tensor(0.),
 tensor(0.0050),
 tensor(0.0100),
 tensor(0.2833),
 tensor(0.8250),
 tensor(0.8917),
 tensor(1.),
 tensor(1.),
 tensor(1.)]

counts_end = [tensor(0.1350),
 tensor(0.1467),
 tensor(0.1517),
 tensor(0.2233),
 tensor(0.2350),
 tensor(0.2417),
 tensor(0.4100),
 tensor(0.4200),
 tensor(0.4483),
 tensor(0.4600)]

Following this I learned that I need the bottom of each list to be the sum of all the lists that came before, so I got this code:

counts_start = [torch.tensor(0.),
    torch.tensor(0.),
    torch.tensor(0.0050),
    torch.tensor(0.0100),
    torch.tensor(0.2833),
    torch.tensor(0.8250),
    torch.tensor(0.8917),
    torch.tensor(1.),
    torch.tensor(1.),
    torch.tensor(1.)]
plt.bar(indices_end,counts_end, color='r')

counts_end = [torch.tensor(0.1350),
    torch.tensor(0.1467),
    torch.tensor(0.1517),
    torch.tensor(0.2233),
    torch.tensor(0.2350),
    torch.tensor(0.2417),
    torch.tensor(0.4100),
    torch.tensor(0.4200),
    torch.tensor(0.4483),
     torch.tensor(0.4600)]

indices_start = range(len(counts_start))
plt.bar(indices_start,counts_start, bottom=counts_end)
plt.show()

Which outputs

enter image description here

Each list has a probability value and hence the max is 1 and min is 0 which is where I need the bars to start and end but can't get it to work. Also note that the third and fourth columns have the blue color on top which makes it look like blue is of higher value (which is not).

I also tried replacing the second plt.bar line with plt.bar(indices_start,[a_i - b_i for a_i, b_i in zip(counts_start, counts_end)], bottom=counts_end) which outputs

enter image description here

Which is almost correct (the max is at 1), except that the first few columns have the wrong color (I think it's because of the negative value).

To clarify, I want the highest value for each column between the 2 lists to be the max value for the column. If there's a higher value in counts_start list the blue color should be on top with it's value (e.g 0.8917), and the smaller value should be with a max of it's value (e.g 0.4100). So it will look like a column with 0-0.4100 red, and 0.4100-0.8917 blue. However, if there's a higher value in the counts_end list (e.g 0.1517), the top part of that column should be red with the top of the column at 0.1517, and the blue color should be at it's corresponding value (e.g 0.0050). Which will look like a column with 0-0.0050 blue, and 0.0050-0.1517 red.


Solution 1:

If I understood correctly, you want to normalize your list values so that counts_start[i] + counts_end[i] == 1.

This can be done by something like:

start = [0, 0, 0.005, 0.01, 0.2833, 0.825, 0.8917, 1, 1, 1]
end = [0.135, 0.1467, 0.1517, 0.2233, 0.235, 0.2417, 0.41, 0.42, 0.4483, 0.46]

new_start, new_end = [], []
for x, y in zip(start, end):
    t = x + y
    new_start.append(x / t)
    new_end.append(y / t)

n = len(start)

plt.bar(range(n), new_end, color="r")
plt.bar(range(n), new_start, bottom=new_end)
plt.show()

Which produces the following plot:

enter image description here

EDIT: Ok, for that I believe you're going to need to keep track of 4 lists, like so:

start = [0, 0, 0.005, 0.01, 0.2833, 0.825, 0.8917, 1, 1, 1]
end = [0.135, 0.1467, 0.1517, 0.2233, 0.235, 0.2417, 0.41, 0.42, 0.4483, 0.46]

lo_start, hi_start, lo_end, hi_end = [], [], [], []
for x, y in zip(start, end):
    if x > y:
        a, b, c, d = 0, x - y, y, 0
    else:
        a, b, c, d = x, 0, 0, y - x

    lo_start.append(a)
    hi_start.append(b)
    lo_end.append(c)
    hi_end.append(d)

n = len(start)
offset = [max(x, y) for x, y in zip(lo_start, lo_end)]

plt.bar(range(n), lo_start, color="tab:blue")
plt.bar(range(n), lo_end, color="tab:red")
plt.bar(range(n), hi_start, bottom=offset, color="tab:blue")
plt.bar(range(n), hi_end, bottom=offset, color="tab:red")
plt.show()

Which produces:

enter image description here