Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 更有效的循环方式?_Python_Performance_For Loop_Vectorization_Micro Optimization - Fatal编程技术网

Python 更有效的循环方式?

Python 更有效的循环方式?,python,performance,for-loop,vectorization,micro-optimization,Python,Performance,For Loop,Vectorization,Micro Optimization,我从一个大得多的脚本中得到了一小段代码。我发现当调用函数t\u area时,它负责大部分运行时间。我自己测试了这个函数,它并不慢,它需要很多时间,因为我相信它需要运行很多次。以下是调用函数的代码: tri_area = np.zeros((numx,numy),dtype=float) for jj in range(0,numy-1): for ii in range(0,numx-1): xp = x[ii,jj] yp = y[ii,jj] zp

我从一个大得多的脚本中得到了一小段代码。我发现当调用函数
t\u area
时,它负责大部分运行时间。我自己测试了这个函数,它并不慢,它需要很多时间,因为我相信它需要运行很多次。以下是调用函数的代码:

tri_area = np.zeros((numx,numy),dtype=float)
for jj in range(0,numy-1):
    for ii in range(0,numx-1):
      xp = x[ii,jj]
      yp = y[ii,jj]
      zp = surface[ii,jj]
      ap = np.array((xp,yp,zp))

      xp = xp+dx
      zp = surface[ii+1,jj]
      bp = np.array((xp,yp,zp))

      yp = yp+dx
      zp = surface[ii+1,jj+1]
      dp = np.array((xp,yp,zp))

      xp = xp-dx
      zp = surface[ii,jj+1]
      cp = np.array((xp,yp,zp))

      tri_area[ii,jj] = t_area(ap,bp,cp,dp)
此处使用的数组大小为
216 x 217
,因此
x
y
的值也是如此。我对python编码相当陌生,过去我使用过MATLAB。所以我的问题是,有没有一种方法可以绕过这两个for循环,或者有一种更有效的方法来运行这段代码?正在寻找任何帮助加快这一进程!谢谢

编辑:

谢谢大家的帮助,这已经消除了很多困惑。我被问及循环中使用的函数t_区域,下面是代码:

def t_area(a,b,c,d):
ab=b-a
ac=c-a
tri_area_a = 0.5*linalg.norm(np.cross(ab,ac))

db=b-d
dc=c-d
tri_area_d = 0.5*linalg.norm(np.cross(db,dc))

ba=a-b
bd=d-b
tri_area_b = 0.5*linalg.norm(np.cross(ba,bd))

ca=a-c
cd=d-c
tri_area_c = 0.5*linalg.norm(np.cross(ca,cd))

av_area = (tri_area_a + tri_area_b + tri_area_c + tri_area_d)*0.5
return(av_area)

抱歉让人困惑的符号,在当时它是有意义的,现在回头看,我可能会改变它。谢谢

在我们开始之前先提出警告<代码>范围(0,numy-1),它等于
范围(numy-1)
,生成从0到numy-2的数字,不包括numy-1。这是因为numy-1的值从0到numy-2。虽然MATLAB有基于1的索引,Python有基于0的索引,所以在转换过程中要小心索引。考虑到您有
tri_-area=np.zero((numx,numy),dtype=float)
tri_-area[ii,jj]
从不以设置循环的方式访问最后一行或最后一列。因此,我怀疑正确的意图是编写
range(numy)

由于函数
t_area()
是可矢量化的,因此可以完全消除循环。矢量化意味着numpy通过处理引擎盖下的环路,同时对整个阵列应用一些操作,这样会更快

首先,我们为(m,n,3)数组中的每个(i,j)元素堆叠所有的
ap
s,其中(m,n)是
x
的大小。如果我们取两个(m,n,3)数组的叉积,默认情况下,该操作将应用于最后一个轴。这意味着
np。对于每个元素(i,j),交叉(a,b)
将取
a[i,j]
b[i,j]
中3个数字的叉积。类似地,
np.linalg.norm(a,轴=2)
将为每个元素(i,j)计算
a[i,j]
中3个数字的范数。这也将有效地将我们的数组减小到大小(m,n)。不过这里需要注意一点,因为我们需要明确说明我们希望在第二轴上完成此操作

