Reputation: 88628
I'm a Numpy newbie.
I'd like to create an array with a million numbers, that has a sine wave with exponential decay on the amplitude.
In other words, I want the value of each cell n
to be sin(n) * 2 ** (-n * factor)
.
What would be the most efficient way to do that?
Upvotes: 4
Views: 3109
Reputation: 6482
Numpy has efficient implementations for simple operations like sin(array), exp(array),.. The problem is that each expression (sin(n); -n * factor, 2 ** previous_Temp_array
) is done on its own using temporary arrays for intermediate results. This leads to a quite high memory footprint and also has a negative impact on performance.
Code
import numpy as np
import numexpr as ne
def orig(n_max,factor):
n=np.arange(n_max)
return np.sin(n) * 2 ** (-n * factor)
#Rory Daulton's solution
def mod(n_max,factor):
n=np.arange(n_max)
newfactor = -np.log(2) * factor
return np.sin(n) * np.exp(newfactor * n)
def mod_2(n_max,factor):
n=np.arange(n_max)
return ne.evaluate("sin(n) * 2**(-n * factor)")
#Rory Daulton's solution using Numexpr
def mod_3(n_max,factor):
n=np.arange(n_max)
newfactor = -np.log(2) * factor
return ne.evaluate("sin(n) * exp(newfactor * n)")
Timings
%timeit res=orig(1e6,0.5)
81 ms ± 4.75 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit res=mod(1e6,0.5)
46.3 ms ± 5.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit res=mod_2(1e6,0.5)
16 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit res=mod_3(1e6,0.5)
11.1 ms ± 48.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Upvotes: 1
Reputation: 22544
Numpy has its own sin
function, which is efficient at the task you want to do. The main cause of inefficiency would be in the exponentiation 2 ** (-n * factor)
.
However, numpy does have efficient exponentiation in its exp
function. So we can transform the base to e
to use exp
by using
exp(-n * factor * log(2))
where log
is another numpy function and uses base e
. You could speed up your code even more by doing as much of the calculation as possible outside the loop that numpy sets up in its vectorization. In other words, you first set up the scalar
newfactor = -np.log(2) * factor
and set up your x
array using x = np.linspace(0, 10, 1000000)
or something similar. Then your y
array is created with
y = np.sin(x) * np.exp(newfactor * x)
Now y
is an array with all the calculated values corresponding to the values in the x
array.
Numpy does the looping itself, very efficiently for current technology. My experiments show that doing the exponentiation this way takes less than 1/5th the time that np.power(2, -factor * x)
or 2 ** (-x * factor)
or np.exp2(-x * factor)
take. Looking at the entire expression, for x
an array of length one million, my code takes a total of 29.2 ms
to execute. The code of @ComplicatedPhenomenon, which does look good, takes a total of 81.3 ms
to execute, which is almost 3 times as long. (A hat-tip to @ComplicatedPhenomenon for pointing out an error in my code--I have corrected it and it seems to work well now.)
Upvotes: 3