Python 向量化函数的麻烦

Python 向量化函数的麻烦,python,numpy,vectorization,Python,Numpy,Vectorization,对于一个项目,我需要从函数生成样本。我希望能够尽快生成这些样本 我有这个例子(在最终版本中,参数中将提供函数lambda),目标是使用lambda函数在开始和停止之间生成n个点的ys def get_ys(系数,num_输出=20,开始=0,停止=1): 函数=lambda x,args:args[0]*(x-args[1])**2+args[2]*(x-args[3])+args[4] linspace(开始、停止、num=num_输出、端点=True) ys=[x中x的函数(x,系数)] 返

对于一个项目,我需要从函数生成样本。我希望能够尽快生成这些样本

我有这个例子(在最终版本中,参数中将提供
函数
lambda),目标是使用lambda
函数
开始
停止
之间生成n个点的
ys

def get_ys(系数,num_输出=20,开始=0,停止=1):
函数=lambda x,args:args[0]*(x-args[1])**2+args[2]*(x-args[3])+args[4]
linspace(开始、停止、num=num_输出、端点=True)
ys=[x中x的函数(x,系数)]
返回Y
%%次
n=1000
xs=np.random.random((n,5))
ys=np。沿_轴应用_(获取_ys,1,xs)
墙壁时间:616毫秒
我正在尝试将其矢量化,并找到
numpy.apply\u沿\u轴

%%次
对于范围(1000)内的i:
xs=np.random.random(5)
ys=获取_ys(xs)
墙壁时间:622毫秒
不幸的是,它仍然相当缓慢:/

我不太熟悉函数矢量化,有人可以指导我如何提高脚本的速度吗

谢谢

编辑: 输入/输出示例:

xs = np.ones(5)
ys = get_ys(xs)

[1.0, 0.9501385041551247, 0.9058171745152355, 0.8670360110803323, 0.8337950138504155,0.8060941828254848, 0.7839335180055402, 0.7673130193905817, 0.7562326869806094, 0.7506925207756232, 0.7506925207756232, 0.7562326869806094, 0.7673130193905817, 0.7839335180055401, 0.8060941828254847, 0.8337950138504155,  0.8670360110803323, 0.9058171745152354, 0.9501385041551246, 1.0]
您试图绕过调用
get_ys
1000次,每行
xs
一次

要将
xs
作为一个整体传递到
get_ys
需要什么?换句话说,如果
系数
是(n,5)而不是(5,)呢

xs
是(20,),而
ys
将是相同的(对吗)

lambda的写入要求标量
x
和(5,)args。可以将其更改为与(20,)
x和(n,5)
args一起使用吗

作为第一步,如果给定
xs
,那么
函数会产生什么?而不是

ys = [function(x, coefficients) for x in xs]

ys = function(xs, coefficients)
编写时,您的代码(以较慢的Python速度)迭代
n(1000)行和20
linspace
。因此,
函数
被调用20000次。这就是你的代码变慢的原因

让我们试试改变吧 函数的示例运行:

In [126]: np.array(get_ys(np.arange(5)))
Out[126]: 
array([-2.        , -1.89473684, -1.78947368, -1.68421053, -1.57894737,
       -1.47368421, -1.36842105, -1.26315789, -1.15789474, -1.05263158,
       -0.94736842, -0.84210526, -0.73684211, -0.63157895, -0.52631579,
       -0.42105263, -0.31578947, -0.21052632, -0.10526316,  0.        ])
只需调用一次
函数
,即可替换列表理解:

In [127]: def get_ys1(coefficients, num_outputs=20, start=0., stop=1.):
     ...:     function = lambda x, args: args[0]*(x-args[1])**2 + args[2]*(x-args[3]) + args[4]
     ...: 
     ...:     xs = np.linspace(start, stop, num=num_outputs, endpoint=True)
     ...:     ys = function(xs, coefficients)
     ...:     return ys
     ...: 
     ...: 
相同值:

In [128]: get_ys1(np.arange(5))
Out[128]: 
array([-2.        , -1.89473684, -1.78947368, -1.68421053, -1.57894737,
       -1.47368421, -1.36842105, -1.26315789, -1.15789474, -1.05263158,
       -0.94736842, -0.84210526, -0.73684211, -0.63157895, -0.52631579,
       -0.42105263, -0.31578947, -0.21052632, -0.10526316,  0.        ])
比较时间:

In [129]: timeit np.array(get_ys(np.arange(5)))
345 µs ± 16.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [130]: timeit get_ys1(np.arange(5))
89.2 µs ± 162 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这就是我们所说的“矢量化”——用一个等价物取代python级迭代(列表理解),使
numpy
数组方法的用户更充分

我想我们可以继续使用(n,5)
系数,但这应该足以让您开始

完全矢量化 通过
广播
将(n,5)与(20,)进行对比,我可以得到一个没有任何python循环的函数:

def get_ys2(coefficients, num_outputs=20, start=0., stop=1.):
    function = lambda x, args: args[:,0]*(x-args[:,1])**2 + args[:,2]*(x-args[:,3]) + args[:,4]
    xs = np.linspace(start, stop, num=num_outputs, endpoint=True)
    ys = function(xs[:,None], coefficients)
    return ys.T
并使用(1,5)输入:

使用您的测试用例:

In [146]: n = 1000
     ...: xs = np.random.random((n,5))
     ...: ys = np.apply_along_axis(get_ys, 1, xs)
In [147]: ys.shape
Out[147]: (1000, 20)
两个时间安排:

In [148]: timeit ys = np.apply_along_axis(get_ys, 1, xs)
     ...: 
106 ms ± 303 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [149]: timeit ys = np.apply_along_axis(get_ys1, 1, xs)
     ...: 
88 ms ± 98.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
并对此进行了测试

In [150]: ys2 = get_ys2(xs)
In [151]: ys2.shape
Out[151]: (1000, 20)
In [152]: np.allclose(ys, ys2)
Out[152]: True
In [153]: timeit ys2 = get_ys2(xs)
424 µs ± 484 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
它匹配值,并大大提高了速度


在新函数中,
args
现在可以是(n,5)。如果
x
为(20,1),则结果为(20,n),我在返回时将其转置。

沿_轴应用
是一种方便的方法,可以将只需要1d数组的函数应用于3d(或更大)数组。对于2d,只在一维上迭代更简单。无论如何,它不是一个速度工具,因此也不是我们通常所说的“矢量化”。“矢量化”通常意味着重写函数,使其与二维数组一起工作,使用与多维数组一起工作的
numpy
方法。没有捷径可走。你必须学习
numpy
基础知识。另一种说法是-
apply
仍然调用
get_ys
1000次。它不会编译它或以其他方式使它更快。@Gulzar,它可以用给定的代码运行。e、 g.
get_ys(np.arange(5))
返回20个值的列表。这不是一个调试问题,而是一个重写问题。@hpaulj哦,我不知道,我以为它会矢量化,我的错。我尝试在函数上使用
numpy.vectorize
,但它每次插入一个元素而不是一行,因此我认为需要使用此函数。
np.vectorize
传递标量值,而不是行。它有一个性能免责声明<代码>应用…
也应该有一个。这非常令人印象深刻,非常感谢!我仍在努力理解你在回答中所做的一切,包括广播、转置和创建新的axis
In [148]: timeit ys = np.apply_along_axis(get_ys, 1, xs)
     ...: 
106 ms ± 303 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [149]: timeit ys = np.apply_along_axis(get_ys1, 1, xs)
     ...: 
88 ms ± 98.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [150]: ys2 = get_ys2(xs)
In [151]: ys2.shape
Out[151]: (1000, 20)
In [152]: np.allclose(ys, ys2)
Out[152]: True
In [153]: timeit ys2 = get_ys2(xs)
424 µs ± 484 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)