Ecossistema Python para Computação Numérica

Palestrante: Darlan Cavalcante Moreira

Objetivos

  • Aprender como usar a pilha de bibliotecas para computação científica em Python
  • Aprender algumas ferramentas eficientes como IPython, Jupyter Notebook, debuggers, etc
  • Aprender a criar plots em Python semelhantes a curva como abaixo

Qual "bateria" devo usar?

  • Python é uma linguagem de programação de uso geral
  • O ecossistema para pesquisa científica em Python pode ser visto como diversas camadas que incluem: A linguagem Python, a biblioteca Numpy, bibliotecas baseadas no Numpy, bibliotecas para plot, terminais mais avançados, IDEs e similares
  • Nesse curso vamos ver parte dessas ferramentas

Instalando Python

  • Python possui duas versões: Python2 e Python3
  • Podem baixar a partir do site oficial em https://www.python.org/downloads/
  • No windows pode ser complicado instalar bibliotecas e suas dependências
  • Um alternativa mais fácil é usar umas das distribuições Python disponíveis:
  • Nota: Podem me perguntar sobre o anaconda para mais detalhes depois

IPython

  • IPython é poderoso shell interativo para a linguagem Python, muito superior ao shell padrão
  • As entradas e saídas são numeradas e você pode recuperar seus valores
  • É possível acessar os valores das entradas com variáveis _i1, _i2, etc
  • Os valores das saídas podem ser acessadas por _1, _2, onde _, __ e ___ acessam a última, penúltima e antepenúltima saída
a = 10 a + 4 # Esqueci de salvar em uma variável 14 b = 16 c = b + _ # '_' possui o valor 14 referente a última saída c * 2 # c possui valor de 16 + 14 60

IPython

  • Um dos recursos mais úteis do IPython é completar um comando
  • Após digitar parte do comando aperte TAB para completar
  • Caso haja mais de uma opção uma lista de opções será mostrada
  • TAB completion do IPython
  • Também acessar o shell do sistema usando !comando (muito útil para alternar entre pastas, remover arquivos, etc)
!pwd # Em que pasta estou agora (apenas pwd também funcionaria) /home/darlan/Desktop arquivos = !ls # Lista com nome dos arquivos em Desktop

IPython

  • Obtendo Ajuda com "?"
    • Digitar algum_obj? mostra diversas informações sobre qualquer objeto, incluindo docstring, definições de funções, informações do construtor, etc
    • Digitar algum_obj?? mostrará o código fonte se possível
    • abs? Docstring: Return the absolute value of the argument. Type: builtin_function_or_method
  • Através da introspecção com ? e apertando TAB para completar comandos é possível realmente explorar bem funções, classes, etc.

IPython

  • Outro recurso extremamente útil do IPython são os comandos mágicos
  • Alguns dos comandos mágicos mais úteis são:
    • %whos: Lista variáveis e funções atuais (não mostra variáveis criadas pelo IPython como _3, _i14, etc)
    • a = [1,2,3] b = "Olá Mundo" whos Variable Type Data/Info ---------------------------- a list n=3 b str Hello whos list Variable Type Data/Info ---------------------------- a list n=3
    • %edit testando.py: Abre 'testando.py' e executa o código após sair
    • edit testando.py Editing... Waiting for Emacs...
    • %run testando.py: Executa testando.py como um script Também podemos passar opções como "-p" ou "-t"
    • cat testando.py # Vamos ver o conteúdo que salvei no arquivo a = 10 b = 20 print(a * b) run testando.py # TAB completa o nome do arquivo 200 whos Variable Type Data/Info ---------------------------- a int 10 b int 20 run -p testando.py Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 2 0.000 0.000 0.000 0.000 {built-in method io.open} 1 0.000 0.000 0.000 0.000 interactiveshell.py:2431(safe_execfile) 1 0.000 0.000 0.000 0.000 {built-in method builtins.compile} 2/1 0.000 0.000 0.000 0.000 {built-in method builtins.exec} 1 0.000 0.000 0.000 0.000 py3compat.py:182(execfile) 1 0.000 0.000 0.000 0.000 {built-in method builtins.print} 1 0.000 0.000 0.000 0.000 posixpath.py:318(normpath) 1 0.000 0.000 0.000 0.000 posixpath.py:145(dirname) 1 0.000 0.000 0.000 0.000 _bootlocale.py:23(getpreferredenco ... ... ...
    • %paste: Cola e executa um bloco de código da área de transferência
    • %timeit: Executa uma expressão e mostra tempo decorrido
    • %timeit 4 * 5 100000000 loops, best of 3: 12.3 ns per loop
    • %autocall: Dispensa os parêntesis em uma chamada de função
    • sum [1,2,3] -------> sum([1,2,3]) 6
    • %quickref: Cartão de referência do IPython Veja também %comando?

