【问题标题】:incrementing a multidimensional numpy array (python) with products generated from a set of vectors corresponding to axes of the array使用从一组对应于数组轴的向量生成的产品递增多维 numpy 数组(python)
【发布时间】:2020-06-12 04:55:08
【问题描述】:

A 是一个 k 维 numpy 浮点数数组(k 可能非常大,例如最多 10 个)

我需要通过递增每个值来实现对 A 的更新(如下所述)。我想知道是否有一种 numpy 风格的方式会很快。

L_iaxis i的长度

此数组的更新分两步生成:

  1. 为 A 的每个轴生成一个对应的向量 G。 例如,对应于轴 i,生成长度为 L_i 的向量 G_i(从数据中)。

  2. 通过计算 A 中每个位置的 G 向量的增量来更新所有位置的 A

要在任何特定位置执行此操作,设 p 是一个由 k 个索引组成的数组,对应于 A 中的一个位置。然后 p 处的 A 增加一个计算为乘积的值:

Product(G_i[p[i]], for i from 0 to k-1)

对 A 的完整更新涉及对 A 中的所有位置(即 p 的所有可能值)执行此操作

此操作通过循环逐个执行位置会非常慢。

有没有一种 numpy 风格的方法可以快速做到这一点?

编辑

##  this for three dimensions, final matrix at pos i,j,k has the 
## product of c[i]*b[j]*a[k]
## but for arbitrary # of dimensions it will have a loop in a loop 
## and will be slow
import numpy as np

a = np.array([1,2])
b = np.array([3,4,5])
c = np.array([6,7,8,9])

ab = []
for bval in b:
    ab.append(bval*a)
ab = np.stack(ab)

abc = []
for cval in c:
    abc.append(cval*ab)
abc = np.stack(abc)

作为一个函数

def loopfunc(arraylist):
    ndim = len(arraylist)
    m = arraylist[0]
    for i in range(1,ndim):
        ml = []
        for val in arraylist[i]:
            ml.append(val*m)
        m = np.stack(ml)
    return m

【问题讨论】:

  • 描述很难理解。您能否提供一个 A 形状 (3, 4) 的代码示例,即 k=2?
  • 对于 k=2 它只是两个向量的点积,例如一个长度为 L_i 的向量乘以一个长度为 L_j 的向量,得到一个暗淡 L_i,L_j 的矩阵。将此矩阵称为 x。现在,如果我们有长度为 L_k 的第三个向量 c,我正在谈论的产品将包含一个新的 dim L_i,L_j, L_k 数组。该数组的第一个元素是 x 的副本,其中所有值都乘以 c 的第一个元素。这个 3D 数组的第二个元素是 x 的副本,其中所有值都乘以 c 的第二个元素。等等等等希望有帮助
  • 请展示几行代码,您将如何手动处理一个小数组,否则我们会因对您想要的内容的误解而反复出现。
  • 我在原帖中加了一段代码

标签: python numpy


【解决方案1】:

这是一个古怪的问题,但我喜欢它。

如果我从您的示例中了解您需要什么,您可以通过一些重塑技巧和 NumPy 的常用广播规则来完成此操作。这个想法是重塑每个数组,使其具有正确的维数,然后直接相乘。

这是一个实现这个的函数。

from functools import reduce
import operator
import numpy as np
import scipy.linalg

def wacky_outer_product(*arrays):
    assert len(arrays) >= 2
    assert all(arr.ndim == 1 for arr in arrays)
    ndim = len(arrays)
    shapes = scipy.linalg.toeplitz((-1,) + (1,) * (ndim - 1))
    reshaped = (arr.reshape(new_shape) for arr, new_shape in zip(arrays, shapes))
    return reduce(operator.mul, reshaped).T

在您的示例数组上对此进行测试,我们有:

>>> foo = wacky_outer_product(a, b, c)
>>> np.all(foo, abc)
True

编辑

好的,上面的功能很有趣,但下面的功能可能要好得多。没有转置,更清晰,并且小很多

from functools import reduce
import operator
import numpy as np

def wacky_outer_product(*arrays):
    return reduce(operator.mul, np.ix_(*reversed(arrays)))

【讨论】:

  • 有趣。我不会想到的。我做了一些计时,看起来 wacky_outer_product() 始终花费大约 60% 的时间,就像我的代码的函数版本一样(添加到原始评论中)
  • @J.Hey Bueno!我做了基本的计时测试,但如果没有更大的输入,就很难判断。似乎更快,但不是特别快。
  • 优雅,但所需时间大约是原始 wacky_outer_product() 的 4 倍
  • @J.Hey 第二个版本?根据我的时间,它快了大约 2 倍。再说一次,虽然输入很小,但 YMMV。
  • 注意:因为高速缓存对数据在内存中的排列和访问方式很敏感,所以乘法的顺序很重要。在第二个(“更清晰、更小”)实现中,将表达式更改为 reduce(operator.mul, reversed(np.ix_(*reversed(arrays)))) 速度提高了 2 倍(在 a、b、c 大小为 100、101、102 上)。这让我感到惊讶,因为最初的顺序,形状 (102, 101, 1) 和 (1, 1, 100) 的最终乘法步骤对我来说比 (102, 1, 1) * (1) 对缓存更友好, 101, 100)。
猜你喜欢
  • 1970-01-01
  • 2014-08-08
  • 2011-11-17
  • 1970-01-01
  • 1970-01-01
  • 2023-01-04
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
相关资源
最近更新 更多