Reputation: 179
I need to return the sin and cos values of every element in a large array. At the moment I am doing:
a,b=np.sin(x),np.cos(x)
where x is some large array. I need to keep the sign information for each result, so:
a=np.sin(x)
b=(1-a**2)**0.5
is not an option. Is there any faster way to return both sin and cos at once?
Upvotes: 18
Views: 9523
Reputation: 3443
Using Numba you could improve speed by ~20%, if that matters to you.
import numba
import numpy as np
x = np.random.uniform((1000,))
@numba.njit
def sincos_simple(x):
return np.sin(x), np.cos(x)
@numba.njit
def sincos_prealloc(x):
r = np.empty(x.shape + (2,))
r[..., 0] = np.sin(x)
r[..., 1] = np.cos(x)
return r
# compile numba function (run once before timing)
sincos_simple(x)
sincos_prealloc(x)
%timeit np.sin(x), np.cos(x)
%timeit sincos_simple(x)
%timeit sincos_prealloc(x)
Results:
1.02 µs ± 16.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
927 ns ± 13.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
819 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Upvotes: 0
Reputation: 58861
I compared the suggested solution with perfplot and found that nothing beats calling sin
and cos
explicitly.
Code to reproduce the plot:
import perfplot
import numpy as np
def sin_cos(x):
return np.sin(x), np.cos(x)
def exp_ix(x):
eix = np.exp(1j * x)
return eix.imag, eix.real
def cos_from_sin(x):
sin = np.sin(x)
abs_cos = np.sqrt(1 - sin**2)
sgn_cos = np.sign(((x - np.pi / 2) % (2 * np.pi)) - np.pi)
cos = abs_cos * sgn_cos
return sin, cos
b = perfplot.bench(
setup=lambda n: np.linspace(0.0, 2 * np.pi, n),
kernels=[sin_cos, exp_ix, cos_from_sin],
n_range=[2**k for k in range(20)],
xlabel="n",
)
b.save("out.png")
b.show()
Upvotes: 8
Reputation: 15273
For completeness, another way to combine this down to a single cos()
call is to prepare an angle array where the second half has a phase shift of pi/2.
Borrowing the profiling code from Nico Schlömer, we get:
import perfplot
import numpy as np
def sin_cos(x):
return np.sin(x), np.cos(x)
def exp_ix(x):
eix = np.exp(1j * x)
return eix.imag, eix.real
def cos_shift(x):
angles = x[np.newaxis, :] + np.array(((-np.pi/2,), (0,)))
return tuple(np.cos(angles))
perfplot.save(
"out.png",
setup=lambda n: np.linspace(0.0, 2 * np.pi, n),
kernels=[sin_cos, exp_ix, cos_shift],
n_range=[2 ** k for k in range(1, 16)],
xlabel="n",
)
So it's slower than the separate sin
/cos
calls, but in some (narrow) contexts might be more convenient because - from the cos()
onward - it only needs to deal with a single array.
Upvotes: 1
Reputation: 21
A pure numpy version via complex numbers, e iφ = cosφ + i sinφ, inspired by the answer from das-g.
x = np.arange(2 * np.pi, step=0.01)
eix = np.exp(1j*x)
cosx, sinx = eix.real, eix.imag
This is faster than the nprect
, but still slower than sin
and cos
calls:
In [6]: timeit c = nprect(1, x); cosx, sinx = cos(x), sin(x)
1000 loops, best of 3: 242 us per loop
In [7]: timeit eix = np.exp(1j*x); cosx, sinx = eix.real, eix.imag
10000 loops, best of 3: 49.1 us per loop
In [8]: timeit cosx, sinx = cos(x), sin(x)
10000 loops, best of 3: 32.7 us per loop
Upvotes: 1
Reputation: 9994
You can use complex numbers and the fact that e i · φ = cos(φ) + i · sin(φ).
import numpy as np
from cmath import rect
nprect = np.vectorize(rect)
x = np.arange(2 * np.pi, step=0.01)
c = nprect(1, x)
a, b = c.imag, c.real
I'm using here the trick from https://stackoverflow.com/a/27788291/674064 to make a version of cmath.rect()
that'll accept and return NumPy arrays.
This doesn't gain any speedup on my machine, though:
c = nprect(1, x)
a, b = c.imag, c.real
takes about three times the time (160μs) that
a, b = np.sin(x), np.cos(x)
took in my measurement (50.4μs).
Upvotes: 4
Reputation: 179
def cosfromsin(x,sinx):
cosx=absolute((1-sinx**2)**0.5)
signx=sign(((x-pi/2)%(2*pi))-pi)
return cosx*signx
a=sin(x)
b=cosfromsin(x,a)
I've just timed this and it is about 25% faster than using sin and cos.
Upvotes: -1
Reputation: 551
You could take advantage by the fact that tan(x) contains both sin(x) and cos(x) function. So you could use the tan(x) and retrieve cos(x) ans sin(x) using the common transformation function.
Upvotes: -1