可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a list of 3-tuples representing a set of points in 3D space. I want to plot a surface that covers all these points. The plot_surface function in the mplot3d package requires as arguments X,Y and Z which are 2d arrays. Is plot_surface the right function to plot surface and how do I transform my data in to the required format ?
data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)]
回答1:
For surfaces it\'s a bit different than a list of 3-tuples, you should pass in a grid for the domain in 2d arrays.
If all you have is a list of 3d points, rather than some function f(x, y) -> z
, then you will have a problem because there are multiple ways to triangulate that 3d point cloud into a surface.
Here\'s a smooth surface example:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D # This import has side effects required for the kwarg projection=\'3d\' in the call to fig.add_subplot
import matplotlib.pyplot as plt
import random
def fun(x, y):
return x**2 + y
fig = plt.figure()
ax = fig.add_subplot(111, projection=\'3d\')
x = y = np.arange(-3.0, 3.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel(\'X Label\')
ax.set_ylabel(\'Y Label\')
ax.set_zlabel(\'Z Label\')
plt.show()
回答2:
I just came across this same problem. I have evenly spaced data that is in 3 1-D arrays instead of the 2-D arrays that matplotlib
\'s plot_surface
wants. My data happened to be in a pandas.DataFrame
so here is the matplotlib.plot_surface
example with the modifications to plot 3 1-D arrays.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
fig = plt.figure()
ax = fig.gca(projection=\'3d\')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter(\'%.02f\'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.title(\'Original Code\')
That is the original example. Adding this next bit on creates the same plot from 3 1-D arrays.
# ~~~~ MODIFICATION TO EXAMPLE BEGINS HERE ~~~~ #
import pandas as pd
from scipy.interpolate import griddata
# create 1D-arrays from the 2D-arrays
x = X.reshape(1600)
y = Y.reshape(1600)
z = Z.reshape(1600)
xyz = {\'x\': x, \'y\': y, \'z\': z}
# put the data into a pandas DataFrame (this is what my data looks like)
df = pd.DataFrame(xyz, index=range(len(xyz[\'x\'])))
# re-create the 2D-arrays
x1 = np.linspace(df[\'x\'].min(), df[\'x\'].max(), len(df[\'x\'].unique()))
y1 = np.linspace(df[\'y\'].min(), df[\'y\'].max(), len(df[\'y\'].unique()))
x2, y2 = np.meshgrid(x1, y1)
z2 = griddata((df[\'x\'], df[\'y\']), df[\'z\'], (x2, y2), method=\'cubic\')
fig = plt.figure()
ax = fig.gca(projection=\'3d\')
surf = ax.plot_surface(x2, y2, z2, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter(\'%.02f\'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.title(\'Meshgrid Created from 3 1D Arrays\')
# ~~~~ MODIFICATION TO EXAMPLE ENDS HERE ~~~~ #
plt.show()
Here are the resulting figures:
回答3:
I do this with some lines in python using PANDAS, the plot is beatiful!
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import pandas as pd
from sys import argv
file = argv[1]
x,y,z = np.loadtxt(file, unpack=True)
df = pd.DataFrame({\'x\': x, \'y\': y, \'z\': z})
fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.savefig(\'teste.pdf\')
plt.show()
If necessary you can pass vmin and vmax to define the colorbar range, e.g.
surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1, vmin=0, vmax=2000)
回答4:
check the official example.
X,Y and Z are indeed 2d arrays, numpy.meshgrid() is a simple way to get 2d x,y mesh out of 1d x and y values.
http://matplotlib.sourceforge.net/mpl_examples/mplot3d/surface3d_demo.py
here\'s pythonic way to convert your 3-tuples to 3 1d arrays.
data = [(1,2,3), (10,20,30), (11, 22, 33), (110, 220, 330)]
X,Y,Z = zip(*data)
In [7]: X
Out[7]: (1, 10, 11, 110)
In [8]: Y
Out[8]: (2, 20, 22, 220)
In [9]: Z
Out[9]: (3, 30, 33, 330)
Here\'s mtaplotlib delaunay triangulation (interpolation), it converts 1d x,y,z into something compliant (?):
http://matplotlib.sourceforge.net/api/mlab_api.html#matplotlib.mlab.griddata
回答5:
In Matlab I did something similar using the delaunay
function on the x
, y
coords only (not the z
), then plotting with trimesh
or trisurf
, using z
as the height.
SciPy has the Delaunay class, which is based on the same underlying QHull library that the Matlab\'s delaunay
function is, so you should get identical results.
From there, it should be a few lines of code to convert this Plotting 3D Polygons in python-matplotlib example into what you wish to achieve, as Delaunay
gives you the specification of each triangular polygon.
回答6:
Just to chime in, Emanuel had the answer that I (and probably many others) are looking for. If you have 3d scattered data in 3 separate arrays, pandas is an incredible help and works much better than the other options. To elaborate, suppose your x,y,z are some arbitrary variables. In my case these were c,gamma, and errors because I was testing a support vector machine. There are many potential choices to plot the data:
- scatter3D(cParams, gammas, avg_errors_array) - this works but is overly simplistic
- plot_wireframe(cParams, gammas, avg_errors_array) - this works, but will look ugly if your data isn\'t sorted nicely, as is potentially the case with massive chunks of real scientific data
- ax.plot3D(cParams, gammas, avg_errors_array) - similar to wireframe
Wireframe plot of the data
3d scatter of the data
The code looks like this:
fig = plt.figure()
ax = fig.gca(projection=\'3d\')
ax.set_xlabel(\'c parameter\')
ax.set_ylabel(\'gamma parameter\')
ax.set_zlabel(\'Error rate\')
#ax.plot_wireframe(cParams, gammas, avg_errors_array)
#ax.plot3D(cParams, gammas, avg_errors_array)
#ax.scatter3D(cParams, gammas, avg_errors_array, zdir=\'z\',cmap=\'viridis\')
df = pd.DataFrame({\'x\': cParams, \'y\': gammas, \'z\': avg_errors_array})
surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.savefig(\'./plots/avgErrs_vs_C_andgamma_type_%s.png\'%(k))
plt.show()
Here is the final output:
回答7:
It is not possible to directly make a 3d surface using your data. I would recommend you to build an interpolation model using some tools like pykridge. The process will include three steps:
- Train an interpolation model using
pykridge
- Build a grid from
X
and Y
using meshgrid
- Interpolate values for
Z
Having created your grid and the corresponding Z
values, now you\'re ready to go with plot_surface
. Note that depending on the size of your data, the meshgrid
function can run for a while. The workaround is to create evenly spaced samples using np.linspace
for X
and Y
axes, then apply interpolation to infer the necessary Z
values. If so, the interpolated values might different from the original Z
because X
and Y
have changed.