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

7

我需要存储一个大小为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

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

请假设已经导入了numpy库,并将其命名为np - Alejandro Sazo
4
你的代码很有效。@justhalf的回答更短,而且它还可以使用lambda版本使得更加简短。 - emesday
3个回答

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}

OP's

      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}

也许时间测量可以帮助我们决定哪一个是更好的 :P (感谢介绍vstack) - Alejandro Sazo
1
我可以在我的回答中包含你的时间吗? - justhalf

2

您的代码已经看起来很好了,但是这里还有一些想法。

这是一个一行的代码。 它比您的版本稍微慢一些。

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

I get these timings for n=10000

Yours:

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版本最快。

-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


6
虽然这是有效的Python代码,但它并没有充分利用NumPy的向量化。由于你将涉及for循环从C(如OP的解决方案)移动到Python中,所以运行速度会明显减慢。 - IanH
+1 @IanH:是的,我正在进行计时实验,确实比较慢。会继续尝试实验的。 - justhalf
你们觉得将问题从“更短或最有效”改为“最有效”会更好吗?以避免回答中有两个答案。 - Alejandro Sazo
你的解决方案中,“shorter”的价格太高了。正如指出的那样,答案很慢。此外,一行代码太长了,这使它难看且难以阅读,而OP的解决方案非常清晰。 - Akavall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接