【问题标题】:How to optimize this code having multiple loops?如何优化具有多个循环的代码?
【发布时间】:2020-08-13 23:35:05
【问题描述】:

所以我尝试了这个,我发现它真的不可行。我不太了解执行以下操作的聪明方法。有人可以帮忙吗?列表的输入也很大。

此任务是根据我生成的值创建图像。 center_star 包含 [x,y] 对的列表,它们是各种点状对象的中心。

1800 值表示要生成的图像为 1800x1800 像素。

Sigma 变量默认值为 2。

final=[[0]*1800]*1800
for i in range(len(center_stars)):
    xi=center_stars[i][0]
    yi=center_stars[i][1]
    print(i)
    for j in range(1800):
        for k in range(1800):
            final[j][k]+=gauss_amplitude[i]*(math.e**((-1*((xi-j)**2+(yi-k)**2))/2*sigma*sigma))

有没有更聪明的方法来使用一些 numpy 操作来节省时间并在更短的时间内执行这段代码?

【问题讨论】:

  • 作为一个小修复,保存重复使用的表达式。 2*sigma*sigma while small 每次循环都会重新计算。您应该将此表达式保存在 i 上的循环之外。类似地,在 k 上的循环中,即使 j 没有变化,您也会在每次迭代时重新计算 (xi-j)**2
  • 这看起来像是用numpy进行矢量化。
  • 列表 final 的所有 1800 个元素都是相同的列表!,在循环中更改 final[j][k] 将更改 ALL final[:][k]s。可能不是你想要的。绝对使用 numpy。
  • 顺便说一句,你知道a=[[0]*2]*2;a[0][0]=1;a[1][0] 显示1 吗? * 创建对同一个 object 的多个引用,这对于 1D 中的不可变数字来说不是问题,但对于 2nd 维的列表来说却是问题。请改用列表推导,例如 b=[[0 for i in range(2)] for j in range(2)];b[0][0]=1;b[1][0],它显示 0

标签: python numpy loops optimization


【解决方案1】:

类似的东西:

import math
import numpy as np
N = 1800
final = np.empty((N, N))
final1 = np.empty((N, N))
j = np.arange(N)
k = np.arange(N)
jj, kk = np.meshgrid(j, k)
sigma = 2.
s = 0.5 / (sigma * sigma)
for i in range(len(center_stars)):
    xi = center_stars[i][0]
    yi = center_stars[i][1]
    final += gauss_amplitude[i] * np.exp(- ((xi - jj.T)**2 + (yi - kk.T)**2) * s)
# Code below is for comparison:
    for j in range(N):
        for k in range(N):
            final1[j][k]+=gauss_amplitude[i] * (math.e** (-((xi-j)**2+(yi-k)**2)/(2*sigma*sigma)))

此外,我假设您错过了 2*sigma*sigma 周围的括号

【讨论】:

  • 我试过这个,并尝试在 center_star 列表上测试大约 1400 个元素大约需要 250 秒,而对于 2300 个元素大约需要 410 秒。是否仍然可以优化它?我的数据列表很大。
  • 您显然应该从我的示例中删除最后 4 行 - 它们只是为了比较。
  • 另外,如果您需要更快的速度,请查看 scipy.ndimage.convolve
  • final += gauss_amplitude[i] * np.exp(- ((xi - jj.T)**2 + (yi - kk.T)**2) * s) 我把它改成了final[x_min:x_max,y_min:y_max] += gauss_amplitude[i] * np.exp(- ((xs - jj.T[x_min:x_max,y_min:y_max]) ** 2 + (ys - kk.T[x_min:x_max,y_min:y_max]) ** 2) * s)。这样我就不必在这么大的网格上进行操作了。我正在使用用户给定的宽度计算 x_max、x_min、y_max 和 y_min。
【解决方案2】:

你可以尝试像这样压缩你的代码:

Gauss=lambda i,j,k,xi,yi:gauss_amplitude[i]*(math.e**((-((xi-j)**2+(yi-k)**2))/(2*sigma*sigma)))
final=[[Gauss(i,j,k,x[0],x[1]) for j in range(1800) for k in range(1800)] for i,x in enumerate(center_starts)]

【讨论】:

  • 这只会减少代码行。即使在这种列表理解形式中,它仍然循环 1800*1800 次。它比 numpy 慢。
【解决方案3】:

如果你的 sigma 都是一样的,你可以通过使用没有任何循环来实现这一点 scipy.signal.convolve2d.

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import convolve2d
from scipy.stats import multivariate_normal

sigma = 3
width = 200   # smaller than yours so you can see the single pixels
n_stars = 50

# draw some random stars
star_row = np.random.randint(0, width, n_stars)
star_col = np.random.randint(0, width, n_stars)
star_amplitude = np.random.normal(50, 10, n_stars)

# assign amplitudes to center pixel of stars
amplitudes = np.zeros((width, width))
amplitudes[star_row, star_col] = star_amplitude


# create 2d gaussian kernel
row = col = np.arange(-4 * sigma, 4 * sigma + 1)
grid = np.stack(np.meshgrid(row, col)).T
kernel = multivariate_normal(
    [0, 0],
    [[sigma**2, 0], [0, sigma**2]]
).pdf(grid)
kernel /= kernel.sum()


# convolve with 2d gaussian
final = convolve2d(amplitudes, kernel, mode='same')


fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(9, 3))

img = ax1.imshow(amplitudes)
fig.colorbar(img, ax=ax1)
ax1.set_label('Before Convolution')

img = ax2.imshow(kernel)
fig.colorbar(img, ax=ax2)
ax2.set_label('Convolution Kernel')

img = ax3.imshow(final)
fig.colorbar(img, ax=ax3)
ax3.set_label('After Convolution')

fig.tight_layout()
fig.savefig('conv2d.png', dpi=300)

结果:

如果 sigma 不同,您可以通过单个循环遍历可能的 sigma。

【讨论】:

  • 使用此代码作为我的输入生成的图像全为零矩阵
  • 那你的输入是什么?
  • 它们肯定不适合这里@MaxNoe
  • 一个例子应该足以看出为什么输出正好是0
猜你喜欢
  • 2022-01-06
  • 2013-03-08
  • 2018-10-15
  • 2011-03-12
  • 1970-01-01
  • 2011-10-12
  • 1970-01-01
  • 2020-02-18
  • 1970-01-01
相关资源
最近更新 更多