├── README.md ├── datasets └── ibge-peso-altura-brasil.csv ├── en ├── README.md └── notebooks │ ├── First_steps.ipynb │ └── ETL.ipynb └── pt-br ├── README.md └── notebooks ├── Visualização_interativa.ipynb ├── PassoPasso.ipynb ├── ETL.ipynb ├── DataframeBD.ipynb ├── Relações.ipynb └── Distribuição_Dados.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # Data science before coding 2 | 3 | This repository was planned for those who don't know how to code, but work or want to work with data science 🙃 4 | 5 | If you want to access it in English, follow [this link](en/README.md). 6 | 7 | Se você quer acessá-lo em português do Brasil, siga [este link](pt-br/README.md). 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /datasets/ibge-peso-altura-brasil.csv: -------------------------------------------------------------------------------- 1 | Idade;Altura;Peso 2 | Menos de 1 ano;67;8,1 3 | 1 ano;81,5;11,5 4 | 2 anos;92;13,9 5 | 3 anos;98,9;16 6 | 4 anos;106,2;18 7 | 5 anos;112;19,9 8 | 6 anos;118,3;22,2 9 | 7 anos;124,9;25,1 10 | 8 anos;129,7;27,7 11 | 9 anos;135,2;31,6 12 | 10 anos;139,9;33,4 13 | 11 anos;143,6;36,8 14 | 12 anos;151;42 15 | 13 anos;157,5;47,4 16 | 14 anos;164,1;52,3 17 | 15 anos;167,8;57 18 | 16 anos;170;60,1 19 | 17 anos;171,8;63,1 20 | 18 anos;172,6;65,3 21 | 19 anos;172;65,9 22 | 20 a 24 anos;173;69,4 23 | 25 a 29 anos;173;72,7 24 | 30 a 34 anos;171,6;74,2 25 | 35 a 44 anos;171;74,6 26 | 45 a 54 anos;169,9;74,6 27 | 55 a 64 anos;168,2;73,1 28 | 65 a 74 anos;166,9;70,3 29 | 75 anos ou mais;165,7;66,8 -------------------------------------------------------------------------------- /en/README.md: -------------------------------------------------------------------------------- 1 | # Data science before coding 2 | 3 | This repository was planned for those who don't know how to code, but work or want to work with data science 🙃 4 | 5 | > If you already code and want a repo with a faster pace, check out [this one](https://github.com/ivanovitchm/datascience_one_2019_1) 😎 6 | 7 | ### Disclaimer 8 | 9 | This is a collaborative repository, created by the students of [Instituto Metrópole Digital](imd.ufrn.br) from [UFRN](ufrn.br). 10 | 11 | The author of each notebook is properly acknowledged 😉 12 | 13 | ## Choosing a tool 14 | 15 | Several tools are available for this profile. 16 | 17 | In general, they can be grouped into GUI tools and CLI tools: 18 | - GUI (graphical user interface): All the user interaction is done graphically. These are software like Google Spreadsheets and [Orange3](https://orange.biolab.si). 19 | - CLI (command-line interface): User interaction is done through a programming language. The main open source languages used in data science are Python, R, and Julia. 20 | 21 | A very nice alternative that gathers a bit of both worlds are interactive notebooks, originally from project Jupyter and currently supported also by Google Colaboratory. 22 | 23 | [This post](https://medium.com/@leobezerra_90682/python-r-messi-and-cristiano-d2b5278dbd5a) discusses the main supported languages. 24 | 25 | In this repo, we will use notebooks with the Python ecossystem and its main library, Pandas. 26 | 27 | > The whole material was planned so you don't need to learn how to code, but if you do want to, check out this [this repo](https://github.com/leobezerra/python-zero). 28 | 29 | > The notebooks in this repository were either created or translated by the authors indicated. 30 | 31 | ## Meeting Pandas 32 | 33 | #### [[jonathanjalles]](https://github.com/jonathanjalles)[[leobezerra]](https://github.com/leobezerra) First steps 34 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/en/notebooks/First_steps.ipynb) 35 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 36 | [![Watch on YouTube](https://img.shields.io/badge/Watch%20on-YouTube-red.svg)](https://youtu.be/B0WAbK1JMw0) 37 | 38 | #### [[natanlimas]](https://github.com/natanlimas)[[babschlott]](https://github.com/babschlott) Dataframes as databases 39 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/en/notebooks/DataframeDB.ipynb) 40 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 41 | [![Watch on YouTube](https://img.shields.io/badge/Watch%20on-YouTube-red.svg)](https://youtu.be/DdXcrXc142g) 42 | 43 | #### [[kallil12]](https://github.com/kallil12)[[eBetcel]](https://github.com/eBetcel) Data analysis and presentation 44 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/en/notebooks/Visualization.ipynb) 45 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 46 | [![Watch on YouTube](https://img.shields.io/badge/Watch%20on-YouTube-red.svg)](https://youtu.be/T6vWiEvAy4U) 47 | 48 | #### [[mildo]](https://github.com/mildo)[[isaacgdo]](https://github.com/isaacgdo) Extraction, transformation and load (ETL) 49 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/en/notebooks/ETL.ipynb) 50 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 51 | 52 | ## Working with multiples bases 53 | 54 | #### [[leobezerra]](https://github.com/leobezerra)[[samuellucas97]](https://github.com/Samuellucas97) Combining information from multiple bases 55 | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/en/notebooks/Combining_Information_From_Multiple_Bases.ipynb) 56 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 57 | 58 | 59 | -------------------------------------------------------------------------------- /pt-br/README.md: -------------------------------------------------------------------------------- 1 | # Ciência de dados antes da programação 2 | 3 | Este repositório foi pensado para quem não sabe programar, mas trabalha ou quer trabalhar com ciência de dados 🙃 4 | 5 | > Outro repositório bem legal pra isso é o [ds-zero](https://github.com/leobezerra/ds-zero) 👍 6 | 7 | > Se você já programa e quer um repo em um ritmo acelerado, dê uma olhada [nesse aqui](https://github.com/ivanovitchm/datascience2020.6) 😎 8 | 9 | ### Disclaimer 10 | 11 | Este é um repositório colaborativo, criado pelos alunos do [Instituto Metrópole Digital](imd.ufrn.br) da UFRN. 12 | 13 | O autor de cada material está devidamente creditado e agradecido 😉 14 | 15 | ## Escolhendo a ferramenta 16 | 17 | Existem várias ferramentas disponíveis pensadas para este perfil. 18 | 19 | Em geral, elas se dividem entre ferramentas GUI e ferramentas CLI: 20 | - GUI (interface gráfica de usuário): Toda a interação com o usuário é feita de forma gráfica. São programas como o Google Spreadsheets e o [Orange3](https://orange.biolab.si). 21 | - CLI (interface de linha de comando): A interação com o usuário é feita através de uma linguagem de programação. As principais linguagens gratuitas usadas em ciência de dados são Python, R e Julia. 22 | 23 | Um alternativa bem legal que combina um pouco dos dois mundos são os notebooks interativos, originalmente do projeto Jupyter e atualmente suportados também pelo Google Colaboratory. 24 | 25 | [Esse post](https://medium.com/@leobezerra_90682/python-r-messi-and-cristiano-d2b5278dbd5a) traz uma discussão sobre as principais linguagens suportadas. 26 | 27 | Neste repo, usaremos notebooks com o ecossistema Python e sua biblioteca principal, o Pandas. 28 | 29 | > Todo o material foi pensado para que você não precise aprender a programar, mas se você quiser aprender, dê uma olhada [nesse repositório](https://github.com/leobezerra/python-zero). 30 | 31 | ## Conhecendo o Pandas 32 | 33 | #### [[jonathanjalles]](https://github.com/jonathanjalles) Primeiros passos 34 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/PassoPasso.ipynb) 35 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 36 | 37 | #### [[natanlimas]](https://github.com/natanlimas) Dataframes como bancos de dados 38 | 39 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/DataframeBD.ipynb) 40 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 41 | 42 | #### [[kallil12]](https://github.com/kallil12) Análise e apresentação de dados 43 | 44 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/Visualizacao.ipynb) 45 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 46 | 47 | #### [[mildo]](https://github.com/mildo) Extração, transformação e carga de dados (ETL) 48 | 49 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/ETL.ipynb) 50 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 51 | 52 | > Os notebooks acima foram originalmente criados pelos autores indicados e posteriormente revisados com a adição de conteúdos de múltiplos autores deste repositório. 53 | 54 | ## Trabalhando com múltiplas bases de dados 55 | 56 | Uma das possibilidades que ferramentas CLI abre é trabalhar com múltiplas bases de dados ao mesmo tempo. 57 | 58 | Os notebooks abaixo são alguns exemplos de análises que agregam informações espalhadas em múltiplas bases. 59 | 60 | #### [[leobezerra]](https://github.com/leobezerra) Unindo informações de múltiplas bases 61 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/UFRN-diversidade.ipynb) 62 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 63 | 64 | #### [[leobezerra]](https://github.com/leobezerra) Cruzando informações de múltiplas bases 65 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/UFRN-disciplinas.ipynb) 66 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 67 | 68 | ## Explorando seus dados 69 | 70 | Uma parte essencial do processo de ciência de dados é investigar de forma exploratória os seus dados. 71 | 72 | Os notebooks a seguir apresentam conceitos importantes sobre distribuições, relações entre dados e análise interativa de dados. 73 | 74 | #### [[kallil12]](https://github.com/kallil12) Visualizando e identificando distribuições 75 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/Distribuição_Dados.ipynb) 76 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 77 | 78 | #### [[mildo]](https://github.com/mildo) Relações entre características 79 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/Relações.ipynb) 80 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 81 | 82 | #### [[jonathanjalles]](https://github.com/jonathanjalles) Interagindo visualmente com dados 83 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/leobezerra/pandas-zero/blob/master/pt-br/notebooks/Visualização_interativa.ipynb) 84 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/leobezerra/pandas-zero/master/) 85 | 86 | -------------------------------------------------------------------------------- /pt-br/notebooks/Visualização_interativa.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Visualização interativa", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | } 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "id": "x1XwdUSpiK1g", 20 | "colab_type": "text" 21 | }, 22 | "source": [ 23 | "# Interagindo visualmente com dados" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": { 29 | "id": "Sc0DUvlEjxQE", 30 | "colab_type": "text" 31 | }, 32 | "source": [ 33 | "Analisar visualmente um conjunto de dados é uma tarefa importante no processo de ciência de dados. Até aqui, vimos como fazer isso usando gráficos estáticos produzidos pelas bibliotecas `matplotlib` e `seaborn`. Neste notebook, vamos explorar a biblioteca de visualição interativa de dados [Plotly](https://plot.ly/python/), uma biblioteca compatível com diferentes linguagens de programação. Para o ecossistema Python, podemos usar o módulo `plotly.express`, criado para facilitar a produção de visualizações interativas." 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "metadata": { 39 | "id": "jBOtyPS7hz3K", 40 | "colab_type": "code", 41 | "colab": {} 42 | }, 43 | "source": [ 44 | "import pandas as pd\n", 45 | "import plotly.express as px" 46 | ], 47 | "execution_count": 0, 48 | "outputs": [] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": { 53 | "id": "d9ZtuGitkBuG", 54 | "colab_type": "text" 55 | }, 56 | "source": [ 57 | "Para esta análise, vamos utilizar [uma base de dados aberta sobre preços de combustíveis](https://http://dados.gov.br/dataset/infopreco), disponibilizada pelos postos no sítio da ANP. No código abaixo, vamos informar ao Pandas que trate a característica `DATA CADASTRO` como uma data:" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "metadata": { 63 | "id": "VykuV_h1BoOe", 64 | "colab_type": "code", 65 | "colab": {} 66 | }, 67 | "source": [ 68 | "preços = pd.read_csv('http://www.anp.gov.br/images/infopreco/infopreco.csv', encoding='latin-1', sep=';', decimal=',', parse_dates=['DATA CADASTRO'])\n", 69 | "preços.head()" 70 | ], 71 | "execution_count": 0, 72 | "outputs": [] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": { 77 | "id": "5jEEFCRwrW3J", 78 | "colab_type": "text" 79 | }, 80 | "source": [ 81 | "## Validando os dados" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": { 87 | "id": "ZAQImIlClPrU", 88 | "colab_type": "text" 89 | }, 90 | "source": [ 91 | "Vamos começar nossa análise validando os dados presentes na base. Isto é particularmente importante em bases de dados abertos governamentais, que muitas vezes não contam com boa curadoria de dados." 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "metadata": { 97 | "id": "LSiskOVNligE", 98 | "colab_type": "code", 99 | "colab": {} 100 | }, 101 | "source": [ 102 | "preços.isnull().sum()" 103 | ], 104 | "execution_count": 0, 105 | "outputs": [] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "metadata": { 110 | "id": "qF-FHamq2B20", 111 | "colab_type": "code", 112 | "colab": {} 113 | }, 114 | "source": [ 115 | "preço_produto = preços.pivot_table(index=\"PRODUTO\", values=\"VALOR VENDA\")\n", 116 | "preço_produto.head()" 117 | ], 118 | "execution_count": 0, 119 | "outputs": [] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": { 124 | "id": "wWyBT6MZlZkQ", 125 | "colab_type": "text" 126 | }, 127 | "source": [ 128 | "Os dados faltantes se limitam a bairro e complemento, que não serão foco da nossa análise. Em compensação, a análise do preço médio dos combustíveis listados indica valores consideravelmente elevados. Isto é um indicativo da presença de valores incorretos, que podemos verificar analisando a distribuição dos dados. Para interagir com o gráfico, passe o cursor do mouse sobre o gráfico e explore as opções no canto superior direito:" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "metadata": { 134 | "id": "Qqo4Qmyoma0y", 135 | "colab_type": "code", 136 | "colab": {} 137 | }, 138 | "source": [ 139 | "px.box(preços, x=\"PRODUTO\", y=\"VALOR VENDA\")" 140 | ], 141 | "execution_count": 0, 142 | "outputs": [] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": { 147 | "id": "3pDo7ksAn_SJ", 148 | "colab_type": "text" 149 | }, 150 | "source": [ 151 | "Note que o gráfico produzido pelo Plotly traz um número de ferramentas que podemos usar para aprofundar nossa investigação. Vamos destacar alguns deles:\n", 152 | "\n", 153 | "* Ao posicionar o mouse sobre um boxplot, vemos suas informação.\n", 154 | "* Ao posicionar o mouse sobre um outlier, podemos ver seu valor.\n", 155 | "* Podemos dar zoom sobre as partes do gráfico que mais nos interessem.\n", 156 | "* Podemos salvar o gráfico diretamente.\n", 157 | "\n", 158 | "Analisando especificamente os dados da base em questão, os valores inválidos aparentam ter sido informados sem casas decimais. Vamos verificar quantos valores estão acima de 10 reais:" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "metadata": { 164 | "id": "CVHddNxJpTRo", 165 | "colab_type": "code", 166 | "colab": {} 167 | }, 168 | "source": [ 169 | "preços.query(\"`VALOR VENDA` > 10\")" 170 | ], 171 | "execution_count": 0, 172 | "outputs": [] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": { 177 | "id": "zDJlhkW-pqNZ", 178 | "colab_type": "text" 179 | }, 180 | "source": [ 181 | "Por serem poucas observações, podemos removê-las sem prejuízo à base:" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "metadata": { 187 | "id": "8dqF27f9pwBt", 188 | "colab_type": "code", 189 | "colab": {} 190 | }, 191 | "source": [ 192 | "preços = preços.query(\"`VALOR VENDA` <= 10\")" 193 | ], 194 | "execution_count": 0, 195 | "outputs": [] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "metadata": { 200 | "id": "StO-RupEppqd", 201 | "colab_type": "code", 202 | "colab": {} 203 | }, 204 | "source": [ 205 | "px.box(preços, x=\"PRODUTO\", y=\"VALOR VENDA\")" 206 | ], 207 | "execution_count": 0, 208 | "outputs": [] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": { 213 | "id": "s8l8H10wp6gH", 214 | "colab_type": "text" 215 | }, 216 | "source": [ 217 | "Curiosamente, agora vemos que também há outliers abaixo dos boxplots. Vamos investigar esses casos:" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "metadata": { 223 | "id": "59hmd0u3qCnN", 224 | "colab_type": "code", 225 | "colab": {} 226 | }, 227 | "source": [ 228 | "preços.query(\"`VALOR VENDA` <= 1\")" 229 | ], 230 | "execution_count": 0, 231 | "outputs": [] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": { 236 | "id": "FQcU5a2bqMI1", 237 | "colab_type": "text" 238 | }, 239 | "source": [ 240 | "Novamente, parece seguro remover estes casos:" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "metadata": { 246 | "id": "TvDsZxSGqN70", 247 | "colab_type": "code", 248 | "colab": {} 249 | }, 250 | "source": [ 251 | "preços = preços.query(\"`VALOR VENDA` > 1\")" 252 | ], 253 | "execution_count": 0, 254 | "outputs": [] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "metadata": { 259 | "id": "8wkzc7esqPpc", 260 | "colab_type": "code", 261 | "colab": {} 262 | }, 263 | "source": [ 264 | "px.box(preços, x=\"PRODUTO\", y=\"VALOR VENDA\")" 265 | ], 266 | "execution_count": 0, 267 | "outputs": [] 268 | }, 269 | { 270 | "cell_type": "markdown", 271 | "metadata": { 272 | "id": "SocBu6uAsP8i", 273 | "colab_type": "text" 274 | }, 275 | "source": [ 276 | "## Preço médio do combustível" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": { 282 | "id": "rrOakzPkrl6o", 283 | "colab_type": "text" 284 | }, 285 | "source": [ 286 | "Agora que limpamos os dados, vamos visualizar o preço médio por tipo de combustível:" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "metadata": { 292 | "id": "2Q8l4QhcBH-P", 293 | "colab_type": "code", 294 | "colab": {} 295 | }, 296 | "source": [ 297 | "preço_produto = preços.pivot_table(index=\"PRODUTO\", values=\"VALOR VENDA\")\n", 298 | "preço_produto.head()" 299 | ], 300 | "execution_count": 0, 301 | "outputs": [] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "metadata": { 306 | "id": "_0E7G3r3wVKl", 307 | "colab_type": "code", 308 | "colab": {} 309 | }, 310 | "source": [ 311 | "px.bar(preço_produto, x=preço_produto.index, y=\"VALOR VENDA\", title='Preço médio por tipo de combustível')" 312 | ], 313 | "execution_count": 0, 314 | "outputs": [] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": { 319 | "id": "B4VPNEyWh42H", 320 | "colab_type": "text" 321 | }, 322 | "source": [ 323 | "Botemos refinar nossa análise analisando o preço da gasolina comum por estado:" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "metadata": { 329 | "id": "aEMixqS2iMCG", 330 | "colab_type": "code", 331 | "colab": {} 332 | }, 333 | "source": [ 334 | "preço_gasolina = preços.query(\"PRODUTO == 'Gasolina C Comum'\")\n", 335 | "gasolina_por_estado = preço_gasolina.pivot_table(index=\"UF\", values='VALOR VENDA')\n", 336 | "gasolina_por_estado.head()" 337 | ], 338 | "execution_count": 0, 339 | "outputs": [] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": { 344 | "id": "6RPjjcqzivqg", 345 | "colab_type": "text" 346 | }, 347 | "source": [ 348 | "Para gerar este gráfico a partir da tabela dinâmica acima, vamos informar que os índices das observações devem ser usados como valores para o eixo x:" 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "metadata": { 354 | "id": "wMeiJxa3MJ7D", 355 | "colab_type": "code", 356 | "colab": {} 357 | }, 358 | "source": [ 359 | "px.bar(gasolina_por_estado, x=gasolina_por_estado.index, y=\"VALOR VENDA\", title = 'Preço médio da Gasolina comum por UF')" 360 | ], 361 | "execution_count": 0, 362 | "outputs": [] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": { 367 | "id": "I-swQZ75lhbC", 368 | "colab_type": "text" 369 | }, 370 | "source": [ 371 | "Podemos expandir essa análise para incluir todos os produtos considerados. Para isto, vamos usar um histograma, informando com o parâmetro `histfunc=\"avg\"` que estamos interessados no valor médio. Note que, apesar de ser um gráfico com muitas informações, é possível selecionar quais produtos observar clicando na legenda mostrada no lado direito." 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "metadata": { 377 | "id": "cKAywH8eiLOJ", 378 | "colab_type": "code", 379 | "colab": {} 380 | }, 381 | "source": [ 382 | "px.histogram(preços, x=\"UF\", y=\"VALOR VENDA\", color=\"PRODUTO\", histfunc=\"avg\",\n", 383 | " barmode=\"group\", title='Distribuição de preços por combustível')" 384 | ], 385 | "execution_count": 0, 386 | "outputs": [] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": { 391 | "id": "dn70Krqo0RrR", 392 | "colab_type": "text" 393 | }, 394 | "source": [ 395 | "## Analisando a evolução do preço dos combustíveis" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": { 401 | "id": "wAeZbp5M0eME", 402 | "colab_type": "text" 403 | }, 404 | "source": [ 405 | "Os dados disponíveis na base que baixamos são referentes a vários meses distintos. Chamamos este tipo de dado de uma **série temporal**, ou série histórica. Podemos visualizar a evolução destas séries usando gráficos de linhas. Para isso, precisamos inicialmente gerar a série para o valor médio de cada produto por mês.\n", 406 | "\n", 407 | "O primeiro passo é produzir uma característica contendo apenas os dados de ano e mês, para que tenhamos dados suficientes para uma agregação. Fazemos isso usando o método `.dt.to_period(\"M\")` que características reconhecidas como data apresentam:" 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "metadata": { 413 | "colab_type": "code", 414 | "id": "0qLpWf1s3wkN", 415 | "colab": {} 416 | }, 417 | "source": [ 418 | "preços['MES'] = preços['DATA CADASTRO'].dt.to_period('M').astype(str)\n", 419 | "preços.head()" 420 | ], 421 | "execution_count": 0, 422 | "outputs": [] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": { 427 | "id": "yeHQfnSt91XC", 428 | "colab_type": "text" 429 | }, 430 | "source": [ 431 | "Um detalhe técnico do código acima é que o método `.dt.to_period(\"M\")` gera um objeto do tipo `Period` atualmente incompatível com o gráfico de linhas da biblioteca Plotly. Por isso, pedimos para que o Pandas trate esta coluna como um texto usando o método `astype(str)`.\n", 432 | "\n", 433 | "Agora que temos informação para cada mês, podemos gerar uma tabela dinâmica para visualizar a média por mês e produto:" 434 | ] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "metadata": { 439 | "id": "wfVedrMiyy_U", 440 | "colab_type": "code", 441 | "colab": {} 442 | }, 443 | "source": [ 444 | "preços_mês = preços.pivot_table(index=\"MES\", columns=\"PRODUTO\", values=\"VALOR VENDA\")\n", 445 | "preços_mês" 446 | ], 447 | "execution_count": 0, 448 | "outputs": [] 449 | }, 450 | { 451 | "cell_type": "markdown", 452 | "metadata": { 453 | "id": "qp4Fb4_TzIy-", 454 | "colab_type": "text" 455 | }, 456 | "source": [ 457 | "Note que o `DataFrame`a acima contém muitos valores faltantes para o GNV. Assim, vamos descartar dados referentes a este produto:" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "metadata": { 463 | "id": "CSoSsxfLzIN1", 464 | "colab_type": "code", 465 | "colab": {} 466 | }, 467 | "source": [ 468 | "preços_mês = preços_mês.drop(\"GNV\", axis=1)\n", 469 | "preços_mês.head()" 470 | ], 471 | "execution_count": 0, 472 | "outputs": [] 473 | }, 474 | { 475 | "cell_type": "markdown", 476 | "metadata": { 477 | "id": "eHL4cVQa-ndX", 478 | "colab_type": "text" 479 | }, 480 | "source": [ 481 | "Apesar de útil para exploração de dados, o formato da tabela dinâmica acima não é adequado para a produção de um gráfico de linhas com a biblioteca Plotly. O código abaixo converte o formato *wide* acima em um formato *longo*, usando os métodos `stack` e `reset_index`:" 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "metadata": { 487 | "id": "3OX_woyI_AKb", 488 | "colab_type": "code", 489 | "colab": {} 490 | }, 491 | "source": [ 492 | "preços_mês = preços_mês.stack().reset_index(name=\"VALOR VENDA\")\n", 493 | "preços_mês.head()" 494 | ], 495 | "execution_count": 0, 496 | "outputs": [] 497 | }, 498 | { 499 | "cell_type": "markdown", 500 | "metadata": { 501 | "id": "ieEFrZyr_Pvh", 502 | "colab_type": "text" 503 | }, 504 | "source": [ 505 | "Agora podemos investigar a evolução dos preços médios mensais de cada produto ao longo do período abrangido pela base de dados. Assim como no caso do histograma, podemos selecionar as séries que desejamos analisar interagindo com a legenda do gráfico:" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "metadata": { 511 | "id": "oN2t60Ks3osH", 512 | "colab_type": "code", 513 | "colab": {} 514 | }, 515 | "source": [ 516 | "px.line(preços_mês, x=\"MES\", y=\"VALOR VENDA\", color=\"PRODUTO\",\n", 517 | " title='Evolução de preços por mês de observação (média do mês)')" 518 | ], 519 | "execution_count": 0, 520 | "outputs": [] 521 | } 522 | ] 523 | } -------------------------------------------------------------------------------- /pt-br/notebooks/PassoPasso.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "7FAHc8hEbt26" 8 | }, 9 | "source": [ 10 | "# Primeiros passos com Pandas para análise de dados\n", 11 | "\n", 12 | "Neste tutorial, vamos utilizar a linguagem Python e a biblioteca Pandas para analisar dados tabulares, que são qualquer conjunto de informações armazenados na forma de tabelas, onde:\n", 13 | "\n", 14 | "- **Colunas** indicam **características** ou atributos.\n", 15 | "- **Linhas** representam **observações** ou registros.\n", 16 | "\n", 17 | "Vamos usar o Pandas para analisar um conjunto de dados sobre preços de combustíveis no Brasil, disponível [aqui](https://www.kaggle.com/ficosta/combustible-price-brasil/download), aprendendo como ler dados no formato `.csv` (valores separados por vírgulas), um dos formatos mais comuns de dados tabulares. Em seguida vamos explorar os dados para aprender sobre eles e descobrir que informações eles podem nos fornecer.\n", 18 | "\n", 19 | "Baixe o arquivo `.zip` no link fornecido, descompacte em uma pasta do seu computador e coloque o arquivo no seu Google Drive. Em seguida, execute a célula abaixo e siga as instruções para ter acesso aos seus arquivos do Drive por esse Colab." 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": { 26 | "colab": { 27 | "base_uri": "https://localhost:8080/", 28 | "height": 122 29 | }, 30 | "colab_type": "code", 31 | "id": "qC3CAEgXP-Bb", 32 | "outputId": "1bd09ed7-1bce-495c-b4c4-944f51cc93cc" 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "from google.colab import drive\n", 37 | "drive.mount('/content/drive')" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": { 43 | "colab_type": "text", 44 | "id": "fGnAcspOfQiB" 45 | }, 46 | "source": [ 47 | "## Importando o pandas" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "Primeiro devemos importar alguns recursos do pacote Pandas para o ambiente de análise Colab. Para importar um recurso em Python, devemos informar a biblioteca de onde vamos importar e quais recursos precisaremos. Neste exemplo valos importar o recurso `read_csv`, um **método** para ler arquivos de dados tabulares do tipo csv. " 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": { 60 | "colab_type": "text", 61 | "id": "uV4yHsVrKGcU" 62 | }, 63 | "source": [ 64 | "1 - Descomente o código abaixo (apague o \\#) e aperte o play no lado esquerdo da linha (ou utilize ***shift + enter***)." 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "colab": {}, 72 | "colab_type": "code", 73 | "id": "IY-aXrzOfJkU" 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "# from pandas import read_csv" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": { 83 | "colab_type": "text", 84 | "id": "lVh_Fcao2vGq" 85 | }, 86 | "source": [ 87 | "## Lendo um arquivo csv" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": { 93 | "colab_type": "text", 94 | "id": "W1gxzWI74jMw" 95 | }, 96 | "source": [ 97 | "Com o arquivo csv pronto, precisamos acessá-lo pelo colab. Primeiro, defina um nome para o conjunto de dados (por exemplo, *dados*). A função `read_csv()` será usada para ler o arquivo, da seguinte maneira:\n", 98 | "\n", 99 | "```\n", 100 | "dados = read_csv('/caminho/do/arquivo.csv')\n", 101 | "```\n", 102 | "No exemplo acima, o nome *dados* é associado a um objeto do Pandas. Falaremos desse objeto adiante.\n", 103 | "\n", 104 | "Obs.: Para obter o caminho do arquivo no diretório do colab, navegue até o seu arquivo através das pastas mostradas no painel lateral esquerdo deste colab, clique com o botão direito sobre o arquivo e em seguida em ***copiar caminho***.\n", 105 | "\n" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": { 111 | "colab_type": "text", 112 | "id": "aswBfMut9At3" 113 | }, 114 | "source": [ 115 | "2 - Crie um nome para o conjunto de dados e leia o arquivo usando o método `read_csv`." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": { 122 | "colab": {}, 123 | "colab_type": "code", 124 | "id": "PF1KC8RvfjyR" 125 | }, 126 | "outputs": [], 127 | "source": [] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": { 132 | "colab_type": "text", 133 | "id": "0YgumbZR9sjr" 134 | }, 135 | "source": [ 136 | "## Explorando os dados" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": { 142 | "colab_type": "text", 143 | "id": "GwanfJ3c_AHl" 144 | }, 145 | "source": [ 146 | "Depois de ler os dados, precisamos descobrir quais são as **características** presentes e quantas **observações** existem. O Pandas possui vários métodos que podemos aplicar a um conjunto de dados a fim de conseguir mais informações sobre ele. Para descobrir quantas observações e características existem, verificamos o atributo `shape` nos dados lidos do arquivo csv. No exemplo anterior, você criou um objeto Pandas de nome `dados`. Em Python, visualizamos atributos de objetos Pandas da seguinte forma:\n", 147 | "\n", 148 | "```\n", 149 | "dados.nome_do_atributo\n", 150 | "```" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": { 156 | "colab_type": "text", 157 | "id": "jZ4o4uOWWprd" 158 | }, 159 | "source": [ 160 | "3.1 - Encontre a quantidade de registros e atributos (linhas, colunas) do conjunto de dados através do atributo `shape`." 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": { 167 | "colab": { 168 | "base_uri": "https://localhost:8080/", 169 | "height": 34 170 | }, 171 | "colab_type": "code", 172 | "id": "ViMCY5RKuMTT", 173 | "outputId": "516d2f44-415e-483e-e2d2-808254c83c2d" 174 | }, 175 | "outputs": [], 176 | "source": [ 177 | "# dados.shape" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": { 183 | "colab_type": "text", 184 | "id": "H988_L2yH7r7" 185 | }, 186 | "source": [ 187 | "É possível visualizar as primeiras linhas de um conjunto de dados em pandas utilizando o método `head(número_de_linhas)`. Em Python, utilizamos métodos e atributos da mesma forma, com a diferença que métodos utilizam parênteses em seguida ao seu nome. O `número_de_linhas` é um número positivo que, se informado como **argumento**, define quantas linhas serão exibidas. \n", 188 | "\n", 189 | "**Observação**: Se você optar por não informar o número de linhas, o Python usa 5 como padrão." 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": { 195 | "colab_type": "text", 196 | "id": "xgjTC1-xJht7" 197 | }, 198 | "source": [ 199 | "3.2 - Com o método `head()`, mostre as primeiras linhas do conjunto de dados. A quantidade de colunas mostrada é a mesma indicada pelo atributo `shape`?" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "metadata": { 206 | "colab": { 207 | "base_uri": "https://localhost:8080/", 208 | "height": 199 209 | }, 210 | "colab_type": "code", 211 | "id": "cXwBku3IuONg", 212 | "outputId": "3af28d8c-1bac-4dc4-fccd-fd8657d90f4f" 213 | }, 214 | "outputs": [], 215 | "source": [ 216 | "# dados.head(2)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": { 222 | "colab_type": "text", 223 | "id": "dzm4Q-VrndUi" 224 | }, 225 | "source": [ 226 | "Perceba que na tabela acima, a primeira observação possui índice `0` (zero). Assim, o índice da última linha da tabela será igual ao número de linhas da tabela menos 1.\n", 227 | "\n", 228 | "Para visualizar as últimas linhas dos dados, basta utilizar o método `tail()` do Pandas." 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": { 234 | "colab_type": "text", 235 | "id": "r59tWWe6oK4c" 236 | }, 237 | "source": [ 238 | "3.3 - Utilize a função `tail()` para visualizar as últimas linhas dos dados." 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": { 245 | "colab": { 246 | "base_uri": "https://localhost:8080/", 247 | "height": 326 248 | }, 249 | "colab_type": "code", 250 | "id": "ZtPdbwTD4B1y", 251 | "outputId": "28f8fe3d-3599-4905-ef35-a361ff3f397e" 252 | }, 253 | "outputs": [], 254 | "source": [] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": { 259 | "colab_type": "text", 260 | "id": "vfV4ANFUrFgT" 261 | }, 262 | "source": [ 263 | "## Objetos do Pandas e tipos de dados\n", 264 | "\n", 265 | "O Pandas tem duas estruturas de dados principais, sendo:\n", 266 | "\n", 267 | "**- Series:** representa uma série unidimensional de dados tabulares de uma mesma natureza (números, nomes, idades, etc.).\n", 268 | "**- DataFrame:** coleção de séries, onde cada série pode apresentar sua própria natureza.\n", 269 | "\n", 270 | "O objeto Pandas criado a partir do método `read_csv()` é um DataFrame, já que o arquivo `.csv` apresenta várias características, cada uma interpretada pelo Pandas como uma série. É possível identificar o tipo de um objeto utilizando o método `type()` do Python.\n", 271 | "\n", 272 | "Objetos do tipo `DataFrame` apresentam o método `info()`, que mostra também um sumário dos dados, a quantidade de linhas, o tipo de dados dos atributos (colunas), a quantidade de valores não nulos em cada coluna, entre outras. " 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": { 278 | "colab_type": "text", 279 | "id": "6bSwtUU24Tw5" 280 | }, 281 | "source": [ 282 | "4.1 - Utilize o método `type()` para ver o tipo do objeto associado ao nome `dados`:" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": null, 288 | "metadata": { 289 | "colab": { 290 | "base_uri": "https://localhost:8080/", 291 | "height": 374 292 | }, 293 | "colab_type": "code", 294 | "id": "ooO8jOxZmhXs", 295 | "outputId": "b568c82a-1eb8-42c7-f296-a23cc9039366" 296 | }, 297 | "outputs": [], 298 | "source": [ 299 | "# type(dados)" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "4.2 - Utilize o método `info()` para ver um resumo do seu `DataFrame`:" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": { 313 | "scrolled": true 314 | }, 315 | "outputs": [], 316 | "source": [] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": { 321 | "colab_type": "text", 322 | "id": "775_kWpW63uB" 323 | }, 324 | "source": [ 325 | "\n", 326 | "Ao analisar dados, é importante se certificar de utilizar os tipos de dados corretos. Do contrário, resultados equivocados podem ser obtidos, ou ainda, pode não ser possível executar determinadas operações com alguns atributos.\n", 327 | "O pandas é capaz de inferir alguns tipos de dados, o que permite avança na análise dos dados. Contudo, é importante se certificar de que os tipos determinados automaticamente pelo pandas são os mais indicados para a sua situação. Para mais informações sobre tipos de dados do pandas, consulte esse [link](https://pbpython.com/pandas_dtypes.html).\n", 328 | "\n", 329 | "\n", 330 | "\n" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": { 336 | "colab_type": "text", 337 | "id": "exC3uedG8ja0" 338 | }, 339 | "source": [ 340 | "Os principais tipos de dados do pandas são:\n", 341 | "\n", 342 | "| Tipo | Descrição |\n", 343 | "|------------|------------------|\n", 344 | "| object | Formato de texto |\n", 345 | "| int64 | Números inteiros |\n", 346 | "| float64 | Números decimais |\n", 347 | "| datetime64 | Data e hora |\n", 348 | "| bool | Verdadeiro (V) ou falso (F) |\n", 349 | "\n", 350 | "É importante perceber que colunas do dataframe que não possuam o tipo de dado adequado para determinadas operações não poderão ser analisadas integralmente. Nesses casos, será preciso converter o tipo de dado para um adequado às necessidades de análise. No pandas, o atributo `dtypes` permite visualizar todos os tipos de dados de um dataframe." 351 | ] 352 | }, 353 | { 354 | "cell_type": "markdown", 355 | "metadata": { 356 | "colab_type": "text", 357 | "id": "v20ZUxUVDPHn" 358 | }, 359 | "source": [ 360 | "4.2 - Use o atributo `dtypes` para ver os tipos de dados do dataframe:" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": null, 366 | "metadata": { 367 | "colab": { 368 | "base_uri": "https://localhost:8080/", 369 | "height": 306 370 | }, 371 | "colab_type": "code", 372 | "id": "Y_CI_FiR3GOG", 373 | "outputId": "16b286c5-b166-449f-9e8e-031c7d9bfbfa" 374 | }, 375 | "outputs": [], 376 | "source": [ 377 | "dados.dtypes" 378 | ] 379 | }, 380 | { 381 | "cell_type": "markdown", 382 | "metadata": { 383 | "colab_type": "text", 384 | "id": "z-H_YIaEF-h7" 385 | }, 386 | "source": [ 387 | "É possível converter dados em pandas utilizando o método `astype()`, para forçar um novo tipo em uma coluna do DataFrame. Como exemplo, em um DataFrame de nome *dados*, é possível mudar o tipo de uma coluna da seguinte forma:\n", 388 | "\n", 389 | "```python\n", 390 | "dados['nome_da_coluna'] = dados['nome_da_coluna'].astype('tipo_desejado')\n", 391 | "```" 392 | ] 393 | }, 394 | { 395 | "cell_type": "markdown", 396 | "metadata": { 397 | "colab_type": "text", 398 | "id": "6M6E3nK8HRls" 399 | }, 400 | "source": [ 401 | "4.3 - Utilize o método `astype()` para mudar o tipo da coluna 'PREÇO MÉDIO DE REVENDA' para `float64`." 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": null, 407 | "metadata": { 408 | "colab": { 409 | "base_uri": "https://localhost:8080/", 410 | "height": 306 411 | }, 412 | "colab_type": "code", 413 | "id": "8V39_bbXCx0w", 414 | "outputId": "ae4471d1-b220-4931-f1de-a7d16dcbd6d6" 415 | }, 416 | "outputs": [], 417 | "source": [ 418 | "# dados['PREÇO MÉDIO REVENDA'] = dados['PREÇO MÉDIO REVENDA'].astype('float64')" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": { 424 | "colab_type": "text", 425 | "id": "RDDQ8cy7Iy8G" 426 | }, 427 | "source": [ 428 | "No exercício anterior, o seguinte erro deve ter ocorrido:\n", 429 | "\n", 430 | "```python3\n", 431 | "ValueError: could not convert string to float: '1,948'\n", 432 | "```\n", 433 | "\n", 434 | "Esse erro ocorre por que, para o Python, o delimitador de um número decimal é o ponto e não a vírgula. Assim, para poder converter os dados do nosso DataFrame, seria preciso substituir a vírgula, em cada número de cada coluna, por um ponto." 435 | ] 436 | }, 437 | { 438 | "cell_type": "markdown", 439 | "metadata": { 440 | "colab_type": "text", 441 | "id": "t2XhghSFLJKV" 442 | }, 443 | "source": [ 444 | "O método `read_csv()`, utilizado para ler o arquivo csv e transformá-lo em um objeto pandas do tipo DataFrame, possui vários argumentos (parâmetros que permitem que a função exerça outras tarefas). Um deles permite indicar qual o delimitador de números decimais do conjunto de dados. Podemos utilizar esse argumento conforme segue:\n", 445 | "```python\n", 446 | "dados = read_csv('/content/SEMANAL_BRASIL-DESDE_2013.csv', decimal=',')\n", 447 | "```" 448 | ] 449 | }, 450 | { 451 | "cell_type": "markdown", 452 | "metadata": { 453 | "colab_type": "text", 454 | "id": "x2m5YlM-Mya3" 455 | }, 456 | "source": [ 457 | "4.4 - Leia o arquivo csv novamente e nomeie o `DataFrame` gerado como `dados_2`:\n", 458 | " 1 - informe no método `read_csv()` que o delimitador decimal dos dados é a vírgula, de acordo com o exemplo acima;\n", 459 | " 2 - utilize o atributo `dtypes` e verifique os tipos dos dados" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": null, 465 | "metadata": { 466 | "colab": {}, 467 | "colab_type": "code", 468 | "id": "InupM7IHNHM9" 469 | }, 470 | "outputs": [], 471 | "source": [ 472 | "# use o método read_csv()" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": null, 478 | "metadata": { 479 | "colab": { 480 | "base_uri": "https://localhost:8080/", 481 | "height": 306 482 | }, 483 | "colab_type": "code", 484 | "id": "11wB10qGNJ75", 485 | "outputId": "6e9aaae7-4378-4879-f413-a914023f66b5", 486 | "scrolled": true 487 | }, 488 | "outputs": [], 489 | "source": [ 490 | "# verifique o atributo dtypes" 491 | ] 492 | } 493 | ], 494 | "metadata": { 495 | "colab": { 496 | "collapsed_sections": [], 497 | "name": "pandas-passo_a_passo.ipynb", 498 | "provenance": [], 499 | "toc_visible": true 500 | }, 501 | "kernelspec": { 502 | "display_name": "Python 3", 503 | "language": "python", 504 | "name": "python3" 505 | }, 506 | "language_info": { 507 | "codemirror_mode": { 508 | "name": "ipython", 509 | "version": 3 510 | }, 511 | "file_extension": ".py", 512 | "mimetype": "text/x-python", 513 | "name": "python", 514 | "nbconvert_exporter": "python", 515 | "pygments_lexer": "ipython3", 516 | "version": "3.6.8" 517 | } 518 | }, 519 | "nbformat": 4, 520 | "nbformat_minor": 1 521 | } 522 | -------------------------------------------------------------------------------- /en/notebooks/First_steps.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "First steps", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "display_name": "Python 3", 12 | "language": "python", 13 | "name": "python3" 14 | }, 15 | "language_info": { 16 | "codemirror_mode": { 17 | "name": "ipython", 18 | "version": 3 19 | }, 20 | "file_extension": ".py", 21 | "mimetype": "text/x-python", 22 | "name": "python", 23 | "nbconvert_exporter": "python", 24 | "pygments_lexer": "ipython3", 25 | "version": "3.6.8" 26 | } 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "colab_type": "text", 33 | "id": "7FAHc8hEbt26" 34 | }, 35 | "source": [ 36 | "# First steps with Pandas for data analysis\n", 37 | "\n", 38 | "In this tutorial, we'll use the Python programming language and its Pandas library to analyze tabular data, i.e., any set of information stored as a table, where:\n", 39 | "\n", 40 | "- **Columns** indicate **features**.\n", 41 | "- **Rows** indicate **samples**.\n", 42 | "\n", 43 | "Let's use Pandas to analyze a dataset about fuel prices in Brazil, available [here](https://www.kaggle.com/ficosta/combustible-price-brasil/download). This dataset is store as a `.csv` file (comma-separated values), one of the most used tabular data formats. Next we'll explore the data and see what information it can provide us.\n", 44 | "\n", 45 | "Download the `.zip` file from the link above, unzip it to your computer, and upload the `.csv` file to your Google Drive. Next, run the cell below and follow the instructions to access your Drive files from this Colab." 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "metadata": { 51 | "colab_type": "code", 52 | "id": "y1uAJz-5j5QJ", 53 | "colab": {} 54 | }, 55 | "source": [ 56 | "from google.colab import drive\n", 57 | "drive.mount('/content/drive')" 58 | ], 59 | "execution_count": 0, 60 | "outputs": [] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": { 65 | "colab_type": "text", 66 | "id": "fGnAcspOfQiB" 67 | }, 68 | "source": [ 69 | "## Importing pandas" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": { 75 | "id": "dKSnJZY20Hy0", 76 | "colab_type": "text" 77 | }, 78 | "source": [ 79 | "First, we need to import some resources from the Pandas package to the Colab environment. To import some resource in Python, we must inform which library the resource is coming from. In this example, we will import the `read_csv` resource, a **method** that reads tabular data stored as a `.csv`." 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": { 85 | "colab_type": "text", 86 | "id": "uV4yHsVrKGcU" 87 | }, 88 | "source": [ 89 | "1 - Uncomment the code below (delete the \\# symbol) and press play at the left side of the cell (or use ***shift + enter***)." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "metadata": { 95 | "colab_type": "code", 96 | "id": "IY-aXrzOfJkU", 97 | "colab": {} 98 | }, 99 | "source": [ 100 | "# from pandas import read_csv" 101 | ], 102 | "execution_count": 0, 103 | "outputs": [] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": { 108 | "colab_type": "text", 109 | "id": "lVh_Fcao2vGq" 110 | }, 111 | "source": [ 112 | "## Reading a CSV file" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": { 118 | "colab_type": "text", 119 | "id": "W1gxzWI74jMw" 120 | }, 121 | "source": [ 122 | "Define a name for your dataset (e.g. *data*). The method `read_csv()` reads the CSV file, as follows:\n", 123 | "\n", 124 | "```\n", 125 | "data = read_csv('/path/to/your/file.csv')\n", 126 | "```\n", 127 | "In the example above, the name *data* is associated to a Pandas object. We'll talk about this later.\n", 128 | "\n", 129 | "Obs.: To fetch the file path in Colab, navigate to your file using the folders given on the left panel, right-click the file and choose ***Copy path***." 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": { 135 | "colab_type": "text", 136 | "id": "aswBfMut9At3" 137 | }, 138 | "source": [ 139 | "2 - Create a name for your dataset and read the CSV file using the `read_csv` method." 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "metadata": { 145 | "colab_type": "code", 146 | "id": "PF1KC8RvfjyR", 147 | "colab": {} 148 | }, 149 | "source": [ 150 | "" 151 | ], 152 | "execution_count": 0, 153 | "outputs": [] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": { 158 | "colab_type": "text", 159 | "id": "0YgumbZR9sjr" 160 | }, 161 | "source": [ 162 | "## Exploring the data" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": { 168 | "colab_type": "text", 169 | "id": "GwanfJ3c_AHl" 170 | }, 171 | "source": [ 172 | "After we read the data, we need to find out which **features** it presents and how many **samples** it has. Pandas presents various methods to get information about a dataset. To find out the number of features and samples in a dataset, we can check the `shape` attribute. \n", 173 | "\n", 174 | "In the previous example, you created a Pandas object called `data`. In Python, we check the atributes from an object as follows:\n", 175 | "\n", 176 | "```\n", 177 | "data.attribute_name\n", 178 | "```" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": { 184 | "colab_type": "text", 185 | "id": "jZ4o4uOWWprd" 186 | }, 187 | "source": [ 188 | "3.1 - Find the number of samples and features of the dataset using the `shape` attribute." 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "metadata": { 194 | "colab_type": "code", 195 | "id": "ViMCY5RKuMTT", 196 | "colab": {} 197 | }, 198 | "source": [ 199 | "# data.shape" 200 | ], 201 | "execution_count": 0, 202 | "outputs": [] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": { 207 | "colab_type": "text", 208 | "id": "H988_L2yH7r7" 209 | }, 210 | "source": [ 211 | "It's possible to view the first samples in a dataset using the method `head(n_lines)`. In Python, we refer to attributes and methods in the same way, but in the case of methods we append parentheses to its name. `n_lines` is a positive number that, if passed on as an `argument`, defines how many samples will be displayed.\n", 212 | "\n", 213 | "**Observation**: If you choose not to provide `n_lines`, Python uses 5 as default." 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": { 219 | "colab_type": "text", 220 | "id": "xgjTC1-xJht7" 221 | }, 222 | "source": [ 223 | "3.2 - Using the method `head()`, display the first samples of the dataset. Does the number of features match what the `shape` attribute had indicated?" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "metadata": { 229 | "colab_type": "code", 230 | "id": "cXwBku3IuONg", 231 | "colab": {} 232 | }, 233 | "source": [ 234 | "# data.head(2)" 235 | ], 236 | "execution_count": 0, 237 | "outputs": [] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": { 242 | "colab_type": "text", 243 | "id": "dzm4Q-VrndUi" 244 | }, 245 | "source": [ 246 | "Note that, in the table above, the first sample presents index `0` (zero). Therefore, the index of the last sample in the dataset equals the number of samples minus one. To display the last samples, use the method `tail()`." 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": { 252 | "colab_type": "text", 253 | "id": "r59tWWe6oK4c" 254 | }, 255 | "source": [ 256 | "3.3 - Use the method `tail()` to display the last samples in the dataset." 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "metadata": { 262 | "colab_type": "code", 263 | "id": "ZtPdbwTD4B1y", 264 | "colab": {} 265 | }, 266 | "source": [ 267 | "" 268 | ], 269 | "execution_count": 0, 270 | "outputs": [] 271 | }, 272 | { 273 | "cell_type": "markdown", 274 | "metadata": { 275 | "id": "eE_miTX6eTQr", 276 | "colab_type": "text" 277 | }, 278 | "source": [ 279 | "Since the dataset is provided in Brazilian Portuguese, we will translate the name of the features to help understand the data. \n", 280 | "\n", 281 | "We can do this fiddling with the attribute `columns` of the `DataFrame`:" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "metadata": { 287 | "id": "fQXycpIvdt82", 288 | "colab_type": "code", 289 | "colab": {} 290 | }, 291 | "source": [ 292 | "# data.columns" 293 | ], 294 | "execution_count": 0, 295 | "outputs": [] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": { 300 | "id": "t67ZDvJYfV44", 301 | "colab_type": "text" 302 | }, 303 | "source": [ 304 | "Pandas response is a bit verbose (polluted), but the part that matters to us is the list of column names.\n", 305 | "\n", 306 | "In Python, a list is represented by the notation `[element_1, element_2, ..., element_n]`:\n", 307 | "\n", 308 | "```python3\n", 309 | "['DATA INICIAL', 'DATA FINAL', 'PRODUTO', 'NÚMERO DE POSTOS PESQUISADOS',\n", 310 | " 'UNIDADE DE MEDIDA', 'PREÇO MÉDIO REVENDA', 'DESVIO PADRÃO REVENDA',\n", 311 | " 'PREÇO MÍNIMO REVENDA', 'PREÇO MÁXIMO REVENDA', 'MARGEM MÉDIA REVENDA',\n", 312 | " 'COEF DE VARIAÇÃO REVENDA', 'PREÇO MÉDIO DISTRIBUIÇÃO',\n", 313 | " 'DESVIO PADRÃO DISTRIBUIÇÃO', 'PREÇO MÍNIMO DISTRIBUIÇÃO',\n", 314 | " 'PREÇO MÁXIMO DISTRIBUIÇÃO', 'COEF DE VARIAÇÃO DISTRIBUIÇÃO']\n", 315 | "```\n", 316 | "\n", 317 | "We can replace the list of feature names associating the attribute `columns` to a new list:" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "metadata": { 323 | "id": "tnHseBLeehS_", 324 | "colab_type": "code", 325 | "colab": {} 326 | }, 327 | "source": [ 328 | "data.columns = [\n", 329 | " \"start_date\",\n", 330 | " \"end_date\",\n", 331 | " \"product\",\n", 332 | " \"n_stations\",\n", 333 | " \"metric\",\n", 334 | " \"average_retail_price\",\n", 335 | " \"stddev_retail_price\",\n", 336 | " \"min_retail_price\",\n", 337 | " \"max_retail_price\",\n", 338 | " \"avg_retail_margin\",\n", 339 | " \"retail_variance\",\n", 340 | " \"avg_distribution_price\",\n", 341 | " \"stddev_distribution_price\",\n", 342 | " \"min_distribution_price\",\n", 343 | " \"max_distribution_price\",\n", 344 | " \"distribution_variance\"\n", 345 | " ]" 346 | ], 347 | "execution_count": 0, 348 | "outputs": [] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": { 353 | "colab_type": "text", 354 | "id": "vfV4ANFUrFgT" 355 | }, 356 | "source": [ 357 | "## Pandas objects and data types\n", 358 | "\n", 359 | "Pandas two main data containers:\n", 360 | "- **Series:** represents a unidimensional data series of the same type (numbers, names, ages, etc.).\n", 361 | "- **DataFrame:** collection of series, where each series presents its own type.\n", 362 | "\n", 363 | "The Pandas object we create with the `read_csv()` method is a `DataFrame`, since the `.csv` file presents several features. Each column in that file is interpreted by Pandas as a `Series`. We can check the type of an object using the method `type()`.\n", 364 | "\n", 365 | "In addition, `DataFrame` objects present an `info()` method, which summarizes the data types used to represent features and the number of not null values in each feature, to cite a couple." 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": { 371 | "colab_type": "text", 372 | "id": "6bSwtUU24Tw5" 373 | }, 374 | "source": [ 375 | "4.1 - Use the `type()` method to check the type of the object to which the name `data` refers:" 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "metadata": { 381 | "colab_type": "code", 382 | "id": "ooO8jOxZmhXs", 383 | "colab": {} 384 | }, 385 | "source": [ 386 | "# type(data)" 387 | ], 388 | "execution_count": 0, 389 | "outputs": [] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": { 394 | "id": "U5gO2zgC0Hz3", 395 | "colab_type": "text" 396 | }, 397 | "source": [ 398 | "4.2 - Use the `info()` method to check for a summary of your `DataFrame`:" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "metadata": { 404 | "scrolled": true, 405 | "id": "mtuOPpId0Hz4", 406 | "colab_type": "code", 407 | "colab": {} 408 | }, 409 | "source": [ 410 | "" 411 | ], 412 | "execution_count": 0, 413 | "outputs": [] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "metadata": { 418 | "colab_type": "text", 419 | "id": "775_kWpW63uB" 420 | }, 421 | "source": [ 422 | "When we analyze data, it's important to make sure that the correct data types are being used, since some operations can only be performed for certain data types. Pandas is able to infer some data types, but it's important to double-check the automatically selected types. For more info on Pandas data types, check this [link](https://pbpython.com/pandas_dtypes.html).\n", 423 | "\n", 424 | "\n", 425 | "\n" 426 | ] 427 | }, 428 | { 429 | "cell_type": "markdown", 430 | "metadata": { 431 | "colab_type": "text", 432 | "id": "exC3uedG8ja0" 433 | }, 434 | "source": [ 435 | "Pandas main data types are the following:\n", 436 | "\n", 437 | "| Type | Description |\n", 438 | "|------------|------------------|\n", 439 | "| object | Text format |\n", 440 | "| int64 | Integer numbers |\n", 441 | "| float64 | Decimal numbers |\n", 442 | "| datetime64 | Date and time |\n", 443 | "| bool | True or False |\n", 444 | "\n", 445 | "It's important to notice that dataframe features incorrectly represented will prevent their proper analysis. In this case, we need to convert the data type to the correct one. In Pandas, the attribute `dtypes` gives the data types for all features in a `DataFrame`:" 446 | ] 447 | }, 448 | { 449 | "cell_type": "markdown", 450 | "metadata": { 451 | "colab_type": "text", 452 | "id": "v20ZUxUVDPHn" 453 | }, 454 | "source": [ 455 | "4.2 - Use the `dtypes` attribute to check the data types of the dataframe:" 456 | ] 457 | }, 458 | { 459 | "cell_type": "code", 460 | "metadata": { 461 | "colab_type": "code", 462 | "id": "Y_CI_FiR3GOG", 463 | "colab": {} 464 | }, 465 | "source": [ 466 | "data.dtypes" 467 | ], 468 | "execution_count": 0, 469 | "outputs": [] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": { 474 | "colab_type": "text", 475 | "id": "z-H_YIaEF-h7" 476 | }, 477 | "source": [ 478 | "It's possible to convert data types in Pandas using the `astype()` method. For instance, we can change the type of a feature in a `DataFrame` named `data` as follows:\n", 479 | "\n", 480 | "```python\n", 481 | "data['feature'] = data['feature'].astype('new_type')\n", 482 | "```" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": { 488 | "colab_type": "text", 489 | "id": "6M6E3nK8HRls" 490 | }, 491 | "source": [ 492 | "**4.3** - Use the `astype()` method to change the type of feature 'avg_retail_price' to `float64`." 493 | ] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "metadata": { 498 | "colab_type": "code", 499 | "id": "8V39_bbXCx0w", 500 | "colab": {} 501 | }, 502 | "source": [ 503 | "# data['avg_retail_price'] = data['avg_retail_price'].astype('float64')" 504 | ], 505 | "execution_count": 0, 506 | "outputs": [] 507 | }, 508 | { 509 | "cell_type": "markdown", 510 | "metadata": { 511 | "colab_type": "text", 512 | "id": "RDDQ8cy7Iy8G" 513 | }, 514 | "source": [ 515 | "Running the cell above must have produced the following error:\n", 516 | "\n", 517 | "```python3\n", 518 | "ValueError: could not convert string to float: '1,948'\n", 519 | "```\n", 520 | "\n", 521 | "Python produces this error because the original dataset used commas rather than periods to indicate decimal values. This way, we would need to fix the type of each feature of our `DataFrame`." 522 | ] 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "metadata": { 527 | "colab_type": "text", 528 | "id": "t2XhghSFLJKV" 529 | }, 530 | "source": [ 531 | "Fortunately, the `read_csv()` method presents a number of arguments, one of which indicates to Pandas that the dataset being read is using commas instead of periods:\n", 532 | "```python\n", 533 | "data = read_csv('/content/SEMANAL_BRASIL-DESDE_2013.csv', decimal=',')\n", 534 | "```" 535 | ] 536 | }, 537 | { 538 | "cell_type": "markdown", 539 | "metadata": { 540 | "colab_type": "text", 541 | "id": "x2m5YlM-Mya3" 542 | }, 543 | "source": [ 544 | "4.4 - Read the CSV file again and name it `data2`:\n", 545 | "\n", 546 | "- tell the `read_csv()` method to use the comma as decimal value separator, like in the example above.\n", 547 | "- check the `dtypes` attribute to verify the data types inferred by Pandas." 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "metadata": { 553 | "colab_type": "code", 554 | "id": "InupM7IHNHM9", 555 | "colab": {} 556 | }, 557 | "source": [ 558 | "# use the read_csv() method" 559 | ], 560 | "execution_count": 0, 561 | "outputs": [] 562 | }, 563 | { 564 | "cell_type": "code", 565 | "metadata": { 566 | "colab_type": "code", 567 | "id": "11wB10qGNJ75", 568 | "scrolled": true, 569 | "colab": {} 570 | }, 571 | "source": [ 572 | "# check the dtypes attribute" 573 | ], 574 | "execution_count": 0, 575 | "outputs": [] 576 | } 577 | ] 578 | } -------------------------------------------------------------------------------- /pt-br/notebooks/ETL.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "0B2-Fre4om96" 8 | }, 9 | "source": [ 10 | "# Extração, transformação e carga de dados" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "O processo de ETL é uma parte fundamental do trabalho com dados e consiste em três etapas:\n", 18 | "\n", 19 | "- **Extração**: a coleta de dados, potencialmente a partir de múltiplas fontes heterogêneas. Pode envolver raspagem de páginas web, acesso a interfaces de programação (APIs) ou consultas a bancos de dados.\n", 20 | "- **Transformação**: a reorganização dos dados, envolvendo operações como união, cruzamento e agregação.\n", 21 | "- **Carga**: a persistência do novo conjunto de dados onde se quer armazená-lo.\n", 22 | "\n", 23 | "Este notebook foca em exemplos de métodos de transformação com o Pandas.\n", 24 | "\n", 25 | "Para isso usaremos três dataframes artificiais em nossos exemplos: `df_a`, `df_b` e `df_c`." 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Criando dataframes a partir de dicionários" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "Como vamos criar dataframes customizados para nossos exemplos, precisaremos do auxílio de **dicionários**.\n", 40 | "\n", 41 | "Um dicionário é um tipo de objeto Python que permite armazenar valores indexados por chaves, similar ao que o `DataFrame` do Pandas faz.\n", 42 | "\n", 43 | "Usamos a notação abaixo para criar um dicionário:\n", 44 | "\n", 45 | "```python3\n", 46 | "nome = {\n", 47 | " chave1: valor1,\n", 48 | " chave2: valor2,\n", 49 | " ...\n", 50 | " chaveN: valorN\n", 51 | " }\n", 52 | "```\n", 53 | "\n", 54 | "Acessamos um valor em um dicionário através da sua chave, usando a notação `dicionário[chave]`.\n", 55 | "\n", 56 | "No exemplo a seguir, o dicionário `dados_df_a` têm como chaves os nomes das séries associadas:" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": { 63 | "colab": {}, 64 | "colab_type": "code", 65 | "id": "nlIGu2BGr8ei" 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "import pandas as pd" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": { 76 | "colab": { 77 | "base_uri": "https://localhost:8080/", 78 | "height": 206 79 | }, 80 | "colab_type": "code", 81 | "id": "kqM_UUgHpnvg", 82 | "outputId": "fa6fdb9d-d936-4692-f27b-e96db6ca8497" 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "dados_df_a = {\n", 87 | " 'id_indivíduo': ['1', '2', '3', '4', '5'],\n", 88 | " 'nome': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung'], \n", 89 | " 'sobrenome': ['Anderson', 'Ackerman', 'Ali', 'Aoni', 'Atiches']\n", 90 | " }" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "Note que cada série é representada como uma lista.\n", 98 | "\n", 99 | "Criar um `DataFrame` a partir de um dicionário é bem simples:" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "df_a = pd.DataFrame(dados_df_a)\n", 109 | "df_a" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "Seguindo o mesmo modelo, vamos criar o dataframe `df_b`:" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": { 123 | "colab": { 124 | "base_uri": "https://localhost:8080/", 125 | "height": 206 126 | }, 127 | "colab_type": "code", 128 | "id": "x3Zbn8mZpq14", 129 | "outputId": "1f8cb4f1-13f0-4188-bce3-1e063c9aac77" 130 | }, 131 | "outputs": [], 132 | "source": [ 133 | "dados_df_b = {\n", 134 | " 'id_indivíduo': ['4', '5', '6', '7', '8'],\n", 135 | " 'nome': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'], \n", 136 | " 'sobrenome': ['Bonder', 'Black', 'Balwner', 'Brice', 'Btisan']\n", 137 | " }" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "df_b = pd.DataFrame(dados_df_b)\n", 147 | "df_b" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": { 154 | "colab": { 155 | "base_uri": "https://localhost:8080/", 156 | "height": 363 157 | }, 158 | "colab_type": "code", 159 | "id": "ta7bz-NupvaQ", 160 | "outputId": "fa86c30f-2931-4cb5-e13d-bf14407433e9" 161 | }, 162 | "outputs": [], 163 | "source": [ 164 | "dados_df_c = {\n", 165 | " 'id_indivíduo': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'],\n", 166 | " 'id_exame': [51, 15, 15, 61, 16, 14, 15, 1, 61, 16]\n", 167 | " }" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "df_c = pd.DataFrame(dados_df_c)\n", 177 | "df_c" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": { 183 | "colab_type": "text", 184 | "id": "b7N9kTsvq7je" 185 | }, 186 | "source": [ 187 | "## União de dados" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "Uma das operações comuns é unir observações que apresentam as mesmas características, mas estão em diferentes dataframes. \n", 195 | "\n", 196 | "Para isso usaremos o comando `concat` que recebe uma lista com ***n*** objetos `DataFrame` como parâmetro." 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": null, 202 | "metadata": { 203 | "colab": { 204 | "base_uri": "https://localhost:8080/", 205 | "height": 363 206 | }, 207 | "colab_type": "code", 208 | "id": "vooEfpVNqBWO", 209 | "outputId": "6ff866f5-1291-44d3-dff8-8555f549fdbf" 210 | }, 211 | "outputs": [], 212 | "source": [ 213 | "df_new = pd.concat([df_a, df_b])\n", 214 | "df_new" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "Também seria possíve unir objetos `DataFrame` com características distintas.\n", 222 | "\n", 223 | "No entanto, essa operação produziria um `DataFrame` com muitos dados faltando:" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "pd.concat([df_a, df_c])" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": { 238 | "colab_type": "text", 239 | "id": "VORqJSeiro6V" 240 | }, 241 | "source": [ 242 | "## Cruzando dados" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "No exemplo anterior, vimos o resultado de unir dataframes cujas características não são idênticas.\n", 250 | "\n", 251 | "No entanto, quanto temos pelo menos uma característica em comum entre dois dataframes, podemos **cruzar**\n", 252 | " esses dados, produzindo um novo dataframe que reúne toda a informação dos dataframes originais.\n", 253 | "\n", 254 | "No exemplo, abaixo as observações do dataframe **à esquerda** (`df_a`) e do dataframe **à direita** (`df_c`) foram cruzadas, tomando como característica em comum `id_indivíduo`. \n", 255 | "\n", 256 | "Como você pode ver, o novo dataframe reúne as informações de ambos os dataframes usados no cruzamento dos dados:" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": { 263 | "colab": { 264 | "base_uri": "https://localhost:8080/", 265 | "height": 206 266 | }, 267 | "colab_type": "code", 268 | "id": "hOYwt4AhrASD", 269 | "outputId": "865c76f2-a7ba-481a-eb78-d11bc1150198" 270 | }, 271 | "outputs": [], 272 | "source": [ 273 | "pd.merge(df_a, df_c, on='id_indivíduo')" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "Em algumas situações, a mesma característica pode estar representada por diferentes nomes nos dataframes que se deseja cruzar.\n", 281 | "\n", 282 | "Nesses casos, podemos usar os argumentos `left_on` e `right_on` para especificar, respectivamente, os nomes da característica no dataframe à esquerda e no dataframe à direita." 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "### Tipos de cruzamento" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": { 295 | "colab_type": "text", 296 | "id": "AFgwiPl1tfpZ" 297 | }, 298 | "source": [ 299 | "Uma operação de cruzamento de dados combina dados de dois dataframes que apresentem uma característica em comum.\n", 300 | "\n", 301 | "No exemplo anterior, a característica em comum era o campo `id_indivíduo`.\n", 302 | "\n", 303 | "Note que as observações presentes no dataframe `df_c` cujos valores para `id_indivíduo` não estão presentes no dataframe `df_a` não foram mostradas.\n", 304 | "\n", 305 | "Se quisermos que essas observações sejam preservadas, podemos usar um **cruzamento à direita**." 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": { 312 | "colab": { 313 | "base_uri": "https://localhost:8080/", 314 | "height": 363 315 | }, 316 | "colab_type": "code", 317 | "id": "6d1e2sotriDg", 318 | "outputId": "b330cb6a-3031-4cde-da88-8fbc19d04e7b" 319 | }, 320 | "outputs": [], 321 | "source": [ 322 | "pd.merge(df_a, df_c, on='id_indivíduo', how='right')" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "O resultado acima mostra tanto as observações com `id_indivíduo` presentes nos dois dataframes como o restante das observações do dataframe à direita. \n", 330 | "\n", 331 | "Note que as observações adicionadas pelo cruzamento à direita apresentam dados faltando.\n", 332 | "\n", 333 | "O mesmo aconteceria se usássemos um **cruzamento à esquerda**:" 334 | ] 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": null, 339 | "metadata": { 340 | "colab": { 341 | "base_uri": "https://localhost:8080/", 342 | "height": 206 343 | }, 344 | "colab_type": "code", 345 | "id": "eQtXG1O9tyxC", 346 | "outputId": "520afea5-31ae-449f-9989-8547e3f1edc0" 347 | }, 348 | "outputs": [], 349 | "source": [ 350 | "pd.merge(df_b, df_c, on='id_indivíduo', how='left')" 351 | ] 352 | }, 353 | { 354 | "cell_type": "markdown", 355 | "metadata": {}, 356 | "source": [ 357 | "Nesse caso, a observação do dataframe `df_b` cujo `id_indivíduo` não estava presente no dataframe `df_c` foi mantida.\n", 358 | "\n", 359 | "Em um caso mais extremo, podemos usar um **cruzamento externo**, que mantém todas as observações de ambos os dataframes:" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "metadata": { 366 | "scrolled": true 367 | }, 368 | "outputs": [], 369 | "source": [ 370 | "pd.merge(df_b, df_c, on='id_indivíduo', how='outer')" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": {}, 376 | "source": [ 377 | "## Agregando dados" 378 | ] 379 | }, 380 | { 381 | "cell_type": "markdown", 382 | "metadata": { 383 | "colab_type": "text", 384 | "id": "mr0bivNhz5g1" 385 | }, 386 | "source": [ 387 | "As operações de união e cruzamento tem por objetivo reunir informações espalhadas em múltiplas bases em um único dataframe.\n", 388 | "\n", 389 | "Um tipo complementar de operação é a **agregação**, que visa resumir blocos de informações através de estatísticas descritivas. \n", 390 | "\n", 391 | "As principais formas de agregação são obtidas por meio de pivoteamento, seja unidimensional (**grupos**) ou bidimensional (**tabelas dinâmicas**). " 392 | ] 393 | }, 394 | { 395 | "cell_type": "markdown", 396 | "metadata": {}, 397 | "source": [ 398 | "### Grupos" 399 | ] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "metadata": {}, 404 | "source": [ 405 | "Organizar os dados em grupos pode ser útil tanto para analisar cada grupo como para calcular estatísticas por grupo.\n", 406 | "\n", 407 | "O primeiro passo da agregação é definir uma ou mais características usadas como fatores do agrupamento.\n", 408 | "\n", 409 | "No exemplo abaixo, agrupamos os dados do dataset `iris`.\n", 410 | "\n", 411 | "Este dataset é o mais baixado do repositório de aprendizado de máquina [UCI](https://archive.ics.uci.edu/ml/), listando medidas de pétalas e sépalas de três espécies de flores de íris.\n", 412 | "\n", 413 | "Por conveniência, vamos baixá-lo da biblioteca `seaborn`:" 414 | ] 415 | }, 416 | { 417 | "cell_type": "code", 418 | "execution_count": null, 419 | "metadata": { 420 | "colab": { 421 | "base_uri": "https://localhost:8080/", 422 | "height": 424 423 | }, 424 | "colab_type": "code", 425 | "id": "L4hQN72UwOXG", 426 | "outputId": "0074b94b-b657-483e-dbd9-dc622729eb20", 427 | "scrolled": false 428 | }, 429 | "outputs": [], 430 | "source": [ 431 | "import seaborn as sns\n", 432 | "dados_íris = sns.load_dataset('iris')\n", 433 | "dados_íris" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "Como podemos ver, o dataset contém largura e altura das sépalas e pétalas de 150 amostras de flor íris.\n", 441 | "\n", 442 | "Vamos ver quantos exemplos temos por espécie:" 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": null, 448 | "metadata": { 449 | "scrolled": true 450 | }, 451 | "outputs": [], 452 | "source": [ 453 | "dados_íris['species'].value_counts()" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": {}, 459 | "source": [ 460 | "Para agrupar este dataset por espécie, podemos usar o método `groupby()`:" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": null, 466 | "metadata": {}, 467 | "outputs": [], 468 | "source": [ 469 | "grupos_íris = dados_íris.groupby(['species'])" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": {}, 475 | "source": [ 476 | "Podemos, então, tratar cada um grupo como um `DataFrame` usando o método `get_group()`:" 477 | ] 478 | }, 479 | { 480 | "cell_type": "code", 481 | "execution_count": null, 482 | "metadata": { 483 | "colab": { 484 | "base_uri": "https://localhost:8080/", 485 | "height": 206 486 | }, 487 | "colab_type": "code", 488 | "id": "au2wu-eoz7xy", 489 | "outputId": "0fe0e347-1391-4334-8416-859a27cdc425" 490 | }, 491 | "outputs": [], 492 | "source": [ 493 | "grupos_íris.get_group('versicolor').head()" 494 | ] 495 | }, 496 | { 497 | "cell_type": "markdown", 498 | "metadata": { 499 | "colab_type": "text", 500 | "id": "V9KZvaTs3xeE" 501 | }, 502 | "source": [ 503 | "O agrupamento nos permite computar estatísticas sobre os grupos ao mesmo tempo ou individualmente:" 504 | ] 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "metadata": {}, 509 | "source": [ 510 | "#### Ao mesmo tempo" 511 | ] 512 | }, 513 | { 514 | "cell_type": "code", 515 | "execution_count": null, 516 | "metadata": { 517 | "colab": { 518 | "base_uri": "https://localhost:8080/", 519 | "height": 175 520 | }, 521 | "colab_type": "code", 522 | "id": "ExMniwVl2HzQ", 523 | "outputId": "20357bce-ccfc-4526-c72e-562cf503cc90", 524 | "scrolled": true 525 | }, 526 | "outputs": [], 527 | "source": [ 528 | "grupos_íris.min()" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": null, 534 | "metadata": { 535 | "colab": { 536 | "base_uri": "https://localhost:8080/", 537 | "height": 175 538 | }, 539 | "colab_type": "code", 540 | "id": "vU8eE5YI38a8", 541 | "outputId": "8634478d-7955-4164-dcec-6818f746c019", 542 | "scrolled": true 543 | }, 544 | "outputs": [], 545 | "source": [ 546 | "grupos_íris.max()" 547 | ] 548 | }, 549 | { 550 | "cell_type": "code", 551 | "execution_count": null, 552 | "metadata": { 553 | "colab": { 554 | "base_uri": "https://localhost:8080/", 555 | "height": 175 556 | }, 557 | "colab_type": "code", 558 | "id": "Z0aRCbeb0MZx", 559 | "outputId": "85d4e9db-70c2-4835-c4a0-1c8ac42608f1", 560 | "scrolled": false 561 | }, 562 | "outputs": [], 563 | "source": [ 564 | "grupos_íris.mean()" 565 | ] 566 | }, 567 | { 568 | "cell_type": "markdown", 569 | "metadata": {}, 570 | "source": [ 571 | "#### Individualmente" 572 | ] 573 | }, 574 | { 575 | "cell_type": "code", 576 | "execution_count": null, 577 | "metadata": { 578 | "colab": { 579 | "base_uri": "https://localhost:8080/", 580 | "height": 175 581 | }, 582 | "colab_type": "code", 583 | "id": "Z0aRCbeb0MZx", 584 | "outputId": "85d4e9db-70c2-4835-c4a0-1c8ac42608f1", 585 | "scrolled": false 586 | }, 587 | "outputs": [], 588 | "source": [ 589 | "grupos_íris.get_group(\"versicolor\").describe()" 590 | ] 591 | }, 592 | { 593 | "cell_type": "code", 594 | "execution_count": null, 595 | "metadata": { 596 | "scrolled": true 597 | }, 598 | "outputs": [], 599 | "source": [ 600 | "grupo_versicolor = grupos_íris.get_group(\"versicolor\")\n", 601 | "grupo_versicolor.count()" 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "metadata": {}, 607 | "source": [ 608 | "#### Agregando por múltiplas características" 609 | ] 610 | }, 611 | { 612 | "cell_type": "markdown", 613 | "metadata": {}, 614 | "source": [ 615 | "Um recurso poderoso do Pandas é permitir agregações a partir de múltiplas características.\n", 616 | "\n", 617 | "Em geral, usamos esse recurso quando temos um conjunto de dados que apresentam características categóricas e númericas.\n", 618 | "\n", 619 | "No dataset `iris`, no entanto, temos apenas uma características categórica disponível.\n", 620 | "\n", 621 | "Vamos aproveitar essa situação e dar uma olhada em um recurso bem legal do Pandas, chamado discretização em intervalos:" 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "metadata": { 628 | "scrolled": true 629 | }, 630 | "outputs": [], 631 | "source": [ 632 | "pd.cut(dados_íris[\"petal_width\"], bins=3)" 633 | ] 634 | }, 635 | { 636 | "cell_type": "markdown", 637 | "metadata": {}, 638 | "source": [ 639 | "Entendeu o que aconteceu? \n", 640 | "\n", 641 | "O método `cut()` calculou os valores máximo e mínimo para a característica `petal_width` e dividiu esse intervalo em três subintervalos.\n", 642 | "\n", 643 | "Assim, cada um dos valores originais foi substituído pelo subintervalo ao qual ele pertecene e passamos ter uma varíavel categórica 😄\n", 644 | "\n", 645 | "Vamos substituir os dados originais pelos dados categorizados:" 646 | ] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": null, 651 | "metadata": {}, 652 | "outputs": [], 653 | "source": [ 654 | "dados_íris[\"petal_width\"] = pd.cut(dados_íris[\"petal_width\"], bins=3)\n", 655 | "dados_íris" 656 | ] 657 | }, 658 | { 659 | "cell_type": "markdown", 660 | "metadata": {}, 661 | "source": [ 662 | "Um recurso adicional do Pandas para lidar com características categóricas é renomear as categorias.\n", 663 | "\n", 664 | "Vamos renomear os subintervalos gerados.\n", 665 | "\n", 666 | "Note que desta vez estamos alterando os dados originais diretamente usando a opção `inplace=True` (quase todos os métodos Pandas aceitam essa opção)." 667 | ] 668 | }, 669 | { 670 | "cell_type": "code", 671 | "execution_count": null, 672 | "metadata": { 673 | "scrolled": true 674 | }, 675 | "outputs": [], 676 | "source": [ 677 | "dados_íris[\"petal_width\"].cat.rename_categories([\"low\", \"medium\", \"high\"], inplace=True)\n", 678 | "dados_íris" 679 | ] 680 | }, 681 | { 682 | "cell_type": "markdown", 683 | "metadata": {}, 684 | "source": [ 685 | "Agora que nosso dataset apresenta duas características categóricas, podemos fazer agregações por múltiplas características:" 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": null, 691 | "metadata": {}, 692 | "outputs": [], 693 | "source": [ 694 | "grupo2_íris = dados_íris.groupby([\"species\",\"petal_width\"]).size()\n", 695 | "grupo2_íris" 696 | ] 697 | }, 698 | { 699 | "cell_type": "markdown", 700 | "metadata": {}, 701 | "source": [ 702 | "Neste caso, em vez de produzirmos os grupos, produzimos diretamente a agregação usando o método `size()`, que conta o tamanho de cada grupo.\n", 703 | "\n", 704 | "Pelos dados acima, podemos verificar que todas as flores de íris da espécie `setosa` presentes no dataset apresentam uma largura de pétala pequena.\n", 705 | "\n", 706 | "Também é possível fazer uma excelente separação entre as espécies `versicolor` e `virginica`.\n", 707 | "\n", 708 | "Note que os dados acima são uma série que apresentam um índice em múltiplos níveis (conhecido no Pandas como `MultiIndex`):" 709 | ] 710 | }, 711 | { 712 | "cell_type": "code", 713 | "execution_count": null, 714 | "metadata": {}, 715 | "outputs": [], 716 | "source": [ 717 | "grupo2_íris.index" 718 | ] 719 | }, 720 | { 721 | "cell_type": "markdown", 722 | "metadata": {}, 723 | "source": [ 724 | "Em meio às mensagens verbosas do Pandas, vemos que há dois níveis neste índice (`levels`), cujos nomes (`names`) são `species` e `petal_width`.\n", 725 | "\n", 726 | "Podemos indexar esta série de várias formas diferentes" 727 | ] 728 | }, 729 | { 730 | "cell_type": "code", 731 | "execution_count": null, 732 | "metadata": {}, 733 | "outputs": [], 734 | "source": [ 735 | "grupo2_íris[\"virginica\",\"high\"]" 736 | ] 737 | }, 738 | { 739 | "cell_type": "code", 740 | "execution_count": null, 741 | "metadata": {}, 742 | "outputs": [], 743 | "source": [ 744 | "grupo2_íris[\"virginica\",]" 745 | ] 746 | }, 747 | { 748 | "cell_type": "code", 749 | "execution_count": null, 750 | "metadata": {}, 751 | "outputs": [], 752 | "source": [ 753 | "grupo2_íris[:,\"high\"]" 754 | ] 755 | }, 756 | { 757 | "cell_type": "markdown", 758 | "metadata": {}, 759 | "source": [ 760 | "Também podemos converter essa série em um `DataFrame`. \n", 761 | "\n", 762 | "Para isso, usamos o método `reset_index()` e informamos o nome que queremos dar à série:" 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": null, 768 | "metadata": { 769 | "scrolled": false 770 | }, 771 | "outputs": [], 772 | "source": [ 773 | "df_íris = grupo2_íris.reset_index(name=\"count\")\n", 774 | "df_íris" 775 | ] 776 | }, 777 | { 778 | "cell_type": "markdown", 779 | "metadata": {}, 780 | "source": [ 781 | "### Tabelas dinâmicas" 782 | ] 783 | }, 784 | { 785 | "cell_type": "markdown", 786 | "metadata": {}, 787 | "source": [ 788 | "Uma outra forma de agregação disponível no Pandas é através de tabelas dinâmicas.\n", 789 | "\n", 790 | "Neste caso, usamos o método `pivot_table()` e devemos informar as caraterísticas para o agrupamento a nível de linhas (`index`) e de colunas (`columns`).\n", 791 | "\n", 792 | "Também podemos informar um método de agregação usando a opção `aggfunc`, que por padrão calcula a média:" 793 | ] 794 | }, 795 | { 796 | "cell_type": "code", 797 | "execution_count": null, 798 | "metadata": { 799 | "scrolled": true 800 | }, 801 | "outputs": [], 802 | "source": [ 803 | "pt_íris = dados_íris.pivot_table(index=\"species\", columns=\"petal_width\", aggfunc=\"size\")\n", 804 | "pt_íris" 805 | ] 806 | }, 807 | { 808 | "cell_type": "markdown", 809 | "metadata": {}, 810 | "source": [ 811 | "Note que a tabela dinâmica tenta gerar todas as combinações possíveis entre os valores das característica de linha e de coluna.\n", 812 | "\n", 813 | "Como nosso dataset não apresenta observações da espécie `setosa` com largura de pétala `medium` ou `high`, esses valores são marcados como faltando/inválidos.\n", 814 | "\n", 815 | "O método `pivot_table()` fornece a opção `fill_value`, que nos permite escolher como preencher esses casos:" 816 | ] 817 | }, 818 | { 819 | "cell_type": "code", 820 | "execution_count": null, 821 | "metadata": { 822 | "scrolled": false 823 | }, 824 | "outputs": [], 825 | "source": [ 826 | "pt_íris = dados_íris.pivot_table(index=\"species\", columns=\"petal_width\", aggfunc=\"size\", fill_value=0)\n", 827 | "pt_íris" 828 | ] 829 | }, 830 | { 831 | "cell_type": "markdown", 832 | "metadata": {}, 833 | "source": [ 834 | "O método `pivot_table()` produz um objeto do tipo `DataFrame`.\n", 835 | "\n", 836 | "Assim, a indexação funciona da maneira como já conhecemos:" 837 | ] 838 | }, 839 | { 840 | "cell_type": "code", 841 | "execution_count": null, 842 | "metadata": {}, 843 | "outputs": [], 844 | "source": [ 845 | "pt_íris.loc[\"versicolor\"]" 846 | ] 847 | }, 848 | { 849 | "cell_type": "code", 850 | "execution_count": null, 851 | "metadata": {}, 852 | "outputs": [], 853 | "source": [ 854 | "pt_íris.loc[\"versicolor\",\"low\"]" 855 | ] 856 | }, 857 | { 858 | "cell_type": "code", 859 | "execution_count": null, 860 | "metadata": {}, 861 | "outputs": [], 862 | "source": [ 863 | "pt_íris.loc[:,\"low\"]" 864 | ] 865 | } 866 | ], 867 | "metadata": { 868 | "colab": { 869 | "name": "Aula 01 - Mineração.ipynb", 870 | "provenance": [], 871 | "toc_visible": true 872 | }, 873 | "kernelspec": { 874 | "display_name": "Python 3", 875 | "language": "python", 876 | "name": "python3" 877 | }, 878 | "language_info": { 879 | "codemirror_mode": { 880 | "name": "ipython", 881 | "version": 3 882 | }, 883 | "file_extension": ".py", 884 | "mimetype": "text/x-python", 885 | "name": "python", 886 | "nbconvert_exporter": "python", 887 | "pygments_lexer": "ipython3", 888 | "version": "3.6.8" 889 | } 890 | }, 891 | "nbformat": 4, 892 | "nbformat_minor": 1 893 | } 894 | -------------------------------------------------------------------------------- /pt-br/notebooks/DataframeBD.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "DataframeBD.ipynb", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "display_name": "Python 3", 12 | "language": "python", 13 | "name": "python3" 14 | }, 15 | "language_info": { 16 | "codemirror_mode": { 17 | "name": "ipython", 18 | "version": 3 19 | }, 20 | "file_extension": ".py", 21 | "mimetype": "text/x-python", 22 | "name": "python", 23 | "nbconvert_exporter": "python", 24 | "pygments_lexer": "ipython3", 25 | "version": "3.6.8" 26 | } 27 | }, 28 | "cells": [ 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "colab_type": "text", 33 | "id": "E-vde5UCQRkQ" 34 | }, 35 | "source": [ 36 | "# Dataframes como bancos de dados" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "colab_type": "text", 43 | "id": "9He81AacH7Dk" 44 | }, 45 | "source": [ 46 | "Os dataframes do Pandas oferecem diferentes formas de consultar dados.\n", 47 | "\n", 48 | "Em alguns casos, essas consultas podem se tornar tão elaboradas como em bancos de dados tradicionais.\n", 49 | "\n", 50 | "Neste notebook, vamos ver como fazer consultas simples a um dataset carregado da internet.\n", 51 | "\n", 52 | "## Carregando dados da internet\n", 53 | "Há várias formas de se trabalhar com dados carregados da internet. \n", 54 | "\n", 55 | "Aqui, vamos baixar o dataset usando uma ferramenta Linux e carregá-lo no Pandas." 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": { 61 | "colab_type": "text", 62 | "id": "MyR5JpdlW4Oq" 63 | }, 64 | "source": [ 65 | "### Baixando um dataset com `wget`\n", 66 | "\n", 67 | "A execução de um notebook no Colab é feita em um computador na infraestrutura da Google.\n", 68 | "\n", 69 | "Os computadores da Google usam o sistema operacional Linux e nós podemos aproveitar isso quando algum programa do Linux pode nos ajudar.\n", 70 | "\n", 71 | "Um exemplo é o programa ``wget``, que baixa a URL que informamos.\n", 72 | "\n", 73 | "Para rodar um programa Linux no Colab, temos que fazer isso pelas células de código, usando comandos do terminal Linux iniciados pelo símbolo ``!``\n", 74 | "\n", 75 | "Nesse caso, usei o ``wget`` para fazer o download de um dataset do portal de dados abertos da UFRN que contém os discentes ingressantes em 2019:" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "metadata": { 81 | "colab_type": "code", 82 | "id": "OxJWg_znp9sr", 83 | "colab": {} 84 | }, 85 | "source": [ 86 | "!wget http://dados.ufrn.br/dataset/554c2d41-cfce-4278-93c6-eb9aa49c5d16/resource/a55aef81-e094-4267-8643-f283524e3dd7/download/discentes-2019.csv" 87 | ], 88 | "execution_count": 0, 89 | "outputs": [] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": { 94 | "colab_type": "text", 95 | "id": "uGNACj0msDF4" 96 | }, 97 | "source": [ 98 | "O arquivo ``discentes-2019.csv`` deve aparecer na lista de arquivos do lado esquerdo da tela.\n", 99 | "\n", 100 | "### Carregando o dataset\n", 101 | "\n", 102 | "Vamos carregar o arquivo como um dataframe do Pandas:" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "metadata": { 108 | "colab_type": "code", 109 | "id": "gwSjQx6bvTCM", 110 | "colab": {} 111 | }, 112 | "source": [ 113 | "import pandas as pd\n", 114 | "data = pd.read_csv('discentes-2019.csv', sep=';')\n", 115 | "data.head()" 116 | ], 117 | "execution_count": 0, 118 | "outputs": [] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": { 123 | "colab_type": "text", 124 | "id": "cxMtfVo6sqgl" 125 | }, 126 | "source": [ 127 | "Caso você tenha tido alguma dúvida, vamos rever o código acima:\n", 128 | " - ```python\n", 129 | " import pandas as pd\n", 130 | " ```\n", 131 | " Importamos o Pandas e pedimos para chamá-lo de ``pd``\n", 132 | " ```python\n", 133 | "data = pd.read_csv('discentes-2019.csv', sep=';')\n", 134 | " ```\n", 135 | " - Como usamos um nome para o Pandas, todos os seus comandos serão localizados a partir desse nome (ex.: ``pd.read_csv()``)\n", 136 | " - Informamos o caracter que é usado no dataset como delimitador de características usando a opção ``sep=';'`` (normalmente o Pandas consegue detectar isso automaticamente, mas em datasets brasileiros é comum dar errado)\n", 137 | " ```python\n", 138 | " data.head()\n", 139 | " ```\n", 140 | " Visualizamos as primeiras observações do dataset com o método ``head()``\n" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": { 146 | "colab_type": "text", 147 | "id": "0bLccn8pwD8o" 148 | }, 149 | "source": [ 150 | "## Consultando um dataframe\n", 151 | "\n", 152 | "Bom, já temos nosso dataframe pronto para consultas.\n", 153 | "\n", 154 | "As formas mais simples de consulta são a **indexação** e o **fatiamento**.\n", 155 | "\n", 156 | "### Indexando um dataset\n", 157 | "\n", 158 | "Consultas em um dataframe Pandas são feitas a partir de **índices**.\n", 159 | "\n", 160 | "O índice principal em um dataframe é o das colunas, que representam as características:\n", 161 | "\n" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "metadata": { 167 | "colab_type": "code", 168 | "id": "EPanQZfunULU", 169 | "colab": {} 170 | }, 171 | "source": [ 172 | "data.columns" 173 | ], 174 | "execution_count": 0, 175 | "outputs": [] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": { 180 | "colab_type": "text", 181 | "id": "Q99w62hIGwxo" 182 | }, 183 | "source": [ 184 | "A resposta do Pandas é um pouco verbosa (poluída), mas a parte que nos importa é a lista de nomes de colunas.\n", 185 | "\n", 186 | "Em Python, uma lista é representada pela notação `[elemento_1, elemento_2, ..., elemento_n]`:\n", 187 | "\n", 188 | "````python\n", 189 | "['matricula', 'nome_discente', 'sexo', 'ano_ingresso',\n", 190 | "'periodo_ingresso', 'forma_ingresso', 'tipo_discente', 'status',\n", 191 | "'sigla_nivel_ensino', 'nivel_ensino', 'id_curso', 'nome_curso',\n", 192 | "'modalidade_educacao', 'id_unidade', 'nome_unidade',\n", 193 | "'id_unidade_gestora', 'nome_unidade_gestora']\n", 194 | "````\n" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": { 200 | "colab_type": "text", 201 | "id": "QQkfJXoWHJ18" 202 | }, 203 | "source": [ 204 | "Isto significa que podemos acessar qualquer uma dessas colunas do dataframe usando as notações `data['nome_da_coluna']` e `data.nome_da_coluna`\n", 205 | "\n", 206 | "Como cada coluna é considerada uma série (objeto do tipo `Series`), podemos usar os métodos desse tipo:" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "metadata": { 212 | "colab_type": "code", 213 | "id": "PWnsq32eHJTB", 214 | "colab": {} 215 | }, 216 | "source": [ 217 | "data[\"nome_discente\"].head()" 218 | ], 219 | "execution_count": 0, 220 | "outputs": [] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "metadata": { 225 | "colab_type": "code", 226 | "id": "PKFVIl4uJO_w", 227 | "colab": {} 228 | }, 229 | "source": [ 230 | "data.nome_unidade.tail()" 231 | ], 232 | "execution_count": 0, 233 | "outputs": [] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": { 238 | "colab_type": "text", 239 | "id": "MuP5PPjsJY4p" 240 | }, 241 | "source": [ 242 | "Os dados em uma série também estão indexados. \n", 243 | "\n", 244 | "Podemos acessá-los individualmente usando a notação `série[número_da_linha]`:" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "metadata": { 250 | "colab_type": "code", 251 | "id": "kiTFTNURKj1y", 252 | "colab": {} 253 | }, 254 | "source": [ 255 | "nomes_discentes = data[\"nome_discente\"]\n", 256 | "nomes_discentes[0]" 257 | ], 258 | "execution_count": 0, 259 | "outputs": [] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "metadata": { 264 | "colab_type": "code", 265 | "id": "FXpX4DS1LWCw", 266 | "colab": {} 267 | }, 268 | "source": [ 269 | "data[\"nome_discente\"][0]" 270 | ], 271 | "execution_count": 0, 272 | "outputs": [] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "metadata": { 277 | "colab_type": "code", 278 | "id": "HcZGxZm1LiXQ", 279 | "colab": {} 280 | }, 281 | "source": [ 282 | "data.nome_unidade[0]" 283 | ], 284 | "execution_count": 0, 285 | "outputs": [] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": { 290 | "colab_type": "text", 291 | "id": "Zd8LWkveyWBv" 292 | }, 293 | "source": [ 294 | "Também é possível acessar diretamente os dados usando os métodos `loc` e `iloc`:\n", 295 | "- Se referindo às colunas pelos seus nomes, usando a notação `data.loc[linha, nome_coluna]`\n", 296 | ":" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "metadata": { 302 | "colab_type": "code", 303 | "id": "WgePGZkWNOU1", 304 | "colab": {} 305 | }, 306 | "source": [ 307 | "data.loc[0, \"nome_discente\"]" 308 | ], 309 | "execution_count": 0, 310 | "outputs": [] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": { 315 | "colab_type": "text", 316 | "id": "mWl7e570OUP0" 317 | }, 318 | "source": [ 319 | "- Se referindo às colunas pela sua posição no índice de colunas, usando a notação `data.iloc[linha, índice_coluna]`" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "metadata": { 325 | "colab_type": "code", 326 | "id": "T7Gq23b2Nohl", 327 | "colab": {} 328 | }, 329 | "source": [ 330 | "data.iloc[0, 1]" 331 | ], 332 | "execution_count": 0, 333 | "outputs": [] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "metadata": { 338 | "colab_type": "text", 339 | "id": "BcDrpBu1Ph04" 340 | }, 341 | "source": [ 342 | "Note que os índices são contados a partir do número 0. Como `\"nome_discente\"` é a segunda coluna, usamos o índice 1 para acessá-la.\n", 343 | "\n", 344 | "Os métodos `loc` e `iloc` também aceitam que você informe uma lista de índices." 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "metadata": { 350 | "colab_type": "code", 351 | "id": "7QUL-EOQQn2o", 352 | "colab": {} 353 | }, 354 | "source": [ 355 | "data.loc[0, [\"nome_discente\",\"nome_curso\"]]" 356 | ], 357 | "execution_count": 0, 358 | "outputs": [] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "metadata": { 363 | "colab_type": "code", 364 | "id": "p8oN5MMZRBwi", 365 | "colab": {} 366 | }, 367 | "source": [ 368 | "data.iloc[[1,3,7], 1]" 369 | ], 370 | "execution_count": 0, 371 | "outputs": [] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": { 376 | "colab_type": "text", 377 | "id": "ogHt3AlMRG5i" 378 | }, 379 | "source": [ 380 | "**Observação:** Para quem conhece um pouco mais sobre Python, os métodos `loc` e `iloc` aceitam qualquer tipo iterável." 381 | ] 382 | }, 383 | { 384 | "cell_type": "markdown", 385 | "metadata": { 386 | "colab_type": "text", 387 | "id": "hjtzZhzlP-ku" 388 | }, 389 | "source": [ 390 | "### Fatiando um dataset" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "metadata": { 396 | "colab_type": "text", 397 | "id": "iN1qf9NgNpVN" 398 | }, 399 | "source": [ 400 | "Na maioria das vezes, nosso interesse é em um bloco contíguo de linhas e/ou colunas.\n", 401 | "\n", 402 | "Isso pode ser feito através de operações de fatiamento:\n", 403 | "- Por linhas, usando a notação `data.loc[linha_início:linha_fim, nome_coluna]`:" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "metadata": { 409 | "colab_type": "code", 410 | "id": "AJPtTJxqDmTD", 411 | "colab": {} 412 | }, 413 | "source": [ 414 | "data.loc[0:500,'nome_discente']" 415 | ], 416 | "execution_count": 0, 417 | "outputs": [] 418 | }, 419 | { 420 | "cell_type": "markdown", 421 | "metadata": { 422 | "colab_type": "text", 423 | "id": "tETV_5sCOcoQ" 424 | }, 425 | "source": [ 426 | "* Por linhas e colunas, usando a notação `data.loc[linha_início:linha_fim, coluna_início:coluna_fim]`:" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "metadata": { 432 | "colab_type": "code", 433 | "id": "X716AoGBDvcN", 434 | "colab": {} 435 | }, 436 | "source": [ 437 | "data.iloc[0:5, 5:8]" 438 | ], 439 | "execution_count": 0, 440 | "outputs": [] 441 | }, 442 | { 443 | "cell_type": "markdown", 444 | "metadata": { 445 | "colab_type": "text", 446 | "id": "PqlltwoMOqsv" 447 | }, 448 | "source": [ 449 | "É importante observar que operações de fatiamento em Python costumam incluir o elemento referido pelo primeiro índice, mas não o elemento referido pelo segundo índice.\n", 450 | "\n", 451 | "Assim, no exemplo `data.iloc[0:5, 5:8]` temos 5 linhas e 3 colunas sendo retornadas.\n", 452 | "\n", 453 | "O método `loc` foge a esse padrão, incluindo também a linha referida pelo segundo índice.\n", 454 | "\n", 455 | "Por isso, o exemplo `data.loc[0:500,'nome_discente']` retorna 501 linhas.\n", 456 | "\n", 457 | "Isso acontece porque no método `loc`, é possível fatiar o dataframe também por colunas. Neste caso, faz sentido que a segunda referência seja inclusa:" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "metadata": { 463 | "colab_type": "code", 464 | "id": "xt4OTMXsPGrH", 465 | "colab": {} 466 | }, 467 | "source": [ 468 | "data.loc[0:500, 'nome_discente':'ano_ingresso']" 469 | ], 470 | "execution_count": 0, 471 | "outputs": [] 472 | }, 473 | { 474 | "cell_type": "markdown", 475 | "metadata": { 476 | "colab_type": "text", 477 | "id": "zK2I__X1VaCz" 478 | }, 479 | "source": [ 480 | "## Consultas como em bancos de dados" 481 | ] 482 | }, 483 | { 484 | "cell_type": "markdown", 485 | "metadata": { 486 | "colab_type": "text", 487 | "id": "7nGWbGXrVdUz" 488 | }, 489 | "source": [ 490 | "As operações de indexação e fatiamento são inerentes à linguagem Python e por isso são implementadas pelo Pandas.\n", 491 | "\n", 492 | "Em parte, elas ajudam a operacionalizar a **seleção** e a **projeção** comuns em bancos de dados:\n", 493 | "- **Seleção**: escolher um subconjunto de observações\n", 494 | "- **Projeção**: escolher um subconjunto de características\n", 495 | "\n", 496 | "Os dataframes do Pandas fornecem mais métodos para estes tipos de consulta." 497 | ] 498 | }, 499 | { 500 | "cell_type": "markdown", 501 | "metadata": { 502 | "colab_type": "text", 503 | "id": "Yr8LpSswZ_HV" 504 | }, 505 | "source": [ 506 | "#### Pesquisando pelo nome das características\n", 507 | "\n", 508 | "O método **filter()** escolhe um subconjunto de características baseado em seu nome:" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "metadata": { 514 | "colab_type": "code", 515 | "id": "FzTKAWMsd_U4", 516 | "colab": {} 517 | }, 518 | "source": [ 519 | "data.filter(like='ingresso')" 520 | ], 521 | "execution_count": 0, 522 | "outputs": [] 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "metadata": { 527 | "colab_type": "text", 528 | "id": "Vn1YdnXPXOjC" 529 | }, 530 | "source": [ 531 | "O resultado do método **filter** é um novo `DataFrame` que pode ser associado a um novo nome:" 532 | ] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "metadata": { 537 | "colab_type": "code", 538 | "id": "cdBjaM_RXawY", 539 | "colab": {} 540 | }, 541 | "source": [ 542 | "data_ingresso = data.filter(like='ingresso')\n", 543 | "data_ingresso.head()" 544 | ], 545 | "execution_count": 0, 546 | "outputs": [] 547 | }, 548 | { 549 | "cell_type": "markdown", 550 | "metadata": { 551 | "colab_type": "text", 552 | "id": "I0fb6cKPL-wL" 553 | }, 554 | "source": [ 555 | "### Pesquisando por condições\n", 556 | "\n", 557 | "Uma outra maneira de filtrar pelos valores das colunas é através de **condições**.\n", 558 | "\n", 559 | "Para isso, usamos o método `query('condição')`, onde `condição` é uma expressão lógica do Python.\n", 560 | "\n", 561 | "Por exemplo, vamos escolher apenas as observações cuja **forma_ingresso** tenha valor **REINGRESSO SEGUNDO CICLO**:" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "metadata": { 567 | "colab_type": "code", 568 | "id": "q0oc1e_PCDii", 569 | "colab": {} 570 | }, 571 | "source": [ 572 | "data.query(\"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\")" 573 | ], 574 | "execution_count": 0, 575 | "outputs": [] 576 | }, 577 | { 578 | "cell_type": "markdown", 579 | "metadata": { 580 | "colab_type": "text", 581 | "id": "OY0VDG5_jiQv" 582 | }, 583 | "source": [ 584 | "Vamos discutir o exemplo acima:\n", 585 | "* `forma_ingresso` é uma `Series` (coluna) do `DataFrame` que chamamos de `data` \n", 586 | "* Comparamos cada valor nesta série com o valor `'REINGRESSO SEGUNDO CICLO'` usando o operador de igualdade `==`\n", 587 | "```python\n", 588 | "data.query(\"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\")\n", 589 | "```\n", 590 | "Escolhemos apenas as observações que satisfazem essa condição\n", 591 | "\n", 592 | "Note que podemos usar nomes tanto para referência ao `DataFrame` retornado:" 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "metadata": { 598 | "colab_type": "code", 599 | "id": "0mwwuJ7Xkd2g", 600 | "colab": {} 601 | }, 602 | "source": [ 603 | "data_segundo_ciclo = data.query(\"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\")\n", 604 | "data_segundo_ciclo.head()" 605 | ], 606 | "execution_count": 0, 607 | "outputs": [] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": { 612 | "colab_type": "text", 613 | "id": "l5S3EdmLp1F8" 614 | }, 615 | "source": [ 616 | "#### Condições e operadores de comparação\n", 617 | "\n", 618 | "No exemplo acima, usamos o operador de igualdade. \n", 619 | "\n", 620 | "Note que é diferente usar `==` (comparação de igualdade) e `=` (associação de nome a objeto).\n", 621 | "\n", 622 | "O Python oferece mais operadores de comparação:\n", 623 | "\n", 624 | "| Símbolo | Significado |\n", 625 | "|:----:|---|\n", 626 | "| == | Igualdade |\n", 627 | "| != | Diferença |\n", 628 | "| < | Menor |\n", 629 | "| > | Maior |\n", 630 | "| <= | Menor ou igual |\n", 631 | "| >= | Maior ou igual |\n", 632 | "\n", 633 | "Também é importante observar que os operadores menor/maior (ou igual) costumam ser aplicados a dados numéricos.\n", 634 | "\n", 635 | "Para dados nominais, podemos usar o operador `in`.\n", 636 | "\n", 637 | "Vamos dar uma olhada nos valores existentes para a característica `\"status\"` usando o método `unique()`:" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "metadata": { 643 | "colab_type": "code", 644 | "id": "EA9ORMrg1yS2", 645 | "colab": {} 646 | }, 647 | "source": [ 648 | "data['status'].unique()" 649 | ], 650 | "execution_count": 0, 651 | "outputs": [] 652 | }, 653 | { 654 | "cell_type": "markdown", 655 | "metadata": { 656 | "colab_type": "text", 657 | "id": "6Y7Z7ngK12aq" 658 | }, 659 | "source": [ 660 | "Novamente temos um resultado verboso, mas nos interessa a lista de valores:\n", 661 | "\n", 662 | "```python3\n", 663 | "['ATIVO', 'CANCELADO', 'CADASTRADO', 'TRANCADO', 'ATIVO - FORMANDO',\n", 664 | " 'CONCLUÍDO', 'DEFENDIDO']\n", 665 | "````" 666 | ] 667 | }, 668 | { 669 | "cell_type": "markdown", 670 | "metadata": { 671 | "colab_type": "text", 672 | "id": "TQdwp3y32AaC" 673 | }, 674 | "source": [ 675 | "Vamos escolher apenas as observações cujo status seja \"CANCELADO\" ou \"TRANCADO\":" 676 | ] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "metadata": { 681 | "colab_type": "code", 682 | "id": "qh-TQoNbsXNC", 683 | "colab": {} 684 | }, 685 | "source": [ 686 | "status_desejados = [\"CANCELADO\", \"TRANCADO\"]\n", 687 | "data_cancelado_trancado = data.query(f\"status in {status_desejados}\")\n", 688 | "data_cancelado_trancado.tail()" 689 | ], 690 | "execution_count": 0, 691 | "outputs": [] 692 | }, 693 | { 694 | "cell_type": "markdown", 695 | "metadata": { 696 | "id": "mMabVSaXE0Rp", 697 | "colab_type": "text" 698 | }, 699 | "source": [ 700 | "Vamos discutir o exemplo acima:\n", 701 | "* `status_desejados` é uma lista com os status que desejamos filtrar \n", 702 | "* Filtramos o dataframe `data` indicando que queremos apenas as observações cujo status esteja especificada na lista `status_desejados`\n", 703 | "```python\n", 704 | "data.query(f\"status in {status_desejados}\")\n", 705 | "```\n", 706 | "Note que usamos um recurso do Python chamado `f-strings`, que permitem converter em texto objetos especificados entre chaves (uma `f-string` sempre começa com um `f` antes das aspas)." 707 | ] 708 | }, 709 | { 710 | "cell_type": "markdown", 711 | "metadata": { 712 | "colab_type": "text", 713 | "id": "FLLC_2stfihh" 714 | }, 715 | "source": [ 716 | "#### Condições e operadores lógicos\n", 717 | "Podemos também usar condições mais complexas, usando **operadores lógicos**.\n", 718 | "\n", 719 | "Vamos restringir um pouco mais a consulta acima para que, além de **forma_ingresso** ter valor **REINGRESSO SEGUNDO CICLO**, **nome_curso** tenha valor **ENGENHARIA DE SOFTWARE**:" 720 | ] 721 | }, 722 | { 723 | "cell_type": "code", 724 | "metadata": { 725 | "colab_type": "code", 726 | "id": "y25iRIQnldzG", 727 | "colab": {} 728 | }, 729 | "source": [ 730 | "condição_segundo_ciclo = \"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\"\n", 731 | "condição_engenharia_software = \"nome_curso == 'ENGENHARIA DE SOFTWARE'\"\n", 732 | "data_2ciclo_engsoft = data.query(f\"{condição_segundo_ciclo} and {condição_engenharia_software}\")\n", 733 | "data_2ciclo_engsoft.head()" 734 | ], 735 | "execution_count": 0, 736 | "outputs": [] 737 | }, 738 | { 739 | "cell_type": "markdown", 740 | "metadata": { 741 | "colab_type": "text", 742 | "id": "6gBoCgGdl33r" 743 | }, 744 | "source": [ 745 | "Revendo o código acima:\n", 746 | "* ```python\n", 747 | "condição_segundo_ciclo = \"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\"\n", 748 | "````\n", 749 | "Condição para escolher apenas os ingressantes através de reingresso de segundo ciclo\n", 750 | "```python\n", 751 | "condição_engenharia_software = \"nome_curso == 'ENGENHARIA DE SOFTWARE'\"\n", 752 | "```\n", 753 | "Condição para escolher apenas os ingressantes do curso de engenharia de software\n", 754 | "```python\n", 755 | "data_2ciclo_engsoft = data.query(f\"{condição_segundo_ciclo} and {condição_engenharia_software}\")\n", 756 | "```\n", 757 | "Combinando as duas condições através do operador `and`." 758 | ] 759 | }, 760 | { 761 | "cell_type": "markdown", 762 | "metadata": { 763 | "colab_type": "text", 764 | "id": "_-dAt2lgodAi" 765 | }, 766 | "source": [ 767 | "#### Outros operadores lógicos\n", 768 | "\n", 769 | "Além do operador `and`, o Pandas também disponibiliza o operador `or`.\n", 770 | "\n", 771 | "Enquanto o operador `and` escolhe a linha apenas se as duas condições forem verdadeiras, para o operador `or` basta que uma das condições seja satisfeita.\n", 772 | "\n", 773 | "Seguindo essa definição, o que o exemplo abaixo faz?" 774 | ] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "metadata": { 779 | "colab_type": "code", 780 | "id": "C8yVonNefiqB", 781 | "colab": {} 782 | }, 783 | "source": [ 784 | "condição_segundo_ciclo = \"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\"\n", 785 | "condição_engenharia_software = \"nome_curso == 'ENGENHARIA DE SOFTWARE'\"\n", 786 | "condição_ciência_computação = \"nome_curso == 'CIÊNCIA DA COMPUTAÇÃO'\"\n", 787 | "condição_dimap = f\"{condição_ciência_computação} or {condição_engenharia_software}\"\n", 788 | "data_2ciclo_dimap = data.query(f\"{condição_segundo_ciclo} and {condição_dimap}\")\n", 789 | "data_2ciclo_dimap.head()" 790 | ], 791 | "execution_count": 0, 792 | "outputs": [] 793 | }, 794 | { 795 | "cell_type": "markdown", 796 | "metadata": { 797 | "colab_type": "text", 798 | "id": "lYIp5ZOcomNe" 799 | }, 800 | "source": [ 801 | "Revendo o código acima:\n", 802 | "* ```python\n", 803 | "condição_segundo_ciclo = \"forma_ingresso == 'REINGRESSO SEGUNDO CICLO'\"\n", 804 | "````\n", 805 | "Condição para escolher apenas os ingressantes através de reingresso de segundo ciclo\n", 806 | "```python\n", 807 | "condição_engenharia_software = \"nome_curso == 'ENGENHARIA DE SOFTWARE'\"\n", 808 | "```\n", 809 | "Condição para escolher apenas os ingressantes do curso de engenharia de software\n", 810 | "```python\n", 811 | "condição_ciência_computação = \"nome_curso == 'CIÊNCIA DA COMPUTAÇÃO'\"\n", 812 | "```\n", 813 | "Condição para escolher apenas os ingressantes do curso de ciência da computação\n", 814 | "```python\n", 815 | "condição_dimap = f\"{condição_ciência_computação} or {condição_engenharia_software}\"\n", 816 | "```\n", 817 | "Combinando as duas condições através do operador `or`\n", 818 | "```python\n", 819 | "data_2ciclo_dimap = data.query(f\"{condição_segundo_ciclo} and {condição_dimap}\")\n", 820 | "```\n", 821 | "Combinando as duas condições através do operador `and`\n", 822 | "\n", 823 | "Note que usamos o operador `or` quando poderíamos ter usado o operador `in`, que é mais legível.\n", 824 | "\n", 825 | "Em geral, adotamos o operador `or` quando as condições envolvem características distintas, em vez de valores distintos para uma mesma característica.\n", 826 | "\n", 827 | "Por último, o operador `not` serve para inverter uma condição:" 828 | ] 829 | }, 830 | { 831 | "cell_type": "code", 832 | "metadata": { 833 | "colab_type": "code", 834 | "id": "VM2AENbdpel7", 835 | "colab": {} 836 | }, 837 | "source": [ 838 | "data_ingresso_direto = data.query(f\"not {condição_segundo_ciclo}\")\n", 839 | "data_ingresso_direto.head()" 840 | ], 841 | "execution_count": 0, 842 | "outputs": [] 843 | }, 844 | { 845 | "cell_type": "markdown", 846 | "metadata": { 847 | "colab_type": "text", 848 | "id": "N4ij0OwfuPUu" 849 | }, 850 | "source": [ 851 | "* **Observação**: expressões lógicas complexas merecem uma pesquisa específica sobre o assunto. Cobrir esse tópico em profundidade foge do escopo deste notebook 🙃" 852 | ] 853 | } 854 | ] 855 | } -------------------------------------------------------------------------------- /en/notebooks/ETL.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "ETL.ipynb", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | } 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "id": "0EEJO_jG6MIT", 20 | "colab_type": "text" 21 | }, 22 | "source": [ 23 | "# Extract, transform and load data (ETL) \n", 24 | "\n" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": { 30 | "id": "tk3uuY8rA_uT", 31 | "colab_type": "text" 32 | }, 33 | "source": [ 34 | "The ETL process is a fundamental part of working with data and is based on three steps:\n", 35 | "\n", 36 | "* **Extraction**: collecting data, potentially from multiple heterogeneous sources. It can involve scraping web pages, access to programming interfaces (APIs), or consulting databases.\n", 37 | "* **Transformation**: reorganizing data, involving operations such as merging and aggregation.\n", 38 | "* **Load**: persistence of the new dataset where one wants to store it.\n", 39 | "\n", 40 | "This notebook focuses on examples of transformation with Pandas. For this, we will use three dataframes in our examples: `df_a`, `df_b` and `df_c`.\n", 41 | "\n", 42 | "\n", 43 | "\n", 44 | "\n" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": { 50 | "id": "1w0Ufe3d64DG", 51 | "colab_type": "text" 52 | }, 53 | "source": [ 54 | "## Creating dataframes from dictionaries\n", 55 | "\n" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": { 61 | "id": "fsgGHYp5BED3", 62 | "colab_type": "text" 63 | }, 64 | "source": [ 65 | "Since we'll create customized dataframes, we'll need the help of **dictionaries**. A dictionary is an object type in Python which stores values indexed by keys, similar to what `DataFrame` does. We use the following notation for creating a dictionary:\n", 66 | "\n", 67 | "```\n", 68 | "name = {\n", 69 | " key1: value1,\n", 70 | " key2: value2,\n", 71 | " ...\n", 72 | " keyN: valueN\n", 73 | " }\n", 74 | "```\n", 75 | "We access a value in a dictionary through its key, with the `dictionary[key]` notation. In the following example, the dictionary `data_df_a` has feature names as keys, and the associated data series as values.\n", 76 | "\n", 77 | "\n" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "metadata": { 83 | "id": "5xA7utuCBEX7", 84 | "colab_type": "code", 85 | "colab": {} 86 | }, 87 | "source": [ 88 | "import pandas as pd" 89 | ], 90 | "execution_count": 0, 91 | "outputs": [] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "metadata": { 96 | "id": "9_e6KIctBElU", 97 | "colab_type": "code", 98 | "colab": {} 99 | }, 100 | "source": [ 101 | "data_df_a = {\n", 102 | " 'id_individual': ['1', '2', '3', '4', '5'],\n", 103 | " 'name': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung'],\n", 104 | " 'surname': ['Anderson', 'Ackerman', 'Ali', 'Aoni', 'Atiches']\n", 105 | " }" 106 | ], 107 | "execution_count": 0, 108 | "outputs": [] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": { 113 | "id": "Ec4b76d9BE03", 114 | "colab_type": "text" 115 | }, 116 | "source": [ 117 | "Note that each series is represented as a list. Creating a `DataFrame` from a list is really simple:" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "metadata": { 123 | "id": "jRF20ifhBGNH", 124 | "colab_type": "code", 125 | "colab": {} 126 | }, 127 | "source": [ 128 | "df_a = pd.DataFrame(data_df_a)\n", 129 | "df_a" 130 | ], 131 | "execution_count": 0, 132 | "outputs": [] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": { 137 | "id": "vwQ-nmgcBGUv", 138 | "colab_type": "text" 139 | }, 140 | "source": [ 141 | "Following the same approach, let's create dataframe `df_b`:" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "metadata": { 147 | "id": "FdqaySrLBMlr", 148 | "colab_type": "code", 149 | "colab": {} 150 | }, 151 | "source": [ 152 | "data_df_b = {\n", 153 | " 'id_individual': ['4', '5', '6', '7', '8'],\n", 154 | " 'name': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'], \n", 155 | " 'surname': ['Bonder', 'Black', 'Balwner', 'Brice', 'Btisan']\n", 156 | " }" 157 | ], 158 | "execution_count": 0, 159 | "outputs": [] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "metadata": { 164 | "id": "_AgCCpraBMtj", 165 | "colab_type": "code", 166 | "colab": {} 167 | }, 168 | "source": [ 169 | "df_b = pd.DataFrame(data_df_b)\n", 170 | "df_b" 171 | ], 172 | "execution_count": 0, 173 | "outputs": [] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "metadata": { 178 | "id": "_NmYyDk_BM2U", 179 | "colab_type": "code", 180 | "colab": {} 181 | }, 182 | "source": [ 183 | "data_df_c = {\n", 184 | " 'id_individual': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'],\n", 185 | " 'id_exam': [51, 15, 15, 61, 16, 14, 15, 1, 61, 16]\n", 186 | " }" 187 | ], 188 | "execution_count": 0, 189 | "outputs": [] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "metadata": { 194 | "id": "s2C4H5RkBM9h", 195 | "colab_type": "code", 196 | "colab": {} 197 | }, 198 | "source": [ 199 | "df_c = pd.DataFrame(data_df_c)\n", 200 | "df_c" 201 | ], 202 | "execution_count": 0, 203 | "outputs": [] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": { 208 | "colab_type": "text", 209 | "id": "d--hw56V7DEv" 210 | }, 211 | "source": [ 212 | "## Appending data" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": { 218 | "id": "mzTz4xs3BT8_", 219 | "colab_type": "text" 220 | }, 221 | "source": [ 222 | "One of the common operations is to concatenate samples that have the same features, but that are in different dataframes. For this we will use the `concat` command, which receives a list with ***n*** `DataFrame` objects as a parameter." 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "metadata": { 228 | "id": "MfbKS_PJBUNQ", 229 | "colab_type": "code", 230 | "colab": {} 231 | }, 232 | "source": [ 233 | "df_new = pd.concat([df_a, df_b])\n", 234 | "df_new" 235 | ], 236 | "execution_count": 0, 237 | "outputs": [] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": { 242 | "id": "IJ8xFZ-NBUTn", 243 | "colab_type": "text" 244 | }, 245 | "source": [ 246 | "It's also possible to concatenate `DataFrame` objects with distinct features. However, this operation would produce a `DataFrame` with many missing data:" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "metadata": { 252 | "id": "LMhDTAHY8zUO", 253 | "colab_type": "code", 254 | "colab": {} 255 | }, 256 | "source": [ 257 | "pd.concat([df_a, df_c])" 258 | ], 259 | "execution_count": 0, 260 | "outputs": [] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": { 265 | "colab_type": "text", 266 | "id": "NHdshguT9kpz" 267 | }, 268 | "source": [ 269 | "## Merging data" 270 | ] 271 | }, 272 | { 273 | "cell_type": "markdown", 274 | "metadata": { 275 | "id": "Gsd3TBFT9tns", 276 | "colab_type": "text" 277 | }, 278 | "source": [ 279 | "In the previous example, we saw the result of the concatenating data between different `DataFrames` whose features are not identical. However, when there is at least one feature in common between two `DataFrames`, we can use the technique called **data merging**, using the pandas **merge** method, which returns a new `DataFrame` containing the combined data from the two base `DataFrames`.\n", 280 | "\n", 281 | "In the example below, the samples of the `DataFrame` on the left (```df_a```) and the `DataFrame` on the right (```df_c```) are merged, taking ```id_individual``` as a common feature. As you can see, the new `DataFrame` gathers information from both `DataFrames` used to merge data:\n" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "metadata": { 287 | "id": "v7yS9sej9rfL", 288 | "colab_type": "code", 289 | "colab": {} 290 | }, 291 | "source": [ 292 | "pd.merge(df_a, df_c, on='id_individual')" 293 | ], 294 | "execution_count": 0, 295 | "outputs": [] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": { 300 | "id": "vG8yh9HL9uV8", 301 | "colab_type": "text" 302 | }, 303 | "source": [ 304 | "In summary, the **merging** operation combines the data from two `DataFrames` with at least one common **feature**. In the previous example, the common feature was the **id_individual** field. In other situations, the same feature may be represented by different names in the `DataFrames` one wants to merge. In this case, we use arguments `left_on` and `right_on` to respectively specify the names of the features in the `DataFrame` on the left and in the `DataFrame` on the right." 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": { 310 | "colab_type": "text", 311 | "id": "ICEg3nBN7RyB" 312 | }, 313 | "source": [ 314 | "### Merge types" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": { 320 | "id": "Zu-UT39LBYTz", 321 | "colab_type": "text" 322 | }, 323 | "source": [ 324 | "Note that the samples present in the `df_c` dataframe whose values ​​for `id_individual` are not present in the `df_a` dataframe were not shown. If we want these samples to be preserved, we can use a **right join**. We specify that we want a right join using the argument `how`:" 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "metadata": { 330 | "id": "xQ85UpYSBYhP", 331 | "colab_type": "code", 332 | "colab": {} 333 | }, 334 | "source": [ 335 | "pd.merge(df_a, df_c, on='id_individual', how='right')" 336 | ], 337 | "execution_count": 0, 338 | "outputs": [] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": { 343 | "id": "0Wiklj-3BYrn", 344 | "colab_type": "text" 345 | }, 346 | "source": [ 347 | "\n", 348 | "The result above shows both the samples with `id_individual` present in the two `DataFrames` and the rest of the samples on the right `DataFrame`. Note that the samples added by the intersection on the right have missing data. The **left join** also has the same behavior:" 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "metadata": { 354 | "id": "yCyrZfVLBYy4", 355 | "colab_type": "code", 356 | "colab": {} 357 | }, 358 | "source": [ 359 | "pd.merge(df_b, df_c, on='id_individual', how='left')" 360 | ], 361 | "execution_count": 0, 362 | "outputs": [] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": { 367 | "id": "bCwBR2A1BY49", 368 | "colab_type": "text" 369 | }, 370 | "source": [ 371 | "In this case, the sample of the `df_b` dataframe whose `id_individual` was not present in the `df_c` dataframe was maintained. In a more extreme case, we can use an **external merging**, which holds all samples from both dataframes. Instead of **right** or **left**, we set `how` to **outer**:" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "metadata": { 377 | "id": "qlt5taboBZAJ", 378 | "colab_type": "code", 379 | "colab": {} 380 | }, 381 | "source": [ 382 | "pd.merge(df_b, df_c, on='id_individual', how='outer')" 383 | ], 384 | "execution_count": 0, 385 | "outputs": [] 386 | }, 387 | { 388 | "cell_type": "markdown", 389 | "metadata": { 390 | "colab_type": "text", 391 | "id": "rAphyQC_7gco" 392 | }, 393 | "source": [ 394 | "## Aggregating data" 395 | ] 396 | }, 397 | { 398 | "cell_type": "markdown", 399 | "metadata": { 400 | "id": "p5SCH_VbBav_", 401 | "colab_type": "text" 402 | }, 403 | "source": [ 404 | "Merging operations aim to gather information spread across multiple bases on a single `DataFrame`. A complementary type of operation is **aggregation**, which aims to summarize blocks of information using **descriptive statistics**. The main forms of aggregation are obtained through **pivoting**, be it one-dimensional (**groups**)\n", 405 | " or two-dimensional (**pivot** **tables**)." 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": { 411 | "colab_type": "text", 412 | "id": "cYyGcdd07qL4" 413 | }, 414 | "source": [ 415 | "### Groups" 416 | ] 417 | }, 418 | { 419 | "cell_type": "markdown", 420 | "metadata": { 421 | "id": "76Xa3PyhBgTb", 422 | "colab_type": "text" 423 | }, 424 | "source": [ 425 | "Organizing data into groups can be useful to analyse each group or to calculate per-group statistics. The first step for aggregation is to define one or more features to be used as grouping factors. In the example below, we group the data from the `iris` dataset. This dataset is the most downloaded of the machine learning repository [UCI](https://archive.ics.uci.edu/ml/). It lists petal and sepal measurements of three species of iris flowers. For convenience, we are going to download it from the `seaborn` library:" 426 | ] 427 | }, 428 | { 429 | "cell_type": "code", 430 | "metadata": { 431 | "id": "boHi_1XlBgbx", 432 | "colab_type": "code", 433 | "colab": {} 434 | }, 435 | "source": [ 436 | "import seaborn as sns\n", 437 | "iris_dataset = sns.load_dataset('iris')\n", 438 | "iris_dataset" 439 | ], 440 | "execution_count": 0, 441 | "outputs": [] 442 | }, 443 | { 444 | "cell_type": "markdown", 445 | "metadata": { 446 | "id": "LfwGEx03Bgib", 447 | "colab_type": "text" 448 | }, 449 | "source": [ 450 | "As we can see, the dataset contains sepal and petal widths and heights of 150 iris flowers samples. Let's see how many samples we have per species: " 451 | ] 452 | }, 453 | { 454 | "cell_type": "code", 455 | "metadata": { 456 | "id": "69nD8aSLBgoX", 457 | "colab_type": "code", 458 | "colab": {} 459 | }, 460 | "source": [ 461 | "iris_dataset['species'].value_counts()" 462 | ], 463 | "execution_count": 0, 464 | "outputs": [] 465 | }, 466 | { 467 | "cell_type": "markdown", 468 | "metadata": { 469 | "id": "k8hsNEtGBgv9", 470 | "colab_type": "text" 471 | }, 472 | "source": [ 473 | "To group this dataset by species, we can use the `groupby()` method:" 474 | ] 475 | }, 476 | { 477 | "cell_type": "code", 478 | "metadata": { 479 | "id": "iYnHlDFhBg4j", 480 | "colab_type": "code", 481 | "colab": {} 482 | }, 483 | "source": [ 484 | "iris_groups = iris_dataset.groupby(['species'])" 485 | ], 486 | "execution_count": 0, 487 | "outputs": [] 488 | }, 489 | { 490 | "cell_type": "markdown", 491 | "metadata": { 492 | "id": "hPM8p6jABg_i", 493 | "colab_type": "text" 494 | }, 495 | "source": [ 496 | "We can then treat each group as a `DataFrame` using the `get_group()` method:" 497 | ] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "metadata": { 502 | "id": "cQ-uEOc-BhHJ", 503 | "colab_type": "code", 504 | "colab": {} 505 | }, 506 | "source": [ 507 | "iris_groups.get_group('versicolor').head()" 508 | ], 509 | "execution_count": 0, 510 | "outputs": [] 511 | }, 512 | { 513 | "cell_type": "markdown", 514 | "metadata": { 515 | "id": "jvgYikt6BhvZ", 516 | "colab_type": "text" 517 | }, 518 | "source": [ 519 | "Grouping allow us to compute statistics about the all groups at the same time or individually:" 520 | ] 521 | }, 522 | { 523 | "cell_type": "markdown", 524 | "metadata": { 525 | "colab_type": "text", 526 | "id": "iaT9Dy0370K7" 527 | }, 528 | "source": [ 529 | "#### At the same time" 530 | ] 531 | }, 532 | { 533 | "cell_type": "code", 534 | "metadata": { 535 | "id": "PsEy_9hEBkIV", 536 | "colab_type": "code", 537 | "colab": {} 538 | }, 539 | "source": [ 540 | "iris_groups.min()" 541 | ], 542 | "execution_count": 0, 543 | "outputs": [] 544 | }, 545 | { 546 | "cell_type": "code", 547 | "metadata": { 548 | "id": "aLau61OzBkP3", 549 | "colab_type": "code", 550 | "colab": {} 551 | }, 552 | "source": [ 553 | "iris_groups.max()" 554 | ], 555 | "execution_count": 0, 556 | "outputs": [] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "metadata": { 561 | "id": "sguL5gCABkVm", 562 | "colab_type": "code", 563 | "colab": {} 564 | }, 565 | "source": [ 566 | "iris_groups.mean()" 567 | ], 568 | "execution_count": 0, 569 | "outputs": [] 570 | }, 571 | { 572 | "cell_type": "markdown", 573 | "metadata": { 574 | "colab_type": "text", 575 | "id": "sIDkzSYN7p1I" 576 | }, 577 | "source": [ 578 | "#### Individually" 579 | ] 580 | }, 581 | { 582 | "cell_type": "code", 583 | "metadata": { 584 | "id": "1Sgw7MgDBl5t", 585 | "colab_type": "code", 586 | "colab": {} 587 | }, 588 | "source": [ 589 | "iris_groups.get_group(\"versicolor\").describe()" 590 | ], 591 | "execution_count": 0, 592 | "outputs": [] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "metadata": { 597 | "id": "SYrl3PM8Bl_V", 598 | "colab_type": "code", 599 | "colab": {} 600 | }, 601 | "source": [ 602 | "versicolor_group = iris_groups.get_group(\"versicolor\")\n", 603 | "versicolor_group.count()" 604 | ], 605 | "execution_count": 0, 606 | "outputs": [] 607 | }, 608 | { 609 | "cell_type": "markdown", 610 | "metadata": { 611 | "colab_type": "text", 612 | "id": "c2Zaf_ic7rd_" 613 | }, 614 | "source": [ 615 | "#### Aggregating multiple features" 616 | ] 617 | }, 618 | { 619 | "cell_type": "markdown", 620 | "metadata": { 621 | "id": "03yN1hQhBqAR", 622 | "colab_type": "text" 623 | }, 624 | "source": [ 625 | "A powerful resource of Pandas is to allow aggregations of multiple features. In general, we use this resource when we have a set of data that has categorical and numerical features. In the `iris` dataset, however, we only have one categorical feature available. Let's take advantage of this situation and take a look at a really cool feature of Pandas, called discretization:" 626 | ] 627 | }, 628 | { 629 | "cell_type": "code", 630 | "metadata": { 631 | "id": "5ZjICJrbBqFc", 632 | "colab_type": "code", 633 | "colab": {} 634 | }, 635 | "source": [ 636 | "pd.cut(iris_dataset[\"petal_width\"], bins=3)" 637 | ], 638 | "execution_count": 0, 639 | "outputs": [] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": { 644 | "id": "ZePc6iAwBqRO", 645 | "colab_type": "text" 646 | }, 647 | "source": [ 648 | "Did you understand what happened? The `cut()` method calculated the maximum and minimum values for the `petal_width` feature and split this interval into three subintervals. Thus, each of the original values was replaced by the sub-interval to which it belongs and, now, we have a categorical feature 😄\n", 649 | "\n", 650 | "Let's replace the original data with categorized data:" 651 | ] 652 | }, 653 | { 654 | "cell_type": "code", 655 | "metadata": { 656 | "id": "uQCXpPHRBqWf", 657 | "colab_type": "code", 658 | "colab": {} 659 | }, 660 | "source": [ 661 | "iris_dataset[\"petal_width\"] = pd.cut(iris_dataset[\"petal_width\"], bins=3)\n", 662 | "iris_dataset" 663 | ], 664 | "execution_count": 0, 665 | "outputs": [] 666 | }, 667 | { 668 | "cell_type": "markdown", 669 | "metadata": { 670 | "id": "jgjEwrZZBqcs", 671 | "colab_type": "text" 672 | }, 673 | "source": [ 674 | "For readability, let's rename the categories created. Note that this time we are changing the original data directly using the option `inplace=True` (almost all Pandas methods accept this option)." 675 | ] 676 | }, 677 | { 678 | "cell_type": "code", 679 | "metadata": { 680 | "id": "Fc6chAGEBqi4", 681 | "colab_type": "code", 682 | "colab": {} 683 | }, 684 | "source": [ 685 | "iris_dataset[\"petal_width\"].cat.rename_categories([\"low\", \"medium\", \"high\"], inplace=True)\n", 686 | "iris_dataset" 687 | ], 688 | "execution_count": 0, 689 | "outputs": [] 690 | }, 691 | { 692 | "cell_type": "markdown", 693 | "metadata": { 694 | "id": "68IiapMKBqpn", 695 | "colab_type": "text" 696 | }, 697 | "source": [ 698 | "Now that our dataset has two categorical features, we can aggregate by multiple features:" 699 | ] 700 | }, 701 | { 702 | "cell_type": "code", 703 | "metadata": { 704 | "id": "xOZutKVrBqvZ", 705 | "colab_type": "code", 706 | "colab": {} 707 | }, 708 | "source": [ 709 | "group2_iris = iris_dataset.groupby([\"species\",\"petal_width\"]).size()\n", 710 | "group2_iris" 711 | ], 712 | "execution_count": 0, 713 | "outputs": [] 714 | }, 715 | { 716 | "cell_type": "markdown", 717 | "metadata": { 718 | "id": "PV6jBWKKBq17", 719 | "colab_type": "text" 720 | }, 721 | "source": [ 722 | "In this case, instead of producing the groups, we directly produce the aggregation using the `size()` method, which computes the size of each group. From the data above, we can see that all iris flowers of the `setosa` species present in the dataset have a small petal width. It is also possible to make an excellent separation between the `versicolor` and `virginica` species.\n", 723 | "\n", 724 | "Note that the data above is a series that has a multi-level index (known in Pandas as `MultiIndex`):" 725 | ] 726 | }, 727 | { 728 | "cell_type": "code", 729 | "metadata": { 730 | "id": "hPfr2JlbBq6R", 731 | "colab_type": "code", 732 | "colab": {} 733 | }, 734 | "source": [ 735 | "group2_iris.index" 736 | ], 737 | "execution_count": 0, 738 | "outputs": [] 739 | }, 740 | { 741 | "cell_type": "markdown", 742 | "metadata": { 743 | "id": "la70vEZuBwzB", 744 | "colab_type": "text" 745 | }, 746 | "source": [ 747 | "Amid the verbose messages of Pandas, we see that there are two levels in this index (`levels`), whose names (`names`) are `species` and `petal_width`. We can index this series in several different ways:" 748 | ] 749 | }, 750 | { 751 | "cell_type": "code", 752 | "metadata": { 753 | "id": "I5EKD6NVBxAo", 754 | "colab_type": "code", 755 | "colab": {} 756 | }, 757 | "source": [ 758 | "group2_iris[\"virginica\",\"high\"]" 759 | ], 760 | "execution_count": 0, 761 | "outputs": [] 762 | }, 763 | { 764 | "cell_type": "code", 765 | "metadata": { 766 | "id": "TWIWzSjGBxHE", 767 | "colab_type": "code", 768 | "colab": {} 769 | }, 770 | "source": [ 771 | "group2_iris[\"virginica\",]" 772 | ], 773 | "execution_count": 0, 774 | "outputs": [] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "metadata": { 779 | "id": "F926Z9a8BxOG", 780 | "colab_type": "code", 781 | "colab": {} 782 | }, 783 | "source": [ 784 | "group2_iris[:,\"high\"]" 785 | ], 786 | "execution_count": 0, 787 | "outputs": [] 788 | }, 789 | { 790 | "cell_type": "markdown", 791 | "metadata": { 792 | "id": "tUxqIUBMBxa-", 793 | "colab_type": "text" 794 | }, 795 | "source": [ 796 | "We can also convert this series into a `DataFrame`. For this, we use the `reset_index()` method and inform the name we want to give to the series:" 797 | ] 798 | }, 799 | { 800 | "cell_type": "code", 801 | "metadata": { 802 | "id": "gB6nEcIuBxiZ", 803 | "colab_type": "code", 804 | "colab": {} 805 | }, 806 | "source": [ 807 | "df_iris = group2_iris.reset_index(name=\"count\")\n", 808 | "df_iris" 809 | ], 810 | "execution_count": 0, 811 | "outputs": [] 812 | }, 813 | { 814 | "cell_type": "markdown", 815 | "metadata": { 816 | "colab_type": "text", 817 | "id": "ylty4CtM7rln" 818 | }, 819 | "source": [ 820 | "### Pivot tables" 821 | ] 822 | }, 823 | { 824 | "cell_type": "markdown", 825 | "metadata": { 826 | "id": "Am3cMEp9B0VE", 827 | "colab_type": "text" 828 | }, 829 | "source": [ 830 | "Another form of aggregation available in Pandas is through pivot tables. In this case, we use the `pivot_table()` method. We must inform the features for the grouping at the level of rows (`index`) and columns (` columns`). We also can inform the aggregation method using the `aggfunc` option, which by default calculates the mean: " 831 | ] 832 | }, 833 | { 834 | "cell_type": "code", 835 | "metadata": { 836 | "id": "ju-3vBWzB0cT", 837 | "colab_type": "code", 838 | "colab": {} 839 | }, 840 | "source": [ 841 | "pt_iris = iris_dataset.pivot_table(index=\"species\", columns=\"petal_width\", aggfunc=\"size\")\n", 842 | "pt_iris" 843 | ], 844 | "execution_count": 0, 845 | "outputs": [] 846 | }, 847 | { 848 | "cell_type": "markdown", 849 | "metadata": { 850 | "id": "chniLZo3B0hd", 851 | "colab_type": "text" 852 | }, 853 | "source": [ 854 | "Note that the pivot table tries to generate all possible combinations between row and column features. Since our dataset does not have samples of the `setosa` species with petal width `medium` or `high`, these values are marked as missing/invalid. The `pivot_table()` method provides the `fill_value` option, which allow us to choose how to fill these cases:" 855 | ] 856 | }, 857 | { 858 | "cell_type": "code", 859 | "metadata": { 860 | "id": "ZPe3eskZB0oH", 861 | "colab_type": "code", 862 | "colab": {} 863 | }, 864 | "source": [ 865 | "pt_iris = iris_dataset.pivot_table(index=\"species\", columns=\"petal_width\", aggfunc=\"size\", fill_value=0)\n", 866 | "pt_iris" 867 | ], 868 | "execution_count": 0, 869 | "outputs": [] 870 | }, 871 | { 872 | "cell_type": "markdown", 873 | "metadata": { 874 | "id": "8uDi3sk_B0v6", 875 | "colab_type": "text" 876 | }, 877 | "source": [ 878 | "The `pivot_table()` produces an object of type `DataFrame`, so the indexing works the way we already know:" 879 | ] 880 | }, 881 | { 882 | "cell_type": "code", 883 | "metadata": { 884 | "id": "slUmJBSpB03i", 885 | "colab_type": "code", 886 | "colab": {} 887 | }, 888 | "source": [ 889 | "pt_iris.loc[\"versicolor\"]" 890 | ], 891 | "execution_count": 0, 892 | "outputs": [] 893 | }, 894 | { 895 | "cell_type": "code", 896 | "metadata": { 897 | "id": "3x0WZR8jB0-8", 898 | "colab_type": "code", 899 | "colab": {} 900 | }, 901 | "source": [ 902 | "pt_iris.loc[\"versicolor\",\"low\"]" 903 | ], 904 | "execution_count": 0, 905 | "outputs": [] 906 | }, 907 | { 908 | "cell_type": "code", 909 | "metadata": { 910 | "id": "S35SVpRvB1F8", 911 | "colab_type": "code", 912 | "colab": {} 913 | }, 914 | "source": [ 915 | "pt_iris.loc[:,\"low\"]" 916 | ], 917 | "execution_count": 0, 918 | "outputs": [] 919 | }, 920 | { 921 | "cell_type": "code", 922 | "metadata": { 923 | "id": "WeCe4S97NrCT", 924 | "colab_type": "code", 925 | "colab": {} 926 | }, 927 | "source": [ 928 | "" 929 | ], 930 | "execution_count": 0, 931 | "outputs": [] 932 | } 933 | ] 934 | } -------------------------------------------------------------------------------- /pt-br/notebooks/Relações.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "colab_type": "text", 7 | "id": "-PAYjGstGPTR" 8 | }, 9 | "source": [ 10 | "# Relações entre características" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": { 16 | "colab_type": "text", 17 | "id": "tQAiVnBxGT7w" 18 | }, 19 | "source": [ 20 | "Uma parte importante da análise de dados é compreender as relações existentes entre as características do seu conjunto de dados. A partir dessas relações, podemos eventualmente identificar características redundantes, o que nos permite trabalhar com um conjunto menor de características selecionadas de forma a preservar a parte mais importante da informação existente em nosso conjunto de dados. \n", 21 | "\n", 22 | "Os principais tipos de relação entre características que investigamos em nossos dados são covariância, correlação, causalidade e condicionalidade." 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": { 28 | "colab_type": "text", 29 | "id": "uZRepOEWGWCZ" 30 | }, 31 | "source": [ 32 | "## Covariância e correlação" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": { 38 | "colab_type": "text", 39 | "id": "QaydL55MIHgL" 40 | }, 41 | "source": [ 42 | "A covariância é um conceito estatístico que avalia o comportamento de uma característica em função de outra. Podemos observar três tipos de correlação:\n", 43 | "* **Positiva**: representa uma relação de proporcionalidade direta entre as características. Em outras palavras, o aumento no valor de uma característica implica no aumento no valor da outra característica. \n", 44 | "* **Negativa**: representa uma relação de proporcionalidade inversa entre as características. Em outras palavras, o aumento no valor de uma característica implica na redução do valor da outra característica. \n", 45 | "* **Nula**: representa uma ausência de relação entre as características. Em outras palavras, mudanças em uma característica não produzem um padrão claro de mudança em outra característica.\n", 46 | "\n", 47 | "Em geral, usamos a covariância para analisar a correlação entre um ou mais pares de características. Enquanto a covariância nos informa se existe uma relação entre as características, a correlação quantifica essa relação. Podemos analisar a correlação entre os dados fazendo uso de recursos gráficos e analíticos." 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": { 53 | "colab_type": "text", 54 | "id": "XxWpTQy1LUeX" 55 | }, 56 | "source": [ 57 | "### Entre um par de características" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": { 63 | "colab_type": "text", 64 | "id": "naEbg6IMUKRI" 65 | }, 66 | "source": [ 67 | "Podemos analisar a correlação entre um par de características graficamente, através de um **gráfico de dispersão**, ou analiticamente, através de diferentes **métricas de correlação**." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": { 73 | "colab_type": "text", 74 | "id": "8ypX76vPUXYb" 75 | }, 76 | "source": [ 77 | "#### Gráfico de dispersão" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": { 83 | "colab_type": "text", 84 | "id": "IDdGh1j6Gx94" 85 | }, 86 | "source": [ 87 | "Nesse tipo de gráfico, os dados são apresentados como pontos em um plano cartesiano, onde cada eixo representa uma característica que se deseja comparar. A correlação entre as características é avaliada em função do padrão que se apresente. Pra ver um exemplo concreto, vamos usar os dados estimados das medianas da altura e peso do Brasil fornecidas pelo IBGE, divididas por faixas etárias." 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 0, 93 | "metadata": { 94 | "colab": {}, 95 | "colab_type": "code", 96 | "id": "bRq7IRoHL4jd" 97 | }, 98 | "outputs": [], 99 | "source": [ 100 | "import pandas as pd\n", 101 | "import matplotlib.pyplot as plt\n", 102 | "import seaborn as sns\n", 103 | "sns.set()" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 0, 109 | "metadata": { 110 | "colab": {}, 111 | "colab_type": "code", 112 | "id": "tBSh_03cOqcg" 113 | }, 114 | "outputs": [], 115 | "source": [ 116 | "peso_altura = pd.read_csv(\"https://raw.githubusercontent.com/leobezerra/pandas-zero/master/datasets/ibge-peso-altura-brasil.csv\", sep=\";\", decimal=\",\")\n", 117 | "peso_altura.head()" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": { 123 | "colab_type": "text", 124 | "id": "tdlNgV-0Pmqm" 125 | }, 126 | "source": [ 127 | "Quando um par de características apresenta covariância **positiva**, observamos uma tendência com inclinação positiva. Esse é o padrão esperado para a relação entre as características `\"Altura\"` e `\"Peso\"`, que podemos verificar com o método `regplot` da biblioteca `seaborn`:" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 0, 133 | "metadata": { 134 | "colab": {}, 135 | "colab_type": "code", 136 | "id": "Vdnr3rFqOtln" 137 | }, 138 | "outputs": [], 139 | "source": [ 140 | "sns.regplot(x=\"Altura\", y=\"Peso\", data=peso_altura)\n", 141 | "plt.show()" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": { 147 | "colab_type": "text", 148 | "id": "ioQDHz-cP_E8" 149 | }, 150 | "source": [ 151 | "Note que o gráfico acima parece apresentar a mistura de dois padrões. Para a região onde o peso é menor que 65kg, há uma covariância positiva entre as características analisadas (reta de tendência com inclinação positiva):" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 0, 157 | "metadata": { 158 | "colab": {}, 159 | "colab_type": "code", 160 | "id": "wMG5x2SSPMsU" 161 | }, 162 | "outputs": [], 163 | "source": [ 164 | "peso_ate_65 = peso_altura.query(\"Peso < 65\")\n", 165 | "sns.regplot(x=\"Altura\", y=\"Peso\", data=peso_ate_65)\n", 166 | "plt.show()" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": { 172 | "colab_type": "text", 173 | "id": "01R1b2huQOCV" 174 | }, 175 | "source": [ 176 | "Já para a região onde o peso é maior que 65kg, não há padrão formado, o que caracteriza uma **covariância nula** (reta de tendência paralela a um dos eixos):" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 0, 182 | "metadata": { 183 | "colab": {}, 184 | "colab_type": "code", 185 | "id": "SY0wgmjnPaBY" 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "peso_maior_que_65 = peso_altura.query(\"Peso > 65\")\n", 190 | "sns.regplot(x=\"Altura\", y=\"Peso\", data=peso_maior_que_65)\n", 191 | "plt.show()" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": { 197 | "colab_type": "text", 198 | "id": "7Kqi3QeCQW6Q" 199 | }, 200 | "source": [ 201 | "Esse resultado faz sentido quando avaliamos que, em geral, medianas de peso até 65kg representam crianças e adolescentes em idade escolar, onde há uma forte relação entre a altura e o ganho de peso:" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": 0, 207 | "metadata": { 208 | "colab": {}, 209 | "colab_type": "code", 210 | "id": "o6n1oO-JQkQx" 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "peso_ate_65[\"Idade\"].unique()" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": { 220 | "colab_type": "text", 221 | "id": "7lp7E9kjQ3lj" 222 | }, 223 | "source": [ 224 | "Já para medianas de peso maiores que 65kg, estamos falando sobre adolescentes em idade universitária, adultos e idosos, onde não há uma relação direta entre altura e peso:" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 0, 230 | "metadata": { 231 | "colab": {}, 232 | "colab_type": "code", 233 | "id": "L3CwB8CtRBwr" 234 | }, 235 | "outputs": [], 236 | "source": [ 237 | "peso_maior_que_65[\"Idade\"].unique()" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": { 243 | "colab_type": "text", 244 | "id": "9e_RiZS0RWg6" 245 | }, 246 | "source": [ 247 | "Para finalizar, quando um par de características apresenta covariância **negativa**, observamos uma tendência com inclinação negativa. Podemos gerar uma nova característica chamada de `\"Faltam para 100kg\"` a partir da característica `\"Peso\"`:" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 0, 253 | "metadata": { 254 | "colab": {}, 255 | "colab_type": "code", 256 | "id": "qUQP7PbjSIse" 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "peso_altura[\"Faltam para 100kg\"] = 100 - peso_altura[\"Peso\"]\n", 261 | "sns.regplot(x=\"Faltam para 100kg\", y=\"Altura\", data=peso_altura)\n", 262 | "plt.show()" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "colab_type": "text", 269 | "id": "uCdHuffLSWm0" 270 | }, 271 | "source": [ 272 | "Note que a criação dessa nova característica não afetou a existência de dois padrões observada para a característica original. " 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": { 278 | "colab_type": "text", 279 | "id": "D1oKPljfT7HP" 280 | }, 281 | "source": [ 282 | "#### Métricas de correlação" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": { 288 | "colab_type": "text", 289 | "id": "sdMMVDf1VpRi" 290 | }, 291 | "source": [ 292 | "As métricas de correlação mais comuns são as métricas de **Pearson** e de **Spearman**, que avaliam a aderência dos pontos à tendência observada. Para a correlação de Pearson, utilizamos como tendência uma reta, como nos gráfico produzidos com o `regplot`. Os dados avaliados a seguir apresentam boa aderência à reta de tendência, o que é capturado pela correlação de Pearson como um valor próximo a 1:" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 0, 298 | "metadata": { 299 | "colab": {}, 300 | "colab_type": "code", 301 | "id": "nKU906uXVjgo" 302 | }, 303 | "outputs": [], 304 | "source": [ 305 | "sns.regplot(x=peso_ate_65[\"Altura\"], y=peso_ate_65[\"Peso\"])\n", 306 | "plt.show()" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": 0, 312 | "metadata": { 313 | "colab": {}, 314 | "colab_type": "code", 315 | "id": "hkmT1Cb8dWnG" 316 | }, 317 | "outputs": [], 318 | "source": [ 319 | "peso_ate_65[[\"Altura\",\"Peso\"]].corr()" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": { 325 | "colab_type": "text", 326 | "id": "umRUdVa4W2H1" 327 | }, 328 | "source": [ 329 | "Para o caso de correlação negativa, temos que uma correlação forte é avaliada como valores próximos a -1:" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 0, 335 | "metadata": { 336 | "colab": {}, 337 | "colab_type": "code", 338 | "id": "X623_FbgXE4Y" 339 | }, 340 | "outputs": [], 341 | "source": [ 342 | "peso_ate_65[\"Faltam para 100kg\"] = 100 - peso_ate_65[\"Peso\"]" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": 0, 348 | "metadata": { 349 | "colab": {}, 350 | "colab_type": "code", 351 | "id": "QguuCTE8XXtH" 352 | }, 353 | "outputs": [], 354 | "source": [ 355 | "sns.regplot(x=peso_ate_65[\"Altura\"], y=peso_ate_65[\"Faltam para 100kg\"])\n", 356 | "plt.show()" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 0, 362 | "metadata": { 363 | "colab": {}, 364 | "colab_type": "code", 365 | "id": "Jm7LMsyvdThD" 366 | }, 367 | "outputs": [], 368 | "source": [ 369 | "peso_ate_65[[\"Altura\",\"Faltam para 100kg\"]].corr()" 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "metadata": { 375 | "colab_type": "text", 376 | "id": "fOq8wOvOXgEp" 377 | }, 378 | "source": [ 379 | "Por fim, uma correlação fraca será avaliada com valores próximos a 0:" 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": 0, 385 | "metadata": { 386 | "colab": {}, 387 | "colab_type": "code", 388 | "id": "VEFDXmUvXmpI" 389 | }, 390 | "outputs": [], 391 | "source": [ 392 | "sns.regplot(x=peso_maior_que_65[\"Altura\"], y=peso_maior_que_65[\"Peso\"])\n", 393 | "plt.show()" 394 | ] 395 | }, 396 | { 397 | "cell_type": "code", 398 | "execution_count": 0, 399 | "metadata": { 400 | "colab": {}, 401 | "colab_type": "code", 402 | "id": "0BcG2P9wdN-z" 403 | }, 404 | "outputs": [], 405 | "source": [ 406 | "peso_maior_que_65[[\"Altura\",\"Peso\"]].corr()" 407 | ] 408 | }, 409 | { 410 | "cell_type": "markdown", 411 | "metadata": { 412 | "colab_type": "text", 413 | "id": "RcuShHkXbcfA" 414 | }, 415 | "source": [ 416 | "É importante observar que a correlação de Pearson mede apenas a aderência dos dados à tendência, não sendo afetada pela inclinação da reta de tendência (a não ser para indicar a correlação positiva ou negativa). Vamos ver este caso gerando uma característica para o índice de massa corporal:" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": 0, 422 | "metadata": { 423 | "colab": {}, 424 | "colab_type": "code", 425 | "id": "jl-pEqdCRmrZ" 426 | }, 427 | "outputs": [], 428 | "source": [ 429 | "peso_altura[\"IMC\"] = peso_altura[\"Peso\"] / (peso_altura[\"Altura\"] * peso_altura[\"Altura\"])" 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": 0, 435 | "metadata": { 436 | "colab": {}, 437 | "colab_type": "code", 438 | "id": "YwmfFEJgRzKf" 439 | }, 440 | "outputs": [], 441 | "source": [ 442 | "sns.regplot(x=\"Peso\", y=\"IMC\", data=peso_altura)\n", 443 | "plt.show()" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": 0, 449 | "metadata": { 450 | "colab": {}, 451 | "colab_type": "code", 452 | "id": "YjZbtUz7dA3o" 453 | }, 454 | "outputs": [], 455 | "source": [ 456 | "peso_altura[[\"Peso\",\"IMC\"]].cov()" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 0, 462 | "metadata": { 463 | "colab": {}, 464 | "colab_type": "code", 465 | "id": "iABUQK6LdCDx" 466 | }, 467 | "outputs": [], 468 | "source": [ 469 | "peso_altura[[\"Peso\",\"IMC\"]].corr()" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": { 475 | "colab_type": "text", 476 | "id": "GkKv449wBuUG" 477 | }, 478 | "source": [ 479 | "Note que, apesar de uma covariância baixa, os dados acima apresentam uma alta correlação. Este exemplo reflete bem a diferença entre a covariância e a correlação, uma vez que a covariância foca na inclinação da reta de tendência, enquanto a correlação foca na aderência dos dados a esta reta. Por medir a aderência dos dados à reta de tendência, a correlação de Pearson é descrita como linear. \n", 480 | "\n", 481 | "Em contrapartida, a correlação de Spearman permite avaliar a aderência dos dados a tendências lineares ou não. No exemplo abaixo, vemos que adotar uma medida de tendência não-linear (parâmetro `order=2` do método `regplot`) leva a uma maior aderência dos dados, o que é refletido pela correlação de Spearman:" 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": 0, 487 | "metadata": { 488 | "colab": {}, 489 | "colab_type": "code", 490 | "id": "zcAisUv9dKHR" 491 | }, 492 | "outputs": [], 493 | "source": [ 494 | "peso_maior_que_65[[\"Altura\",\"Peso\"]].corr(method=\"spearman\")" 495 | ] 496 | }, 497 | { 498 | "cell_type": "code", 499 | "execution_count": 0, 500 | "metadata": { 501 | "colab": {}, 502 | "colab_type": "code", 503 | "id": "pZ8exbGGY8nk" 504 | }, 505 | "outputs": [], 506 | "source": [ 507 | "sns.regplot(x=peso_maior_que_65[\"Altura\"], y=peso_maior_que_65[\"Peso\"], order=2)\n", 508 | "plt.show()" 509 | ] 510 | }, 511 | { 512 | "cell_type": "markdown", 513 | "metadata": { 514 | "colab_type": "text", 515 | "id": "4a-CglkZYYN0" 516 | }, 517 | "source": [ 518 | "### Entre múltiplos pares de características" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "metadata": { 524 | "colab_type": "text", 525 | "id": "EN2hy9e7YcHv" 526 | }, 527 | "source": [ 528 | "Quando trabalhamos com múltiplos pares de características, primeiro calculamos analiticamente a correlação entre cada par, para em seguida utilizarmos visualizações gráficas. Como visto acima, o método `corr` presente em objetos `DataFrame` permite o cálculo entre todos os pares de características existentes em um dataframe:" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": 0, 534 | "metadata": { 535 | "colab": {}, 536 | "colab_type": "code", 537 | "id": "Do9LaVlpahJZ" 538 | }, 539 | "outputs": [], 540 | "source": [ 541 | "peso_altura.corr()" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "metadata": { 547 | "colab_type": "text", 548 | "id": "MYatXfRsaDgP" 549 | }, 550 | "source": [ 551 | "Para melhorar a visualização dos dados, podemos gerar um **mapa de calor** a partir da matriz de correlações. Neste tipo de gráfico, as cores têm um papel fundamental no entendimento dos dados. Normalmente, cores mais frias (próximas a branco) representam correlações positivas, enquanto cores mais quentes (próximas a preto) nos apontam correlações negativas:" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": 0, 557 | "metadata": { 558 | "colab": {}, 559 | "colab_type": "code", 560 | "id": "y82BTsiqaA7p" 561 | }, 562 | "outputs": [], 563 | "source": [ 564 | "sns.heatmap(peso_altura.corr())\n", 565 | "plt.show()" 566 | ] 567 | }, 568 | { 569 | "cell_type": "markdown", 570 | "metadata": { 571 | "colab_type": "text", 572 | "id": "gpm1L_KsfEq2" 573 | }, 574 | "source": [ 575 | "Podemos customizar o método `heatmap` de diferentes formas, trocando por exemplo a legenda lateral por anotações dentro de cada célula do mapa:" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 0, 581 | "metadata": { 582 | "colab": {}, 583 | "colab_type": "code", 584 | "id": "j95mUdKNfAUg" 585 | }, 586 | "outputs": [], 587 | "source": [ 588 | "sns.heatmap(peso_altura.corr(), annot=True, cbar=False)\n", 589 | "plt.show()" 590 | ] 591 | }, 592 | { 593 | "cell_type": "markdown", 594 | "metadata": { 595 | "colab_type": "text", 596 | "id": "Xd9eoL_Eb5dD" 597 | }, 598 | "source": [ 599 | "## Causalidade e condicionalidade\n", 600 | "\n", 601 | "É importante salientar que uma alta correlação entre duas características **não implica de forma alguma** que uma característica é causada pela outra. De fato, é bastante difícil investigar causalidade apenas a partir da análise de um par de características.\n", 602 | "\n", 603 | "> Esse site mostra fortes correlações entre características, mas que possivelmente não apresentam causalidade entre si https://www.tylervigen.com/spurious-correlations\n", 604 | "\n", 605 | "Uma situação particular é o caso de características condicionais. Neste caso, o dataset disponível só apresenta dados para uma determinada característica quando o dado para uma outra característica satisfaz a determinada condição. \n", 606 | "\n", 607 | "Vamos analisar essa situação em um dataset sobre preços de casas do Kaggle. Para baixá-lo, siga primeiro [a etapa 1 deste tutorial](https://medium.com/@yvettewu.dw/tutorial-kaggle-api-google-colaboratory-1a054a382de0), que ensina a baixar as credenciais de acesso do Kaggle (`kaggle.json`). Uma vez que você tenha baixado suas credenciais, use o menu ao lado para fazer upload do arquivo para o Colab, e execute as três células abaixo:" 608 | ] 609 | }, 610 | { 611 | "cell_type": "code", 612 | "execution_count": 0, 613 | "metadata": { 614 | "colab": {}, 615 | "colab_type": "code", 616 | "id": "oElqzLZ0kINv" 617 | }, 618 | "outputs": [], 619 | "source": [ 620 | "!mkdir /root/.kaggle\n", 621 | "!cp /content/kaggle.json /root/.kaggle/kaggle.json\n", 622 | "!chmod 600 /root/.kaggle/kaggle.json" 623 | ] 624 | }, 625 | { 626 | "cell_type": "code", 627 | "execution_count": 0, 628 | "metadata": { 629 | "colab": {}, 630 | "colab_type": "code", 631 | "id": "xWfqb2_RjEmt" 632 | }, 633 | "outputs": [], 634 | "source": [ 635 | "!kaggle datasets download -d prevek18/ames-housing-dataset" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": 0, 641 | "metadata": { 642 | "colab": {}, 643 | "colab_type": "code", 644 | "id": "kes7xzxXlAjs" 645 | }, 646 | "outputs": [], 647 | "source": [ 648 | "ames_housing = pd.read_csv(\"ames-housing-dataset.zip\")\n", 649 | "ames_housing.head()" 650 | ] 651 | }, 652 | { 653 | "cell_type": "markdown", 654 | "metadata": { 655 | "colab_type": "text", 656 | "id": "Jt6EnPtxmhhb" 657 | }, 658 | "source": [ 659 | "> Caso alguma das células acima não funcione, entre em contato com os mantenedores do pandas-zero ;)\n", 660 | "\n", 661 | "Este dataset contém a descrição de várias propriedades, apresentando 82 características sobre cada propriedade. No entanto, quando um conjunto de dados apresenta um número alto de características, é comum que parte dessas características sejam informações adicionais em relação a outra característica.\n", 662 | "\n", 663 | "No caso deste dataset, vamos filtrar apenas as características relacionadas ao termo `\"Garage\"`:" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": 0, 669 | "metadata": { 670 | "colab": {}, 671 | "colab_type": "code", 672 | "id": "24Oy5EfOnLE9" 673 | }, 674 | "outputs": [], 675 | "source": [ 676 | "ames_garage = ames_housing.filter(like=\"Garage\")\n", 677 | "ames_garage.head()" 678 | ] 679 | }, 680 | { 681 | "cell_type": "markdown", 682 | "metadata": { 683 | "colab_type": "text", 684 | "id": "B3GxweNInUPZ" 685 | }, 686 | "source": [ 687 | "Note que a primeira característica define o tipo de garagem. Esta característica apresenta os seguintes valores:" 688 | ] 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": 0, 693 | "metadata": { 694 | "colab": {}, 695 | "colab_type": "code", 696 | "id": "ksIjnYa2nbUl" 697 | }, 698 | "outputs": [], 699 | "source": [ 700 | "ames_garage[\"Garage Type\"].value_counts(dropna=False)" 701 | ] 702 | }, 703 | { 704 | "cell_type": "markdown", 705 | "metadata": { 706 | "colab_type": "text", 707 | "id": "Zh2QUH15nrRe" 708 | }, 709 | "source": [ 710 | "Um dos valores possíveis para esta característica é a falta deste dado (`NaN`, que identificamos passando o argumento `dropna=False` para o método `value_counts`). Quando este dado está em falta, as demais características deixam de fazer sentido:" 711 | ] 712 | }, 713 | { 714 | "cell_type": "code", 715 | "execution_count": 0, 716 | "metadata": { 717 | "colab": {}, 718 | "colab_type": "code", 719 | "id": "5PXj4aqmnqfF" 720 | }, 721 | "outputs": [], 722 | "source": [ 723 | "ames_garage[ames_garage[\"Garage Type\"].isna()]" 724 | ] 725 | }, 726 | { 727 | "cell_type": "markdown", 728 | "metadata": { 729 | "colab_type": "text", 730 | "id": "KPtJmcySoPSv" 731 | }, 732 | "source": [ 733 | "A forma como procedemos neste tipo de situação depende do contexto. Neste caso, como temos características nominais dependendo desta característica e um baixo número de casos onde a característica original está ausente, a melhor alternativa é remover estas observações:" 734 | ] 735 | }, 736 | { 737 | "cell_type": "code", 738 | "execution_count": 0, 739 | "metadata": { 740 | "colab": {}, 741 | "colab_type": "code", 742 | "id": "ppffszIfohUi" 743 | }, 744 | "outputs": [], 745 | "source": [ 746 | "ames_housing = ames_housing[~ames_housing[\"Garage Type\"].isna()]\n", 747 | "ames_housing[\"Garage Type\"].value_counts(dropna=False)" 748 | ] 749 | }, 750 | { 751 | "cell_type": "markdown", 752 | "metadata": { 753 | "colab_type": "text", 754 | "id": "HUpVbbCdZEMe" 755 | }, 756 | "source": [ 757 | "## Um exemplo simples de seleção de características" 758 | ] 759 | }, 760 | { 761 | "cell_type": "markdown", 762 | "metadata": { 763 | "colab_type": "text", 764 | "id": "giHuTcRotepi" 765 | }, 766 | "source": [ 767 | "A principal vantagem de analisar as relações entre as características presentes em seu conjunto de dados é a possibilidade de selecionar um subconjunto de características que consegue representar a maior parte da informação contida no conjunto original. Para entender o quanto isso é necessário, vamos tomar como exemplo o dataset de casas acima. Começamos por um mapa de calor, para tentar avaliar se temos características redundantes:" 768 | ] 769 | }, 770 | { 771 | "cell_type": "code", 772 | "execution_count": 0, 773 | "metadata": { 774 | "colab": {}, 775 | "colab_type": "code", 776 | "id": "SKCFf2XTH3wG" 777 | }, 778 | "outputs": [], 779 | "source": [ 780 | "sns.heatmap(ames_housing.corr())\n", 781 | "plt.show()" 782 | ] 783 | }, 784 | { 785 | "cell_type": "markdown", 786 | "metadata": { 787 | "colab_type": "text", 788 | "id": "Wl8MfIKvLf1d" 789 | }, 790 | "source": [ 791 | "Note que é bastante difícil discutir algo em relação a um mapa tão grande. Uma ferramenta útil neste tipo de situação é o método `clustermap` da biblioteca `seaborn`, que agrupa os padrões observados no mapa acima." 792 | ] 793 | }, 794 | { 795 | "cell_type": "code", 796 | "execution_count": 0, 797 | "metadata": { 798 | "colab": {}, 799 | "colab_type": "code", 800 | "id": "BM3Pey8rMlgl" 801 | }, 802 | "outputs": [], 803 | "source": [ 804 | "sns.clustermap(ames_housing.corr())\n", 805 | "plt.show()" 806 | ] 807 | }, 808 | { 809 | "cell_type": "markdown", 810 | "metadata": { 811 | "colab_type": "text", 812 | "id": "YXe7DapxMtVx" 813 | }, 814 | "source": [ 815 | "> Note que a visualização acima inclui um dendrograma, que você pode pesquisar se quiser entender melhor o conceito de **análise de agrupamentos**.\n", 816 | "\n", 817 | "Agora a visualização ficou bem mais interpretável que nossa primeira tentativa, mas ainda é possível melhorá-la um pouco mais. Usando os parâmetros do método `clustermap`, vamos aumentar o tamanho do gráfico para que os nomes de todas as características caibam na legenda (`figsize=(20,20)`) e trocar a legenda de cores pela anotação de valores (`annot=True, fmt='.1g', cbar_pos=None`):" 818 | ] 819 | }, 820 | { 821 | "cell_type": "code", 822 | "execution_count": 0, 823 | "metadata": { 824 | "colab": {}, 825 | "colab_type": "code", 826 | "id": "2kFxJzQQIous" 827 | }, 828 | "outputs": [], 829 | "source": [ 830 | "sns.clustermap(ames_housing.corr(), figsize=(20,20), annot=True, fmt='.1g', cbar_pos=None)\n", 831 | "plt.show()" 832 | ] 833 | }, 834 | { 835 | "cell_type": "markdown", 836 | "metadata": { 837 | "colab_type": "text", 838 | "id": "TG656bYENW2H" 839 | }, 840 | "source": [ 841 | "Para nossa análise, vemos que há dois grandes grupos de características. O primeiro grupo praticamente se correlaciona com característica alguma do dataset. Por sua vez, o segundo grupo se correlaciona fortemente com as características do próprio segundo grupo. \n", 842 | "\n", 843 | "A forma como podemos proceder a partir destas informações depende do nosso objetivo. Se nosso objetivo for prever os valores da característica `\"SalePrice\"` a partir das demais características, podemos investigar inicialmente as características mais correlacionadas a ela. Neste caso, temos `\"Overall Qual\"` e `\"Gr Liv Area\"`, respectivamente a qualidade do imóvel e sua área habitável." 844 | ] 845 | }, 846 | { 847 | "cell_type": "code", 848 | "execution_count": 0, 849 | "metadata": { 850 | "colab": {}, 851 | "colab_type": "code", 852 | "id": "Dw5Tkdo8ZjTe" 853 | }, 854 | "outputs": [], 855 | "source": [ 856 | "sns.regplot(x=\"Overall Qual\", y=\"SalePrice\", data=ames_housing)\n", 857 | "plt.show()" 858 | ] 859 | }, 860 | { 861 | "cell_type": "code", 862 | "execution_count": 0, 863 | "metadata": { 864 | "colab": {}, 865 | "colab_type": "code", 866 | "id": "fjmOqfrchxtI" 867 | }, 868 | "outputs": [], 869 | "source": [ 870 | "sns.regplot(x=\"Gr Liv Area\", y=\"SalePrice\", data=ames_housing)\n", 871 | "plt.show()" 872 | ] 873 | }, 874 | { 875 | "cell_type": "markdown", 876 | "metadata": { 877 | "colab_type": "text", 878 | "id": "9f12Z6DwjlvA" 879 | }, 880 | "source": [ 881 | "Por sua vez, estas duas características têm forte correlação entre si:" 882 | ] 883 | }, 884 | { 885 | "cell_type": "code", 886 | "execution_count": 0, 887 | "metadata": { 888 | "colab": {}, 889 | "colab_type": "code", 890 | "id": "YFjJDaejlCFd" 891 | }, 892 | "outputs": [], 893 | "source": [ 894 | "sns.regplot(x=\"Overall Qual\", y=\"Gr Liv Area\", data=ames_housing)\n", 895 | "plt.show()" 896 | ] 897 | }, 898 | { 899 | "cell_type": "markdown", 900 | "metadata": { 901 | "colab_type": "text", 902 | "id": "tZHbIftolBbg" 903 | }, 904 | "source": [ 905 | "Assim, se quiséssemos trabalhar apenas com um conjunto reduzido de características, seria possível escolher apenas uma dentre as duas para manter em nosso conjunto de dados. \n", 906 | "\n", 907 | "> Na prática da análise de dados, há métodos robustos de **seleção de características** que você também pode pesquisar. Além da correlação, eles podem se basear em modelos estatísticos e de aprendizado de máquina uni e multi-variáveis 😉" 908 | ] 909 | }, 910 | { 911 | "cell_type": "code", 912 | "execution_count": null, 913 | "metadata": {}, 914 | "outputs": [], 915 | "source": [] 916 | } 917 | ], 918 | "metadata": { 919 | "colab": { 920 | "collapsed_sections": [], 921 | "name": "Relações.ipynb", 922 | "provenance": [] 923 | }, 924 | "kernelspec": { 925 | "display_name": "Python 3", 926 | "language": "python", 927 | "name": "python3" 928 | }, 929 | "language_info": { 930 | "codemirror_mode": { 931 | "name": "ipython", 932 | "version": 3 933 | }, 934 | "file_extension": ".py", 935 | "mimetype": "text/x-python", 936 | "name": "python", 937 | "nbconvert_exporter": "python", 938 | "pygments_lexer": "ipython3", 939 | "version": "3.7.3" 940 | } 941 | }, 942 | "nbformat": 4, 943 | "nbformat_minor": 1 944 | } 945 | -------------------------------------------------------------------------------- /pt-br/notebooks/Distribuição_Dados.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Distribuição_Dados.ipynb", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | } 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "id": "SkAAEdG-Ewte", 20 | "colab_type": "text" 21 | }, 22 | "source": [ 23 | "# Visualizando e identificando distribuições" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": { 29 | "id": "Pmeucql-SVty", 30 | "colab_type": "text" 31 | }, 32 | "source": [ 33 | "Uma parte importante da análise exploratória de dados é identificar as distribuições dos dados disponíveis. Diferentes tipos de visualização podem ser utilizadas em função do tipo de dado que se quer analisar." 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": { 39 | "id": "BbhTAG09UHhn", 40 | "colab_type": "text" 41 | }, 42 | "source": [ 43 | "## Visualizando dados discretos" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": { 49 | "id": "ZPGzIijNULgv", 50 | "colab_type": "text" 51 | }, 52 | "source": [ 53 | "Uma variável discreta possui valores contáveis, como a quantidade de meses num ano, a quantidade de alunos numa escola, a quantidade de objetos numa lista, etc. Dessa forma, cada valor possível da variável pode ser associado a uma probabilidade diferente de zero.\n", 54 | "\n", 55 | "Distribuições de dados discretos costumam ser analisadas a partir de contagens de frequência dos valores possíveis. O exemplo mais direto são os **histogramas**, que já vimos anteriormente. \n", 56 | "\n", 57 | "Como discutimos, histogramas são úteis principalmente quando a característica em análise apresenta poucos valores possíveis. Esse não costuma ser o caso quando desejamos analisar os termos mais frequentes em documentos, por exemplo.\n", 58 | "\n", 59 | "Uma opção mais interessante neste caso são as **nuvens de palavras** (em inglês, *word clouds*). Em uma nuvem de palavras, os termos que são mais frequentes aparecem em destaque (posições mais centrais e em tamanho maior). \n", 60 | "\n", 61 | "Em Python, podemos usar a biblioteca `wordcloud` em conjunto com a biblioteca `matplotlib` para gerar uma nuvem de palavras:" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "metadata": { 67 | "id": "GOUgtBdDyek5", 68 | "colab_type": "code", 69 | "colab": {} 70 | }, 71 | "source": [ 72 | "from wordcloud import WordCloud\n", 73 | "import matplotlib.pyplot as plt" 74 | ], 75 | "execution_count": 0, 76 | "outputs": [] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": { 81 | "id": "ogqeRriII4QC", 82 | "colab_type": "text" 83 | }, 84 | "source": [ 85 | "### Uma nuvem de palavras simples" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": { 91 | "id": "68-rit7a1h_2", 92 | "colab_type": "text" 93 | }, 94 | "source": [ 95 | "Vamos começar usando um exemplo artificial para focarmos no uso das bibliotecas:" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "metadata": { 101 | "id": "IT1SiVdO1oft", 102 | "colab_type": "code", 103 | "colab": {} 104 | }, 105 | "source": [ 106 | "texto=\"Natal Calor Sol Quente Calor Praia IMD IMD Calor Natal Tecnologia Digital Tecnologia Praia Praia Sol Verão Verão Verão Calor Calor Quente Quente Quente Quente IMD Sol Sol Calor Calor Calor Calor Digital IMD Verão Praia Praia Litoral Litoral Praia Calor Sol IMD Quente Vulcão\"" 107 | ], 108 | "execution_count": 0, 109 | "outputs": [] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": { 114 | "id": "GQXoMcUZ2N57", 115 | "colab_type": "text" 116 | }, 117 | "source": [ 118 | "A nuvem de palavras é criada em duas etapas. \n", 119 | "\n", 120 | "Primeiro, usamos o método `WordCloud` da biblioteca `wordcloud`, que nos permite passar parâmetros como a altura, largura e cor de fundo." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "metadata": { 126 | "id": "ZGk1CcXF2UHD", 127 | "colab_type": "code", 128 | "colab": {} 129 | }, 130 | "source": [ 131 | "wordcloud = WordCloud(width=550, height=550, background_color=\"white\").generate(texto)" 132 | ], 133 | "execution_count": 0, 134 | "outputs": [] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": { 139 | "id": "dSg4x61u2aTC", 140 | "colab_type": "text" 141 | }, 142 | "source": [ 143 | "Em seguida, usamos o método `imshow` da bibloteca `matplotlib` para visualizar a nuvem produzida. " 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "metadata": { 149 | "id": "DTzvnVc62dKD", 150 | "colab_type": "code", 151 | "colab": {} 152 | }, 153 | "source": [ 154 | "plt.imshow(wordcloud, interpolation='bilinear')\n", 155 | "plt.axis(\"off\")\n", 156 | "plt.show()" 157 | ], 158 | "execution_count": 0, 159 | "outputs": [] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": { 164 | "id": "4BMq64jrIa5O", 165 | "colab_type": "text" 166 | }, 167 | "source": [ 168 | "Alguns pequenos detalhes técnicos do código acima: \n", 169 | "- O parâmetro `interpolation` é usado para ter uma melhor qualidade na imagem produzida:\n", 170 | "```python\n", 171 | "plt.imshow(wordcloud, interpolation='bilinear')\n", 172 | "```\n", 173 | "- Como o `matplotlib` é geralmente usado para gerar gráficos matemáticos, precisamos especificar que não queremos que sejam gerados eixos para nossa nuvem de palavras:\n", 174 | "```python\n", 175 | "plt.axis(\"off\")\n", 176 | "```\n", 177 | "- A renderização final é feita com o método `show` da biblioteca `matplotlib`:\n", 178 | "```python\n", 179 | "plt.show()\n", 180 | "```\n" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": { 186 | "id": "1lFfk4RdJRtG", 187 | "colab_type": "text" 188 | }, 189 | "source": [ 190 | "### Uma nuvem de palavras no mundo real" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": { 196 | "id": "HhB0aBswJVbu", 197 | "colab_type": "text" 198 | }, 199 | "source": [ 200 | "Em termos de bibliotecas e métodos, o exemplo acima é muito próximo do que usamos no dia a dia da análise de documentos. No entanto, com textos reais, é necessário atentarmos para os conceitos de **stopwords** e **corpus**.\n", 201 | "\n", 202 | "Vamos ver um exemplo que ilustra isso, extraído da introdução da página da Wikipedia sobre Natal-RN:" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "metadata": { 208 | "id": "WOygK-w8JN_W", 209 | "colab_type": "code", 210 | "colab": {} 211 | }, 212 | "source": [ 213 | "texto=\"\"\"\n", 214 | " Natal é um município brasileiro, capital do estado do Rio Grande do Norte, na Região Nordeste do país. Com uma área de aproximadamente 167 km², é a segunda capital brasileira com a menor área territorial e a sexta maior capital do país em densidade populacional, distando 2 227 quilômetros de Brasília, a capital federal.\n", 215 | " Fundada em 1599, às margens do Rio Potenji, conta com importantes monumentos, parques e museus e pontos turísticos, como o Teatro Alberto Maranhão e a Coluna Capitolina Del Pretti, no Centro Histórico, além de outras atrações, entre elas a Ponte Newton Navarro, o Museu Câmara Cascudo, o Parque da Cidade Dom Nivaldo Monte, o Museu de Cultura Popular, o Parque das Dunas, a Catedral Metropolitana e praias como Ponta Negra e dos Artistas, e eventos de grande repercussão, tais como a Feira Internacional de Artesanato (FIART), o Carnatal, as festas juninas e as comemorações natalinas.\n", 216 | " Historicamente, a cidade teve grande importância durante a Segunda Guerra Mundial em 1942 durante a Operação Tocha, já que os aviões da base aliada americana se abasteciam com combustível no lugar onde durante muito tempo foi o Aeroporto Internacional Augusto Severo, sendo classificada como \"um dos quatro pontos mais estratégicos do mundo\". Devido às operações da primeira base de foguetes da América do Sul, no Centro de Lançamento da Barreira do Inferno, hoje localizada no município limítrofe de Parnamirim, Natal também passou a ser conhecida como a \"Capital Espacial do Brasil\". A capital potiguar foi também uma das doze sedes da Copa do Mundo de 2014.\n", 217 | " De acordo com a estimativa realizada pelo Instituto Brasileiro de Geografia e Estatística (IBGE) em 2018, a população do município é de 877 640 habitantes, sendo o décimo nono município mais populoso do país e sua região metropolitana, formada por outros treze municípios do Rio Grande do Norte, possui uma população de pouco mais de 1,5 milhão de habitantes, formando a quinta maior aglomeração urbana do Nordeste e a décima nona do Brasil.\n", 218 | " \"\"\"" 219 | ], 220 | "execution_count": 0, 221 | "outputs": [] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "metadata": { 226 | "id": "WvxH6bbwJwKQ", 227 | "colab_type": "code", 228 | "colab": {} 229 | }, 230 | "source": [ 231 | "wordcloud = WordCloud(width=550, height=550, background_color=\"white\").generate(texto)\n", 232 | "plt.imshow(wordcloud, interpolation='bilinear')\n", 233 | "plt.axis(\"off\")\n", 234 | "plt.show()" 235 | ], 236 | "execution_count": 0, 237 | "outputs": [] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": { 242 | "id": "K5Q0XSjGJ6VU", 243 | "colab_type": "text" 244 | }, 245 | "source": [ 246 | "Em textos reais, é bem comum que os termos mais frequentes sejam termos pouco relevantes, como artigos e preposições. No campo de **processamento de linguagem natural** (NLP), esses termos são conhecidos como *stopwords*.\n", 247 | "\n", 248 | "Em geral, stopwords são fornecidas por bibliotecas de NLP como parte de um *corpus*, isto é, uma base de dados em um determinado idioma construída a partir de uma coleção de documentos.\n", 249 | "\n", 250 | "Em Python, as principais bibliotecas de NLP são a `nltk` e a `spacy`. Vamos ver a seguir um exemplo usando a biblioteca `nltk`, onde começamos baixando a coleção de stopwords da biblioteca:" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "metadata": { 256 | "id": "I2UOU-H8GqNW", 257 | "colab_type": "code", 258 | "colab": {} 259 | }, 260 | "source": [ 261 | "import nltk\n", 262 | "nltk.download('stopwords')" 263 | ], 264 | "execution_count": 0, 265 | "outputs": [] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": { 270 | "id": "cILLJlwuL7Ai", 271 | "colab_type": "text" 272 | }, 273 | "source": [ 274 | "Uma vez baixada a coleção de stopwords, podemos selecionar apenas as para o português, idioma do texto da Wikipedia sobre Natal-RN:" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "metadata": { 280 | "id": "Y28AXJecLcH7", 281 | "colab_type": "code", 282 | "colab": {} 283 | }, 284 | "source": [ 285 | "from nltk.corpus import stopwords\n", 286 | "stopwords_pt = stopwords.words(\"portuguese\")" 287 | ], 288 | "execution_count": 0, 289 | "outputs": [] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": { 294 | "id": "I6KsaPeMMDj2", 295 | "colab_type": "text" 296 | }, 297 | "source": [ 298 | "Agora podemos gerar nossa nuvem de palavras como fizemos anteriormente, adicionando apenas o parâmetro `stopwords` ao método `WordCloud`:" 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "metadata": { 304 | "id": "xp2ZLnooLLo2", 305 | "colab_type": "code", 306 | "colab": {} 307 | }, 308 | "source": [ 309 | "wordcloud = WordCloud(width=550, height=550, background_color=\"white\", stopwords=stopwords_pt).generate(texto)\n", 310 | "plt.imshow(wordcloud, interpolation='bilinear')\n", 311 | "plt.axis(\"off\")\n", 312 | "plt.show()" 313 | ], 314 | "execution_count": 0, 315 | "outputs": [] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": { 320 | "id": "KdDwSVzXMVt0", 321 | "colab_type": "text" 322 | }, 323 | "source": [ 324 | "Em destaque, vemos os termos que de fato melhor identificam Natal-RN no texto da Wikipedia.\n", 325 | "\n", 326 | "Note que alguns termos aparecem separados, como Rio Grande do Norte e Região Nordeste. Esse tipo de análise requer o uso de n-gramas, que você pode pesquisar em tutoriais sobre a `nltk` =)" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": { 332 | "id": "BVrs-Bc0RRX4", 333 | "colab_type": "text" 334 | }, 335 | "source": [ 336 | "## Visualizando dados contínuos" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": { 342 | "id": "TRyRtyl4RUcR", 343 | "colab_type": "text" 344 | }, 345 | "source": [ 346 | "Quando desejamos analisar dados contínuos, podemos:\n", 347 | "- usar representações que aplicam uma discretização aos dados\n", 348 | "- usar representações próprias para dados contínuos" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": { 354 | "id": "TvR8cJn4RqrE", 355 | "colab_type": "text" 356 | }, 357 | "source": [ 358 | "O principal exemplo de visualização discretizada é o histograma, como já vimos anteriormente. Já para visualização contínua, podemos usar gráficos de densidade, que também já discutimos. Seja qual for o tipo de visualização adotada, o essencial é conseguir identificar o tipo de distribuição apresentada e como proceder em cada caso." 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": { 364 | "id": "HugIEG0pbTi0", 365 | "colab_type": "text" 366 | }, 367 | "source": [ 368 | "### Principais tipos de distribuição\n", 369 | "\n", 370 | "Em notebooks anteriores, discutimos rapidamente sobre as distribuições normal e bimodal. Aqui, vamos discutir rapidamente sobre outros dois tipos de distribuição que são comuns: a **uniforme** e a **exponencial**." 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": { 376 | "id": "JmKI715IDRzC", 377 | "colab_type": "text" 378 | }, 379 | "source": [ 380 | "#### Distribuição Uniforme\n", 381 | "\n", 382 | "Neste tipo de distribuição, todos os valores possíveis para um dado têm chances iguais de ocorrer. Esse tipo de distribuição é mais comum na teoria do que na prática, então vamos gerar um exemplo artificial usando o método `arange` da biblioteca `numpy`. Esse método gera uma lista de valores com probabilidade uniforme. No exemplo a seguir, geramos valores entre 0 e 10, usando cinco casas decimais de precisão." 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "metadata": { 388 | "id": "wuiisoudck7q", 389 | "colab_type": "code", 390 | "colab": {} 391 | }, 392 | "source": [ 393 | "import numpy as np\n", 394 | "s = np.arange(10, step=0.00001)" 395 | ], 396 | "execution_count": 0, 397 | "outputs": [] 398 | }, 399 | { 400 | "cell_type": "markdown", 401 | "metadata": { 402 | "id": "4n4L9taai7_l", 403 | "colab_type": "text" 404 | }, 405 | "source": [ 406 | "Pra visualizarmos a distribuição desses dados, vamos usar o método `distplot` da biblioteca `seaborn`, que produz tanto um histograma como um gráfico de densidade. No exemplo abaixo, limitamos o gráfico ao intervalo de valores entre 1 e 2." 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "metadata": { 412 | "id": "7yoZYeopDrIu", 413 | "colab_type": "code", 414 | "colab": {} 415 | }, 416 | "source": [ 417 | "import seaborn as sns\n", 418 | "sns.distplot(s)\n", 419 | "plt.xlim(1,2)" 420 | ], 421 | "execution_count": 0, 422 | "outputs": [] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": { 427 | "id": "Fe85_MVgk1k2", 428 | "colab_type": "text" 429 | }, 430 | "source": [ 431 | "Note que a função de densidade é uma reta, indicando que cada valor possível apresenta a mesma probabilidade de ocorrência." 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": { 437 | "id": "p-QWHvIB8DYI", 438 | "colab_type": "text" 439 | }, 440 | "source": [ 441 | "#### Distribuição exponencial\n", 442 | "\n", 443 | "Em uma distribuição exponencial, os valores mais frequentes ocorrem com uma probabilidade exponencialmente maior que os valores menos frequentes. Essa distribuição pode ser observada, por exemplo, ao analisarmos a população dos municípios brasileiros. Vamos carregar inicialmente a estimativa fornecida pelo TCU para o ano de 2019:" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "metadata": { 449 | "id": "72uasQ4DPcw0", 450 | "colab_type": "code", 451 | "colab": {} 452 | }, 453 | "source": [ 454 | "import pandas as pd" 455 | ], 456 | "execution_count": 0, 457 | "outputs": [] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "metadata": { 462 | "id": "zq27f9yXtBvv", 463 | "colab_type": "code", 464 | "colab": {} 465 | }, 466 | "source": [ 467 | "ibge = pd.read_csv(\"https://raw.githubusercontent.com/leobezerra/pandas-zero/master/datasets/estimativa_TCU_2019_20191031_limpa.csv\", header=0, sep=\",\", thousands=\",\")" 468 | ], 469 | "execution_count": 0, 470 | "outputs": [] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "metadata": { 475 | "id": "OTiSRLx0tPXR", 476 | "colab_type": "code", 477 | "colab": {} 478 | }, 479 | "source": [ 480 | "ibge.head()" 481 | ], 482 | "execution_count": 0, 483 | "outputs": [] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": { 488 | "id": "poba0--mzytw", 489 | "colab_type": "text" 490 | }, 491 | "source": [ 492 | "Para nossa análise, nos interessa visualizar a distribuição da característica `\"POPULAÇÃO ESTIMADA\"`:" 493 | ] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "metadata": { 498 | "id": "wO-_K2Uasiyd", 499 | "colab_type": "code", 500 | "colab": {} 501 | }, 502 | "source": [ 503 | "sns.distplot(ibge[\"POPULAÇÃO ESTIMADA\"])" 504 | ], 505 | "execution_count": 0, 506 | "outputs": [] 507 | }, 508 | { 509 | "cell_type": "markdown", 510 | "metadata": { 511 | "id": "cy6MrEEBz8zP", 512 | "colab_type": "text" 513 | }, 514 | "source": [ 515 | "Note que a visualização do gráfico é bastante difícil, tamanha a discrepância entre as frequências. Uma forma de lidar com uma distribuição exponencial é realizar uma transformação logarítmica sobre os dados. Na prática, isto significa que a grandeza passará a ser representada em uma escala logarítmica:" 516 | ] 517 | }, 518 | { 519 | "cell_type": "code", 520 | "metadata": { 521 | "id": "HGnbPgFk0XLo", 522 | "colab_type": "code", 523 | "colab": {} 524 | }, 525 | "source": [ 526 | "ibge_log = np.log(ibge[\"POPULAÇÃO ESTIMADA\"])\n", 527 | "sns.distplot(ibge_log)" 528 | ], 529 | "execution_count": 0, 530 | "outputs": [] 531 | }, 532 | { 533 | "cell_type": "markdown", 534 | "metadata": { 535 | "id": "TDQs9Ag11AvJ", 536 | "colab_type": "text" 537 | }, 538 | "source": [ 539 | "### Identificando o tipo de distribuição" 540 | ] 541 | }, 542 | { 543 | "cell_type": "markdown", 544 | "metadata": { 545 | "id": "Ut0toEwcma7M", 546 | "colab_type": "text" 547 | }, 548 | "source": [ 549 | "A distribuição apresentada no gráfico acima se assemelha a uma distribuição normal, mas gera dois questionamentos:\n", 550 | "- a presença de dois picos é um sinal de bimodalidade?\n", 551 | "- existe uma inclinação da distribuição para a esquerda? \n", 552 | "\n", 553 | "Para visualizarmos esses questionamentos de forma mais clara, vamos adicionar ao gráfico uma distribuição normal de referência usando o método `norm` do módulo `scipy.stats`:" 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "metadata": { 559 | "id": "tZdMUFY72lEL", 560 | "colab_type": "code", 561 | "colab": {} 562 | }, 563 | "source": [ 564 | "from scipy.stats import norm" 565 | ], 566 | "execution_count": 0, 567 | "outputs": [] 568 | }, 569 | { 570 | "cell_type": "code", 571 | "metadata": { 572 | "id": "HqOk3wzilsDf", 573 | "colab_type": "code", 574 | "colab": {} 575 | }, 576 | "source": [ 577 | "sns.distplot(ibge_log, fit=norm)" 578 | ], 579 | "execution_count": 0, 580 | "outputs": [] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": { 585 | "id": "eMZ_udT53r2x", 586 | "colab_type": "text" 587 | }, 588 | "source": [ 589 | "Note que, de fato, a distribuição aparenta estar um pouco para esquerda, e apenas um de seus picos coincide com a distribuição de referência. Vamos então usar outros dois métodos para verificar a normalidade dessa distribuição." 590 | ] 591 | }, 592 | { 593 | "cell_type": "markdown", 594 | "metadata": { 595 | "id": "bdIZ-dy_T30k", 596 | "colab_type": "text" 597 | }, 598 | "source": [ 599 | "#### Gráficos QQ" 600 | ] 601 | }, 602 | { 603 | "cell_type": "markdown", 604 | "metadata": { 605 | "id": "gwwUSdkhT6yX", 606 | "colab_type": "text" 607 | }, 608 | "source": [ 609 | "É possível comparar duas distribuições a partir de seus quantis.\n", 610 | "Em linhas gerais, um gráfico QQ é um gráfico que analisa a **correlação** entre os quantis de cada distribuição comparada. Se duas distribuições de dados forem de mesma natureza, a correlação será máxima, o que fará o gráfico se alinhar com uma reta inclinada 45 graus em relação ao eixo x.\n", 611 | "\n", 612 | "Vamos comparar nossa distribuição com a distribuição normal para ver o resultado que temos. Para isso, vamos usar o método `probplot` da biblioteca `scipy`:" 613 | ] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "metadata": { 618 | "id": "7d2vT1Ly7QIX", 619 | "colab_type": "code", 620 | "colab": {} 621 | }, 622 | "source": [ 623 | "from scipy.stats import probplot" 624 | ], 625 | "execution_count": 0, 626 | "outputs": [] 627 | }, 628 | { 629 | "cell_type": "code", 630 | "metadata": { 631 | "id": "Ncxd9dALlzTy", 632 | "colab_type": "code", 633 | "colab": {} 634 | }, 635 | "source": [ 636 | "probplot(ibge_log, plot=plt)\n", 637 | "plt.show()" 638 | ], 639 | "execution_count": 0, 640 | "outputs": [] 641 | }, 642 | { 643 | "cell_type": "markdown", 644 | "metadata": { 645 | "id": "Svbf2_oW8R73", 646 | "colab_type": "text" 647 | }, 648 | "source": [ 649 | "Um pequeno detalhe técnico do código acima é que precisamos informar ao método que exibisse o resultado através do matplotlib (`plot=plt`).\n", 650 | "\n", 651 | "Note que a curva apresentada não está perfeitamente alinhada com a reta ideal. No entanto, vamos ver o que aconteceria se comparássemos nossos dados com uma distribuição uniforme." 652 | ] 653 | }, 654 | { 655 | "cell_type": "code", 656 | "metadata": { 657 | "id": "SljeGiybShXF", 658 | "colab_type": "code", 659 | "colab": {} 660 | }, 661 | "source": [ 662 | "probplot(ibge_log, plot=plt, dist=\"uniform\")\n", 663 | "plt.show()" 664 | ], 665 | "execution_count": 0, 666 | "outputs": [] 667 | }, 668 | { 669 | "cell_type": "markdown", 670 | "metadata": { 671 | "id": "RKMy0N3NU6rZ", 672 | "colab_type": "text" 673 | }, 674 | "source": [ 675 | "Note que a discrepância em relação à reta de referência nas extremidades é ainda mais acentuada. Assim, temos a certeza de que a distribuição que temos se aproxima mais de uma distribuição normal que de uma distribuição uniforme." 676 | ] 677 | }, 678 | { 679 | "cell_type": "markdown", 680 | "metadata": { 681 | "id": "nTU12-C-VDSO", 682 | "colab_type": "text" 683 | }, 684 | "source": [ 685 | "#### Testes de hipótese" 686 | ] 687 | }, 688 | { 689 | "cell_type": "markdown", 690 | "metadata": { 691 | "id": "27istcCsVH2K", 692 | "colab_type": "text" 693 | }, 694 | "source": [ 695 | "Uma outra ferramenta da estatística para análise de normalidade são os testes de hipótese.\n", 696 | "\n", 697 | "A módulo `scipy.stats` traz uma série de testes que poderíamos adotar aqui.\n", 698 | "\n", 699 | "Por simplicidade, vamos adotar o método `normaltest`:" 700 | ] 701 | }, 702 | { 703 | "cell_type": "code", 704 | "metadata": { 705 | "id": "GQghGsxJVXai", 706 | "colab_type": "code", 707 | "colab": {} 708 | }, 709 | "source": [ 710 | "from scipy.stats import normaltest" 711 | ], 712 | "execution_count": 0, 713 | "outputs": [] 714 | }, 715 | { 716 | "cell_type": "code", 717 | "metadata": { 718 | "id": "L_LnKKzXl8WX", 719 | "colab_type": "code", 720 | "colab": {} 721 | }, 722 | "source": [ 723 | "normaltest(ibge_log)" 724 | ], 725 | "execution_count": 0, 726 | "outputs": [] 727 | }, 728 | { 729 | "cell_type": "markdown", 730 | "metadata": { 731 | "id": "XTFoyzmmVhNu", 732 | "colab_type": "text" 733 | }, 734 | "source": [ 735 | "Em um teste de hipótese, estamos tentando refutar uma hipótese base (conhecida como nula). Neste caso, a hipótese nula é de que os dados que temos vêm de uma distribuição normal. Se não conseguirmos refutar essa hipótese, aceitamos que os dados disponíveis seguem de fato uma distribuição normal.\n", 736 | "\n", 737 | "Em geral, o resultado de um teste de hipótese é dado por um p-valor, que representa o nível de significância do teste. Uma boa referência de descarte da hipótese nula é adotar um p-valor de 0.05 como limite. Assim, para p-valores abaixo de 0.05, podemos rejeitar a hipótese nula.\n", 738 | "\n", 739 | "Neste caso, vemos que o p-valor do teste foi consideravelmente menor que este limite, o que nos dá confiança para dizer que os dados não seguem uma distribuição normal." 740 | ] 741 | }, 742 | { 743 | "cell_type": "markdown", 744 | "metadata": { 745 | "id": "Zz3jUX6aWRE5", 746 | "colab_type": "text" 747 | }, 748 | "source": [ 749 | "### Desvios comuns em distribuições normais" 750 | ] 751 | }, 752 | { 753 | "cell_type": "markdown", 754 | "metadata": { 755 | "id": "CvzBtkBR6eTq", 756 | "colab_type": "text" 757 | }, 758 | "source": [ 759 | "Por ser uma das distribuições mais estudadas, a distribuição normal apresenta muitos recursos teóricos e práticos disponíveis para sua análise. Em relação a desvios de normalidade, os dois conceitos mais comuns são a assimetria, discutida acima, e a **curtose** (*kurtosis*).\n", 760 | "\n", 761 | "\n", 762 | "Enquanto a assimetria é percebida em relação ao centro da distribuição, a curtose é percebida como um pico ou como uma distribuição plateau (sem pico). Podemos medir ambas estas medidas usando métodos do módulo `scipy`:" 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "metadata": { 768 | "id": "zt-ycmtN9nms", 769 | "colab_type": "code", 770 | "colab": {} 771 | }, 772 | "source": [ 773 | "from scipy.stats import skew, kurtosis" 774 | ], 775 | "execution_count": 0, 776 | "outputs": [] 777 | }, 778 | { 779 | "cell_type": "code", 780 | "metadata": { 781 | "id": "o4ioHjUS-2zh", 782 | "colab_type": "code", 783 | "colab": {} 784 | }, 785 | "source": [ 786 | "skew(ibge_log)" 787 | ], 788 | "execution_count": 0, 789 | "outputs": [] 790 | }, 791 | { 792 | "cell_type": "code", 793 | "metadata": { 794 | "id": "h3GFicUg-YVB", 795 | "colab_type": "code", 796 | "colab": {} 797 | }, 798 | "source": [ 799 | "kurtosis(ibge_log)" 800 | ], 801 | "execution_count": 0, 802 | "outputs": [] 803 | }, 804 | { 805 | "cell_type": "markdown", 806 | "metadata": { 807 | "id": "tyC83xikAqWy", 808 | "colab_type": "text" 809 | }, 810 | "source": [ 811 | "Em ambos os casos, uma distribuição normal deveria apresentar valores próximos a zero. Poderíamos tratar a assimetria e a curtose desta distribuição aplicando novas transformações logarítmicas.\n" 812 | ] 813 | }, 814 | { 815 | "cell_type": "code", 816 | "metadata": { 817 | "id": "eFci-5DYBRnZ", 818 | "colab_type": "code", 819 | "colab": {} 820 | }, 821 | "source": [ 822 | "ibge_4logs = np.log(np.log(np.log(ibge_log)))\n", 823 | "print(skew(ibge_4logs))\n", 824 | "print(kurtosis(ibge_4logs))\n", 825 | "sns.distplot(ibge_4logs, fit=norm)" 826 | ], 827 | "execution_count": 0, 828 | "outputs": [] 829 | }, 830 | { 831 | "cell_type": "code", 832 | "metadata": { 833 | "id": "uwd4E74LBgx8", 834 | "colab_type": "code", 835 | "colab": {} 836 | }, 837 | "source": [ 838 | "print(normaltest(ibge_4logs))\n", 839 | "probplot(ibge_4logs, plot=plt)\n", 840 | "plt.show()" 841 | ], 842 | "execution_count": 0, 843 | "outputs": [] 844 | }, 845 | { 846 | "cell_type": "markdown", 847 | "metadata": { 848 | "id": "M8lB8cGcByiX", 849 | "colab_type": "text" 850 | }, 851 | "source": [ 852 | "Note que, apesar do teste de hipótese continuar não indicando a normalidade da distribuição, as demais medidas melhoraram consideravelmente. No entanto, transformar os dados seguidas vezes pode compremeter severamente sua interpretação no domínio original.\n", 853 | "\n", 854 | "Assim, o uso deste tipo de técnica de transformação deve ser avaliado cuidadosamente." 855 | ] 856 | } 857 | ] 858 | } --------------------------------------------------------------------------------