使用轮廓绘制三个平面中3D绘图的投影

时间:2014-11-04 16:05:18

标签: python matplotlib contour

我有data的三列目录,我想制作它们的3D图,加上每个轴的投影作为另外两个轴平面中的投影轮廓。到目前为止,我可以使用matplotlib创建3D图,但仍然没有显示数据属性的任何内容。

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from numpy import * 
data=loadtxt('test.cat')
X=data[:,0]
Y=data[:,1]
Z=data[:,2]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X, Y, Z, c='r', marker='.')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

plt.show()

我如何使用colorbar绘制每个平面中数据的投影?

3 个答案:

答案 0 :(得分:5)

确实,显示难以显示的数据。也许沿着一个轴创建一些切片并创建一定数量的2D图是最好的。然而3D图很花哨。我玩了一些数据,得到了一个3D图,就像你做的一样,还有一个单独的投影图。

  • 点的颜色取决于缺少
  • 添加透明度以提供密度
  • 两个图的轴保持相同

enter image description here enter image description here

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np

data = np.loadtxt('test.cat', skiprows=1)

X=data[:,0]
Y=data[:,1]
Z=data[:,2]

plt.figure()
ax1 = plt.subplot(111,  projection='3d')

ax1.scatter(X, Y, Z, c='b', marker='.', alpha=0.2)
ax1.set_xlabel('X - axis')
ax1.set_ylabel('Y - axis')
ax1.set_zlabel('Z - axis')

plt.figure()
ax2 = plt.subplot(111,  projection='3d')

plt.hot()

cx = np.ones_like(X) * ax1.get_xlim3d()[0]
cy = np.ones_like(X) * ax1.get_ylim3d()[1]
cz = np.ones_like(Z) * ax1.get_zlim3d()[0]

ax2.scatter(X, Y, cz, c=Z,  marker='.', lw=0, alpha=0.2)
ax2.scatter(X, cy, Z, c=-Y, marker='.', lw=0, alpha=0.2)
ax2.scatter(cx, Y, Z, c=X,  marker='.', lw=0, alpha=0.2)
ax2.set_xlim3d(ax1.get_xlim3d())
ax2.set_ylim3d(ax1.get_ylim3d())
ax2.set_zlim3d(ax1.get_zlim3d())
ax2.set_xlabel('X - axis')
ax2.set_ylabel('Y - axis')
ax2.set_zlabel('Z - axis')

答案 1 :(得分:5)

我希望我不会用[非常丑陋的]大炮射击蚊子,我最近为一个学校项目做了Binning_Array课程,我想它可以帮助你: ThreeBinners

import numpy as np
import matplotlib.pyplot as plt
import binning_array as ba
from mpl_toolkits.mplot3d import axes3d

data = np.loadtxt('test.cat')
X = data[:,0]
Y = data[:,1]
Z = data[:,2]

n_points = data.shape[0]

X_min = np.round(np.min(data[:,0])-0.5)
X_max = np.round(np.max(data[:,0])+0.5)
Y_min = np.round(np.min(data[:,1])-0.5)
Y_max = np.round(np.max(data[:,1])+0.5)
Z_min = np.round(np.min(data[:,2])-0.5)
Z_max = np.round(np.max(data[:,2])+0.5)
n_min_bins = 25
step = min([(X_max-X_min)/n_min_bins, (Y_max-Y_min)/n_min_bins, (Z_max-Z_min)/n_min_bins])

# Using three Binners
BinnerXY = ba.Binning_Array([[X_min, X_max, step],
                             [Y_min, Y_max, step]])
BinnerYZ = ba.Binning_Array([[Y_min, Y_max, step],
                             [Z_min, Z_max, step]])
BinnerXZ = ba.Binning_Array([[X_min, X_max, step],
                             [Z_min, Z_max, step]])

for point in data:
    BinnerXY.add_value([point[0], point[1]])
    BinnerXZ.add_value([point[0], point[2]])
    BinnerYZ.add_value([point[1], point[2]])

fig = plt.figure()
ax = [fig.add_subplot(221, projection='3d'),
      fig.add_subplot(222),
      fig.add_subplot(223),
      fig.add_subplot(224)]

