Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 32: Processamento Numérico


No Python, além dos recursos matemáticos que fazem parte da distribuição padrão, o processamento numérico pode ser feito através do NumPy e outros pacotes que foram construídos a partir dele.

NumPy

NumPy é um pacote que inclui:

  • Classe array.
  • Classe matrix.
  • Várias funções auxiliares.

Arranjos

A classe array implementa um arranjo homogêneo mutável com número arbitrário de elementos, semelhante à lista comum do Python, porém mais poderosa.

Exemplos:

In [1]:
import numpy

# Criando arranjos

print 'Arranjo criado a partir de uma lista:'
a = numpy.array([0, 1, 2, 3, 4, 5, 6, 7, 8])

print a
# [0 1 2 3 4 5 6 7 8]

print 'Arranjo criado a partir de um intervalo:'
z = numpy.arange(0., 4.5, .5)

print z
# [ 0.   0.5  1.   1.5  2.   2.5  3.   3.5  4. ]

print 'Arranjo de 1s 2x3:'
y = numpy.ones((2, 3))

print y
# [[ 1.  1.  1.]
#  [ 1.  1.  1.]]

print 'Arranjos podem gerar novos arranjos:'
# numpy.round() é uma função do numpy
# semelhante ao builtin round(), porém aceita
# arranjos como parâmetro
cos = numpy.round(numpy.cos(z), 1)

print cos
# [ 1.   0.9  0.5  0.1 -0.4 -0.8 -1.  -0.9 -0.7]

print 'Multiplicando cada elemento por um escalar:'
print 5 * z
# [  0.    2.5   5.    7.5  10.   12.5  15.   17.5  20. ]

print 'Somando arranjos elemento por elemento:'
print z + cos
# [ 1.   1.4  1.5  1.6  1.6  1.7  2.   2.6  3.3]

print 'Redimensionando o arranjo:'
z.shape = 3, 3

print z
# [[ 0.   0.5  1. ]
#  [ 1.5  2.   2.5]
#  [ 3.   3.5  4. ]]

print 'Arranjo transposto:'
print z.transpose()
# [[ 0.   1.5  3. ]
#  [ 0.5  2.   3.5]
#  [ 1.   2.5  4. ]]

print '"Achata" o arranjo:'
print z.flatten()
# [ 0.   0.5  1.   1.5  2.   2.5  3.   3.5  4. ]

print 'O acesso aos elementos funciona como nas listas:'
print z[1]
# [ 1.5  2.   2.5]

print 'Caso especial, diferente da lista:'
print z[1, 1]
# 2.0

# Dados sobre o arranjo

print 'Formato do arranjo:'
print z.shape
# (3, 3)

print 'Quantidade de eixos:'
print z.ndim
# 2

print 'Tipo dos dados:'
print z.dtype
# float64
Arranjo criado a partir de uma lista:
[0 1 2 3 4 5 6 7 8]
Arranjo criado a partir de um intervalo:
[ 0.   0.5  1.   1.5  2.   2.5  3.   3.5  4. ]
Arranjo de 1s 2x3:
[[ 1.  1.  1.]
 [ 1.  1.  1.]]
Arranjos podem gerar novos arranjos:
[ 1.   0.9  0.5  0.1 -0.4 -0.8 -1.  -0.9 -0.7]
Multiplicando cada elemento por um escalar:
[  0.    2.5   5.    7.5  10.   12.5  15.   17.5  20. ]
Somando arranjos elemento por elemento:
[ 1.   1.4  1.5  1.6  1.6  1.7  2.   2.6  3.3]
Redimensionando o arranjo:
[[ 0.   0.5  1. ]
 [ 1.5  2.   2.5]
 [ 3.   3.5  4. ]]
Arranjo transposto:
[[ 0.   1.5  3. ]
 [ 0.5  2.   3.5]
 [ 1.   2.5  4. ]]
"Achata" o arranjo:
[ 0.   0.5  1.   1.5  2.   2.5  3.   3.5  4. ]
O acesso aos elementos funciona como nas listas:
[ 1.5  2.   2.5]
Caso especial, diferente da lista:
2.0
Formato do arranjo:
(3, 3)
Quantidade de eixos:
2
Tipo dos dados:
float64

Ao contrário da lista, os arranjos sempre são homogêneos, ou seja, todos elementos são do mesmo tipo.

Matrizes

A classe matrix implementa operações de matrizes.

Exemplos:

In [2]:
import numpy

print 'Criando uma matriz a partir de uma lista:'
l = [[3,4,5], [6, 7, 8], [9, 0, 1]]
Z = numpy.matrix(l)
print Z
# [[3 4 5]
#  [6 7 8]
#  [9 0 1]]

print 'Transposta da matriz:'
print Z.T
# [[3 6 9]
#  [4 7 0]
#  [5 8 1]]

