【问题标题】:Creating a matrix from a function with multiple arrays without for loops从具有多个数组的函数创建矩阵而不使用 for 循环
【发布时间】:2020-07-16 02:01:54
【问题描述】:

我有以下五个数组:

t = np.linspace(0,100,100)
Q = t**2

x = np.linspace(10,20,20)
y = np.linspace(5,8,5)
z = np.linspace(100,125,10)

我还定义了一个函数如下:

def f(t, x, y, z):
    return (1+x)**3.2 * y**x * np.cos(y**x)*(t-z)

现在对于x 的每个第 i 个元素、y 的第 j 个元素和 z 的第 k 个元素,我想确定 f(t, x, y, z) 在所有 @ 的值上的值987654327@。 我还想用Q 数组对这些结果中的每一个进行“内积”。 所以我最终想要的是一个 3 维矩阵,它存储大小为len(t) 的数组,该矩阵的每个元素 (i,j,k) 等于np.sum( Q * f(t, x[i], y[j], z[k]))。 所有这些都可以通过如下的 for 循环来实现:

result = np.zeros(len(x)*len(y)*len(z)).reshape((len(x),len(y), len(z)))
for i in range(len(x)):
    for j in range(len(y)):
        for k in range(len(z)):
            intermediate_result = f(t, x[i], y[j], z[k]) #This is an array of length len(t)
            result[i][j][k] = (np.nansum(intermediate_result * Q))**2

问题是这些 for 循环在计算超出这个简化示例的更大数组 x、y 和 z 时非常耗时,因此我正在寻找一种减少计算时间的方法。 有没有有效的方法来做到这一点?

【问题讨论】:

  • 看`p.xi_(x,y,z)`
  • 你的意思是 np.ix_(x,y,z) 吗?你能向我解释一下在这种情况下它是如何工作的吗?这对我来说似乎不是很明显。

标签: python numpy for-loop optimization numpy-ndarray


【解决方案1】:

您不需要循环。您可以通过重塑参数变量让 numpy 进行广播:

result = f(t,x[:,None,None,None],y[:,None,None],z[:,None])

result = np.sum(Q*result,axis=3)

请注意,您可以使用 np.ix_ 在函数内部执行此操作

def f(t, x, y, z):
    x,y,z,t = np.ix_(x,y,z,t)
    return (1+x)**3.2 * y**x * np.cos(y**x)*(t-z)

该函数将返回一个 4D 矩阵,然后您可以使用 np.sum 进行处理:

result = np.sum(Q*f(t,x,y,z),axis=3)

这将明显快于循环(对于这个 100 x 20x5x10 的示例,速度快 10 倍以上)

[编辑] 鉴于 4D 矩阵会消耗大量内存,随着维度变大,您最终会遇到另一种瓶颈。您可以通过将矩阵限制为 3 维并在循环中手动添加 t 维来规避此问题。这可确保您使用的 3D 矩阵的大小永远不会超过两倍:

def f2(t, x, y, z):
    x,y,z = np.ix_(x,y,z)
    return (1+x)**3.2 * y**x * np.cos(y**x)*(t-z)

result = np.zeros((x.size,y.size,z.size))
for tn,Qn in zip(t,Q):
    result += f2(tn,x,y,z)*Qn

通过这种方法,我能够在 19 秒内获得 t.size=300 和 x,y,z 为 256x256x256 的结果。 4D 矩阵方法使 IDLE shell 崩溃(可能是由于内存溢出),我没有耐心等待原始 for 循环完成。

请注意,由于 np.sum() 添加值的顺序,与原始解决方案相比,最后一种方法可能会在尾数的第 16 位附近产生微小差异

【讨论】:

  • 看起来不错。这是您与 OP 相比的解决方案:rextester.com/VPKX47287 其中np.array_equal(result, result2) 呈现True
  • 这确实是一个显着的改进。我也遇到了一些问题,我的计算机在使用您针对较大阵列的第一个解决方案时冻结,但编辑解决了这个问题。如果我想计算result[i][j][k] = (np.nansum(intermediate_result * Q))**2,你能否也展示一下这个解决方案的样子?让我更新我的帖子。
  • 没关系,看起来就像在末尾添加result *= result 一样简单。这没有考虑到 nansum,所以我必须找到一种方法来实现它,但对于我的情况来说,这实际上并不是必需的。
  • 实际上,您的解决方案看起来很完美。如果函数 f2(t,x,y,z) 返回 nan 值,唯一的区别是您的结果将返回 nan 值,而我使用 np.nansum 的 for 循环方法将返回 0,但这很容易解释。
猜你喜欢
  • 2015-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 2017-11-22
  • 2021-09-24
  • 2015-02-06
相关资源
最近更新 更多