【问题标题】:plotting the projection of 3D plot in three planes using contours使用等高线在三个平面上绘制 3D 图的投影
【发布时间】:2015-07-10 19:01:04
【问题描述】:

我有一个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 绘制每个平面中的数据投影?

【问题讨论】:

    标签: python matplotlib contour


    【解决方案1】:

    嗯,确实,很难显示数据。也许沿着一个轴创建一些切片并创建一定数量的二维图是最好的。然而 3D 情节很花哨。我像您一样对数据进行了一些处理,生成了一个 3D 图和一个带有投影的单独图。

    • 点的颜色是根据缺失的
    • 添加了透明度以提供密度概念
    • 保持两个绘图的轴相同

    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')
    

    【讨论】:

    • 我真正感兴趣的是每个平面上的等高线图,它将第三轴的投影描述为颜色代码。
    【解决方案2】:

    根据您的需要,您需要为contourcontourf 函数使用zdir 参数。举个例子:

    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()
    

    结果:

    【讨论】:

      【解决方案3】:

      希望我不会用 [非常丑陋的] 大炮射蚊子,我最近为一个学校项目制作了 Binning_Array 课程,我认为它可能会对您有所帮助:

      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,但请注意,您将在平面相交处得到工件:

      # ...
      # 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
      

      如果代码中有任何错误或错误,请说出来,我会编辑以上内容(学校项目已经评分,所以你不会做我的作业)。
      另外,如果有什么不清楚的地方请说出来,我会根据需要添加 cmets 或解释。

      【讨论】:

      • 预计的剧情很感人!
      猜你喜欢
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      • 1970-01-01
      • 2011-11-25
      • 2021-05-19
      相关资源
      最近更新 更多