Lista de Alunos | Exercício 1 | Exercício 3

Exercício 2 - Introdução ao Python, Numpy e Graphviz

Autor:Wendell "thejesus"
Data:05/03/2012
  • Quais são os tipos de alto nível que Python possui?
  • Procure fazer uma pequena demonstração de cada um dos tipos do Python.
  • Qual é a diferença entre lista e tupla? Exemplifique.
  • Qual é a importância da identação na linguagem?
  • Qual os tipos adicionais que o NumPy oferece? Procure exercitar várias operações com matrizes.

Python

O Python é uma linguagem interpretada de uso geral que utiliza tipagem dinâmica. Além dos tipos numericos, tais como inteiros(int), ponto flutuante(float), booleano(bool) e complexo(complex), o Python possui os seguintes tipos de alto nível:

  • Lista

    Listas são sequências lineares de valores. Comportam-se de forma parecida com os vetores de outras linguagens, mas com a diferença de permitirem valores mistos, ou seja, é possível armazenar valores de tipos diferentes em uma única lista. As listas são iniciadas em zero e são indexadas sequencialmente. Uma lista pode ser inicializada passando-se os valores entre colchetes ([...]), separados por vírgulas.

    Exemplo:

    Criação de uma lista e atribuição de um novo valor.

1 lista = [1, 2, 3]
2 print lista
3 lista[1] = 5
4 print lista
[1, 2, 3]
[1, 5, 3]
  • Tuplas

    As tuplas são parecidas com as listas, entretanto, são imutáveis, ou seja, uma vez criadas, não podem mais ser alteradas. Uma tupla é criada com parênteses no lugar dos colchetes.

    Exemplo:

    Criação de uma tupla. A tentativa de inserir um novo valor deve gerar um erro em tempo de execução.

1 tupla = (1, 2, 3)
2 print tupla
3 tupla[1] = 5
ERROR execute
(1, 2, 3)
------------------------------------------------------------
*** Exception while evaluating code:
  File "<string>", line 3, in <module>
TypeError: 'tuple' object does not support item assignment

------------------------------------------------------------

  • String

    A cadeia de caracteres em Python, também é uma sequência imutável. Ou seja, não é possível alterar seus elementos após a criação.

    Exemplo:

    Criação de uma string. A tentativa de mudar um valor gera erro.

1 str = "Oi, eu sou uma string."
2 print str
3 print str[1]
4 str[1] = 'e'
ERROR execute
Oi, eu sou uma string.
i
------------------------------------------------------------
*** Exception while evaluating code:
  File "<string>", line 4, in <module>
TypeError: 'str' object does not support item assignment

------------------------------------------------------------

  • Dicionário

    O dicionário é uma coleção de valores indexados por qualquer tipo imutável. Difere das listas, que são indexadas por números inteiros. Um dicionário é um conjunto de pares (chave/valor). Para declarar um dicionário, usa-se chaves ({...}). A chave e o valor são separados por dois pontos (:) e cada para chave:valor é separado por vírgulas.

    Exemplo:

    Criação de um dicionário e alteração de seus valores.

1 dic = {"cafe":"pao", "almoco":"salada", "jantar":"sopa", "sobremesa":"sorvete"}
2 print dic
3 print dic["cafe"]
4 dic["almoco"] = "macarrao"
5 print dic
{'sobremesa': 'sorvete', 'cafe': 'pao', 'almoco': 'salada', 'jantar': 'sopa'}
pao
{'sobremesa': 'sorvete', 'cafe': 'pao', 'almoco': 'macarrao', 'jantar': 'sopa'}

Conforme demonstrado, a diferença entre listas e tuplas é que as primeiras podem ter seus valores alterados, enquanto as últimas são imutáveis.

No Python, a indentação do código é importante porque é ela que delimita os blocos de código, por exemplo, em laços e estruturas condicionais.

Exemplo:
Primeiro, uma variável seleciona a permissão de imprimir uma string.
1 imprima = True
2 if imprima:
3     print "Eu posso imprimir"
4 imprima = False
5 if imprima:
6     print "Eu posso imprimir"
Eu posso imprimir

Agora, a falha na indentação gera um erro.

1 imprima = True
2 if imprima:
3 print "Eu posso imprimir"
4 imprima = False
5 if imprima:
6 print "Eu posso imprimir"
ERROR execute

------------------------------------------------------------
*** Exception while evaluating code:
  File "<string>", line 3
    print "Eu posso imprimir"
        ^
IndentationError: expected an indented block

------------------------------------------------------------

Numpy

O Numpy é um módulo do Python que permite operações matriciais. Tem uma sintaxe bastante parecida com a do Matlab. É bastante eficiente, focando na velocidade de execução do código.

