While my solution is similar to Chris's, I'm also showing how to define custom colours and generalise the approach for multiple overlays.
For each position you get a contribution of the integer in a
and the integer in b
. Since those can be either 0 or 1, you can use a binary description. By shifting the integer in b
to the second position, i.e. multiplying by 2, you can represent any combination with 2 bits, ba
, one for each integer in each list (for each position).
Matplotlib also allows you to create your own custom colour map. Combining these aspects you can achieve the result you're after.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
a = np.array([0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1])
b = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1])
lists = [a, b]
overlap = np.zeros_like(a)
for k, row in enumerate(lists):
overlap += row * 2**k
cmap = mpl.colors.ListedColormap(['white', 'blue', 'red', 'purple'])
bounds = range((2**len(lists))+1)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
plt.pcolor(overlap.reshape((1, overlap.shape[0])), edgecolor='k', cmap=cmap, norm=norm)
plt.axes().set_aspect('equal')
plt.xticks([])
plt.yticks([])
plt.xlim(0, len(a))
plt.show()
Output:

If you had 3 overlays, you'd have to give a list of colours in the following order:
000 white
001 colour A
010 colour B
011 colour A+B
100 colour C
101 colour A+C
110 colour B+C
111 colour A+B+C
Unfortunately, this has to be done manually. You can see a list of colour names here. If a colour doesn't have an explicit name, you can always describe it by its hexadecimal value in a string, like '#RRGGBB'
, where RR
would be the hexadecimal value for the red channel, etc. So, instead of 'white'
you could have said '#FFFFFF'
(the letters can also be lowercase).
Code notes:
bounds
is the list of numbers [0, 1, 2, 3, 4]
. This means that any value from 0 to 1, but not including 1, will be mapped to white, any value between 1 and 2 to blue, etc. If we have k
overlays, we need 2**k + 1
boundary numbers.
I chose to represent the data with pcolor()
since it has the edgecolor
option, which better allows to visualise runs of the same colour. However, the function requires a 2D array for input, hence why I had to reshape the overlap
array from size (29,)
to (1, 29)
. Generally though, this code would also work even if a
and b
were 2D arrays, in which case you would skip any reshaping.
In this section I briefly discuss other approaches I considered but found lacking or unnecessarily complex considering the scope of the OP.
Alpha blending (transparency)
One could create a row of white-blue for the first list and stack on top of it another row of white-red. In theory, white-white would be white, white-blue would be blue, white-red would be red and blue-red would be purple. However, since each row is only semi-transparent, red looks like pink and blue with layers of white becomes "diluted" to light blue. This effect would be even more pronounced with multiple layers, but at least the combination of colours would emerge without any manual definitions.
An advantage of this method is that it doesn't only support 1 or 0 for any individual overlay, but any gradient in the between.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
a = np.array([0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1])
b = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1])
blue = mpl.colors.LinearSegmentedColormap.from_list('blue', ['white', 'blue'], 256)
red = mpl.colors.LinearSegmentedColormap.from_list('red', ['white', 'red'], 256)
plt.pcolor(a.reshape((1, a.shape[0])), cmap=blue, edgecolor='k', alpha=1.0)
plt.pcolor(b.reshape((1, b.shape[0])), cmap=red, edgecolor='k', alpha=0.5)
plt.axes().set_aspect('equal')
plt.xticks([])
plt.yticks([])
plt.xlim(0, len(a))
plt.show()
Output:
RGB blending
What if one translated the overlays to RGB values? For example, each 1 in a
could be represented by the [0, 0, 255]
triplet. By mixing the RGB values for each overlay, we could obtain a final RGB value for each position which we could then plot with an RGB colour map. However, this isn't as simple as it sounds.