Jupyter Notebook

  • Surgiu inicialmente como IPython Notebook, inspirado pelo Mathematica
  • Cada notebook quando aberto está associado a um kernel
  • Pode ser usado online em https://try.jupyter.org
  • nbviewer: Site com vários notebooks interessantes, como por exemplo esse aqui (versão local)
  • Pode ser instalado pelo Anaconda
  • Rode o comando jupyter notebook para iniciar Tentem agora
  • Nota: Existem serviços que rodam na nuvem e permitem criar e executar notebooks como SageMathCloud e Wakario

Jupyter Notebook

Dashboard

  • Ao executar o notebook uma aba do navegador é aberta com o dashboard
  • Vemos os arquivos na pasta onde o comando jupyter notebook foi executado

Jupyter Notebook

Editor

  • No dashboard, cliquem em "New" e escolham o kernel indicado como Python3 para criar um novo notebook
  • Executar o tour pela interface em "Help User Interface Tour"
  • As células podem ser de dois tipos: markdown e code
  • Células do tipo "markdown" contém texto que é renderizado usando a notação markdown
  • Células do tipo "code" contém código que será executado, possivelmente resultando em alguma saída mostrada abaixo
  • Em alguns caso a saída será mostrada em um formato mais apropriado

Jupyter Notebook

Exercício

  • Crie uma célula markdown contendo texto com alguma formatação em markdown
  • Uma célula abaixo definindo duas variáveis com números
  • Crie uma célula abaixo somando as duas variáveis
  • Faça um merge das duas células anteriores
  • Crie uma célula vazia
  • Delete a célula vazia recém criada

Jupyter Notebook

Mais que um terminal

  • O editor do Jupyter possui algumas melhorias que não são possíveis em um terminal
    • obj? mostra informações do objeto em um pager
    • Shift+Enter em uma função mostra a docstring da função em um popup
    • Rich Display System: Estende o método nativo __repr__ do Python para outras representações e o notebook vai escolher a mais apropriada
      • Representações possíveis: HTML, JSON, PNG, JPEG, SVG, LaTeX
    • Etc.

Numpy Arrays

  • Numpy nos fornece um array multidimensional de alto desempenho e ferramentas para trabalhar com esses arrays
  • Um array armazena um conjunto de valores, todos do mesmo tipo, e possui um formato (shape)
  • O shape de um array é uma tupla de inteiros não negativos e seu número de elementos corresponde ao rank do array
1D numpy array
Shape: (4,)
Tipo: int
2D numpy array
Shape: (2,3)
Tipo: float
3D numpy array
Shape: (4,3,2)
Tipo: int

Criando Arrays

  • Existem diversas maneiras de criar um array
  • A mais direta é listando seus elementos
import numpy as np # Importa numpy como 'np' a = np.array([7, -2, 9, 10]) a array([ 7, -2, 9, 10]) a.shape (4,) a.ndim 1 b = np.array([[5.2, 2, -9, 10], [-10, 2, 9, 10]]) b array([[ 5.2, 2. , -9. , 10. ], [-10. , 2. , 9. , 10. ]]) b.shape (2,4)

