How to correctly generate a 3d histogram using numpy or matplotlib built in functions in python?

20,983

Solution 1

I posted this in a related thread about colored 3d bar plots, but I think it's also relevant here as I couldn't find a complete answer for what I needed in either thread. This code generates a histogram scatterplot for any sort of x-y data. The height represents the frequency of values in that bin. So, for example, if you had many data point where (x,y) = (20,20) it would be high and red. If you had few data points in the bin where (x,y) = (100,100) it would be low and blue.

Note: result will vary substantially depending on how much data you have and how many bins your choose for you histogram. Adjust accordingly!

xAmplitudes = #your data here
yAmplitudes = #your other data here

x = np.array(xAmplitudes)   #turn x,y data into numpy arrays
y = np.array(yAmplitudes)

fig = plt.figure()          #create a canvas, tell matplotlib it's 3d
ax = fig.add_subplot(111, projection='3d')

#make histogram stuff - set bins - I choose 20x20 because I have a lot of data
hist, xedges, yedges = np.histogram2d(x, y, bins=(20,20))
xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:])

xpos = xpos.flatten()/2.
ypos = ypos.flatten()/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()

The results for about 75k data points of mine are below. Note, you can drag and drop to different perspectives and may want to save multiple views for presentations, posterity.

3d histogram side view 3d histogram perspective 2

Solution 2

Have a look at https://matplotlib.org/stable/gallery/mplot3d/hist3d.html, this has a working example script.

I've improved the code at that link to be more of a histogram:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [1, 3, 5, 8, 6, 7, 1, 2, 4, 5]
y = [3, 4, 3, 6, 5, 3, 1, 2, 3, 8]

hist, xedges, yedges = np.histogram2d(x, y, bins=(4,4))
xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:])

xpos = xpos.flatten()/2.
ypos = ypos.flatten()/2.
zpos = np.zeros_like (xpos)

dx = xedges [1] - xedges [0]
dy = yedges [1] - yedges [0]
dz = hist.flatten()

ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average')
plt.xlabel ("X")
plt.ylabel ("Y")

plt.show()

I'm not sure how to do it with Axes3D.hist ().

Solution 3

In this answer there is a solution for 2D and 3D Histograms of scattered points. The usage is simple:

points, sub = hist2d_scatter( radius, density, bins=4 )

points, sub = hist3d_scatter( temperature, density, radius, bins=4 )

Where sub is a matplotlib "Subplot" instance (3D or not) and pointscontains the points used for the scatter plot.

Solution 4

I've added to @lxop's answer to allow for arbitrary size buckets:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

x = np.array([0, 2, 5, 10, 2, 3, 5, 2, 8, 10, 11])
y = np.array([0, 2, 5, 10, 6, 4, 2, 2, 5, 10, 11])
# This example actually counts the number of unique elements.
binsOne = sorted(set(x))
binsTwo = sorted(set(y))
# Just change binsOne and binsTwo to lists.
hist, xedges, yedges = np.histogram2d(x, y, bins=[binsOne, binsTwo])

# The start of each bucket.
xpos, ypos = np.meshgrid(xedges[:-1], yedges[:-1])

xpos = xpos.flatten()
ypos = ypos.flatten()
zpos = np.zeros_like(xpos)

# The width of each bucket.
dx, dy = np.meshgrid(xedges[1:] - xedges[:-1], yedges[1:] - yedges[:-1])

dx = dx.flatten()
dy = dy.flatten()
dz = hist.flatten()

ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average')
Share:
20,983
Chris
Author by

Chris

Updated on May 01, 2021

Comments

  • Chris
    Chris about 3 years

    This is more of a general question about 3d histogram creation in python.

    I have attempted to create a 3d histogram using the X and Y arrays in the following code

    import matplotlib
    import pylab
    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d.axes3d import Axes3D
    from matplotlib import cm
    
    def threedhist():
        X = [1, 3, 5, 8, 6, 7, 1, 2, 4, 5]
        Y = [3, 4, 3, 6, 5, 3, 1, 2, 3, 8]
        fig = pylab.figure()
        ax = Axes3D(fig)
        ax.hist([X, Y], bins=10, range=[[0, 10], [0, 10]])
        plt.xlabel('X')
        plt.ylabel('Y')
        plt.zlabel('Frequency')
        plt.title('Histogram')
        plt.show()
    

    However, I am getting the following error

    Traceback (most recent call last):
      File "<pyshell#0>", line 1, in <module>
        a3dhistogram()
      File "C:/Users/ckiser/Desktop/Projects/Tom/Python Files/threedhistogram.py", line 24, in a3dhistogram
        ax.hist([X, Y], bins=10, range=[[0, 10], [0, 10]])
      File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 7668, in hist
        m, bins = np.histogram(x[i], bins, weights=w[i], **hist_kwargs)
      File "C:\Python27\lib\site-packages\numpy\lib\function_base.py", line 169, in histogram
        mn, mx = [mi+0.0 for mi in range]
    TypeError: can only concatenate list (not "float") to list
    

    I have tried the code with and without the "[" in the line ax.hist([X, Y], bins=10, range=[[0, 10], [0, 10]]) I have also tried the function from numpy without success H, xedges, yedges = np.histogram2d(x, y, bins = (10, 10)) Am I missing a step or a parameter? Any advice would be greatly appreciated.

  • Agape Gal'lo
    Agape Gal'lo over 5 years
    Nice plots, but your datas are not center on zero... If you take an odd bins (try bins=(7,7)), then one bar start at zero when it should be center onto zero. @ArtifexR
  • Agape Gal'lo
    Agape Gal'lo over 5 years
    I have solve it, but I don't feel powerfull enough to correct your code. You have to remplace ' xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:]) ' by ' xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:]) - abs(xedges[1]-xedges[0]) ' and you the offset will be corrected.