Добавление 4-й переменной в 3D-график в Python
У меня есть 3 различных параметра X,Y и Z в диапазоне значений, и для каждой их комбинации определенное значение V. Чтобы было понятнее, данные будут выглядеть примерно так.
X Y Z V
1 1 2 10
1 2 3 15
etc...
Я хотел бы визуализировать данные с помощью графика поверхности / контура, используя V в качестве цвета, чтобы увидеть его значение в этой точке, но я не вижу, как добавить свою собственную схему окраски в смесь, используя Python. Любая идея о том, как это сделать (или это визуализация глупо)?
Большое спасибо!
2 ответа
Matplotlib позволяет передавать маску в качестве аргумента, например, ax.plot_surface
,
Тогда это будет означать, что вам придется выполнять 2D-интерполяцию для вашего текущего массива цветов, потому что в настоящее время у вас есть только цвета в углах прямоугольных граней (вы упомянули, что у вас есть прямолинейная сетка).
Вы могли бы использовать scipy.interpolate.interp2d
для этого, но, как вы видите из документации, предлагается использовать scipy.interpolate.RectBivariateSpline
,
Чтобы дать вам простой пример:
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
Теперь у меня есть такой же набор данных, как у вас (одномерные массивы для x, y, z
а также colors
). Обратите внимание, что цвета определены для каждой точки (x,y). Но когда вы хотите построить с plot_surface
, вы будете создавать прямоугольные пятна, углы которых задаются этими точками.
Итак, к интерполяции тогда:
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.
На этом последнем шаге вы могли бы также использовать interp2d
(с kind='linear'
заменяя kx=1, ky=1
). Но так как документы предлагают использовать быстрее RectBivariateSpline
...
Теперь вы готовы построить это:
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
Как видите, цвета на гранях больше не имеют ничего общего с высотой набора данных.
Обратите внимание, что вы могли бы подумать, просто передавая двумерный массив C facecolors
будет работать, и Matplotlib не будет жаловаться. Тем не менее, результат не является точным, потому что matplotlib будет использовать только подмножество C для лицевых цветов (кажется, игнорирует последний столбец и последнюю строку C). Это эквивалентно использованию только цвета, определенного одной координатой (например, верхний левый) по всему патчу.
Более простым способом было бы позволить matplotlib выполнить интерполяцию и получить лицевые цвета, а затем передать их на реальный график:
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)
Однако, это не будет работать в выпусках <= 1.4.1 из-за этой недавно представленной ошибки.
Это действительно зависит от того, как вы планируете строить эти данные. Мне нравится строить графики с gnuplot
: это легко, бесплатно и интуитивно понятно. Чтобы построить свой пример с gnuplot
вам нужно будет распечатать эти строки в файл (только с этими четырьмя столбцами) и построить с использованием кода, подобного следующему
reset
set terminal png
set output "out.png"
splot "file.txt" using 1:2:3:4 with lines palette
Предполагая, что вы сохраните свои данные в файл file.txt
, splot
обозначает поверхностный участок. Конечно, это минимальный пример.
В качестве альтернативы вы можете использовать matplotlib
Но это, на мой взгляд, не так интуитивно понятно. Хотя он имеет преимущество в том, что центрирует всю обработку в python.