【问题标题】:Python time optimisation of for loop using newaxis使用 newaxis 对 for 循环进行 Python 时间优化
【发布时间】:2012-11-05 00:41:28
【问题描述】:

我需要沿定义的线 (3D) 以相等的间距计算 n 个点 (3D)。 我知道这条线的起点和终点。首先,我使用了

for k in range(nbin):
  step = k/float(nbin-1)
  bin_point.append(beam_entry+(step*(beamlet_intersection-beam_entry)))

然后,我发现对大型数组使用追加需要更多时间,然后我更改了这样的代码:

bin_point = [start_point+((k/float(nbin-1))*(end_point-start_point)) for k in range(nbin)]

我收到一个建议,使用 newaxis 将进一步缩短时间。 修改后的代码如下所示。

step      = arange(nbin) / float(nbin-1)
bin_point = start_point + ( step[:,newaxis,newaxis]*((end_pint - start_point))[newaxis,:,:] )  

但是,我无法理解 newaxis 函数,我也有疑问,如果改变 start_point 和 end_point 的结构或形状,相同的代码是否可以工作。同样如何使用newaxis来mdoify以下代码

 for j in range(32):      # for all los
   line_dist[j] = sqrt([sum(l) for l in (end_point[j]-start_point[j])**2])

抱歉这么笨拙,要更清楚start_point和end_point的结构是

array([ [[1,1,1],[],[],[]....[]], 
        [[],[],[],[]....[]],
        [[],[],[],[]....[]]......,
        [[],[],[],[]....[]] ])

【问题讨论】:

  • start_point等是什么类型的?如何创建一个?
  • @Alfe 正如我定义的所有点都是 3 维的,单个 start_point 或 end_point 看起来像这样 [1,2,3],我将这些点作为输入。因此,在上面的代码中,start_point 或 end_point 本质上是点的集合,例如 [[1,2,3],[2,1,3],[4,5,3]]。我试图在我的问题的最后一部分,即“数组”中解释这一点。不好意思,我把嵌套列表做成了数组操作,大大减少了时间。
  • 所以start_point实际上是一个点列表,而不是一个点?

标签: python time for-loop numpy


【解决方案1】:

我不是通过理解你写的所有东西,但有些事情我已经可以告诉你了;也许他们有帮助。

newaxis 与其说是一个函数,不如说是一个标记(事实上,它是普通的None)。它用于将(未使用的)维度添加到多维值。有了它,您可以从 2D 值(甚至更多)中生成 3D 值。输入值中已经存在的每个维度必须由索引中的冒号: 表示(假设您要使用所有值,否则会变得复杂超出我们的用例),要添加的维度由newaxis 表示。

示例:
输入是一维向量 (1D):1,2,3
输出应为矩阵 (2D)。
有两种方法可以做到这一点;向量可以每行填充一个值,或者向量可以仅填充矩阵的第一行也是唯一一行。第一个由vector[:,newaxis] 创建,第二个由vector[newaxis,:] 创建。结果:

>>> array([ 7,8,9 ])[:,newaxis]
array([[7],
       [8],
       [9]])
>>> array([ 7,8,9 ])[newaxis,:]
array([[7, 8, 9]])

(多维值的维度当然是用数组的嵌套来表示的。)

如果输入中有更多维度,请多次使用冒号(否则更深的嵌套维度将被忽略,即数组被视为简单值)。我不会在此处粘贴此表示,因为当使用嵌套括号在 2D 显示器上写入 3D 和 4D 值时,由于光学复杂性,它不会澄清事情。无论如何,我希望它变得清晰。

【讨论】:

  • 因此,问题中的 newaxis 代码部分本质上是 3D 矩阵乘法,而不是法线向量乘法。如果我是正确的,那么我使用的是 nx3 * 3x1 的矩阵乘法,结果为 nx1。
【解决方案2】:

问题中新轴版本的解释:这些是不是矩阵乘法,ndarray 乘法是与broadcasting 的逐个元素乘法。 step[:,newaxis,newaxis] 是 num_steps x 1 x 1,而 point[newaxis,:,:] 是 1 x num_points x num_dimensions。将形状为 (num_steps x 1 x 1) 和 (1 x num_points x num_dimensions) 的 ndarray 一起广播会起作用,因为广播规则是每个维度都应该为 1 或相同;它只是意味着“重复维度为 1 的数组的次数与另一个数组的对应维度一样多”。这会以一种非常有效的方式产生一个形状为 (num_steps x num_points x num_dimensions) 的 ndarray; i, j, k 下标将是沿第 j 行的第 i 步的第 k 个坐标(由第 j 对起点和终点给出)。

