pythonmatplotlibhistogrammatplotlib-3d

Plot a 3D bar histogram


I have some x and y data, with which I would like to generate a 3D histogram, with a color gradient (bwr or whatever).

I have written a script which plot the interesting values, in between -2 and 2 for both x and y abscesses:

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

# To generate some test data
x = np.random.randn(500)
y = np.random.randn(500)

XY = np.stack((x,y),axis=-1)

def selection(XY, limitXY=[[-2,+2],[-2,+2]]):
        XY_select = []
        for elt in XY:
            if elt[0] > limitXY[0][0] and elt[0] < limitXY[0][1] and elt[1] > limitXY[1][0] and elt[1] < limitXY[1][1]:
                XY_select.append(elt)

        return np.array(XY_select)

XY_select = selection(XY, limitXY=[[-2,+2],[-2,+2]])

heatmap, xedges, yedges = np.histogram2d(XY_select[:,0], XY_select[:,1], bins = 7, range = [[-2,2],[-2,2]])
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]


plt.figure("Histogram")
#plt.clf()
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()

And give this correct result:

enter image description here

Now, I would like to turn this into a 3D histogram. Unfortunatly I don't success to plot it correctly with bar3d because it takes by default the length of x and y for abscisse.

I am quite sure that there is a very easy way to plot this in 3D with imshow. Like an unknow option...


Solution

  • I finaly succeded in doing it. I am almost sure there is a better way to do it, but at leat it works:

    import numpy as np
    import numpy.random
    import matplotlib.pyplot as plt
    
    # To generate some test data
    x = np.random.randn(500)
    y = np.random.randn(500)
    
    XY = np.stack((x,y),axis=-1)
    
    def selection(XY, limitXY=[[-2,+2],[-2,+2]]):
            XY_select = []
            for elt in XY:
                if elt[0] > limitXY[0][0] and elt[0] < limitXY[0][1] and elt[1] > limitXY[1][0] and elt[1] < limitXY[1][1]:
                    XY_select.append(elt)
    
            return np.array(XY_select)
    
    XY_select = selection(XY, limitXY=[[-2,+2],[-2,+2]])
    
    
    xAmplitudes = np.array(XY_select)[:,0]#your data here
    yAmplitudes = np.array(XY_select)[:,1]#your other data here
    
    
    fig = plt.figure() #create a canvas, tell matplotlib it's 3d
    ax = fig.add_subplot(111, projection='3d')
    
    
    hist, xedges, yedges = np.histogram2d(x, y, bins=(7,7), range = [[-2,+2],[-2,+2]]) # you can change your bins, and the range on which to take data
    # hist is a 7X7 matrix, with the populations for each of the subspace parts.
    xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:]) -(xedges[1]-xedges[0])
    
    
    xpos = xpos.flatten()*1./2
    ypos = ypos.flatten()*1./2
    zpos = np.zeros_like (xpos)
    
    dx = xedges [1] - xedges [0]
    dy = yedges [1] - yedges [0]
    dz = hist.flatten()
    
    cmap = cm.get_cmap('jet') # Get desired colormap - you can change this!
    max_height = np.max(dz)   # get range of colorbars so we can normalize
    min_height = np.min(dz)
    # scale each z to [0,1], and get their rgb values
    rgba = [cmap((k-min_height)/max_height) for k in dz] 
    
    ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=rgba, zsort='average')
    plt.title("X vs. Y Amplitudes for ____ Data")
    plt.xlabel("My X data source")
    plt.ylabel("My Y data source")
    plt.savefig("Your_title_goes_here")
    plt.show()
    

    I use this example, but I modified it, because it introduced an offset. The result is this:

    enter image description here