{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Gerando gráficos com Matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Primeiro, precisamos importar o módulo e garantir que os gráficos sejam incluidos \"inline\" no notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Diversas formas de gerar gráficos são possíveis:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib --list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dois termos importantes são também **figura** e **eixo**.\n", "\n", "- *Eixo* é o nome dado a um gráficos, possivelmente com várias curvas e pontos plotados simultaneamente.\n", "- *Figura* é um conjunto de eixos que serão apresentados lado a lado." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A figura abaixo mostra alguns termos usados pelo Matplotlib." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Termos](matplotlib-anatomy.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Estilos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O *design* dos gráficos é determinado pelos denominados *estilos*. Existe diversos estilos, que você pode listar:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.style.available" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para escolher o estilo, basta usar o método `use`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.style.use('ggplot')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Gráficos de linha simples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Gráficos simples podem ser gerados pela função `plot`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2*np.pi, 100)\n", "y = np.cos(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(x, y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Interface orientada a objetos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O modo recomendado e mais versátil de gerar gráficos é através da interface orientada a objetos. Começamos criando uma figura com os eixos desejados e em seguida podemos plotar gráficos nos eixos e controlar os vários parâmetros dos eixos ou das figuras.\n", "\n", "No exemplo abaixo, chamamos a função `subplots` para criar os eixos e a figura. Como não é passado nenhuma parâmetro para o `subplots`, será gerada uma figura no tamanho padrão com apenas um eixo.\n", "\n", "Os comando de geração das curvas devem ser neste caso métodos do objeto de eixo." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Múltiplos eixos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos também gerar figuras com múltiplos eixos. Isto se consegue fornecendo para `subplots` as dimensões da grade de eixos a ser criada: número de linhas e número de colunas.\n", "\n", "Neste caso, a subplots retornará a figura e um array de eixos, com o número de linhas e colunas especificado." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "z = np.sin(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(2, 2)\n", "ax[0, 0].plot(x, y)\n", "ax[0, 1].plot(x, z)\n", "ax[1, 0].plot(x, y, x, z)\n", "ax[1, 1].plot(y, z);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note o formato do array de eixos retornado:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Controle de linhas e pontos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos especificar cores distintas para as linhas. No nosso exemplo, isso garante consistência das cores para o seno e o cosseno entre os diversos gráficos." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(2, 2)\n", "fig.set_size_inches(6, 6)\n", "ax[0, 0].plot(x, y, 'b')\n", "ax[0, 1].plot(x, z, 'r')\n", "ax[1, 0].plot(x, y, 'b', x, z, 'r')\n", "ax[1, 1].plot(y, z, 'k');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Lista de cores" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| caracter | cor |\n", "|---|---|\n", "|‘b’| blue |\n", "|‘g’|green|\n", "|‘r’|red|\n", "|‘c’|cyan|\n", "|‘m’|magenta|\n", "|‘y’|yellow|\n", "|‘k’|black|\n", "|‘w’|white|" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plotando com marcadores" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ao invés de usar linhas, podemos usar marcadores. Basta especificar o tipo de marcador desejado." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x1 = np.linspace(0, 2*np.pi, 30)\n", "y1 = np.cos(x1)\n", "z1 = np.sin(x1)\n", "fig, ax = plt.subplots(2, 2)\n", "fig.set_size_inches(6, 6)\n", "ax[0, 0].plot(x1, y1, '.')\n", "ax[0, 1].plot(x1, z1, '-')\n", "ax[1, 0].plot(x1, y1, '.', x1, z1, '-')\n", "ax[1, 1].plot(y1, z1, 'o');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Tipos de marcadores" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| caracter | description|\n", "|---|---|\n", "|'-'|solid line style|\n", "|'--'|dashed line style|\n", "|'-.'|dash-dot line style|\n", "|':'|dotted line style|\n", "|'.'|point marker|\n", "|','|pixel marker|\n", "|'o'|circle marker|\n", "|'v'|triangle_down marker|\n", "|'^'|triangle_up marker|\n", "|'<'|triangle_left marker|\n", "|'>'|triangle_right marker|\n", "|'1'|tri_down marker|\n", "|'2'|tri_up marker|\n", "|'3'|tri_left marker|\n", "|'4'|tri_right marker|\n", "|'s'|square marker|\n", "|'p'|pentagon marker|\n", "|'*'|star marker|\n", "|'h'|hexagon1 marker|\n", "|'H'|hexagon2 marker|\n", "|'+'|plus marker|\n", "|'x'|x marker|\n", "|'D'|diamond marker|\n", "|'d'|thin_diamond marker|\n", "|'|'|vline marker|\n", "|'_'|hline marker|" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos também especificar cores nos marcadores:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x1 = np.linspace(0, 2*np.pi, 30)\n", "y1 = np.cos(x1)\n", "z1 = np.sin(x1)\n", "fig, ax = plt.subplots(2, 2)\n", "fig.set_size_inches(6, 6)\n", "ax[0, 0].plot(x1, y1, '.b')\n", "ax[0, 1].plot(x1, z1, '-r')\n", "ax[1, 0].plot(x1, y1, '.b', x1, z1, '-r')\n", "ax[1, 1].plot(y1, z1, 'ok');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como vimos, podemos realizar vários `plot` no mesmo eixo. Isso pode ser usado para plotar o mesmo conjunto de dados de formas distintas, frequentemente para termos tanto pontos quanto linhas:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x1, y1, 'b')\n", "ax.plot(x1, y1, 'ob');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Barras de erros" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Frequentemente queremos indicar barras de erros, o que se consegue com o método `errorbar`.\n", "\n", "Para mostrar, primeiro criamos um valor em $y$ de erro artificial para cada ponto:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y1err = np.random.normal(0.1, 0.02, size=y1.size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora plotamos a curva com o \"erro\". O parâmetro `fmt` indica o formato dos dados (como nos gráficos anteriores), enquanto `ecolor` indica a cor das barras de erro." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.errorbar(x1, y1, y1err, fmt=':or', ecolor='r')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Rótulos (labels) de curvas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando múlitplas funções são plotadas no mesmo gráfico, é útil termos rótulos que ajudem a indicar o que cada curva representa. Para isso, precisamos especificar um `label` ao fazer o plot e chamar o método `legend` no eixo correspondente para que a legenda seja criada.\n", "\n", "Aproveitamos também para mostrar como dar nomes aos eixos de coordenadas através de `set_xlabel` e `set_ylabel`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(5,5))\n", "ax.plot(x, y, 'b', label=r'$\\cos(x)$')\n", "ax.plot(x, z, 'r', label=r'$\\sin(x)$')\n", "ax.plot(x, 0.25*(x ** 2 - 5*x), 'g', label=r'$\\frac{1}{4}(x^2-5x)$')\n", "ax.set_xlabel(r'$x$')\n", "ax.set_ylabel(r'$y$')\n", "ax.set_title('Some functions')\n", "ax.legend();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Limitando a região mostrada" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos limitar a região a ser mostrada nos eixos x e y com os métodos `set_xlim` e `set_ylim`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(5,5))\n", "ax.plot(x, y, 'b', label=r'$\\cos(x)$')\n", "ax.plot(x, z, 'r', label=r'$\\sin(x)$')\n", "ax.plot(x, 0.25*(x ** 2 - 5*x), 'g', label=r'$\\frac{1}{4}(x^2-5x)$')\n", "ax.set_xlim(1, 4)\n", "ax.set_xlabel(r'$x$')\n", "ax.set_ylabel(r'$y$')\n", "ax.set_title('Some functions')\n", "ax.legend();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Títulos, tamanho de figura, rótulos nas coordenadas e salvando em arquivo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O código abaixo mostra:\n", "\n", "- como especificar um título para a figura com `suptitle`;\n", "- como ajustar o tamanho (em polegadas) da figura com `set_size_inches`;\n", "- como ajustar o título de um eixo com `set_title`;\n", "- como especificar posição (`set_xticks` ou `set_yticks`) e rótulos (com `set_xticklabels` ou `set_yticklabels`) das marcações nos eixos das coordenadas;\n", "- como especificar o *aspect ratio* (razão entre os tamanho horizontal e vertical) de um gráfico com `set_aspect`;\n", "- como salvar a figura gerada em um arquivo, com `savefig`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(1, 2)\n", "fig.suptitle(r'Comparing $\\cos(x)$ and $\\sin(x)$', fontsize=16, fontweight='bold')\n", "fig.set_size_inches(14, 5)\n", "ax[0].plot(x, y, 'b', label=r'$\\cos(x)$')\n", "ax[0].plot(x, z, 'r', label=r'$\\sin(x)$')\n", "ax[0].set_title('Time Functions')\n", "piticks = np.linspace(0, 2*np.pi, 7)\n", "pilabels = [r'$0$', r'$\\pi/3$', r'$2\\pi/3$', r'$\\pi$', r'$4\\pi/3$', r'$5\\pi/3$', r'$2\\pi$']\n", "ax[0].set_xticks(piticks)\n", "ax[0].set_xticklabels(pilabels)\n", "ax[0].legend()\n", "ax[1].plot(y, z, 'k');\n", "ax[1].set_aspect('equal')\n", "ax[1].set_title('Phase')\n", "myticks = np.linspace(-1, 1, 5)\n", "ax[1].set_xticks(myticks)\n", "ax[1].set_yticks(myticks)\n", "fig.savefig('sincos.png')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scatterplots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No caso do método `plot`, se assume que a lista de valores $(x,y)$ apresentada representa uma sequência (isto é, tem uma ordem), que é usada para o traçado da linha da curva (caso se use uma linha).\n", "\n", "Em algumas situações, os valores $(x,y)$ são independentes uns dos outros, e não representam uma sequência. Nestes casos, não devemos usar `plot` e sim `scatterplot`.\n", "\n", "Por exemplo, suponha que tenhamos pares de valores quaisquer (aqui gerados aleatoriamente):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.random.normal(0, 1, size=100)\n", "y = np.random.normal(0, 1.5, size=100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Neste caso, se tentarmos usar `plot`, o resultado será confuso:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Apesar de ser possível gerar um gráfico adequado com o uso de marcadores sem uma linha, o ideal é indicar que os pontos não têm uma sequência específica usando `scatter`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.scatter(x, y);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Histogramas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos gerar histogramas com a função `hist` ou com o método correspondente dos eixos." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.random.normal(10, 3, size=10000)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.hist(x, bins=30);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O parâmetro `bins` especifica acima o número de caixas do histograma. Os limites do histograma são deduzidos dos valores inicial e final dos dados presentes.\n", "\n", "No histograma acima, o eixo vertical indica o número de valores em cada uma das caixas do histograma. Podemos também gerar um histograma de \"densidade de probabilidade\" (isto é, a área sob o histograma é unitária) especificando o parâmetro `density` como `True`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.hist(x, bins=30, density=True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O uso de densidade de probabilidade facilita comparar o resultado com distribuições de probabilidade esperadas:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import scipy.stats as stats" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = np.linspace(np.min(x), np.max(x), 100)\n", "plt.plot(a, stats.norm.pdf(a, 10, 3))\n", "plt.hist(x, bins=50, density=True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se desejarmos, podemos fornecer explicitamente os valores dos limites das caixas do histograma. Basta fornecer um array com esses limites ao invés de simplesmente indicar o número de caixas no parâmetro `bins`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = np.linspace(-5, 25, 100)\n", "h = np.linspace(-5, 25, 30)\n", "plt.plot(a, stats.norm.pdf(a, 10, 3))\n", "plt.hist(x, bins=h, density=True)\n", "plt.xlim(-5, 25);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Funções de mais de uma variável" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Até agora, vimos apenas como plotar funções de apenas uma variável. Plotar funções de múltiplas variáveis é mais complexo e deve-se estudar com cuidado as opções. Para funções de duas variáveis, algumas opções são: usar um gráfico tridimensional (com o valor da função na terceira dimensão); codificar o valor da função em cores ou intensidade num superfície bidimensional; ou linhas de contorno onde os valores da função são constantes.\n", "\n", "Para funções de mais do que duas dimensões, é necessários plotar projeções em um número de dimensões menor (1 ou 2)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Heatmap" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Chamamos de *heatmap* o gráfico de uma função de duas variáveis quando os valores da função são codificados em cores ou intensidade formando uma imagem bidimensional.\n", "\n", "Para isso, usamos `matshow` ou `imshow`. O método `colorbar` coloca uma barra com os valores correspondentes a cada cor." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m = np.random.normal(100, 50, size=(100,100))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "mdr = ax.matshow(m, interpolation='none',cmap='viridis')\n", "ax.grid(False)\n", "fig.colorbar(mdr);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Veja colormaps no [site de referência](https://matplotlib.org/gallery/color/colormap_reference.html#sphx-glr-gallery-color-colormap-reference-py).\n", "\n", "Agora um outro exemplo." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2*np.pi, 100)\n", "y = np.linspace(0, np.pi, 100)\n", "fx = np.cos(x)\n", "fy = np.sin(2 * y)\n", "m = fy.reshape((100, 1)) * fx.reshape((1, 100))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "mdr = ax.matshow(m, interpolation='none', cmap='plasma')\n", "ax.grid(False)\n", "fig.colorbar(mdr);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Contornos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para traçar contornos, usamos `contour`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "mdr = ax.contour(m, cmap='plasma')\n", "ax.grid(False)\n", "fig.colorbar(mdr);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Gráficos 3D" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para trabalhar com gráficos 3D, precisamos importar `Axes3D` do módulo `mplot3d`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from mpl_toolkits.mplot3d import Axes3D" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Depois dessa importação, podemos gerar um eixo 3D de dada uma figura, usando o método `gca` da figura. Neste eixo 3D fazemos então o nosso gráfico, no caso usando `plot_surface`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.gca(projection='3d')\n", "xm, ym = np.meshgrid(x, y)\n", "surf = ax.plot_surface(xm, ym, m, cmap='plasma', linewidth=0, antialiased=False)\n", "fig.colorbar(surf, shrink=0.5);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Adicionando gráficos de contorno" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O método `contour` permite gerar gráficos de contorno (linhas onde a função tem o mesmo valor). Se quisermos preencher os contornos, podemos usar ao invés `countourf`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.gca(projection='3d')\n", "xm, ym = np.meshgrid(x, y)\n", "surf = ax.plot_surface(xm, ym, m, cmap='coolwarm',\n", " linewidth=0, antialiased=False)\n", "cset = ax.contour(xm, ym, m, zdir='z', offset=-1.2, cmap='coolwarm')\n", "cset = ax.contour(xm, ym, m, zdir='x', offset=0, cmap='coolwarm')\n", "cset = ax.contour(xm, ym, m, zdir='y', offset=np.pi, cmap='coolwarm')\n", "\n", "ax.set_xlabel('x')\n", "ax.set_ylabel('y')\n", "ax.set_zlabel(r'$\\cos(x)\\sin(2y)$');\n", "fig.tight_layout()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 }