3D discrete heatmap in matplotlib

匿名 (未验证) 提交于 2019-12-03 07:50:05

问题:

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:

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

回答1:

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() 

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() 



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!