6

I'd like to implement a geometric progression using Python / Pandas / Numpy.

Here is what I did:

N = 10
n0 = 0
n_array = np.arange(n0, n0 + N, 1)
u = pd.Series(index = n_array)
un0 = 1
u[n0] = un0
for n in u.index[1::]:
    #u[n] = u[n-1] + 1.2 # arithmetic progression
    u[n] = u[n-1] * 1.2 # geometric progression
print(u)

I get:

0    1.000000
1    1.200000
2    1.440000
3    1.728000
4    2.073600
5    2.488320
6    2.985984
7    3.583181
8    4.299817
9    5.159780
dtype: float64

I wonder how I could avoid to use this for loop.

I had a look at https://fr.wikipedia.org/wiki/Suite_g%C3%A9om%C3%A9trique and found that u_n can be expressed as: u_n = u_{n_0} * q^{n-n_0}

So I did that

n0 = 0
N = 10
n_array = np.arange(n0, n0 + N, 1)
un0 = 1
q = 1.2
u = pd.Series(map(lambda n: un0 * q ** (n - n0), n_array), index = n_array)

That's ok... but I'm looking for a way to define it in a recurrent way like

u_n0 = 1
u_n = u_{n-1} * 1.2

But I don't see how to do it using Python / Pandas / Numpy... I wonder if it's possible.

2
  • How about un0 * 1.2 ** np.arange(N)? It is not recursive, though. Commented Jan 3, 2014 at 13:06
  • That's easier than what I've written... but not recursive. Thank you anyway. With Pandas Series it looks like u = pd.Series(un0 * q ** np.arange(N), index=n_array. I'm still looking for a recursive solution (without for loop) Commented Jan 3, 2014 at 13:11

6 Answers 6

12

Another possibility, that is probably more computationally efficient than using exponentiation:

>>> N, un0, q = 10, 1, 1.2
>>> u = np.empty((N,))
>>> u[0] = un0
>>> u[1:] = q
>>> np.cumprod(u)
array([ 1.        ,  1.2       ,  1.44      ,  1.728     ,  2.0736    ,
        2.48832   ,  2.985984  ,  3.5831808 ,  4.29981696,  5.15978035])
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your solution. The cumprod idea was very good. @Ashish solution use Pandas Series... which is what I was requesting. I upvote you solution and mark Ashish 's solution.
@working4coins I don't do Pandas, so I am not sure of the details, but Series is built on top of the np.ndarray, so it has to have a .cumprod() method that you can use for this.
10

Use numpy.logspace

>>> import numpy
>>> N=10
>>> u=numpy.logspace(0,N,num=N, base=1.2, endpoint=False)
>>> print u
[ 1.          1.2         1.44        1.728       2.0736      2.48832
  2.985984    3.5831808   4.29981696  5.15978035]

2 Comments

Interesting solution. You should do >>> N, un0, q = 10, 5, 1.2 >>> u = pd.Series(un0 * np.logspace(0, N, num=N, base=q, endpoint=False), index = n_array) u in order to be able to set un0
This is very neat. But for some reason it's 50 to 100 times slower than the other solutions. Not sure why.
2

Here is how it works for me in a Pandas series:

N = 10
n0 = 0
n_array = np.arange(n0, n0 + N, 1)
u = pd.Series(index = n_array)
u[n0] = 1
q = 1.2
# option 1:
u = pd.Series(u[n0]*q**(u.index.values - n0), index = n_array)
# or option 2 with cumprod
u[1:] = q
u = u.cumprod()

3 Comments

If you want to stick with pandas , based on answer from @Jaime you can replace the last line with, >>> u[1:] = q >>> u= u.cumprod()
u = pd.Series(u[n0]*q**(np.array(u.index) - n0), index = n_array) should fix that
Very good. Using np.cumprod is the fastest method I could find other than using a generator (which requires a generator and a for loop)
0

Just defining u(u0, q, n) function should work:

def u(u0, q, n):
    return u0 if n==0 else q*u(u0, q, n-1)

3 Comments

I want this geometric progression in a Pandas Series
May be, list comprehension: [u(1, 1.2, n) for n in range(10)]? But it's like "hidden for loop."
Yes it's an hidden for loop. I run %timeit in an IPython notebook. Results for N, n0, un0, q = 200, 0, 1, 1.2 - 100 loops, best of 3: 5.59 ms per loop with list comprehension and 10000 loops, best of 3: 96.9 µs per loop with np.logspace @ilmarinen solution
0

Itertools helps:

from itertools import accumulate
import operator
import pandas as pd

d, f, n = 1.2, 1, 10  # degree, first element, number
pd.Series([* accumulate([f]+[d] * (n-1), func = operator.mul)])

result is:

0     1.000000
1     1.510000
2     2.280100
3     3.442951
4     5.198856
5     7.850273
6    11.853912
7    17.899406
8    27.028104
9    40.812437
dtype: float64

Comments

0

I think @Ashish's solution with np.cumprod is the simplest but if you are willing to define a generator somewhere then this is probably the most computationally efficient solution:

def geometric_series_generator(x, r, n):
    """Generate a geometric series of length n, starting
    at x and increasing by the ratio r.
    """

    for i in range(n):
        yield x
        x = x*r

N = 10
u0 = 1
r = 1.2
gen = geometric_series_generator(u0, r, N)
geom_series = np.fromiter(gen, float, count=N)
print(pd.Series(geom_series, index=np.arange(0, N, 1)))

Output:

0    1.000000
1    1.200000
2    1.440000
3    1.728000
4    2.073600
5    2.488320
6    2.985984
7    3.583181
8    4.299817
9    5.159780
dtype: float64

1 Comment

This came out at 2.2 µs on my computer for a series of length 10,000 whereas the cumprod method was about 5.15 µs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.