# NumPy para leigos (contado por um leigo) ## proto-sumário - Introdução - Quem é você e por qual motivo está escrevendo isto aqui? - O que é NumPy, pra quê serve? - Outros recursos para estudar - Algumas ideias iniciais - np.array - Mostre-me exemplos de coisas que você fez com numpy - Manipulando imagens - Aplicando máscaras com elementos translúcidos (alpha channel) - Trabalhando com muitos números do ruído de Perlin rapidamente - Calculando muitos pontos de curvas Bezier de uma vez - Simulando partículas - Agora, mostre-me coisas realmente impressionantes - Napari - Networx ? - ... ## Introdução ### Quem é você, Alexandre, e por qual motivo está escrevendo isto aqui? Olá, eu sou Alexandre, um artista-educador que usa programação como meio de expressão e que pesquisa o ensino de programação para públicos variados. Acredito muito que qualquer pessoa com interesse, motivação e recursos adequados, pode fazer uso da programação e mesmo aprendender a programar, sem necessariamente querer se tornar uma pessoa desenvolvedora profissional de software. Dou aulas introdutórias de Python e tenho conhecimentos bastante limitados do NumPy. NumPy é uma biblioteca muito importante do ecossistema do Python, bibliotecas são pacotinhos de código que usamos quando estamos programando para resolver problemas e fazer coisas que outras pessoas já resolveram ou fizeram antes, para não reinventarmos a roda e podermos enxergar mais longe ao subir nos ombros de gigantes. NumPy é usado em tudo quanto é canto, por outras bibliotecas importantes como pandas e SciPy, por exemplo. Me considero um leigo em NumPy e este material vai servir para documentar um pouco os meus avanços recentes e futuros na compreensão e uso do Numpy, e, se der certo, pode servir como um primeiro contato menos assustador para outras pessoas leigas que queiram saber mais dessa ferramenta tão poderosa, elegante e complexa. NumPy tem uma documentação incrível em Português (https://numpy.org/pt/), e vou tentar fazer bom proveito dela, assim como vou cutucar pessoas amigas que entendem de NumPy de verdade e que, espero, vão me impedir de escrever asneiras muito grandes (as asneiras menores são provavelmente inevitáveis). Se você ainda não sabe programar, provavelmente este material não vai fazer muito sentido e a minha sugestão então é que você procure antes material introdutório sobre Python. - [Introdução à programação com Python em um contexto visual](https://abav.lugaralgum.com/material-aulas) (outro material meu) - [Pense em Python - segunda edição](https://penseallen.github.io/PensePython2e/) (o famoso livro do Allen Downey) - [Python para Zumbis](https://www.youtube.com/channel/UCripRddD4BnaMcU833ExuwA/playlists) (o curso com vídeos do Fernando Masanori) - [Automate the Boring Stuff with Python](https://automatetheboringstuff.com) (site/livro em inglẽs do Al Sweigart) - [Whirlwind Tour of Python](https://jakevdp.github.io/WhirlwindTourOfPython/) (Tour furacão do Jake VanderPlas para quem já programa em outra linguagem) #### Apoie a criação de material didático aberto. Se você acha que eu devia continuar a escrever material didático aberto, agradeço se puder fazer uma pequena [doação de qualquer valor](https://www.paypal.com/donate/?hosted_button_id=5B4MZ78C9J724). ### O que é o NumPy, pra quê serve? A biblioteca NumPy é uma biblioteca de código aberto, com uma licença de software livre, destinada a realizar operações em arrays multidimensionais. Isso quer dizer uma porção de coisas, começando pela questão do software livre, que permite que a biblioteca seja desenvolvida e mantida por uma comunidade de pessoas e utilizada por um número imenso de pessoas em inúmeros projetos, ela serve de base para boa parte da computação científica no mundo, projetos como SciPy e scikit-learn além outras aplicações das quais vamos falar mais pra frente. #### Mas o que são arrays multidimensionais? Arrays são estruturas de dados usadas na computação que simplificadamente podem ser descritas como sequencias homogêneas de valores. Homogêneas pois os valores, ou elementos, são de um mesmo tipo, e na maior parte dos casos estamos falando de valores numéricos, como números inteiros ou alguma forma de representação de números reais. Podemos usar diversos "tamanhos" na memória do computador, maior ou menor número de bits, para representar com diferentes precisões esses números, com os chamados tipos int e float (int de integer, inteiros, e float de floating point, números de ponto flutuante). Esses valores podem estar dispostos em uma única fila (uma dimensão); filas e colunas (duas dimensões); ou camadas, filas e colunas (três dimensões); ou ainda mais quantas n-dimensões quisermos e a forma de falar sobre a disposição fica mais confusa (para mim) com muitas dimensões ou "eixos", mas existem convensões para indicar com índices a posição de um elemento ao longo de cada eixo. E também, com uma notação análoga a de fatimento de listas no Python, é possível acessar filas, colunas, camadas ou outras "fatias" de um array. Esses arrays servem para representar vetores (usando arrays de uma dimensão), aqueles objetos matemáticos que tem uma magnitude e direção. Você provavelmente viu vetores usados para falar de forças, velocidades e acelerações na aula de física, representados gráficamente com setinhas. Podemos pensar neles como sequências de valores em um certo número de eixos, os valores são os chamados componentes. Vetores 2D com dois componentes, vetores 3D com 3 componentes, vetores 4D com 4 componentes e assim por diante. Já os arrays multidimensionais servem principalmente para representar os objetos matemáticos conhecidos como matrizes, como por exemplo uma matriz 4x4, com quatro filas e quatro colunas, e portando duas dimensões. Mas as matrizes podem ter três, quatro ou um número arbitrário de dimensões e "formatos" (*shapes*) variados. Também podemos pensar em matrizes como coleções de vetores! Matrizes e vetores servem para muita, muita coisa e o NumPy sabe fazer todas a as operações matemáticas importantes com vetores e com matrizes. Brilha em fazer operações de vetores com vetores, vetores com matrizes, matrizes com matrizes, além das operações de vetores ou matrizes com valores escalares. Escalares são os números comuns sozinhos. #### Onde você já encontrou vetores, matrizes, arrays n-dimensionais e o NumPy? Já vi matrizes como representação de sistemas de equações com n-variáveis. Cada linha da matriz é uma equação e as coluna são os coeficientes que multipicam cada termo da equação mais uma coluna com as constantes, isso meio que é a base da álgebra linear. Tem também a ideia de que matrizes podem ser a representação de transformações lineares em um sistema de coordenadas, você pode descrever translação, rotação, escala e cisalhamento com uma matriz. E isso serve de base para um monte de coisas na computação gráfica. Sei que o Aprendizado de Máquina (Machine Learning) usa massivamente cálculos numéricos em um grande número de matrizes, mas não é algo com o que eu lide no meu dia-a-dia de programação, pode ser que eu venha explorar isso em algum momento. Já algo com a qual eu tenho mais contato são arrays ou matrizes como representação de imagens, onde cada pixel é representado como uma sequência de valores R, G, B ou R, G, B, A (A de alpha, o canal da transparência) em uma grade de duas dimensões, logo uma matriz de três dimensões. Uma imagem de 600x600 pixels pode ser representada como um array de fomato (600, 600, 3). E o NumPy integra muito bem com a bibliteca Pillow (o PIL atual) e o py5 que eu uso para os meus desenhos. A outra grande vantagem que eu vejo no NumPy é que ele permite estratégias de "vetorização" que são, simplificadamente, realizar "de uma vez" várias operações que você incialmente faria dentro de um laço de repetição, ou, pior, dentro de laços de repetição aninhados, de maneira menos eficiente. Então, espero poder mostrar exemplos disso aqui em algum momento. Esse "de uma vez", pode não envolver nenhum processamento paralelo, na verdade, mas ser mais eficiente mesmo assim. Por baixo do capô, pode parecer um pouco assustador e complexo, mas jutamente a ideia é que não tenho que me precocupar muito! Tem gente mais esperta do que eu resolvendo pra mim essas operações usando C ou Fortran que calculam tudo muito rápido e não tenho que me preocupar com gerenciamento de memória e a posição dos bytes nos arrays nem nada disso. Ainda sobre paraleismo, se você tiver vários núcleos na sua CPU (o processador principal do seu computador) e usar uma biblioteca chamada Numba, em conjunto com o Numpy, pode aproveitar melhor os vários núcleos permitindo processamento paralelo. Se você usar a biblioteca CuPy (que tem interface semelhante ao NumPy), pode em alguns casos usar ao mesmo tempo os montes de núcleos da sua GPU (o processador da sua placa de vídeo) para acelarar as operações. ### Outros recursos para estudar - A documentação oficial: https://numpy.org/pt/ - [From Python to NumPy](https://www.labri.fr/perso/nrougier/from-python-to-numpy/) do Rougier, indicação da Melissa. - **TODO** traduzir o tutorial do Jim: https://ixora.io/blog/vectorization-and-numpy/ ## Algumas ideias iniciais ### np.array Para começar, dá pra criar um Numpy Array assim: ```python! >>>import numpy as np >>> a = np.array([0, 1, 2, 3, 4, 5]) # partindo de uma lista comum >>> a array([0, 1, 2, 3, 4, 5]) >>> len(a) # 6 elementos, como na lista original 6 >>> a.shape # para conferir que é um array 1D (6,) >>> b = np.arange(6) # uma outra maneira análoga ao range() >>> b array([0, 1, 2, 3, 4, 5]) ``` Dá pra acessar os elementos como em uma lista comum do Python com os colchetes. Mas as novidades principais são com mais dimensões, então, veja agora um array 2D que vou fazer a partir de uma lista de listas: ```python! >>> c = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> c array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> c.shape (3, 3) >>> c[2] # eu sempre me confundo se isso seria uma fila ou uma coluna array([6, 7, 8]) >>> c[2, 0] 6 >>> c[0, 2] 2 ``` [TODO] diagrama falando sobre filas e colunas Agora olha que legal isso aqui, como as operações com escalares, "números comuns", são aplicadas em todos os elementos do array de uma vez! ```python >>> c * 10 array([[ 0, 10, 20], [30, 40, 50], [60, 70, 80]]) >>> d = c * 10 + 5 >>> d array([[ 5, 15, 25], [35, 45, 55], [65, 75, 85]]) ``` Vamos "empilhar" dois arrays 2D e fazer um 3D: ```python! >>> e = np.dstack([c, d]) >>> e array([[[ 0, 5], [ 1, 15], [ 2, 25]], [[ 3, 35], [ 4, 45], [ 5, 55]], [[ 6, 65], [ 7, 75], [ 8, 85]]]) >>> e.shape (3, 3, 2) ```