I would like to populate a NumPy array with "accumulated" results from a custom function.
Currently, my code is:
import numpy as np
def f(x, mu):
return mu * x * (1 - x)
def populate(x0, mu, n):
s = np.zeros(n)
x = x0
for i in range(n):
s[i], x = x, f(x, mu)
return s
It does not take advantage of the vectorization performance of NumPy.
Is there any way to improve the speed of creating arrays like this?
3 Answers 3
You can't vectorize this. Repeated application of \$x_i = \mu x_{i-1} (1-x_{i-1})\$ is chaotic.
-
1\$\begingroup\$ Yes, I realize that trying to "vectorize" this kind of function makes no sense. But I wonder if there is a way to avoid the Python loop and use only Numpy features. \$\endgroup\$Delgan– Delgan2017年12月07日 15:14:29 +00:00Commented Dec 7, 2017 at 15:14
As the others said it is not vectorizable via numpy since each iteration depends on the previous application of the function.
However, if you need speed, you can always use numba to JIT-compile the whole python loop into machine code...
Time to run populate(0.2,0.3,1000000), for three iterations:
Without numba
0.749
0.766
0.727
With numba
0.356
0.010
0.007
The first iteration is not much faster than original python, as it includes the compilation to machine code (I presume some libraries have to be loaded, etc). The others are a lot faster... 10ms versus 750ms in pure python...
-
\$\begingroup\$ Indeed, I thought that there might be an optimized way to do that in Numpy, but it seems not. \$\endgroup\$Delgan– Delgan2017年12月07日 17:29:50 +00:00Commented Dec 7, 2017 at 17:29
-
\$\begingroup\$ I made a little benchmark, considering adding numba to your program takes 2 lines of code, a 75x speedup on this function aint too bad! \$\endgroup\$bobflux– bobflux2017年12月07日 18:20:26 +00:00Commented Dec 7, 2017 at 18:20
It is not a bad thing to have your code take up more space!
s[i] = x
x = f(x, mu)
is a lot easier to read than
s[i], x = x, f(x, mu)
So overall that would be
import numpy as np
def f(x, mu):
return mu * x * (1 - x)
def populate(x0, mu, n):
s = np.zeros(n)
x = x0
for i in range(n):
s[i] = x
x = f(x, mu)
return s
-
\$\begingroup\$ I am not sure about your updated
populate()
function, it does not add the calculated values tos
. \$\endgroup\$Delgan– Delgan2017年12月07日 17:25:13 +00:00Commented Dec 7, 2017 at 17:25 -
\$\begingroup\$ I thought it did, have you run it? \$\endgroup\$13ros27– 13ros272017年12月07日 17:26:09 +00:00Commented Dec 7, 2017 at 17:26
-
1\$\begingroup\$ Yes, it returns an array full of
0
. Look at the code: it never modifiess
. \$\endgroup\$Delgan– Delgan2017年12月07日 17:27:42 +00:00Commented Dec 7, 2017 at 17:27 -
1\$\begingroup\$ But
num
is just afloat
, so "modifying" it is actually just assigning a new value to the variablenum
. \$\endgroup\$Delgan– Delgan2017年12月07日 17:31:49 +00:00Commented Dec 7, 2017 at 17:31 -
2\$\begingroup\$ Yes, I think you should edit, because currently it does not seem to work as the initial
populate()
. \$\endgroup\$Delgan– Delgan2017年12月07日 17:39:59 +00:00Commented Dec 7, 2017 at 17:39
s[0] == x0
. \$\endgroup\$