O Numpy define alguns tipos de escalares, extendendo o suporte nativo do Python:

Data type Description
bool Boolean (True or False) stored as a byte
int Platform integer (normally either int32 or int64)
int8 Byte (-128 to 127)
int16 Integer (-32768 to 32767)
int32 Integer (-2147483648 to 2147483647)
int64 Integer (9223372036854775808 to 9223372036854775807)
uint8 Unsigned integer (0 to 255)
uint16 Unsigned integer (0 to 65535)
uint32 Unsigned integer (0 to 4294967295)
uint64 Unsigned integer (0 to 18446744073709551615)
float Shorthand for float64.
float16 Half precision float: sign bit, 5 bits exponent, 10 bits mantissa
float32 Single precision float: sign bit, 8 bits exponent, 23 bits mantissa
float64 Double precision float: sign bit, 11 bits exponent, 52 bits mantissa
complex Shorthand for complex128.
complex64 Complex number, represented by two 32-bit floats (real and imaginary components)
complex128 Complex number, represented by two 64-bit floats (real and imaginary components)

Maiores informações sobre os tipos podem ser encontrados na Documentação do Numpy.

Com o Numpy, operações matriciais são muito facilitadas. Isso se deve ao principal tipo do Numpy, a matriz homogênea n-dimensional, representada internamente pelo tipo ndarray.

Uma matriz pode ser declarada de várias formas:

  • Através da função array, passando como parâmetro uma sequência do python. O tipo pode ou não ser explicitamente especificado.
 1 from numpy import *
 2 
 3 # Inicializacao com tipo nao especificado
 4 matriz = array( [[1,2,3,4], [5,6,7,8]])
 5 print matriz
 6 print matriz.dtype
 7 
 8 # Inicializacao com tipo especificado
 9 outra_matriz = array([[1,2,3,4], [5,6,7,8]], dtype=complex)
10 print outra_matriz
11 print outra_matriz.dtype
[[1 2 3 4]
 [5 6 7 8]]
int64
[[ 1.+0.j  2.+0.j  3.+0.j  4.+0.j]
 [ 5.+0.j  6.+0.j  7.+0.j  8.+0.j]]
complex128

Tal como o Matlab, é possível inicializar matrizes que têm tamanho conhecido, mas elementos desconhecidos. Esta operação pode ser feita de trẽs formas, com zeros, uns ou usando empty, que cria uma matriz preenchida aleatoreamente, de acordo com o estado da memória.

 1 from numpy import *
 2 
 3 z = zeros((2,4))
 4 print z
 5 print
 6 o = ones((3,4))
 7 print o
 8 print
 9 e = empty((2,2))
10 print e
11 print
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]]

[[ 1.  1.  1.  1.]
 [ 1.  1.  1.  1.]
 [ 1.  1.  1.  1.]]

[[  6.91687947e-310   3.47321114e-316]
 [  1.13512333e-316   1.13512333e-316]]

Uma operação também bastante útil que é suportada pelo Numpy, é o fatiamento (slicing) de matrizes. O exemplo abaixo mostra essa operação:

1 # A função random é útil para a criação de matrizes aleatóreas
2 m1 = arange(10)**2
3 print m1
4 print
5 print m1[1:5]
6 print
7 print m1[3:7]
8 print
[ 0  1  4  9 16 25 36 49 64 81]

[ 1  4  9 16]

[ 9 16 25 36]

Também é possível iterar em uma matriz com o operador de slicing:

1 m1 = arange(10)**3
2 print m1
3 # Insere o valor 999 nos elementos de índice par
4 m1[:-1:2] = 999
5 print
6 print m1
[  0   1   8  27  64 125 216 343 512 729]

[999   1 999  27 999 125 999 343 999 729]

Como não podia faltar, operações matriciais como multiplicação por escalar, multiplicação entre matrizes, transposição, são diretas ou obtidas através de métodos próprios:

 1 m1 = floor(10*random.random((3,4)))
 2 print m1
 3 print
 4 # Multiplicação por escalar
 5 m1 = m1 * 2
 6 print m1
 7 print
 8 m2 = floor(10*random.random((4,3)))
 9 print m2
10 print
11 m3 = dot(m1,m2)
12 print m3
13 print
14 # Transposição
15 m3 = m3.transpose()
16 print m3
17 print
[[ 4.  7.  7.  1.]
 [ 5.  3.  6.  3.]
 [ 6.  3.  0.  5.]]

[[  8.  14.  14.   2.]
 [ 10.   6.  12.   6.]
 [ 12.   6.   0.  10.]]

[[ 3.  9.  2.]
 [ 5.  2.  1.]
 [ 6.  2.  5.]
 [ 3.  0.  9.]]

