## Contexto: "O incrível crescimento de Python"¶

Fonte: StackOverflow blog The Incredible Growth of Python

Fonte: StackOverflow blog Why is Python Growing So Quickly?

## Exemplo 1: MRUV¶

Exemplo do livro Programming for Computations - Python: A Gentle Introduction to Numerical Simulations with Python de Svein Linge e Hans Petter Langtangen.

Altura ao longo do tempo de um objeto lançado verticalmente:

$$y = {v_0}t + \frac{g{t^2}}{2}$$

In [1]:
import numpy as np
import matplotlib.pyplot as plt

v0 = 5
g = -9.81
t = np.linspace(0, 1, 1001)

y = v0 * t + g * t**2 / 2

plt.plot(t, y)
plt.xlabel('t (s)')
plt.ylabel('y (m)')
plt.show()


## Exemplo 2: Random Walk¶

### Random walk: solução OO¶

In [2]:
import random

class RandomWalker:

def __init__(self):
self.position = 0
self.path = []

def step(self):
self.position += random.choice([-1, 1])
self.path.append(self.position)

def walk(self, steps):
for _ in range(steps):
self.step()
return self.path

In [3]:
%matplotlib inline
import matplotlib.pyplot as plt

In [4]:
walker = RandomWalker()
walker.walk(10)

Out[4]:
[1, 0, 1, 0, 1, 2, 3, 2, 1, 0]
In [6]:
walker = RandomWalker()
plt.plot(walker.walk(1000))
plt.show()

In [7]:
%%timeit -n 1000
walker.walk(1000)

1000 loops, best of 3: 1.68 ms per loop


### Random Walk: solução procedural¶

In [8]:
def random_walk(n):
position = 0
walk = [position]
for i in range(n):
position += random.choice([-1, 1])
walk.append(position)
return walk

In [9]:
%%timeit -n 1000
walk = random_walk(1000)

1000 loops, best of 3: 1.29 ms per loop


### Random Walk: solução vetorizada com itertools¶

In [10]:
from itertools import accumulate
g = accumulate([1,2,3,4,5])
g

Out[10]:
<itertools.accumulate at 0x110879b88>
In [11]:
next(g), next(g), next(g)

Out[11]:
(1, 3, 6)
In [12]:
list(g)

Out[12]:
[10, 15]
In [13]:
g = accumulate([1,2,3,4,5])
list(g)

Out[13]:
[1, 3, 6, 10, 15]
In [15]:
def random_walk_itertools(n):
steps = random.choices([-1, 1], k=n)  # choice plural, Py ≥ 3.6
return [0]+list(accumulate(steps))

In [16]:
%%timeit -n 1000
walk = random_walk_itertools(1000)

1000 loops, best of 3: 322 µs per loop


### Random Walk: solução vetorizada com NumPy¶

In [17]:
import numpy as np
np.random.choice([-1, 1], 10)

Out[17]:
array([ 1,  1, -1,  1, -1, -1,  1,  1,  1,  1])
In [18]:
def random_walk_numpy(n):
steps = np.random.choice([-1, 1], n)  # choice singular!
return np.cumsum(steps)

In [19]:
%%timeit -n 1000
walk = random_walk_numpy(1000)

1000 loops, best of 3: 27.7 µs per loop

In [21]:
plt.plot(random_walk_numpy(1000))
plt.show()


## Comparando desempenhos¶

In [22]:
from timeit import timeit

def cronometrar(expr, vezes=1000):
return timeit(expr, globals=globals(), number=vezes) / vezes

casos = ['RandomWalker().walk(1000)',
'random_walk(1000)',
'random_walk_itertools(1000)',
'random_walk_numpy(1000)',
]

In [23]:
tempos = []
for caso in casos:
t = cronometrar(caso)
print(f'{t:07f}s', caso, sep='\t')
tempos.append(t)

0.001643s	RandomWalker().walk(1000)
0.001292s	random_walk(1000)
0.000335s	random_walk_itertools(1000)
0.000028s	random_walk_numpy(1000)

In [24]:
fig, ax = plt.subplots()
posições = np.arange(len(casos))
ax.barh(posições, tempos)
ax.set_yticks(posições)
ax.set_yticklabels(casos)
plt.show()

In [25]:
max(tempos) / min(tempos)

Out[25]:
58.389202754254384