Datatype

  • Os elementos de um array só podem ser do mesmo tipo
  • Um array é muito eficiente em armazenar e computar se o tipo for int, float, ou complex
  • Podemos verificar o tipo de um array através da propriedade "dtype"
a = np.array([1, 5, -7, 15, -22, 4]) a.dtype dtype('int64') b = np.array([[1, 5, -7], [15, -22, 4]]) b.dtype dtype('float64') c = np.array([2, 4, 2-4j]) dtype('complex128') d = np.array([-2,55.3, 22.1], dtype=complex) # Especifica o dtype d.dtype dtype('complex128')

Data Shape

  • Podemos verificar o formado de um array através da propriedade shape
  • Além da propriedade shape podemos usar size para determinar o número de elementos em um array
  • Use o método reshape para mudar o formato do array
a = np.array([[1, 5, -7, 12], [7, 15, -22, 4], [1, 2, 3, 4]]) a.shape (3, 4) a.size 12 a = a.reshape((2, 6)) # reshape(2, 6) também funciona a.shape (2, 6) a = a.reshape(2,-1,2) # Use -1 para determinar o valor automaticamente a.shape (2, 3, 2)

Criando Arrays

  • Há várias funções que retornam arrays no numpy
  • Mais comuns: zeros, ones, eye, empty
a = np.zeros((2,2)) # Cria um arra de zeros b = np.ones((2,3)) c = np.eye(2, dtype=complex) # Especificamos o dtype como complex print(a), print(b), print(c) [[ 0. 0.] [ 0. 0.]] [[ 1. 1. 1.] [ 1. 1. 1.]] [[ 1.+0.j 0.+0.j] [ 0.+0.j 1.+0.j]] d = np.empty((2,3), dtype=np.int16) # Inteiro de apenas 16 bits print(d) [[91 0 34] [ 7 24 44]]

Criando Arrays

  • Podemos também criar arrays com valores aleatórios, em um intervalo, etc
np.random.random((2,3)) # Números aleatórios entre 0 e 1 [[ 0.17926073 0.55768193 0.91629068] [ 0.42331764 0.81109184 0.94441903]] np.random.normal(loc=10, scale=3, size=(2,4)) # mean=10, var=3 array([[ 0.57156707, 2.24492393, 11.96929131, 5.72524926], [ 8.42069327, 6.6472876 , 11.46130987, 6.91887518]]) 10 + math.sqrt(3) * np.random.randn(2,4) # Mesmo que o comando anterior array([[ 8.6556823 , 11.76621921, 13.06145845, 10.29941938], [ 7.71243543, 6.75014684, 8.8052951 , 8.84438892]]) np.arange(3, 15, 2) # Varia de 3 a 15 (sem incluir o 15) com passo 2 array([ 3, 5, 7, 9, 11, 13]) np.linspace(0, 20, 9) # 9 valores, indo de 0 a 20 array([ 0. , 2.5, 5. , 7.5, 10. , 12.5, 15. , 17.5, 20. ])

Operações Básicas

  • Operadores aritméticos em arrays operam elemento-a-elemento
a = np.array([20,30,40,50]) b = np.arange( 4 ) b array([0, 1, 2, 3]) c = a-b c array([20, 29, 38, 47]) b**2 array([0, 1, 4, 9]) 10*np.sin(a) array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854]) a<35 array([ True, True, False, False], dtype=bool)

Operações Básicas

  • O operador * não é exceção e efetua uma multiplicação elemento-a-elemento
  • Para produto matricial use @ ou o método dot
A = np.array([[1,1], [0,1]]) B = np.array([[2,0], [3,4]]) A*B # Produto elemento-a-elemento array([[2, 0], [0, 4]]) A.dot(B) # Produto matricial array([[5, 4], [3, 4]]) A @ B # Produto matricial: Python 3.5 ou superior array([[5, 4], [3, 4]])

Operações Básicas

  • Algumas operações, como += e *=, modificam o array atual ao invés de criar um novo array Note que o datatype não pode mudar