# Plot 2D projections on the 3D graph
vmin = np.min([BinnerXZ.bin_min(), BinnerYZ.bin_min(), BinnerXY.bin_min()])
vmax = np.max([BinnerXZ.bin_max(), BinnerYZ.bin_max(), BinnerXY.bin_max()])
levels = np.linspace(vmin,vmax,20)

xs_c = np.arange(*BinnerXZ.limits[0])
zs_c = np.arange(*BinnerXZ.limits[1])
ZS_C, XS_C = np.meshgrid(zs_c,xs_c)
ax[0].contourf(X=XS_C, Y=BinnerXZ.bins, Z=ZS_C,
               zdir='y', offset=Y_max,
               vmin=vmin, vmax=vmax,
               cmap=plt.cm.coolwarm, levels=levels,
               alpha=0.5)
xs_c = np.arange(*BinnerXY.limits[0])
ys_c = np.arange(*BinnerXY.limits[1])
YS_C, XS_C = np.meshgrid(ys_c,xs_c)
ax[0].contourf(X=XS_C, Y=YS_C, Z=BinnerXY.bins,
               zdir='z', offset=Z_min,
               vmin=vmin, vmax=vmax,
               cmap=plt.cm.coolwarm, levels=levels,
               alpha=0.5)
ys_c = np.arange(*BinnerYZ.limits[0])
zs_c = np.arange(*BinnerYZ.limits[1])
ZS_C, YS_C = np.meshgrid(zs_c, ys_c)
ax[0].contourf(X=BinnerYZ.bins, Y=YS_C, Z=ZS_C,
               zdir='x', offset=X_min,
               vmin=vmin, vmax=vmax,
               cmap=plt.cm.coolwarm, levels=levels,
               alpha=0.5)

# Plot scatter of all data
ax[0].scatter(X, Y, Z, c='g', marker='.', alpha=0.2)
ax[0].set_xlabel(r"$x$")
ax[0].set_ylabel(r"$y$")
ax[0].set_zlabel(r"$z$")
max_range = max([X_max-X_min, Y_max-Y_min, Z_max-Z_min]) / 2.
pos = [(X_max+X_min)/2., (Y_max+Y_min)/2., (Z_max+Z_min)/2.]
ax[0].set_xlim(pos[0] - max_range, pos[0] + max_range)
ax[0].set_ylim(pos[1] - max_range, pos[1] + max_range)
ax[0].set_zlim(pos[2] - max_range, pos[2] + max_range)

# Plot 2D histograms
BinnerXZ.plot_2d_slice(fig=fig, ax=ax[1], xlabel=r"$x$", ylabel=r'$z$')
BinnerXY.plot_2d_slice(fig=fig, ax=ax[2], xlabel=r"$x$", ylabel=r'$y$')
BinnerYZ.plot_2d_slice(fig=fig, ax=ax[3], xlabel=r"$y$", ylabel=r'$z$')
plt.show()

您也可以只使用一个Binner,但请注意您将获得飞机相交的工件: One Binner

# ...
# Using three Binners
# ...
# Using only one Binner (adds a small error! see comments!)
Binner = ba.Binning_Array([[X_min, X_max, step],
                           [Y_min, Y_max, step],
                           [Z_min, Z_max, step]])
for point in data:
    Binner.add_value([point[0], point[1], Z_min])
    Binner.add_value([point[0], Y_max-step, point[2]])
    Binner.add_value([X_min, point[1], point[2]])
fig = plt.figure()
ax = [fig.add_subplot(221, projection='3d'),
      fig.add_subplot(222),
      fig.add_subplot(223),
      fig.add_subplot(224)]
ax[0].scatter(X, Y, Z, c='g', marker='.', alpha=0.2)
Binner.plot_slices(others={0:X_min, 1:Y_max, 2:Z_min}, fig=fig, ax=ax)
plt.show()

binning_array.py是为一个学校项目而制作的,并没有完全抛光,但它足以满足您的需求。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

