1

I have two heatmaps which are based on 2d histograms that I am trying to overlay on a single graph. The limits of their axes (extent_L and extent_H) do not necessarily coincide exactly. I can make the individual plots satisfactorily if needed, but when trying to show both heatmaps on a single graph nicely, only the most recent one is displayed.

import numpy as np
import numpy.random
import matplotlib.pyplot as plt

# Generate some test data
x_L = np.random.randn(8873)
y_L = np.random.randn(8873)

x_H = np.random.randn(1000)
y_H = np.random.randn(1000)

heatmap_L, xedges_L, yedges_L = np.histogram2d(x_L, y_L, bins=50)
extent_L = [xedges_L[0], xedges_L[-1], yedges_L[0], yedges_L[-1]]

heatmap_H, xedges_H, yedges_H = np.histogram2d(x_H, y_H, bins=50)
extent_H = [xedges_H[0], xedges_H[-1], yedges_H[0], yedges_H[-1]]

plt.clf()
im1 = plt.imshow(heatmap_L.T, extent=extent_L, origin='lower', cmap='Blues')
im2 = plt.imshow(heatmap_H.T, extent=extent_H, origin='lower', cmap='Greens')
plt.show() 

Only most recent heatmap displayed

Edit: If I'm not mistaken, all points are not in exactly the proper location

import numpy as np
import numpy.random
import matplotlib.pyplot as plt

# Generate some test data
x_L = np.random.randn(8873)
y_L = np.random.randn(8873)

x_H = np.random.randn(1000)
y_H = np.random.randn(1000)

heatmap_L, xedges_L, yedges_L = np.histogram2d(x_L, y_L, bins=50)
extent_L = np.array([xedges_L[0], xedges_L[-1], yedges_L[0], yedges_L[-1]])

heatmap_H, xedges_H, yedges_H = np.histogram2d(x_H, y_H, bins=50)
extent_H = np.array([xedges_H[0], xedges_H[-1], yedges_H[0], yedges_H[-1]])

plt.clf()
im1 = plt.imshow(heatmap_L.T, extent=extent_L, origin='lower', cmap='Blues')
im2 = plt.imshow(heatmap_H.T, extent=extent_H, origin='lower', cmap='Greens')
plt.autoscale()
plt.show()

enter image description here

flatHMH = np.reshape(heatmap_H, 2500)  # flatten the 2D arrays
flatHML = np.reshape(heatmap_L, 2500)
maxHMH = flatHMH.max()  # Find the maximum in each
maxHML = flatHML.max()
# Now for each value in the flat array build an RGBA tuple using 
# 1 for the colour we want - either green or blue, and then scaling
# the value by the maximum, finally reshaping back to a 50x50 array
augHMH = np.array([(0, 1, 0, x/maxHMH) for x in flatHMH]).reshape((50, 50, 4))
augHML = np.array([(0, 0, 1, x/maxHML) for x in flatHML]).reshape((50, 50, 4))

plt.clf()
# Plot without cmap as colours are now part of the data array passed.
im1 = plt.imshow(augHML, extent=extent_L, origin='lower')
im2 = plt.imshow(augHMH, extent=extent_H, origin='lower')
plt.autoscale()
plt.show()

enter image description here

If you look closely at the points in the last plot, for example the clustering of points at the edge, you'll notice they are not the same as in the plot above.

Mathews24
  • 681
  • 10
  • 30
  • @ImportanceOfBeingErnest It is literally following [this example](https://stackoverflow.com/a/2461029/6347862), which provides a working example for the case of one plot. I am interested in overlaying two such plots. – Mathews24 Jul 17 '18 at 23:32

2 Answers2

1

You are displaying both plots, the problem is that you are drawing one on top of the other. To see this in action you can shift one of the plots as in:

import numpy as np
import numpy.random
import matplotlib.pyplot as plt

# Generate some test data
x_L = np.random.randn(8873)
y_L = np.random.randn(8873)

x_H = np.random.randn(1000)
y_H = np.random.randn(1000)

heatmap_L, xedges_L, yedges_L = np.histogram2d(x_L, y_L, bins=50)
extent_L = np.array([xedges_L[0], xedges_L[-1], yedges_L[0], yedges_L[-1]])

heatmap_H, xedges_H, yedges_H = np.histogram2d(x_H, y_H, bins=50)
extent_H = np.array([xedges_H[0], xedges_H[-1], yedges_H[0], yedges_H[-1]])

plt.clf()
im1 = plt.imshow(heatmap_L.T, extent=extent_L, origin='lower', cmap='Blues')
im2 = plt.imshow(heatmap_H.T+2, extent=extent_H+2, origin='lower', cmap='Greens')
plt.autoscale()
plt.show() 

You also need the plt.autoscale() call in there as otherwise the limits are not adjusted correctly.

One way to show the two plots on top of each other is to use the argument alpha=X to the imshow call (where 0 < X < 1) in order to set transparency on the plot call. Another, possibly clearer way is to transform each value from the histogram2D to an RGBA value. See the imshow docs for both alternatives to displaying the plots on top of each other.

One way of transforming the values would be to flatten the data, and augment it with the colours you want.

# imports and test data generation as before, removed for clarity...

flatHMH = np.reshape(heatmap_H, 2500)  # flatten the 2D arrays
flatHML = np.reshape(heatmap_L, 2500)
maxHMH = flatHMH.max()  # Find the maximum in each
maxHML = flatHML.max()
# Now for each value in the flat array build an RGBA tuple using 
# 1 for the colour we want - either green or blue, and then scaling
# the value by the maximum, finally reshaping back to a 50x50 array
augHMH = np.array([(0, 1, 0, x/maxHMH) for x in flatHMH]).reshape((50, 50, 4))
augHML = np.array([(0, 0, 1, x/maxHML) for x in flatHML]).reshape((50, 50, 4))

plt.clf()
# Plot without cmap as colours are now part of the data array passed.
im1 = plt.imshow(augHML, extent=extent_L, origin='lower')
im2 = plt.imshow(augHMH, extent=extent_H, origin='lower')
plt.autoscale()
plt.show() 
Gavin
  • 1,070
  • 18
  • 24
  • Yes, the alpha helps with the blending, but if it is too low then it appears completely smeared without clear distinction of concentrated points. But if alpha is too high, then the rectangular grid from each overlaying plot become very noticeable. Is there an optimal way to nicely blend the overlaying plots' backgrounds while keeping the concentrated points noticeable? – Mathews24 Jul 18 '18 at 15:40
  • @Mathews24, See the updated answer for the alternative solution hinted at earlier from the imshow documentation. – Gavin Jul 18 '18 at 16:16
  • Based on my visualization, it appears that points may not be exactly aligned with the original case. When I plotted the above (first example) and then the latter example with RGB, the points were not necessarily in the exact same position on the grid. I'll provide an example in an edit to my original question. – Mathews24 Jul 18 '18 at 21:23
0

You can call

plt.autoscale()

such that the limits are adjusted to the content of the axes.

Example:

import numpy as np
import matplotlib.pyplot as plt

def get(offs=0):
    # Generate some test data
    x = np.random.randn(8873)+offs
    y = np.random.randn(8873)+offs

    heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

    heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
    return heatmap, extent

h1,e1  = get(-3)
h2,e2  = get(+3)
plt.imshow(h1, extent=e1, origin='lower', cmap="RdBu")
plt.imshow(h2, extent=e2, origin='lower', cmap="YlGnBu")
plt.autoscale()
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thank you. I have tested that, but am unable to overlay the plots to confirm if it is necessarily adjusted as intended. – Mathews24 Jul 17 '18 at 23:39