a = np.ones((2,3), dtype=int) # 'a' é uma matriz de inteiros iguais a 1 b = np.random.random((2,3)) # 'b' é uma matriz de números de ponto flutuante a *= 3 a array([[3, 3, 3], [3, 3, 3]]) b += a # Sobrescreve 'b' com o valor de a+b b array([[ 3.417022 , 3.72032449, 3.00011437], [ 3.30233257, 3.14675589, 3.09233859]]) a += b # 'b' NÃO é automaticamente convertido para um ipo inteiro TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64')
with casting rule 'same_kind'

Principais Operações em Arrays

  • As diversas funções matemáticas são implementadas como np.nome_da_função e algumas estão também disponíveis como métodos do array
  • Experimente digitar "a." no IPython e apertar TAB para ver as sugestões e use "?" para ver o help
a = np.random.random((2,3)) a array([[ 0.18626021, 0.34556073, 0.39676747], [ 0.53881673, 0.41919451, 0.6852195 ]]) a.sum() 2.5718191614547998 a.min() 0.1862602113776709 a.max() 0.6852195003967595

Principais Operações em Arrays

  • NumPy possui as funções matemáticas usuais: sin, cos, exp, etc.
  • Elas são chamadas de "universal functions" (ufuncs)
  • Essas funções operam individualmente em cada elemento do array, produzindo um array como saída
B = np.arange(3) B array([0, 1, 2]) np.exp(B) array([ 1. , 2.71828183, 7.3890561 ]) np.sqrt(B) array([ 0. , 1. , 1.41421356]) C = np.array([2., -1., 4.]) np.add(B, C) # Mesmo que B + C array([ 2., 0., 6.])

Indexando Arrays

  • Arrays com uma dimensão podem ser indexados, fatiados, e iterados de forma semelhante a listas
a = np.arange(10)**3 a array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]) a[2] 8 a[2:5] array([ 8, 27, 64]) a[:6:2] = -1000 # Seta elementos nas posições 0, 2 e 4 a array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729]) a[ : :-1] # Inverte a ordem dos elementos de a array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])

Indexando Arrays

  • Arrays multidimensionais podem conter um índice por eixo
  • Esses índices são separados por vírgula
  • Podemos fornecer menos índices que eixos
  • Também podemos usar índices negativos
def f(x,y): return 10*x+y b = np.fromfunction(f, (5,4), dtype=int) b[2,3] 23 b[0:5, 1] array([ 1, 11, 21, 31, 41]) b[ : ,1] # Mesmo que exemplo anterior array([ 1, 11, 21, 31, 41]) print( b[1:3, : ] ) [[10, 11, 12, 13], [20, 21, 22, 23]] print(b[0]) # slices completos p/ os que faltam [0, 1, 2, 3] print(b[-1]) [40, 41, 42, 43] print( b[2:4,::2] ) # Combina dois slices [[20, 22], [30, 32]]

Indexando Arrays

  • Podemos usar também ... para representar tantos ":" quanto necessário para produzir uma indexação completa
  • Ex: Considere por exemplo um array x de rank 5
  • Temos então que:
    • x[1,2,...] equivale a x[1,2,:,:,:],
    • x[...,3] equivale a x[:,:,:,:,3]
    • x[4,...,5,:] equivale a x[4,:,:,5,:]
    • Etc.

Indexando Arrays

  • Ao indexar uma dada dimensão o array resultante não possui aquela dimensão, mas para slices a dimensão continua existindo
  • Compare os exemplos abaixo (especialmente o shape do resultado)

Indexando Arrays

Vetores Booleanos

  • Arrays booleanos podem ser usados para indexar outros arrays
  • Isso é especialmente útil para pegar elementos obedecendo certas condições
  • Exemplo:
b = np.random.randint(0,100, size=(4,5)) linhas = np.array([False, True, True, False]) b[linhas] array([[20, 5, 68, 48, 23], [83, 70, 12, 73, 31]]) b[b < 30] # Pega elementos que 30 array([20, 5, 23, 12, 0]) idx = (b >= 30); b[idx] # Elementos >= 30 array([39, 87, 96, ... ... , 94, 30, 87]) # Colunas com todos os elementos são >= 30 b[:, idx.all(axis=0)] # Indexa colunas array([[97], [48], [73], [30]])

Manipulando o shape

  • shape define o número de elementos em cada dimensão
  • Há várias maneiras de manipular o shape de um array
  • Ao invés de reshape podemos usar resize modifica o array
  • Dica: Use "-1" em uma dimensão e o numpy vai detectar o valor correto
a = np.floor(10*np.random.random((3,4))) a # Ver matriz ao lado a.shape # Apenas retorna o shape atual do array (3, 4) a.ravel() # "achata" o array (sem modificar) array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.]) a.shape = (6, 2) # Semelhante a "a = a.reshape(6, 2)" a.T # Não modifica o array array([[ 2., 0., 4., 1., 8., 3.], [ 8., 6., 5., 1., 9., 6.]]) a.resize(2,6) a array([[ 2., 0., 4., 1., 8., 3.], [ 8., 6., 5., 1., 9., 6.]])

Empilhando Arrays

  • Vários arrays podem ser empilhados ao longo de um determinado eixo
  • Algumas das funções para isso são hstack, vstack, concatenate e stack, np.concatenate
a = np.floor(10*np.random.random((2,2))); a array([[ 8., 8.], [ 0., 0.]]) b = np.floor(10*np.random.random((2,2))); b array([[ 1., 8.], [ 0., 4.]]) np.vstack((a,b)) # Necessário ter o mesmo shape (exceto na primeira dimensão) array([[ 8., 8.], [ 0., 0.], [ 1., 8.], [ 0., 4.]]) np.hstack((a,b)) # Necessário ter o mesmo shape (exceto na segunda dimensão) array([[ 8., 8., 1., 8.], [ 0., 0., 0., 4.]])

Representação de um Array na Memória

  • Um array é armazenado em uma região contínua de memória independentemente de sua dimensão
  • Se mudarmos o shape de um array normalmente não é feita uma cópia
  • A ordem em que os elementos são armazenados na memória pode tanto ser como "em C" (padrão) ou como "em Fortran"
  • Ordem do C: A última dimensão varia mais rapidamente
    Representação no Numpy
    Representação na Memória
  • Ordem do Fortran: A primeira dimensão varia mais rapidamente
    Representação no Numpy
    Representação na Memória

Cópias e Views

  • Quando operando e manipulando arrays, as vezes os dados são copiados em um novo array e as vezes não
  • Casos sem cópia:
    • Assinalamento (Python também funciona assim para tipos mutáveis)
    • Mudança de shape
    • Argumentos de funções
    a = np.arange(12) b = a # Nenhum objeto novo é criado com um assinalamento b is a # "a" e "b" são dois nomes para o mesmo array True b.shape = 3,4 # Muda apenas o shape do array sem mudar os dados na memória a.shape (3, 4) def f(x): ... print(id(x)) id(a) # id é um identificador único de um objeto 148293216 f(a) 148293216

Cópias e Views

  • Diferentes arrays podem compartilhar os mesmos dados na memória
  • O método view de um array cria um novo objeto array que utiliza os mesmos dados
  • Mudanças feitas nos dados de um são refletidas no outro array, mas os dois não são o mesmo objeto como ocorre em Python com um assinalamento
  • Os dois objetos podem ter formas diferentes
c = a.view() c is a False c.base is a # c é uma "view" dos dados de 'a' True c.flags.owndata False c.shape = 2,6 # o shape de 'a' não muda a.shape (3, 4) c[0,4] = 1234 # o shape de 'a' muda a array([[ 0, 1, 2, 3], [1234, 5, 6, 7], [ 8, 9, 10, 11]])

Cópias e Views

  • Fatiar um array retorna um view do array
  • Para criar um cópia independente de um array use o método copy