print 'Inversa da matriz:'
print Z.I
# [[-0.23333333  0.13333333  0.1       ]
#  [-2.2         1.4        -0.2       ]
#  [ 2.1        -1.2         0.1       ]]

# Criando outra matriz
R = numpy.matrix([[3, 2, 1]])

print 'Multiplicando matrizes:'
print R * Z
# [[30 26 32]]

print 'Resolvendo um sistema linear:'
print numpy.linalg.solve(Z, numpy.array([0, 1, 2]))
# [ 0.33333333  1.         -1.        ]
Criando uma matriz a partir de uma lista:
[[3 4 5]
 [6 7 8]
 [9 0 1]]
Transposta da matriz:
[[3 6 9]
 [4 7 0]
 [5 8 1]]
Inversa da matriz:
[[-0.23333333  0.13333333  0.1       ]
 [-2.2         1.4        -0.2       ]
 [ 2.1        -1.2         0.1       ]]
Multiplicando matrizes:
[[30 26 32]]
Resolvendo um sistema linear:
[ 0.33333333  1.         -1.        ]

O módulo numpy.linalg também implementa funções de decomposição de matrizes:

In [5]:
from numpy import *

# Matriz 3x3
A = array([(9, 4, 2), (5, 3, 1), (2, 0, 7)])
print 'Matriz A:'
print A

# Decompondo usando QR
Q, R = linalg.qr(A)

# Resultados
print 'Matriz Q:'
print Q
print 'Matriz R:'
print R

# Produto
print 'Q . R:'
print int0(dot(Q, R))
Matriz A:
[[9 4 2]
 [5 3 1]
 [2 0 7]]
Matriz Q:
[[-0.85811633  0.14841033 -0.49153915]
 [-0.47673129 -0.58583024  0.65538554]
 [-0.19069252  0.79672913  0.57346234]]
Matriz R:
[[-10.48808848  -4.86265921  -3.52781158]
 [  0.          -1.16384941   5.28809431]
 [  0.           0.           3.68654364]]
Q . R:
[[9 4 2]
 [5 3 1]
 [2 0 7]]

O NumPy serve de base para diversos outros projetos de código aberto, como o Matplolib e o SciPy, que complementam o Numpy de várias formas.

SciPy

SciPy é um pacote que expande o NumPy com outras funcionalidades voltadas para a área cientifica.

Entre os módulos que fazem parte do pacote, temos:

  • linalg: funções de álgebra linear.
  • fftpack: transformada de Fourier.
  • integrate: funções de integração.
  • interpolate: funções de interpolação.
  • optimize: funções de optimização.
  • signal: processamento de sinais.
  • special: funções especiais (Airy, Bessel, etc).

Exemplo:

In [6]:
from numpy import arange, cos, sin

# Duas funções do SciPy para processamento de sinais
from scipy.signal import cspline1d, cspline1d_eval

# Duas funções do Matplotlib para gerar um gráfico
from pylab import plot, show

x0 = arange(20) # X original
y0 = cos(x0) * sin(x0 / 2) # Y a partir de X
dx = x0[1]-x0[0] # Diferença original
x1 = arange(-1, 21, 0.1)

# Coeficientes para arranjo de 1 dimensão
cj = cspline1d(y0)

# Avalia o Spline para um novo conjunto de pontos
y1 = cspline1d_eval(cj, x1, dx=dx,x0=x0[0])

plot(x1, y1, '-g', x0, y0, '^y') # Desenha
show() # Mostra o gráfico

Além do SciPy, existe também o ScientificPython, que é outro pacote que implementa rotinas para uso cientifico.

Matplotlib

Existem vários pacotes de terceiros para a geração de gráficos disponíveis para Python, sendo que o mais popular deles é o Pylab / Matplotlib.

O pacote tem dois módulos principais:

  • matplotlib: módulo que oferece uma abstração orientada a objetos aos recursos do pacote.
  • pylab: módulo que oferece uma coleção de comandos que se assemelha ao Matlab, e é mais adequado para o uso interativo.

Exemplo:

In [7]:
from pylab import *

ent = arange(0., 20.1, .1)

# Calcula os cossenos da entrada
sai = cos(ent)

# Plota a curva
plot(ent, sai)

# Texto para o eixo X
xlabel('entrada')

# Texto para o eixo Y
ylabel('cosseno')

# Texto no topo da figura
title('Cossenos')

# Ativa a grade
grid(True)

# Apresenta a figura resultante na tela
show()

Outro exemplo:

In [8]:
from pylab import *

# Dados
ent1 = arange(0., 7., .1)
sai1 = cos(ent1)
sai2 = sin(ent1)
dif = sai2 - sai1

# Divide a figura em 2 linhas e 1 coluna,
# e seleciona a parte superior
subplot(211)

# Plota a curva
# Primeira curva: ent1, sai1, 'bo:'
# Segunda curva: ent1, sai2, 'g^-'
plot(ent1, sai1, 'bo:', ent1, sai2, 'g^-')

