【问题标题】:Order of indexes in a Numpy multidimensional arrayNumpy多维数组中的索引顺序
【发布时间】:2015-02-20 18:14:34
【问题描述】:

例如,假设我正在模拟一堆粒子随着时间的推移做某事,并且我有一个名为 particles 的多维数组,其中包含以下索引:

  • 粒子的 x/y/z 坐标(长度为 a,对于 3d 空间为 3
  • 单个粒子的索引(长度为b
  • 时间步的索引(长度为c

将数组构造成particles.shape == (a, b, c)particles.shape == (c, b, a) 更好吗?

我对约定比效率更感兴趣:Numpy 数组可以设置为 C 风格(最后一个索引变化最快)或 Fortran 风格(第一个索引),因此它可以有效地支持任何一种设置。我也意识到我可以使用transpose 将索引按我需要的任何顺序排列,但我想尽量减少它。

我自己开始对此进行研究,并找到了两种方式的支持:

Pro-(c,b,a):

  • 默认情况下,Numpy 使用 C 样式数组,其中最后一个索引变化最快。
  • 大多数向量代数函数(innercross 等)都作用于最后一个索引。 (dot 作用于一个的最后一个和另一个的倒数第二个。)
  • matplotlib 集合对象(LineCollectionPolyCollection)需要空间坐标位于最后一个轴的数组。

Pro-(a,b,c):

  • 如果我要使用meshgridmgrid 来生成一组点,它会将空间轴放在首位。例如,np.mgrid[0:5,0:5,0:5].shape == (3,5,5,5)。我意识到这些函数主要用于integer array indexing,但使用它们生成点网格并不少见。
  • matplotlib scatterplot 函数拆分了它们的参数,因此它与数组的形状无关,但 ax.plot3d(particles[0], particles[1], particles[2]) 的类型比 particles[..., 0] 的版本短

一般来说,似乎存在两种不同的约定(可能是由于 C 和 Fortran 之间的历史差异),目前尚不清楚哪个在 Numpy 社区中更常见,或更适合我正在做的事情。

【问题讨论】:

    标签: python arrays numpy multidimensional-array matplotlib


    【解决方案1】:

    根据我的经验,类似这样的约定与特定文件格式的关系比其他任何东西都多。但是,有一种快速的方法来回答哪个可能最适合您的工作:

    如果你必须迭代一个轴,你最有可能迭代哪一个?换句话说,以下哪一个最有可能:

    # a first
    for dimension in particles:
        ...
    
    # b first
    for particle in particles:
        ...
    
    # c first
    for timestep in particles:
        ...
    

    就效率而言,这假定了 C 顺序,但这实际上无关紧要。在 python 级别,无论内存布局如何,对 numpy 数组的访问都被视为 C 顺序。 (你总是遍历第一个轴,即使它不是内存中“最连续”的轴。)

    当然,在很多情况下,你应该避免在这个问题上直接迭代 numpy 数组。尽管如此,这是您应该考虑的方式,尤其是在涉及磁盘文件结构时。让您最常见的用例变得最快/最简单。

    如果没有别的,希望这能给你一个有用的方法来思考这个问题。

    【讨论】:

      【解决方案2】:

      另一个偏差是,当必须添加新维度时,numpy 首选项是在左侧添加。那就是x[None,...]是自动的

      np.array([x,y,z])   # produces a (3,...) array
      
      np.ones((3,2)) + np.ones((1,2,10)) # error
      np.ones((3,2,1)) + np.ones((2,10))  # (3,2,10)
      

      但我看不出这种优先广播如何支持x/y/z 坐标的一个位置。

      虽然np.dot 使用last/2nd to last 的约定,但np.tensordotnp.einsum 更为通用。


      Apocheir 指出在最后一个轴上进行归约可能需要在后面添加newaxis,例如

       x / np.linalg.norm(x,axis=0)   # automatic newaxis at beginning
       x / np.linalg.norm(x,axis=-1)[...,np.newaxis]  # explicit newaxis
      

      对于小的x,这个显式的newaxis 增加了可测量的执行时间。但是对于大的x,第二次计算更快。我认为这是因为最后一个轴上的减少速度更快 - 那是变化更快的轴(对于order='C')。

      许多内置归约方法都有一个keepdims 参数,以方便在此类用途中进行广播(例如summean)。

      【讨论】:

      • 其实确实有效果...比较x = np.ones((3,4,5)); y = np.linalg.norm(x,axis=0)x = np.ones((5,4,3)); y = np.linalg.norm(x,axis=-1)。首先使用空间索引,x/y 规范化 x 而没有任何索引旋转。最后使用空间索引,您必须执行 x/y[..., np.newaxis] 之类的操作
      • 许多“缩减”方法(例如summean)都有一个keepdims 参数,以消除重新添加此newaxis 的需要。
      猜你喜欢
      • 1970-01-01
      • 2015-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-05
      • 1970-01-01
      • 2020-03-25
      • 2019-01-04
      相关资源
      最近更新 更多