在Numpy中创建cos和sin数组的最有效方法
我需要存储一个大小为 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 个回答
你可以用列表推导式来让代码变得稍微短一些:
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
你的代码看起来已经不错了,不过我还有一些想法。
这里有一个一行代码的写法。虽然它比你的版本稍微慢一点。
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的版本是最快的。
你的代码已经挺有效了。我觉得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}