# Cria uma legenda
legend(['Cossenos', 'Senos'])

# Seleciona a parte inferior
subplot(212)

# Desenha barras
# Eixo X: arange(len(dif)) + .5
# Eixo Y: dif
# Largura das barras: .5
# Cor: #ccbbaa
bar(arange(len(dif)) + .5, dif, .5, color='#ccbbaa')

# Salva a figura
savefig('graf.png')

O pacote tem funções para gerar gráficos de barra, linha, dispersão, pizza e polar, entre outros.

Exemplo usando matplotlib:

In [15]:
import os

import matplotlib
matplotlib.use('Agg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg

def pie(filename, labels, values):
    """
    Gera um diagrama de Pizza e salva em arquivo.
    """

    # Use a biblioteca Anti-Grain Geometry
    # matplotlib.use('Agg')

    # Cores personalizadas
    colors = ['seagreen', 'lightslategray', 'lavender',
        'khaki', 'burlywood', 'cornflowerblue']

    # Altera as opções padrão
    matplotlib.rc('patch', edgecolor='#406785',
        linewidth=1, antialiased=True)
    # Altera as dimensões da imagem
    matplotlib.rc('figure', figsize=(8., 7.))

    # Inicializa a figura
    fig = Figure()
    fig.clear()
    axes = fig.add_subplot(111)

    if values:
        # Diagrama
        chart = axes.pie(values, colors=colors, autopct='%2.0f%%')

        # Legenda
        pie_legend = axes.legend(labels)
        pie_legend.pad = 0.3

        # Altera o tamanho da fonte
        for i in xrange(len(chart[0])):
            chart[-1][i].set_fontsize(12)
            pie_legend.texts[0].set_fontsize(10)

    else:
        # Mensagem de erro
        # Desliga o diagrama
        axes.set_axis_off()
        # Mostra a mensagem
        axes.text(0.5, 0.5, 'Sem dados',
            horizontalalignment='center',
            verticalalignment='center',
            fontsize=32, color='#6f7c8c')

    # Salva a figura
    canvas = FigureCanvasAgg(fig)
    canvas.print_figure(filename, dpi=600)

# Testes
pie('fig1.png', [], [])
pie('fig2.png', ['A', 'B', 'C', 'D', 'E'],
    [6.7, 5.6, 4.5, 3.4, 2.3])

# Para exibir o gráfico no IPython Notebook
from IPython.display import Image
img = Image(filename='fig2.png')
img
Out[15]:

Existem add ons para o Matplotlib, que expandem a biblioteca com novas funcionalidades, como é o caso do Basemap.

Exemplo com Basemap:

In [2]:
from mpl_toolkits.basemap import Basemap
from matplotlib import pyplot
from numpy import arange

# Cria um mapa usando Basemap
mapa = Basemap(projection='robin', lat_0=-20, lon_0=-50,
    resolution='l', area_thresh=1e3)
    
# desenha a costa dos continentes
mapa.drawcoastlines(color='#777799')
# Desenha as fronteiras
mapa.drawcountries(color='#ccccee')
# Pinta os continentes
mapa.fillcontinents(color='#ddddcc')
# Desenha os meridianos
mapa.drawmeridians(arange(0, 360, 30), color='#ccccee')
# Desenha os paralelos
mapa.drawparallels(arange(-180, 180, 30), color='#ccccee')
# Desenha os limites do mapa
mapa.drawmapboundary()

# Salva a imagem
pyplot.savefig('mapa1.png', dpi=150)

Outro exemplo:

In [3]:
from mpl_toolkits.basemap import Basemap
from matplotlib import pyplot

mapa = Basemap(projection='ortho', lat_0=10, lon_0=-10,
    resolution='l', area_thresh=1e3)
# Preenche o mapa com relevo
mapa.bluemarble()
mapa.drawmapboundary()

lxy = (('Rio\nde\nJaneiro', -43.11, -22.54),
    ('Londres', 0.07, 50.30))

# Transposta
lxy = zip(*lxy)
# Converte as coordenadas
x, y = mapa(lxy[1], lxy[2])
lxy = lxy[0], x, y
# Marca no mapa
mapa.plot(x, y, 'w^')

# Escreve os nomes
for l, x, y in zip(*lxy):
    pyplot.text(x+2e5, y-6e5, l,
        color='#eeeecc')

pyplot.savefig('mapa2.png', dpi=150)

Para processamento de informações georreferenciadas de forma mais sofisticada, existe o projeto MapServer, um servidor de aplicação voltado para GIS (Geographic Information System) que suporta diversas linguagens, inclusive Python.

Além de módulos de terceiros, também é possível usar a planilha do BrOffice.org para gerar gráficos com o Python, através da API chamada Python-UNO Bridge.

In [1]:
 
Out[1]: