在Numpy中创建cos和sin数组的最有效方法

7 投票
3 回答
6997 浏览
提问于 2025-04-18 01:58

我需要存储一个大小为 n 的数组,里面的值是 cos(x)sin(x),假设是这样。

array[[cos(0.9), sin(0.9)],
      [cos(0.35),sin(0.35)],
      ...]

每对 cos 和 sin 的参数是随机选择的。到目前为止,我改进的代码是这样的:

def randvector():
""" Generates random direction for n junctions in the unitary circle """
    x = np.empty([n,2])
    theta = 2 * np.pi * np.random.random_sample((n))
    x[:,0] = np.cos(theta)
    x[:,1] = np.sin(theta)
    return x

有没有更简短或者更有效的方法来实现这个呢?

3 个回答

-2

你可以用列表推导式来让代码变得稍微短一些:

def randvector(n):
    return np.array([(np.cos(theta), np.sin(theta)) for theta in 2*np.pi*np.random.random_sample(n)])

不过,正如IanH在评论中提到的,这样做会比较慢。实际上,通过我的实验发现,这慢了5倍,因为这样没有利用到NumPy的向量化功能。

所以来回答你的问题:

有没有更短的方法?

有的,我在这个回答中给出的就是,虽然只短了几个字符(但节省了很多行代码!)

有没有更有效的方法(我想你是指“高效”)?

我认为这个问题的答案是没有,因为numpy已经优化了向量化(把cos和sin值分配到数组中)。

时间比较

比较几种方法的执行时间:

OP的 randvector: 0.002131 秒

我的 randvector: 0.013218 秒

mskimm的 randvector: 0.003175 秒

所以看起来mskimm的 randvector 在代码长度和效率上都表现不错 =D

2

你的代码看起来已经不错了,不过我还有一些想法。

这里有一个一行代码的写法。虽然它比你的版本稍微慢一点。

def randvector2(n):
    return np.exp((2.0j * np.pi) * np.random.rand(n, 1)).view(dtype=np.float64)

对于n=10000,我得到了这些时间。

你的代码:

1000 loops, best of 3: 716 µs per loop

我简化后的版本:

1000 loops, best of 3: 834 µs per loop

如果你很在意速度的话,你的做法真的很好。还有一个答案提到怎么使用hstack,这个方法也很不错。这里有一个和你的代码稍微不同的版本,它的速度也稍微快一点。

def randvector3(n):
    x = np.empty([n,2])
    theta = (2 * np.pi) * np.random.rand(n)
    np.cos(theta, out=x[:,0])
    np.sin(theta, out=x[:,1])
    return x

这个给我的时间是:

1000 loops, best of 3: 698 µs per loop

如果你能使用numexpr,下面这个在我的电脑上会更快。

import numexpr as ne
def randvector3(n):
    sample = np.random.rand(n, 1)
    c = 2.0j * np.pi
    return ne.evaluate('exp(c * sample)').view(dtype=np.float64)

这个给我的时间是:

1000 loops, best of 3: 366 µs per loop

不过老实说,如果我写这个代码不是为了追求极致的性能,我会做得和你差不多。这样能让读者更清楚你的意图。使用hstack的版本也很好。

再补充一点:当我用n=10时,我的一行代码是最快的。而当n=10000000时,纯numpy的版本是最快的。

5

你的代码已经挺有效了。我觉得justhalf的回答也不错。

如果想要更有效和简洁的话,这段代码怎么样呢?

def randvector(n):
    theta = 2 * np.pi * np.random.random_sample((n))
    return np.vstack((np.cos(theta), np.sin(theta))).T

更新

附上cProfile的结果。

justhalf的代码

      5 function calls in 4.707 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.001    0.001    4.707    4.707 <string>:1(<module>)
     1    2.452    2.452    4.706    4.706 test.py:6(randvector1)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.010    0.010    0.010    0.010 {method 'random_sample' of 'mtrand.RandomState' objects}
     1    2.244    2.244    2.244    2.244 {numpy.core.multiarray.array}

原作者的代码

      5 function calls in 0.088 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.088    0.088 <string>:1(<module>)
     1    0.079    0.079    0.088    0.088 test.py:9(randvector2)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.009    0.009    0.009    0.009 {method 'random_sample' of 'mtrand.RandomState' objects}
     1    0.000    0.000    0.000    0.000 {numpy.core.multiarray.empty}

我的代码

      21 function calls in 0.087 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.087    0.087 <string>:1(<module>)
     2    0.000    0.000    0.000    0.000 numeric.py:322(asanyarray)
     1    0.000    0.000    0.002    0.002 shape_base.py:177(vstack)
     2    0.000    0.000    0.000    0.000 shape_base.py:58(atleast_2d)
     1    0.076    0.076    0.087    0.087 test.py:17(randvector3)
     6    0.000    0.000    0.000    0.000 {len}
     1    0.000    0.000    0.000    0.000 {map}
     2    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.009    0.009    0.009    0.009 {method 'random_sample' of 'mtrand.RandomState' objects}
     2    0.000    0.000    0.000    0.000 {numpy.core.multiarray.array}
     1    0.002    0.002    0.002    0.002 {numpy.core.multiarray.concatenate}

撰写回答