Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

3D discrete heatmap in matplotlib

I have a list of tuples in python containing 3-dimenstional data, where each tuple is in the form: (x, y, z, data_value), i.e., I have data values at each (x, y, z) coordinate. I would like to make a 3D discrete heatmap plot where the colors represent the value of data_values in my list of tuples. Here, I give an example of such a heatmap for a 2D dataset where I have a list of (x, y, data_value) tuples:

import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
from random import randint

# x and y coordinates
x = np.array(range(10))
y = np.array(range(10,15))
data = np.zeros((len(y),len(x)))

# Generate some discrete data (1, 2 or 3) for each (x, y) pair
for i,yy in enumerate(y):
    for j, xx in enumerate(x):
        data[i,j] = randint(1,3)

# Map 1, 2 and 3 to 'Red', 'Green' qnd 'Blue', respectively
colormap = colors.ListedColormap(['Red', 'Green', 'Blue'])
colorbar_ticklabels = ['1', '2', '3']

# Use matshow to create a heatmap
fig, ax = plt.subplots()
ms = ax.matshow(data, cmap = colormap, vmin=data.min() - 0.5, vmax=data.max() + 0.5, origin = 'lower')

# x and y axis ticks
ax.set_xticklabels([str(xx) for xx in x])
ax.set_yticklabels([str(yy) for yy in y])
ax.xaxis.tick_bottom()

# Put the x- qnd y-axis ticks at the middle of each cell 
ax.set_xticks(np.arange(data.shape[1]), minor = False)
ax.set_yticks(np.arange(data.shape[0]), minor = False)

# Set custom ticks and ticklabels for color bar
cbar = fig.colorbar(ms,ticks = np.arange(np.min(data),np.max(data)+1))
cbar.ax.set_yticklabels(colorbar_ticklabels)

plt.show()

This generates a plot like this: enter image description here

How can I make a similar plot in 3D-space (i.e., having a z-axis), if my data have a third dimension. For example, if

# x and y and z coordinates
x = np.array(range(10))
y = np.array(range(10,15))
z = np.array(range(15,20))
data = np.zeros((len(y),len(x), len(y)))

# Generate some random discrete data (1, 2 or 3) for each (x, y, z) triplet. 
# Am I defining i, j and k correctly here?
for i,yy in enumerate(y):
    for j, xx in enumerate(x):
        for k, zz in enumerate(z):
            data[i,j, k] = randint(1,3)

I sounds like plot_surface in mplot3d should be able to do this, but z in the input of this function is essentially the value of data at (x, y) coordinate, i.e., (x, y, z = data_value), which is different from what I have, i.e., (x, y, z, data_value).

like image 816
user3076813 Avatar asked Nov 28 '16 21:11

user3076813


People also ask

How do you make a 3D heatmap in Python?

Creating 3D heatmap with CSV file You can operate the data as your wish. Using plt. figure, we have created a figure of size 8×5 width and height respectively by default the matplotlib will produce 2D plots, so to specify this as a 3d plot we use add_subplot function with projection='3d' to create a 3d plot.

How do you plot 3D contour in Python?

The ax. contour3D() function creates three-dimensional contour plot. It requires all the input data to be in the form of two-dimensional regular grids, with the Z-data evaluated at each point.

Can Matplotlib do 3D graphs?

In order to plot 3D figures use matplotlib, we need to import the mplot3d toolkit, which adds the simple 3D plotting capabilities to matplotlib. Once we imported the mplot3d toolkit, we could create 3D axes and add data to the axes.


1 Answers

New answer:

It seems we really want to have a 3D Tetris game here ;-)

So here is a way to plot cubes of different color to fill the space given by the arrays (x,y,z).

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

