Plot a 3D bar histogram with python

12,272

Solution 1

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

Solution 2

You can generate the same result using something as simple as the following:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-2, 2, 7)
y = np.linspace(-2, 2, 7)

xx, yy = np.meshgrid(x, y)

z = xx*0+yy*0+ np.random.random(size=[7,7])

plt.imshow(z, interpolation='nearest', cmap=plt.cm.viridis, extent=[-2,2,2,2])
plt.show()

from mpl_toolkits.mplot3d import Axes3D
ax = Axes3D(plt.figure())

ax.plot_surface(xx, yy, z, cmap=plt.cm.viridis, cstride=1, rstride=1)
plt.show()

The results are given below: enter image description here

Surface plot

Share:
12,272
Agape Gal'lo
Author by

Agape Gal'lo

Updated on June 09, 2022

Comments

  • Agape Gal'lo
    Agape Gal'lo about 2 years

    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...

  • Agape Gal'lo
    Agape Gal'lo almost 6 years
    Sorry, but it would be better if you use the same x, y and heatmap.T datas. You change the exep
  • Agape Gal'lo
    Agape Gal'lo almost 6 years
    Thanks! But sorry, but it would be better if you use the same x, y and heatmap.T datas. You change the example a bit. Also, could we have some flat levels instead of the tilts surfaces ?
  • Agape Gal'lo
    Agape Gal'lo almost 6 years
    And also, there is a problem I had not notice before: There are 7x7 delimitations for the 2D colored histogram, and 6 for the other one ! Which make it dangerous to observe for data analysis... What would be great is a 7x7 flat square levels, in 3D. @kanayamalakar
  • kanayamalakar
    kanayamalakar almost 6 years
    @AgapeGal'lo, could you show me a picture of how you want the 3D plot to be - maybe just a representative one ? Because I don't seem to get what you mean by flat square levels in 3D.
  • kanayamalakar
    kanayamalakar almost 6 years
    You can look at this link matplotlib.org/examples/mplot3d/…
  • kanayamalakar
    kanayamalakar almost 6 years
    The difference in the 2D and 3D plots are : In 2D, the data point lies at the center of each box. Whereas in 3D the data points lie at the corner of the boxes. The line segments join adjacent data points and form grids. Hence for 7x7 data points you get (7-1)x(7-1) sized grid.
  • Agape Gal'lo
    Agape Gal'lo almost 6 years
    Yes, I had understood, but it's not what you expect in a 3 histogram...
  • kanayamalakar
    kanayamalakar almost 6 years
    Well done ! I was having trouble with applying a color scheme. Thanks for sharing
  • Agape Gal'lo
    Agape Gal'lo almost 6 years
    @kanayamalakar but there is a problem in the renormalisation for the colors... If after this line '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' you replace hist by an other 7X7 matrix, the colors are not anymore well scale. I don't understand why ! There should a better way to get this color gradiant.
  • Daniel González Cortés
    Daniel González Cortés over 3 years
    Perhaps you can add "import matplotlib.cm as cm"