【问题标题】:Multiplying across in a numpy array在一个numpy数组中相乘
【发布时间】:2013-09-02 13:15:09
【问题描述】:

我正在尝试将 2D 数组中的每个项乘以 1D 数组中的相应项。如果我想将每一列乘以一维数组,这非常容易,如numpy.multiply 函数所示。但我想做相反的事情,将行中的每个项相乘。 换句话说,我想乘:

[1,2,3]   [0]
[4,5,6] * [1]
[7,8,9]   [2]

得到

[0,0,0]
[4,5,6]
[14,16,18]

但我得到了

[0,2,6]
[0,5,12]
[0,8,18]

有谁知道 numpy 是否有一种优雅的方法可以做到这一点? 非常感谢, 亚历克斯

【问题讨论】:

  • 啊,我在提交问题时就想通了。先转置方阵,相乘,再转置答案。
  • 最好将行转置为列矩阵,然后您不必重新转置答案。如果A * B 你必须做A * B[...,None] 通过添加一个新轴(None)来转置B
  • 谢谢,确实如此。问题是当您有一个调用 .transpose() 或 .T 的一维数组时,它不会将其转换为列数组,而是将其保留为一行,因此据我所知,您必须将其定义为列马上开始。比如x = [[1],[2],[3]] 什么的。

标签: python arrays numpy


【解决方案1】:

像你展示的正态乘法:

>>> import numpy as np
>>> m = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> c = np.array([0,1,2])
>>> m * c
array([[ 0,  2,  6],
       [ 0,  5, 12],
       [ 0,  8, 18]])

如果你添加一个轴,它会以你想要的方式相乘:

>>> m * c[:, np.newaxis]
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])

你也可以转置两次:

>>> (m.T * c).T
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])

【讨论】:

  • 使用新的轴方法可以将两个一维数组相乘并生成一个二维数组。例如[a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
【解决方案2】:

我比较了不同的速度选项,发现 - 令我惊讶的是 - 所有选项(diag 除外)都同样快。我个人使用

A * b[:, None]

(或(A.T * b).T)因为它很短。


重现情节的代码:

import numpy
import perfplot


def newaxis(data):
    A, b = data
    return A * b[:, numpy.newaxis]


def none(data):
    A, b = data
    return A * b[:, None]


def double_transpose(data):
    A, b = data
    return (A.T * b).T


def double_transpose_contiguous(data):
    A, b = data
    return numpy.ascontiguousarray((A.T * b).T)


def diag_dot(data):
    A, b = data
    return numpy.dot(numpy.diag(b), A)


def einsum(data):
    A, b = data
    return numpy.einsum("ij,i->ij", A, b)


perfplot.save(
    "p.png",
    setup=lambda n: (numpy.random.rand(n, n), numpy.random.rand(n)),
    kernels=[
        newaxis,
        none,
        double_transpose,
        double_transpose_contiguous,
        diag_dot,
        einsum,
    ],
    n_range=[2 ** k for k in range(13)],
    xlabel="len(A), len(b)",
)

【讨论】:

  • 很好地为情节提供代码。谢谢。
【解决方案3】:

您也可以使用矩阵乘法(又名点积):

a = [[1,2,3],[4,5,6],[7,8,9]]
b = [0,1,2]
c = numpy.diag(b)

numpy.dot(c,a)

哪个更优雅可能是品味问题。

【讨论】:

  • 不错,+1,没想到
  • dot 在这里真的是矫枉过正。你只是在做不必要的 0 乘法和 0 加法。
  • 如果您想将一个 nx1 向量乘以一个 d 大于 n 的 nxd 矩阵,这也可能会引发内存问题。
  • 拒绝投票,因为这很慢并且在创建密集的diag 矩阵时会使用大量内存。
【解决方案4】:

另一个技巧(从 v1.6 开始)

A=np.arange(1,10).reshape(3,3)
b=np.arange(3)

np.einsum('ij,i->ij',A,b)

我精通 numpy 广播 (newaxis),但我仍在寻找解决这个新的 einsum 工具的方法。所以我玩了一会儿才找到这个解决方案。

计时(使用 Ipython timeit):

einsum: 4.9 micro
transpose: 8.1 micro
newaxis: 8.35 micro
dot-diag: 10.5 micro

顺便说一句,将 i 更改为 jnp.einsum('ij,j->ij',A,b) 会生成 Alex 不想要的矩阵。而np.einsum('ji,j->ji',A,b) 实际上是双重转置。

【讨论】:

  • 如果您将在具有足够大阵列的计算机上进行计时,至少需要几毫秒,并将结果here连同您的相关系统信息一起发布,我们将不胜感激。
  • 对于更大的数组 (100x100),相对数字大致相同。 einsumm(25 微)是其他速度的两倍(点诊断速度更慢)。这是 np 1.7,使用“libatlas3gf-sse2”和“libatlas-base-dev”(Ubuntu 10.4,单处理器)新编译。 timeit 提供了 10000 个循环中的最佳值。
  • 这是一个很好的答案,我认为它应该被接受。但是,实际上,上面编写的代码确实给出了 Alex 试图避免的矩阵(在我的机器上)。 hpaulj 说错的其实是对的。
  • 这里的时间有误导性。 dot-diag 确实比其他三个选项差得多,并且 einsum 也不比其他选项快。
  • @NicoSchlömer,我的回答已经有将近 5 年的历史了,还有很多 numpy 版本回来了。
【解决方案5】:

对于那些在谷歌上迷失的灵魂,使用numpy.expand_dims 然后numpy.repeat 可以工作,并且也可以在更高维度的情况下工作(例如,将形状 (10, 12, 3) 乘以 (10, 12))。

>>> import numpy
>>> a = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
>>> b = numpy.array([0,1,2])
>>> b0 = numpy.expand_dims(b, axis = 0)
>>> b0 = numpy.repeat(b0, a.shape[0], axis = 0)
>>> b1 = numpy.expand_dims(b, axis = 1)
>>> b1 = numpy.repeat(b1, a.shape[1], axis = 1)
>>> a*b0
array([[ 0,  2,  6],
   [ 0,  5, 12],
   [ 0,  8, 18]])
>>> a*b1
array([[ 0,  0,  0],
   [ 4,  5,  6],
   [14, 16, 18]])

【讨论】:

    【解决方案6】:

    你为什么不这样做

    >>> m = np.array([[1,2,3],[4,5,6],[7,8,9]])
    >>> c = np.array([0,1,2])
    >>> (m.T * c).T
    

    ??

    【讨论】:

    • 接受的答案中已经显示了确切的方法,我看不出这会增加什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多