def cuboid_data(center, size=(1,1,1)):
    # code taken from
    # http://stackoverflow.com/questions/30715083/python-plotting-a-wireframe-3d-cuboid?noredirect=1&lq=1
    # suppose axis direction: x: to left; y: to inside; z: to upper
    # get the (left, outside, bottom) point
    o = [a - b / 2 for a, b in zip(center, size)]
    # get the length, width, and height
    l, w, h = size
    x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in bottom surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in upper surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in outside surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]]]  # x coordinate of points in inside surface
    y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]],  # y coordinate of points in bottom surface
         [o[1], o[1], o[1] + w, o[1] + w, o[1]],  # y coordinate of points in upper surface
         [o[1], o[1], o[1], o[1], o[1]],          # y coordinate of points in outside surface
         [o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]]    # y coordinate of points in inside surface
    z = [[o[2], o[2], o[2], o[2], o[2]],                        # z coordinate of points in bottom surface
         [o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h],    # z coordinate of points in upper surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]],                # z coordinate of points in outside surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]]]                # z coordinate of points in inside surface
    return x, y, z

def plotCubeAt(pos=(0,0,0), c="b", alpha=0.1, ax=None):
    # Plotting N cube elements at position pos
    if ax !=None:
        X, Y, Z = cuboid_data( (pos[0],pos[1],pos[2]) )
        ax.plot_surface(X, Y, Z, color=c, rstride=1, cstride=1, alpha=0.1)

def plotMatrix(ax, x, y, z, data, cmap="jet", cax=None, alpha=0.1):
    # plot a Matrix 
    norm = matplotlib.colors.Normalize(vmin=data.min(), vmax=data.max())
    colors = lambda i,j,k : matplotlib.cm.ScalarMappable(norm=norm,cmap = cmap).to_rgba(data[i,j,k]) 
    for i, xi in enumerate(x):
            for j, yi in enumerate(y):
                for k, zi, in enumerate(z):
                    plotCubeAt(pos=(xi, yi, zi), c=colors(i,j,k), alpha=alpha,  ax=ax)



    if cax !=None:
        cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap,
                                norm=norm,
                                orientation='vertical')  
        cbar.set_ticks(np.unique(data))
        # set the colorbar transparent as well
        cbar.solids.set(alpha=alpha)              



if __name__ == '__main__':

    # x and y and z coordinates
    x = np.array(range(10))
    y = np.array(range(10,15))
    z = np.array(range(15,20))
    data_value = np.random.randint(1,4, size=(len(x), len(y), len(z)) )
    print data_value.shape

    fig = plt.figure(figsize=(10,4))
    ax = fig.add_axes([0.1, 0.1, 0.7, 0.8], projection='3d')
    ax_cb = fig.add_axes([0.8, 0.3, 0.05, 0.45])
    ax.set_aspect('equal')

    plotMatrix(ax, x, y, z, data_value, cmap="jet", cax = ax_cb)

    plt.savefig(__file__+".png")
    plt.show()

enter image description here I find it really hard to see anything here, but that may be a question of taste and now hopefully also answers the question.


Original Answer:

It seems I misunderstood the question. Therefore the following does not answer the question. For the moment I leave it here, to keep the comments below available for others.

I think plot_surface is fine for the specified task.

Essentially you would plot a surface with the shape given by your points X,Y,Z in 3D and colorize it using the values from data_values as shown in the code below.

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

fig = plt.figure()
ax = fig.gca(projection='3d')

# as plot_surface needs 2D arrays as input
x = np.arange(10)
y = np.array(range(10,15))
# we make a meshgrid from the x,y data
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

# data_value shall be represented by color
data_value = np.random.rand(len(y), len(x))
# map the data to rgba values from a colormap
colors = cm.ScalarMappable(cmap = "viridis").to_rgba(data_value)


# plot_surface with points X,Y,Z and data_value as colors
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,
                       linewidth=0, antialiased=True)

plt.show()

enter image description here

like image 80
ImportanceOfBeingErnest Avatar answered Sep 21 '22 12:09

ImportanceOfBeingErnest