【问题标题】:Python : How to vectorize my split function with NumpyPython:如何使用 Numpy 向量化我的拆分函数
【发布时间】:2015-08-05 10:14:22
【问题描述】:

我在 Stack 上找到了一个可以解决我的问题的函数,但现在我想加快我的代码速度,因为我有很多列表要拆分。

我听说对函数进行矢量化是一种解决方案,因此尝试使用 numpy 对我的函数进行矢量化,但它不起作用。

你能帮帮我吗?

原函数:

seq = ([1,1,5,1,5,5,1,5,1,1])

def zigzag(seq):
  return seq[::2], seq[1::2]

结果:

([1, 5, 5, 1, 1], [1, 1, 5, 5, 1])

我的矢量化尝试:

import numpy as np
seq = ([1, 1, 5, 1, 5, 5, 1, 5, 1, 1], [2, 2, 2, 3, 3, 3, 3, 2, 2, 2], [6, 3, 9, 2, 9, 4, 6, 3])

def zigzag(seq):
  return seq[::2], seq[1::2]

vecto = np.vectorize(zigzag)

vecto(seq)

想要的结果:

(([1, 5, 5, 1, 1], [1, 1, 5, 5, 1]), ([2, 2, 3, 3, 2], [2, 3, 3, 2, 2]), ([6, 9, 9, 6], [3, 2, 4, 3]))

【问题讨论】:

  • 你不会从 np.vectorize 中获得太多的加速,基本上它只是在幕后的 for 循环中调用函数。
  • 得知这一点我很难过,还有其他解决方案吗?
  • 并非如此。如果所有序列的长度相同,则可以创建一个 2D numpy 数组,其中每一行都是一个序列,然后使用矢量化索引为:return seq[:, ::2], seq[:, 1::2]... 但是,如果您有可变长度序列,则需要遍历他们。您可能会获得的唯一加速是如果您在 Cython 中编写代码(但是当您使用 python 对象、列表/元组时,我怀疑您可以获得任何相关的加速)。

标签: python numpy vectorization


【解决方案1】:

这对于单个数组很容易:只需将您的序列设为numpy.array,您的zigzag 函数将在后台调用 C 代码。

def zigzag(seq):
  return seq[::2], seq[1::2]

seq = np.array([1,1,5,1,5,5,1,5,1,1])

result = zigzag(seq)
print(result)

结果:

(array([1, 5, 5, 1, 1]), array([1, 1, 5, 5, 1]))

对于多维情况,您会遇到列表长度不同的问题。因此,您无法从中制作出漂亮的numpy.array。我建议你像这样调整它:

import numpy as np

def zigzag(seq):
    try:
        if len(seq.shape) == 1:
            return seq[::2], seq[1::2]
    except AttributeError:
        return [zigzag(x) for x in seq]

def main():
    options = _parse_args()

    seq = np.array([1,1,5,1,5,5,1,5,1,1])
    seq2 = (
        np.array([1, 1, 5, 1, 5, 5, 1, 5, 1, 1]),
        np.array([2, 2, 2, 3, 3, 3, 3, 2, 2, 2]),
        np.array([6, 3, 9, 2, 9, 4, 6, 3]),
    )

    print(zigzag(seq))
    print()
    print(zigzag(seq2))

第二个序列只是numpy.array 中的tuple。该函数检查您的序列是否具有shape 属性,这是一个很好的指标,表明它是numpy.array。如果是这样,它使用 NumPy 切片。如果它是一个元组,它只会为每个元素调用zigzag 函数。

它为您的示例生成所需的输出:

(array([1, 5, 5, 1, 1]), array([1, 1, 5, 5, 1]))

[(array([1, 5, 5, 1, 1]), array([1, 1, 5, 5, 1])), (array([2, 2, 3, 3, 2]), array([2, 3, 3, 2, 2])), (array([6, 9, 9, 6]), array([3, 2, 4, 3]))]

然而,这不是一个完美的解决方案。您不想一直将 Python 列表和元组转换为 NumPy 数组。正如@hpaulj 在 cmets 中指出的那样,这种转换比 Python listtuple 本身的拆分花费的时间更长。想想你的数据在哪里,以及将它们放在 NumPy 数组中的什么位置。那些必须具有矩形形状。一旦你有了这个,你就可以编写一个合适的zigzag 版本。

【讨论】:

  • 观察将列表转换为数组所需的时间。对于 1000 项的列表,拆分需要 4us,拆分相同大小的数组需要 2;但是将列表转换为数组需要 100+us。
  • 确实,我忽略了这一点。如果 OP 能够在整个程序中使用numpy.array,那么转换将非常少见,这样可以加快速度。到numpy.array 的转换可能非常慢,因为它必须将序列中的每个元素转换为数字,而原生 Python 列表切片不关心元素的类型。我认为静态类型语言将有助于解决这个特定问题。
猜你喜欢
  • 1970-01-01
  • 2020-02-26
  • 2021-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-07
  • 1970-01-01
相关资源
最近更新 更多