Plot a 3D bar histogram
Asked Answered
M

3

6

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

Macey answered 18/9, 2018 at 11:11 Comment(6)
Unless you use mplot3d, matplotlib does not feature 3d plotting.Freese
Have you checked the official docs? There is a simple example for that.Hagood
I don't understand what this x,y data is required for. I guess you have a equispaced grid and each square on the grid should get some random value.Trimly
To norok2: yes, I have looked but the examples (like yours) does not fit with what I was looking for...Inchoation
To kanayamalakar: I am doing data treatment, and I have points with x and y coordinates. Then, I want to recreate an histogram to see which events are more populated. The first I do is to filter them , to keep the ones insides the bins.Inchoation
The linked example seems to be directly applicable. If that is "not what I was looking for", you would want to tell in how far it isn't, else one cannot know what's wrong with that. (If you want to notify someone, use @username, else they will not see it)Ironclad
M
13

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

Macey answered 19/9, 2018 at 10:7 Comment(3)
Well done ! I was having trouble with applying a color scheme. Thanks for sharingTrimly
@Trimly 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.Inchoation
Perhaps you can add "import matplotlib.cm as cm"Lucas
T
5

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

Trimly answered 18/9, 2018 at 11:52 Comment(7)
Sorry, but it would be better if you use the same x, y and heatmap.T datas. You change the exepInchoation
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 ?Inchoation
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. @TrimlyInchoation
@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.Trimly
You can look at this link matplotlib.org/examples/mplot3d/…Trimly
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.Trimly
Yes, I had understood, but it's not what you expect in a 3 histogram...Inchoation
C
0

The accepted answer in this post is good but it has one bug. One of the following fixes shall be done. Otherwise, the data looks mirrored.

dz = np.transpose(hist).flatten()

OR

ypos, xpos = np.meshgrid(yedges[:-1] + yedges[1:], xedges[:-1] + xedges[1:])

when you flatten the output of np.histogram2d, you get a full row of y values for each x stacked behind eachother. Then you flatten the output of meshgrid, you get incrementing x and fixed y stacked along the array. Thus, both don't match any more

Callida answered 10/11, 2023 at 8:56 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.