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).
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.
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.
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.
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.
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With