【问题标题】:Python: Speeding up a slow for-loop calculation (np.append)Python:加快缓慢的 for 循环计算 (np.append)
【发布时间】:2016-03-21 14:45:39
【问题描述】:

我有一个名为“cmp_twtt_amp_rho”的输入文件,它有 7795074 行长。 我想计算每条线的声速 c,其中:

c(i) = rho(i-1) * c(i-1) * (-1-amp(i)) / rho(i) * (amp(i)-1)

使用 c=1450 的初始猜测。

我编写了一个我相信会起作用的 for 循环,但是它会随着时间的推移而变得越来越慢,以至于无法想象以当前格式运行。

有人可以帮我加快这段代码的速度吗?

data=np.genfromtxt('./cmp_twtt_amp_rho')
cmp_no=data[:,[0]]
twtt=data[:,[1]]
amp=data[:,[2]]
rho=data[:,[3]]

cs=[]

for i in range(1,len(amp-1)):
  if i == 1:
    print "Using an initial guess of 1450 m/s"
    c = (rho[i-1]*1450*(-1-amp[i]))/(rho[i]*(1-amp[i]))
    cs = np.append(c,cs)
  elif twtt[i] == 0:
    print "Reached new cmp #: ",cmp_no[i],"as twwt has re-started at ",twtt[i]
    c = 1450
    cs = np.append(c,cs)
  else:
    print i
    c = (rho[i-1]*cs[i-1]*(-1-amp[i]))/(rho[i]*(1-amp[i]))
    cs = np.append(c,cs)

print min(cs), max(cs)
print len(cs)

【问题讨论】:

  • 如果你只做cs.append(c)而不是cs = np.append(c, cs)会发生什么?
  • 您有一个符号错误。您帖子中的公式与代码不匹配;公式为(amp(i) - 1),代码为(1 - amp[i])。
  • 抱歉,代码中的一个类型,公式是正确的。

标签: python performance for-loop append


【解决方案1】:

附加到数组真的很慢,因为每次都必须分配一个全新的数组。循环执行几乎总是会降低性能。

您可以通过矢量化操作和累积乘积更快地完成此操作,而不是在循环中追加,甚至根本不使用 Python 级循环:

multipliers = rho[:-1] * (-1 - amp[1:]) / (rho[1:] * (1 - amp[1:])
cs = np.cumprod(np.insert(multipliers, 0, 1450))

(insert 也需要分配一个全新的数组,但没关系,因为我们只做一次。)

另外,您可能有一个符号错误。你的公式是(amp(i) - 1),你的代码是(1 - amp[i])。我已选择匹配您的代码,但您可能需要更正它。

【讨论】:

    【解决方案2】:

    Numpy 数组并不是真的要附加(numpy 每次都需要分配一个全新的数组并复制旧数据)。您可能不希望循环执行此操作。

    最好使用专门用于此类事情的数据结构——通常 python 的 list 可以很好地处理追加,因此我建议您将数据存储在一个列表中并随时追加。然后最后,如果你需要完整的数据集作为一个数组,你可以在那个时候转换回来。

    我建议只更改为 cs.append(c) 而不是 cs = np.append(c, cs)

    【讨论】:

    • 这在最后一个“else”条件中带来了一个问题:代码对 c = (rho[i-1]*cs[i-1]*(-1-Z[ i]))/(rho[i]*(Z[i]-1)) 并给出以下错误:“IndexError: list index out of range”。我不确定为什么它会给出索引错误?我最初使用 cs = [] 像以前一样设置 cs,但我认为这不是问题?
    【解决方案3】:

    np.append 必须重新分配整个数组,女巫很糟糕,但不是唯一的问题。您将cs 附加到c 而不是相反,这意味着cs 将被反转,cs[i-1] 实际上是第一个c。

    通常最好预先分配你的数组:

    cs = np.zeros(len(amp-1))
    

    然后直接设置值:

    cs[i] = c
    

    这样的事情应该会快一点:

    cs=np.zeros(len(amp-1))
    
    print "Using an initial guess of 1450 m/s"
    cs[1] = (rho[i-1]*1450*(-1-amp[i]))/(rho[i]*(1-amp[i]))
    
    for i in range(2,len(amp-1)):
      if twtt[i] == 0:
        print "Reached new cmp #: ",cmp_no[i],"as twwt has re-started at ",twtt[i]
        cs[i] = 1450
      else:
        print i
        cs[i] = (rho[i-1]*cs[i-1]*(-1-amp[i]))/(rho[i]*(1-amp[i]))
    

    【讨论】:

    • 谢谢。这大大加快了速度。
    • 干杯,请考虑 user2357112 的解决方案,它会更快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    相关资源
    最近更新 更多