[[ 184.  128.  118.]
 [ 150.  126.  140.]
 [  96.  120.  120.]]

[[ 184.  150.   96.]
 [ 128.  126.  120.]
 [ 118.  140.  120.]]

O pacote Numpy possui inúmeras outras facilidades. Uma lista detalhada de exemplos pode ser encontrada nesta página.

Graphviz

O Graphviz é uma ferramenta de visualização de grafos. Através de uma linguagem descritiva própria, o usuário pode definir um grafo e atribuir formatos variados a estes. O pacote gera então uma figura que representa o grafo, em uma variedade de formatos comuns, como o PNG ou SVG. O grafo pode então ser incluído em um documento de texto em PDF ou LaTeX, por exemplo.

Abaixo, um exemplo de grafo criado com a linguagem do graphviz e exibido diretamente pelo adessowiki:

1. digraph MeuGrafo {
2.     a -> b
3.     a -> c
4.     b -> c
5.     c -> b
6.     d -> a
7.     d -> b
8.     d -> d
9. }
/media/_xsb/courseIA368Q1S2012/wen_2/GRVIZ75609_001.png

É possível formatar o grafo, utilizando diretivas de formatação. Veja como o exemplo anterior fica depois de formatado:

01. digraph MeuGrafo {
02.     label = "Grafo de Exemplo"
03.     node [style=filled, color=blue]
04. 
05.     a [style=filled, color=green]
06. 
07.     a -> b
08.     a -> c [style=dotted, color=red]
09.     b -> c
10.     c -> b
11.     d -> a [arrowhead=crow]
12.     d -> b
13.     d -> d
14. }
/media/_xsb/courseIA368Q1S2012/wen_2/GRVIZ75609_002.png

Os exemplos anteriores foram criados estaticamente, para serem exibidos pelo Adessowiki. Porém, é possível exibir grafos montados programaticamente, através de módulos no Python.

Existem duas maneiras de realizar esta operação. A primeira, é passando para a função mmgraphviz uma string com o código do grafo:

 1 grafo = """
 2     digraph MeuGrafo {
 3     label = "Grafo de Exemplo"
 4     node [style=filled, color=blue]
 5 
 6     a [style=filled, color=green]
 7 
 8     a -> b
 9     a -> c [style=dotted, color=red]
10     b -> c
11     c -> b
12     d -> a [arrowhead=crow]
13     d -> b
14     d -> d
15 }
16 """
17 mmgraphviz(grafo)
/media/_xsb/courseIA368Q1S2012/wen_2/GRVIZ75609_003.png

Outra maneira, mais flexível, é utilizando o módulo gvgen, que provê uma classe para a descrição do grafo:

 1 import StringIO
 2 
 3 # Declaração da instância do grafo
 4 meuGrafo = gvgen.GvGen()
 5 
 6 # Definição de um estilo padrão para todos os nós
 7 meuGrafo.styleDefaultAppend("shape", "house")
 8 meuGrafo.styleDefaultAppend("color", "blue")
 9 
10 # Declaração dos nós
11 nodeA = meuGrafo.newItem("A")
12 nodeB = meuGrafo.newItem("B")
13 nodeC = meuGrafo.newItem("C")
14 nodeD = meuGrafo.newItem("D")
15 
16 # Definição de um estilo próprio para o nó A
17 meuGrafo.styleAppend("estiloA", "style", "filled")
18 meuGrafo.styleAppend("estiloA", "shape", "box")
19 meuGrafo.styleApply("estiloA", nodeA)
20 
21 #Declaração das arestas
22 meuGrafo.newLink(nodeA, nodeB)
23 meuGrafo.newLink(nodeA, nodeC)
24 meuGrafo.newLink(nodeB, nodeC)
25 meuGrafo.newLink(nodeC, nodeB)
26 da = meuGrafo.newLink(nodeD, nodeA)
27 meuGrafo.newLink(nodeD, nodeB)
28 meuGrafo.newLink(nodeD, nodeC)
29 
30 #Definição de um estilo para a aresta d-a
31 meuGrafo.styleAppend("estiloDA", "style", "dotted")
32 meuGrafo.styleAppend("estiloDA", "color", "red")
33 meuGrafo.styleAppend("estiloDA", "arrowhead", "halfopen")
34 meuGrafo.styleApply("estiloDA", da)
35 
36 str = StringIO.StringIO()
37 meuGrafo.dot(str)
38 texto = str.getvalue()
39 
40 mmgraphviz(texto)
/media/_xsb/courseIA368Q1S2012/wen_2/GRVIZ75609_004.png

A página do Graphviz oferece uma documentação bem completa sobre os comandos e formatos possíveis.