当前位置: 首页 > 知识库问答 >
问题:

在numpy数组上映射函数的最有效方法

殳毅
2023-03-14

在numpy数组上映射函数最有效的方法是什么?我在当前项目中的做法如下:

import numpy as np 

x = np.array([1, 2, 3, 4, 5])

# Obtain array of square of each element in x
squarer = lambda t: t ** 2
squares = np.array([squarer(xi) for xi in x])

然而,这看起来可能是非常低效的,因为我使用列表理解来构造新的数组作为Python列表,然后再将其转换回Numpy数组。

我们能做得更好吗?

共有3个答案

沈凡
2023-03-14

正如@user2357112所指出的,应用函数的“直接”方法总是在Numpy数组上映射函数的最快和最简单的方法:

import numpy as np
x = np.array([1, 2, 3, 4, 5])
f = lambda x: x ** 2
squares = f(x)

通常避免使用np.vectorize,因为它的性能不好,并且存在(或曾经存在)许多问题。如果您正在处理其他数据类型,则可能需要研究下面所示的其他方法。

下面是一些比较映射函数的三种方法的简单测试,本例使用Python3.6和Numpy1.15.4。首先,测试的设置功能:

import timeit
import numpy as np

f = lambda x: x ** 2
vf = np.vectorize(f)

def test_array(x, n):
    t = timeit.timeit(
        'np.array([f(xi) for xi in x])',
        'from __main__ import np, x, f', number=n)
    print('array: {0:.3f}'.format(t))

def test_fromiter(x, n):
    t = timeit.timeit(
        'np.fromiter((f(xi) for xi in x), x.dtype, count=len(x))',
        'from __main__ import np, x, f', number=n)
    print('fromiter: {0:.3f}'.format(t))

def test_direct(x, n):
    t = timeit.timeit(
        'f(x)',
        'from __main__ import x, f', number=n)
    print('direct: {0:.3f}'.format(t))

def test_vectorized(x, n):
    t = timeit.timeit(
        'vf(x)',
        'from __main__ import x, vf', number=n)
    print('vectorized: {0:.3f}'.format(t))

用五个元素进行测试(从最快到最慢排序):

x = np.array([1, 2, 3, 4, 5])
n = 100000
test_direct(x, n)      # 0.265
test_fromiter(x, n)    # 0.479
test_array(x, n)       # 0.865
test_vectorized(x, n)  # 2.906

有100个元素:

x = np.arange(100)
n = 10000
test_direct(x, n)      # 0.030
test_array(x, n)       # 0.501
test_vectorized(x, n)  # 0.670
test_fromiter(x, n)    # 0.883

以及1000个或更多阵列元素:

x = np.arange(1000)
n = 1000
test_direct(x, n)      # 0.007
test_fromiter(x, n)    # 0.479
test_array(x, n)       # 0.516
test_vectorized(x, n)  # 0.945

不同版本的Python/NumPy和编译器优化将有不同的结果,因此请针对您的环境执行类似的测试。

郎弘业
2023-03-14

使用numpy.vectorize怎么样。

import numpy as np
x = np.array([1, 2, 3, 4, 5])
squarer = lambda t: t ** 2
vfunc = np.vectorize(squarer)
vfunc(x)
# Output : array([ 1,  4,  9, 16, 25])
燕刚毅
2023-03-14

我已经测试了所有建议的方法,加上np.array(map(f, x))PERPLANT(我的一个小项目)。

信息#1:如果您可以使用numpy的本机函数,请这样做。

如果您尝试矢量化的函数已经矢量化(如原始帖子中的x**2示例),则使用该函数比使用其他函数快得多(注意日志比例):

如果您真的需要矢量化,那么使用哪种变体其实并不重要。

代码再现的情节:

import numpy as np
import perfplot
import math

def f(x):
    # return math.sqrt(x)
    return np.sqrt(x)

vf = np.vectorize(f)

def array_for(x):
    return np.array([f(xi) for xi in x])

def array_map(x):
    return np.array(list(map(f, x)))

def fromiter(x):
    return np.fromiter((f(xi) for xi in x), x.dtype)

def vectorize(x):
    return np.vectorize(f)(x)

def vectorize_without_init(x):
    return vf(x)

perfplot.show(
    setup=np.random.rand,
    n_range=[2 ** k for k in range(20)],
    kernels=[f, array_for, array_map, fromiter,
             vectorize, vectorize_without_init],
    xlabel="len(x)",
)
 类似资料:
  • 问题内容: 在numpy数组上映射函数的最有效方法是什么?我在当前项目中所做的工作如下: 但是,这似乎效率很低,因为我正在使用列表推导将新数组构造为Python列表,然后再将其转换回numpy数组。 问题答案: 我测试过的所有建议的方法,加上与(我的一个小项目)。 消息1:如果可以使用numpy的本机函数,请执行此操作。 如果你想已经矢量化功能的矢量(如在原岗位的例子),使用的是多比什么都更快(注

  • 问题内容: 信不信由你,在分析当前代码后,numpy数组还原的重复操作将占用大量的运行时间。我现在拥有的是基于视图的常见方法: 还有其他方法可以更有效地执行此操作,还是我对不切实际的numpy性能的痴迷所致的幻觉? 问题答案: 创建时,您正在创建原始数组的视图。然后,您可以更改原始数组,并且视图将更新以反映所做的更改。 您是否经常需要重新创建视图?您应该能够执行以下操作: 我不是numpy专家,但

  • 问题内容: 是否可以将NumPy数组映射到位? 如果是,怎么办? 给定-2D数组-这是目前为我完成窍门的一些代码: 但是它是如此丑陋,以至于我怀疑在NumPy内的某个地方一定有一个函数可以对以下内容执行相同的操作: 但是如果存在上述类似内容,我将无法找到它。 问题答案: 只有在空间有限的情况下,才值得尝试就地执行此操作。如果是这样,可以通过遍历数组的展平视图来稍微加快代码的速度。由于在可能的情况下

  • 问题内容: 在数组中对对象进行分组的最有效方法是什么? 例如,给定此对象数组: 我正在表格中显示此信息。我想对不同的方法进行分组,但是我想对这些值求和。 我将Underscore.js用于其groupby函数,这很有用,但并不能解决所有问题,因为我不希望它们“分裂”而是“合并”,更像SQL 方法。 我正在寻找的是能够总计特定值(如果要求)。 因此,如果我进行了groupby ,我希望收到: 如果我

  • 问题内容: 示例问题 作为一个简单的示例,请考虑以下定义的numpy数组: 其中,像这样在控制台输出: 我现在想按行“向前填充” array中的值。我的意思是用左侧最接近的有效值替换每个值。所需的结果如下所示: 到目前为止尝试过 我试过使用for循环: 我还尝试过使用熊猫数据框作为中间步骤(因为熊猫数据框具有非常整洁的内置方法用于正向填充): 以上两种策略都能产生预期的结果,但是我一直在想:仅使用

  • 对数组中的对象进行分组的最有效的方法是什么? 例如,给定对象数组: 我正在表中显示此信息。我想通过不同的方法进行分组,但我想求和这些值。 我使用underscore.js来实现它的groupby函数,这很有帮助,但并不能完成全部任务,因为我不希望它们“拆分”,而是“合并”,更像SQL方法。 我正在寻找的将能够总计特定的值(如果请求)。 因此,如果我执行了groupby,我希望收到: 如果我执行了分