Adding a 4th variable to a 3D plot in Python

时光毁灭记忆、已成空白 提交于 2019-12-30 16:28:31

问题


I have 3 different parameters X,Y and Z over a range of values, and for each combination of these a certain value of V. To make it clearer, the data would look something like this.

X  Y  Z  V
1  1  2  10
1  2  3  15
etc...

I'd like to visualize the data with a surface/contour plot, using V as a colour to see its value at that point, but I do not see how to add my custom colouring scheme into the mix using Python. Any idea on how to do this (or is this visualization outright silly)?

Thanks a lot!


回答1:


Matplotlib allows one to pass the facecolors as an argument to e.g. ax.plot_surface.

That would imply then that you would have to perform 2D interpolation on your current array of colors, because you currently only have the colors in the corners of the rectangular faces (you did mention that you have a rectilinear grid).

You could use scipy.interpolate.interp2d for that, but as you see from the documentation, it is suggested to use scipy.interpolate.RectBivariateSpline.

To give you a simple example:

import numpy as np
y,x = np.mgrid[1:10:10j, 1:10:10j] # returns 2D arrays
# You have 1D arrays that would make a rectangular grid if properly reshaped.
y,x = y.ravel(), x.ravel()  # so let's convert to 1D arrays
z = x*(x-y)
colors = np.cos(x**2) - np.sin(y)**2

Now I have a similar dataset as you (one-dimensional arrays for x, y, z and colors). Remark that the colors are defined for each point (x,y). But when you want to plot with plot_surface, you'll generate rectangular patches, of which the corners are given by those points.

So, on to interpolation then:

from scipy.interpolate import RectBivariateSpline
# from scipy.interpolate import interp2d # could 've used this too, but docs suggest the faster RectBivariateSpline

# Define the points at the centers of the faces:
y_coords, x_coords = np.unique(y), np.unique(x)
y_centers, x_centers = [ arr[:-1] + np.diff(arr)/2 for arr in (y_coords, x_coords)]

# Convert back to a 2D grid, required for plot_surface:
Y = y.reshape(y_coords.size, -1)
X = x.reshape(-1, x_coords.size)
Z = z.reshape(X.shape)
C = colors.reshape(X.shape)
#Normalize the colors to fit in the range 0-1, ready for using in the colormap:
C -= C.min()
C /= C.max()

interp_func = RectBivariateSpline(x_coords, y_coords, C.T, kx=1, ky=1) # the kx, ky define the order of interpolation. Keep it simple, use linear interpolation.

In this last step, you could also have used interp2d (with kind='linear' replacing the kx=1, ky=1). But since the docs suggest to use the faster RectBivariateSpline...

Now you're ready to plot it:

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

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
r = ax.plot_surface(X,Y,Z,
    facecolors=cm.hot(interp_func(x_centers, y_centers).T),
    rstride=1,  cstride=1) # only added because of this very limited dataset

As you can see, the colors on the faces have nothing to do anymore with the height of the dataset.

Note that you could have thought simply passing the 2D array C to facecolors would work, and matplotlib would not have complained. However, the result isn't accurate then, because matplotlib will use only a subset of C for the facecolors (it seems to ignore the last column and last row of C). It is equivalent to using only the color defined by one coordinate (e.g. the top-left) over the entire patch.

An easier method would have been to let matplotlib do the interpolation and obtain the facecolors and then pass those in to the real plot:

r = ax.plot_surface(X,Y,C, cmap='hot') # first plot the 2nd dataset, i.e. the colors
fc = r.get_facecolors()
ax.clear()
ax.plot_surface(X, Y, Z, facecolors=fc)

However, that won't work in releases <= 1.4.1 due to this recently submitted bug.




回答2:


It really depends on how you plan on plotting this data. I like to plot graphs with gnuplot: it's easy, free and intuitive. To plot your example with gnuplot you'd have to print those line into a file (with only those four columns) and plot using a code like the following

 reset
 set terminal png
 set output "out.png"
 splot "file.txt" using 1:2:3:4 with lines palette

Assuming that you save your data into the file file.txt. splot stands for surface plot. Of course, this is a minimum example.

Alternatively you can use matplotlib, but that is not, in my opinion, as intuitive. Although it has the advantage of centering all the processing in python.



来源:https://stackoverflow.com/questions/28324545/adding-a-4th-variable-to-a-3d-plot-in-python

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