s = a[ : , 1:3] s[:] = 10 # s[:] é um view de s. Note a diferença entre s=10 e s[:]=10 a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]]) d = a.copy() # um novo objeto array com dados novos é criado d is a False d.base is a # "d" não compartilha nada com "a" False d[0,0] = 9999 a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]])

Operações matemáticas

  • Já vimos alguns métodos que operam elemento-a-elemento, onde o resultado possui o mesmo rank que a entrada
  • Alguns métodos reduzem a dimensão obtendo um resultado de rank menor
  • Exemplo: mean, var, sum, etc.
  • É possível indicar sobre qual dimensão efetuar a operação
a = np.random.randint(3, 4) a Ver matriz ao lado array([[31, 11, 15, 69], [ 8, 75, 68, 54], [31, 47, 86, 23]]) a.sum() 518 a.sum(axis=0) array([ 70, 133, 169, 146]) a.sum(axis=1) array([126, 205, 187])

Broadcast

  • Operações no NumPy geralmente são feitas em pares de arrays elemento-a-elemento
  • Broadcast corresponde a maneira como o numpy trata arrays com formas diferentes durante operações aritméticas
  • O exemplo mais simples de broadcast ocorre quando um array e um escalar são combinados
  • o escalar é esticado durante a operação aritmética para ter a mesma forma que o array
a = np.array([1.0, 2.0, 3.0]) b = np.array([2.0, 2.0, 2.0]) a * b # 'a' e 'b' possuem mesmo shape e podemos somar elemento-a-elemento array([ 2., 4., 6.]) a = np.array([1.0, 2.0, 3.0]) b = 2.0 a * b array([ 2., 4., 6.])

Broadcast

  • Além da praticidade o broadcast fornece outras vantagens:
    • É um meio de vetorizar operações com arrays tal que os loops ocorram em C, ao invés de Python
    • Sem cópias desnecessárias dos dados implementações eficientes de algoritmos
  • Exemplo: somar uma matriz (3,3) com um vetor (3,)?
    • Adicionamos uma dimensão extra no vetor novo shape de (1, 3)
    • Replicamos o "vetor" para ter o mesmo shape que a matriz (3,3)

Broadcast

  • Para o broadcast ser possível duas regras precisam ser atendidas:
    • Regra 1: Se os arrays de entrada possuem ranks diferentes adicione uma dimensão no início do array menor repetidamente até que os dois tenham o mesmo rank
    • Regra 2: Arrays de tamanho 1 em um dada dimensão atuam como se eles tivessem o mesmo tamanho que o array de maior tamanho nessa dimensão
  • Após a aplicação das duas regras anteriores as dimensões dos dois arrays devem bater

Broadcast

Exemplos

  • Muito mais que operações entre um array e um simples escalar
  • Nem sempre as dimensões coincidem após aplicar as duas regras
  • Se for o caso você pode manualmente adicionar um eixo novo para não depender da regra 1
a = np.random.randn(2,3,4) b = np.random.randn(3,4) (a * b).shape # OK: adiciona eixo 1 em b e replica para 2 (2, 3, 4) # Gera um array de números aleatórios c = np.random.randn(2, 4) a + c # Erro ValueError: operands could not be broadcast together with shapes (2,3,4) (2,4) (a+c[:, np.newaxis, :]).shape # Adicionamos um eixo extra -> novo shape: (2, 1, 4) (2, 3, 4) d = np.random.randn(2,3) (a - d[...,np.newaxis]).shape # Novamente adicionamos um eixo extra -> novo shape: (2,3,1) (2,3,4)

Broadcast

Exercício

  • Dado o vetor de números complexos abaixo representando posições de pontos em um grid 2D, calcule as distâncias de cada ponto para cada outro ponto no grid.
  • pontos = np.array([-2+1j, 3+2.5j, 2-2j, 0, 1.5+2.7j])
  • Solução:
np.abs(pontos - pontos[:, np.newaxis]) # Só linha e sem loops! array([[ 0. , 5.22015325, 5. , 2.23606798, 3.89101529], [ 5.22015325, 0. , 4.60977223, 3.90512484, 1.5132746 ], [ 5. , 4.60977223, 0. , 2.82842712, 4.72652092], [ 2.23606798, 3.90512484, 2.82842712, 0. , 3.08868904], [ 3.89101529, 1.5132746 , 4.72652092, 3.08868904, 0. ]])

Álgebra Linear

  • Numpy fornece diversos métodos para manipulação de matrizes de maneira eficiente
  • Por baixo dos panos bibliotecas eficientes como BLAS e LAPACK são utilizadas, tornando operações de álgebra linear eficiente
  • Algumas das operações mais comuns são:
  • A @ B # Multiplica duas matrizes A e B A.T # Transposta de A np.linalg.inv(A) # Inversa de A U, V = np.linalg.eig(A) # Calcula autovalores e autovetores de A np.linalg.multi_dot([A, B, C, D]) # Calcula "A @ B @ C @ D" (mais eficiente)

Plotando com Matplotlib

  • A biblioteca mais conhecida para plot no Python é a Matplotlib
  • A função mais importante é a função plt.plot, que recebe arrays com os valores para plotar
  • Chame a função plt.show no final para mostrar o plot
  • Com um pouco esforço a mais podemos facilmente plotar múltiplas linhas de uma só vez, adicionar um título, legenda e labels para os eixos
  • Dica: coloque %matplotlib inline ou %matplotlib notebook na primeira célula do Jupyter notebook
import numpy as np
import matplotlib.pyplot as plt

# Computa as coordenadas 'x' e 'y' para ambas as curvas
x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)

# Plota os pontos usando matplotlib
plt.plot(x, y_sin)
plt.plot(x, y_cos)
plt.xlabel('x axis label')
plt.ylabel('y axis label')
plt.title('Sine and Cosine')
plt.legend(['Sine', 'Cosine'])
plt.show()  # Necessário para mostrar o gráfico
Plot de uma curva seno Plot de uma curva seno

Plotando com Matplotlib

Exercício

  • Repita o plot anterior no notebook, mas agora incluindo círculos nos pontos
  • Dica: rode o comando plt.plot? para ver a ajuda do comando

Plotando com Matplotlib

  • Podemos plotar curvas diferentes na mesma figura usando a função subplot
  • Na documentação há muito mais que se pode fazer com subplots
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)

# Prepara um grid 2x1 para subplots e ativa o primeiro subplot
plt.subplot(2, 1, 1)

# Cria o primeiro plot
plt.plot(x, y_sin)
plt.title('Sine')

# Ativa o segundo plot do grid e cria o segundo plot
plt.subplot(2, 1, 2)
plt.plot(x, y_cos)
plt.title('Cosine')

plt.show()

Mostrando imagens com Matplotlib

  • Matplotlib também possui a função imshow que pode ser usada para mostrar uma imagem
  • Aqui usamos a função imread do scipy para ler a imagem
import numpy as np
from scipy.misc import imread, imresize
import matplotlib.pyplot as plt

img = imread('assets/cat.jpg')
img_tinted = img * [1, 0.95, 0.9]

# Mostra a imagem original
plt.subplot(1, 2, 1)
plt.imshow(img)

# Mostra a imagem modificada
plt.subplot(1, 2, 2)

# Nota: imshow resulta em resultados estranhos se os dados
# não são uint8. Para contornar esse problemas fazemos um
# cast da imagem para uint8 antes de mostrá-la
plt.imshow(np.uint8(img_tinted))
plt.show()

Exercícios de Fixação

  1. Crie uma matriz A com dimensão 3 × 4 de números inteiros menores que 10
  2. Calcule a matriz transposta de A
  3. Calcule a soma de cada linha da matriz A usando a função sum
  4. Calcule a soma de cada coluna da matriz A usando a função sum
  5. Calcule a pseudo-inversa \(A^\dagger = A^T(AA^T)^{-1}\)
  6. Defina B = AA T e calcule os autovalores e os autovetores de B usando a função eig
  7. Calcule a inversa de B usando a função inv