例如,假设我模拟了一堆粒子随着时间的推移做了某些事情,我有一个名为particles
的多维数组,带有这些索引:
a
,对于3d空间为3
)b
)c
)构建数组particles.shape == (a, b, c)
或particles.shape == (c, b, a)
是否更好?
我对约定比对效率更感兴趣:Numpy数组可以设置为C风格(最后一个索引变化最快)或Fortran风格(第一个索引),因此它可以有效地支持任一设置。我也意识到我可以使用transpose
以我需要的任何顺序放置索引,但我想尽量减少这些。
我自己开始研究这个问题并找到了对这两种方式的支持:
亲(C,B,A):
inner
,cross
等)都作用于最后一个索引。 (dot
作用于最后一个,倒数第二个。)matplotlib
集合对象(LineCollection
,PolyCollection
)期望数组的最后一个轴具有空间坐标。亲(A,B,C):
meshgrid
和mgrid
来生成一组点,则会将空间轴放在第一位。例如,np.mgrid[0:5,0:5,0:5].shape == (3,5,5,5)
。我意识到这些函数主要用于integer array indexing,但使用它们生成点网格并不罕见。 matplotlib
scatter
和plot
函数拆分了它们的参数,因此它与数组的形状无关,但ax.plot3d(particles[0], particles[1], particles[2])
的类型更短而不是particles[..., 0]
一般情况下,似乎存在两种不同的约定(可能是由于C和Fortran之间的历史差异),并且不清楚哪些在Numpy社区中更常见,或者更适合我和我#39;正在做。
答案 0 :(得分:4)
根据我的经验,对于这样的事情的约定与特定文件格式的关系远远超过其他任何事情。但是,有一种快速的方法可以回答哪一种可能最适合您所做的事情:
如果你必须遍历一个轴,你最有可能迭代哪一个?换句话说,最有可能是哪一个:
# a first
for dimension in particles:
...
# b first
for particle in particles:
...
# c first
for timestep in particles:
...
就效率而言,这假定为C顺序,但这实际上与此无关。在python级别,无论内存布局如何,对numpy数组的访问都被视为C-ordered。 (你总是迭代第一个轴,即使它不是内存中最连续的"轴。)
当然,在很多情况下你应该避免在这个问题上直接迭代numpy数组。尽管如此,这是您应该考虑的方式,特别是在磁盘文件结构方面。使您最常见的用例最快/最简单。
如果不出意外,希望这能为您提供一个思考问题的有用方法。
答案 1 :(得分: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.tensordot
和np.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
参数,以便在这类用途中进行广播(例如sum
,mean
)。