class Binning_Array:
    def __init__(self, limits=[[-1.,1.,1.],[-1.,1.,1.]]):
        """Create a new binning array.
        The amount of given limits determines the dimension of the array,
        although only 2 and 3D have been tested.
        Each limit must be a list of start, stop and step for the
        axis it represents (x, y or z)."""
        self.limits  = np.array(limits)
        self._shape = []
        for i in xrange(len(self.limits)):
            self._shape.append((self.limits[i][1]-self.limits[i][0]) / \
                               float(self.limits[i][2]))
        self._shape = tuple(self._shape)
        self.dimensions = len(self._shape)
        self.bins = np.zeros(self._shape)
        self.outside = 0.
        self._normalized = 1.

    def __repr__(self):
        """Representation method. <<REVIEW>>"""
        return "Binning Array! Hurray!"

    def __getitem__(self, index):
        """Direct acess to read self.bins (use something like self[index])
        Direct acess to write would be given by __setitem__
        """
        return self.bins.__getitem__(index)

    def position2index(self,position,axis=0):
        """Convert a given position to an index in axis.
        If it is outside, it returns -1.
        """
        if self.limits[axis][0] <= position < self.limits[axis][1]:
            return int((position - self.limits[axis][0]) / self.limits[axis][2])
        else: return -1

    def index2position(self, index, axis=0):
        """Convert a given index to a position in axis.
        If it is outisde, it returns -1.
        """
        if 0 <= index < self._shape[axis]:
            return self.limits[axis][0] + self.limits[axis][2] * index
        else:
            return -1

    def add_value(self, position, value=1., verbose=False):
        """Add a given value to a specified position.
        If verbose it returns a list with the axies at which the position
        is outside the scope of this Binning_Array.
        Not very efficient because of that (verbose was for debugging).
        """
        indexs  = []
        outside = False
        if verbose:
            outs = []
        for i in xrange(self.dimensions):
            # using self.dimensions serves as a filter
            # if position has non valid shape
            index = self.position2index(position[i],i)
            if index == -1:
                if verbose:
                    outside = True
                    outs.append(i)
                else:
                    self.outside += value / self._normalized  
                    return None # nothing, as it is not verbose
            else:
                indexs.append(index)
        if outside: # the only way to get here is if verbose is True...
            self.outside += value / self._normalized
            return outs # so I can just return outs...
        else:
            self.bins[tuple(indexs)] += value / self._normalized
            if verbose:
                return outs

    def get_value(self, position, verbose=False):
        """Return the value at the specified position.
        If verbose it alse returns a list with the axies at which the position
        is outside the scope of this Binning_Array.
        """
        indexs = []
        outside = False
        if verbose:
            outs = []
        for i in xrange(self.dimensions):
            index = self.position2index(position[i],i)
            if index == -1:
                if verbose:
                    outside = True
                    outs.append[i]
                else:
                    return self.outside
            else:
                indexs.append(index)
        if outside: # the only way to get here is if verbose is True
            return self.outside, outs # so I can just return outs...
        else:
            if verbose:
                return self.bins[tuple(indexs)], outs
            else:
                return self.bins[tuple(indexs)]

    def normalize(self, total=None):
        """Divide the entire array by the sum of its values (and outside).
        Any value added after this will be normalized by the same factor.
        """
        if total is None:
            total = self.n_counts()
        self.bins /= total
        self.outside /= total
        self.normalize *= total

    def n_counts(self):
        """Return the number of counts."""
        return np.sum(self.bins) + self.outside

    def bin_max(self):
        """Return the value of the largest bin."""
        return np.max(self.bins)

    def bin_min(self):
        """Return the value of the largest bin."""
        return np.min(self.bins)

    def plot_2d_slice(self, cuts=[0,1], others={},
                      fig=None, ax=None, show=True, **kwargs):
        """Plot a 2D slice."""
        x = min(cuts)
        y = max(cuts)
        xs = np.arange(self.limits[x][0],
                       self.limits[x][1] + self.limits[x][2],
                       self.limits[x][2])
        ys = np.arange(self.limits[y][0],
                       self.limits[y][1] + self.limits[y][2],
                       self.limits[y][2])
        index = []
        title = ''
        for i in xrange(self.dimensions):
            if i in cuts:
                appendix = slice(self._shape[i]+1)
            else:
                appendix = others.get(i,(self.limits[i][0]+
                                         self.limits[i][1]) / 2.)
                title += '%d:%.4e\t' % (i,appendix)
                appendix = self.position2index(appendix,i)
            index.append(appendix)
        index = tuple(index)
        if fig is None:
            fig, ax = plt.subplots(1,1)
        YS,XS = np.meshgrid(ys, xs)
        graph = ax.pcolormesh (XS, YS, self.bins[index], cmap=plt.cm.coolwarm)
        fig.colorbar(graph, ax=ax)
        ax.axis('equal')
        ax.set_xlim(self.limits[x][0], self.limits[x][1])
        ax.set_ylim(self.limits[y][0], self.limits[y][1])
        if 'xticks' in kwargs:
            ax.set_xticks(kwargs['xticks'])
        if 'yticks' in kwargs.keys():
            ax.set_yticks(kwargs['yticks'])
        if 'xlabel' in kwargs:
            ax.set_xlabel(kwargs['xlabel'])
        if 'ylabel' in kwargs:
            ax.set_ylabel(kwargs['ylabel'])
        if 'xlim' in kwargs:
            ax.set_xlim(*kwargs['xlim'])
        if 'ylim' in kwargs:
            ax.set_ylim(*kwargs['ylim'])
        if show:
            fig.tight_layout()
            fig.show()

    def plot_slices(self, others={}, fig=None, ax=None,
                    show=True, projections=True):
        index = []
        pos = []
        title = ''
        for i in xrange(self.dimensions):
            temp = others.get(i,(self.limits[i][0]+self.limits[i][1])/2.)
            title += '%d:%.4e\t' % (i,temp)
            pos.append(temp)
            index.append(self.position2index(temp,i))
        if self.dimensions == 3:
            if fig is None:
                fig = plt.figure()
                if projections:
                    ax = [fig.add_subplot(221, projection='3d'),
                          fig.add_subplot(222),
                          fig.add_subplot(223),
                          fig.add_subplot(224)]
                else:
                    ax  = fig.add_subplot(111, projection='3d')
            if projections:
                xs = np.arange(self.limits[0][0],
                               self.limits[0][1] + self.limits[0][2],
                               self.limits[0][2])
                ys = np.arange(self.limits[1][0],
                               self.limits[1][1] + self.limits[1][2],
                               self.limits[1][2])
                zs = np.arange(self.limits[2][0],
                               self.limits[2][1] + self.limits[2][2],
                               self.limits[2][2])
                xs_c = np.arange(*self.limits[0])
                ys_c = np.arange(*self.limits[1])
                zs_c = np.arange(*self.limits[2])
                vmin = np.min(self.bins)
                vmax = np.max(self.bins)
                levels = np.linspace(vmin,vmax,20)
                #graph 0 (3D)
                ax[0].set_xlabel(r"$x$")
                ax[0].set_ylabel(r"$y$")
                ax[0].set_zlabel(r"$z$")
                #ax[0].axis('equal') #not supported in 3D:
#http://stackoverflow.com/questions/13685386/\
#matplotlib-equal-unit-length-with-equal-aspect-ratio-z-axis-is-not-equal-to
                max_range = max([xs[-1]-xs[0],ys[-1]-ys[0],zs[-1]-zs[0]]) / 2.
#                x_mean = (xs[-1] + xs[0])/2.
#                y_mean = (ys[-1] + ys[0])/2.
#                z_mean = (zs[-1] +zs[0])/2.
                ax[0].set_xlim(pos[0] - max_range, pos[0] + max_range)
                ax[0].set_ylim(pos[1] - max_range, pos[1] + max_range)
                ax[0].set_zlim(pos[2] - max_range, pos[2] + max_range)
                # to understand holes in contour plot:
#http://stackoverflow.com/questions/18897950/\
#matplotlib-pyplot-contourf-function-introduces-holes-or-gaps-when-plotting-regul
                # graph 1 (2D)
                ZS, XS = np.meshgrid(zs,xs)
                ZS_C, XS_C = np.meshgrid(zs_c,xs_c)
                ax[1].pcolormesh(XS, ZS, self.bins[:,index[1],:],
                             vmin=vmin, vmax=vmax,
                             cmap=plt.cm.coolwarm)
                ax[0].contourf(X=XS_C, Y=self.bins[:,index[1],:], Z=ZS_C,
                               zdir='y', offset=pos[1],
                               vmin=vmin, vmax=vmax,
                               cmap=plt.cm.coolwarm, levels=levels,
                               alpha=0.5)
                ax[1].set_xlabel(r"$x$")
                ax[1].set_ylabel(r"$z$")
                ax[1].set_xlim(xs[0],xs[-1])
                ax[1].set_ylim(zs[0],zs[-1])
                ax[1].axis('equal')
                # graph 2 (2D)
                YS, XS = np.meshgrid(ys,xs)
                YS_C, XS_C = np.meshgrid(ys_c,xs_c)
                ax[2].pcolormesh(XS, YS, self.bins[:,:,index[2]],
                                 vmin=vmin, vmax=vmax,
                                 cmap=plt.cm.coolwarm)
                ax[0].contourf(X=XS_C, Y=YS_C, Z=self.bins[:,:,index[2]],
                               zdir='z', offset=pos[2],
                               vmin=vmin, vmax=vmax,
                               cmap=plt.cm.coolwarm, levels=levels,
                               alpha=0.5)
                ax[2].set_xlabel(r"$x$")
                ax[2].set_ylabel(r"$y$")
                ax[2].set_xlim(xs[0],xs[-1])
                ax[2].set_ylim(ys[0],ys[-1])
                ax[2].axis('equal')
                # graph 3 (2D)
                ZS, YS = np.meshgrid(zs, ys)
                ZS_C, YS_C = np.meshgrid(zs_c, ys_c)
                ax[3].pcolormesh(YS, ZS, self.bins[index[0],:,:],
                                 vmin=vmin, vmax=vmax,
                                 cmap=plt.cm.coolwarm)
                ax[0].contourf(X=self.bins[index[0],:,:], Y=YS_C, Z=ZS_C,
                               zdir='x', offset=pos[0],
                               vmin=vmin, vmax=vmax,
                               cmap=plt.cm.coolwarm, levels=levels,
                               alpha=0.5)
                ax[3].set_xlabel(r"$y$")
                ax[3].set_ylabel(r"$z$")
                ax[3].set_xlim(ys[0],ys[-1])
                ax[3].set_ylim(zs[0],zs[-1])
                ax[3].axis('equal')
            else:
                # update to draw a given slice, use it to plot eaxh axes above!
                ax.plot(self.XS,self.YS,self.ZS)
                ax.set_zlabel(r"$z$")
                ax.set_xlabel(r"$x$")
                ax.set_ylabel(r"$y$")
                ax.axis('equal')
        else:
            if fig is None:
                fig, ax = plt.subplots(1)
            xs = np.arange(self.limits[0][0],
                           self.limits[0][1] + self.limits[0][2],
                           self.limits[0][2])
            ys = np.arange(self.limits[1][0],
                           self.limits[1][1] + self.limits[1][2],
                           self.limits[1][2],)
            YS, XS = np.meshgrid(ys, xs)
            graph = ax.pcolormesh(XS, YS, self.bins, cmap=plt.cm.coolwarm)
            fig.colorbar(graph)
            ax.set_xlim(self.limits[0][0], self.limits[0][1])
            ax.set_ylim(self.limits[1][0], self.limits[1][1])
            ax.set_title('Energy Distribution')
            ax.set_xlabel(r"$x$")
            ax.set_ylabel(r"$y$")
            ax.axis('equal')
        if show:
            fig.tight_layout()
            fig.show()
        return fig, ax

如果代码上的任何内容有误或有问题请说出来,我会编辑上面的内容(学校项目已经评分,所以你不会做我的作业)。
此外,如果有任何不明确的地方请说明,我会根据需要添加评论或解释。

答案 2 :(得分:4)

根据您要执行的操作,您需要使用zdircontour函数的contourf参数。这是一个例子:

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = Axes3D(fig)
X = np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X ** 2 + Y ** 2)
Z = np.sin(R)


ax.contourf(X, Y, Z, zdir='x', offset=-4, cmap=plt.cm.hot)
ax.contour(X, Y, Z, zdir='x', offset=-4, colors='k')
ax.contourf(X, Y, Z, zdir='y', offset=4, cmap=plt.cm.hot)
ax.contour(X, Y, Z, zdir='y', offset=4, colors='k')
ax.contourf(X, Y, Z, zdir='z', offset=-1, cmap=plt.cm.hot)
ax.contour(X, Y, Z, zdir='z', offset=-1, colors='k')

plt.show()

结果: enter image description here