3D discrete heatmap in matplotlib

后端 未结 1 701
Happy的楠姐
Happy的楠姐 2020-12-01 05:44

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

1条回答
  •  粉色の甜心
    2020-12-01 06:23

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

    0 讨论(0)
提交回复
热议问题