请注意,在下面的示例中,我的索引关系可能与您的不一致。这项工作的最低要求是
surface
x
y
中增加一行和一列

import numpy as np

def _t_area(a, b, c):
    ab = b - a
    ac = c - a
    return 0.5 * np.linalg.norm(np.cross(ab, ac), axis=2)

def t_area(x, y, surface, dx):
    a = np.zeros((x.shape[0], y.shape[0], 3), dtype=float)
    b = np.zeros_like(a)
    c = np.zeros_like(a)
    d = np.zeros_like(a)

    a[...,0] = x
    a[...,1] = y
    a[...,2] = surface[:-1,:-1]

    b[...,0] = x + dx
    b[...,1] = y
    b[...,2] = surface[1:,:-1]

    c[...,0] = x
    c[...,1] = y + dx
    c[...,2] = surface[:-1,1:]

    d[...,0] = bp[...,0]
    d[...,1] = cp[...,1]
    d[...,2] = surface[1:,1:]

    # are you sure you didn't mean 0.25???
    return 0.5 * (_t_area(a, b, c) + _t_area(d, b, c) + _t_area(b, a, d) + _t_area(c, a, d))

nx, ny = 250, 250

dx = np.random.random()
x = np.random.random((nx, ny))
y = np.random.random((nx, ny))
surface = np.random.random((nx+1, ny+1))

tri_area = t_area(x, y, surface, dx)
本例中的
x
支持索引0-249,而
surface
0-250
surface[:-1]
,是
surface[0:-1]
的缩写,将返回从0开始到最后一行的所有行,但不包括它
-1
在MATLAB中提供相同的功能和
结束
。因此,
surface[:-1]
将返回索引0-249的行。类似地,
surface[1://code>将返回索引1-250的行,这与您的
surface[ii+1]
实现的相同


注意:在知道
t_area()
可以完全矢量化之前,我已经写了这一节。因此,虽然就这个答案而言,这里的内容已经过时,但我将把它留作遗产,以展示如果函数不可矢量化,可以进行哪些优化

不要为每个元素调用函数(这很昂贵),而应该传递它
x
y、
surface
dx
,并在内部进行迭代。这意味着只有一个函数调用,开销更少

此外,您不应该为
ap
bp
cp
dp
每个循环创建一个数组,这同样会增加开销。在循环外分配它们一次,然后只更新它们的值

最后一个变化应该是循环的顺序。默认情况下,Numpy数组是行主数组(而MATLAB是列主数组),因此
ii
作为外部循环的性能更好。你不会注意到你这样大小的数组之间的差异,但是,嘿,为什么不呢

总的来说,修改后的函数应该是这样的

def t_area(x, y, surface, dx):
    # I assume numx == x.shape[0]. If not, pass it as an extra argument.
    tri_area = np.zeros(x.shape, dtype=float)

    ap = np.zeros((3,), dtype=float)
    bp = np.zeros_like(ap)
    cp = np.zeros_like(ap)
    dp = np.zeros_like(ap)

    for ii in range(x.shape[0]-1): # do you really want range(numx-1) or just range(numx)?
        for jj in range(x.shape[1]-1):
            xp = x[ii,jj]
            yp = y[ii,jj]
            zp = surface[ii,jj]
            ap[:] = (xp, yp, zp)

            # get `bp`, `cp` and `dp` in a similar manner and compute `tri_area[ii,jj]`

至少,您不必在每次迭代中分配新数组。您可以通过将
ap、bp、cp、dp
存储在数组中并重写
t\u区域来迭代这些数组,从而节省约40000个函数调用。另外,您也可以不使用
np.array
只存储3个值。您能告诉我们在函数
t\u area()
中发生了什么吗?它是矢量化的吗?好消息是你的函数可以完全矢量化。为了考虑到这一点,我编辑了我的答案。我对措辞做了一些调整,使问题的矢量化处于首位。否则,添加的内容不多。非常感谢!您让我对python结构有了更好的理解!我在完整的脚本中还有几个其他地方,在这些地方,这个实现将非常有用!与最终计算的数据相比,现在输入到这个脚本中的数据很小。我前面提到的数组大小来自~2mb的文本文件,而稍后来自文本文件的数据将为~1.5-2GB!所以这肯定会加快速度!