演练:

>>> start_points = numpy.array([[1, 0, 0], [0, 1, 0]])
>>> end_points = numpy.array([[10, 0, 0], [0, 10, 0]])
>>> steps = numpy.arange(10)/9.0
>>> start_points.shape
(2, 3)
>>> steps.shape
(10,)
>>> steps[:,numpy.newaxis,numpy.newaxis].shape
(10, 1, 1)
>>> (steps[:,numpy.newaxis,numpy.newaxis] * start_points).shape
(10, 2, 3)
>>> (steps[:,numpy.newaxis,numpy.newaxis] * (end_points - start_points)) + start_points
array([[[  1.,   0.,   0.],
        [  0.,   1.,   0.]],
       [[  2.,   0.,   0.],
        [  0.,   2.,   0.]],
       [[  3.,   0.,   0.],
        [  0.,   3.,   0.]],
       [[  4.,   0.,   0.],
        [  0.,   4.,   0.]],
       [[  5.,   0.,   0.],
        [  0.,   5.,   0.]],
       [[  6.,   0.,   0.],
        [  0.,   6.,   0.]],
       [[  7.,   0.,   0.],
        [  0.,   7.,   0.]],
       [[  8.,   0.,   0.],
        [  0.,   8.,   0.]],
       [[  9.,   0.,   0.],
        [  0.,   9.,   0.]],
       [[ 10.,   0.,   0.],
        [  0.,  10.,   0.]]])

如您所见,这会产生正确的答案 :) 在这种情况下,广播 (10,1,1) 和 (2,3) 会导致 (10,2,3)。你所拥有的是广播 (10,1,1) 和 (1,2,3) ,它们完全相同,也产生 (10,2,3)。

问题的距离部分的代码不需要newaxis:输入是num_points x num_dimensions,输出是num_points,所以必须去除一维。这实际上是你总结的轴。这应该有效:

line_dist = numpy.sqrt( numpy.sum( (end_point - start_point) ** 2, axis=1 )

这里 numpy.sum(..., axis=1) 表示仅沿该轴求和,而不是所有元素:形状为 num_points x num_dimensions 的 ndarray 沿 axis=1 求和会产生具有 num_points 的结果,这是正确的。

编辑:删除代码示例而不进行广播。 编辑:修复索引的顺序。 编辑:添加 line_dist

【讨论】:

  • 我希望你尝试使用转置来创建矩阵,如果是这样,问题中定义的newaxis方法会更有效。因为,我主要担心的是时间。因为,需要计算 500 或 1000 组 1280 对的起点和终点。(可能会增加)。那就是我的问题中定义的数组部分在一行/行中有 1280 个 3D 点,并且数组有 500 或 1000(是 nbin)
  • @Alissham:没有矩阵乘法,这些不是矩阵。矩阵乘法是 ndarray.dot()。这是使用 broadcasting 的逐元素乘法,它只是沿一个维度复制输入数组。在这种情况下,这些步骤将沿点数和点坐标数的维度进行复制。两个关键部分是 (1) 这完全是逐个元素的(与矩阵相乘不同),以及 (2) 数组根据需要进行复制。
  • @Alissham:如果上面的答案对您有用,请记得接受,或者如果您需要任何澄清,请告诉我。
  • 感谢您的回答,但我仍然不知道如何生成“n”个点(bin_points)。对于 line_dist 来说,使用起来很酷,而且很有效。
【解决方案3】:

newaxis 以这样一种方式重塑数组,以便当您乘以 numpy 时使用广播。 Here is a good tutorial on broadcasting.

step[:, newaxis, newaxis]step.reshape((step.shape[0], 1, 1)) 相同(如果 step 是 1d)。任何一种重塑方法都应该非常快,因为在 numpy 中重塑数组非常便宜,它只是查看数组,尤其是因为你应该只做一次。

【讨论】:

    猜你喜欢
    • 2021-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多