Olá Devs!
Se você chegou até aqui, significa que já deve ter aprendido como carregar os dados no Google Colab utilizando as bibliotecas padrão do python. E deve ter percebido que a tarefa não é simples, e requer muitas linhas de código (Rapadura é doce mas não é mole não, diria o Yusuke Urameshi).
Justamente com a intenção de simplificar o trabalho dos cientistas e engenheiros de dados é que a biblioteca Pandas foi criada. Com a biblioteca Pandas, todo o trabalho de manipulação dos dados fica simplificado, rápido e mais organizado.
Instalando Pandas
Tenho uma boa notícia: como estamos utilizando o Google Colab para nossas aulas, ele já vem instalado no nosso ambiente! Mas caso você esteja também se aventurando em fazer este curso no VS Code, PyCharm, Sublime ou até mesmo Notepad, o comando para instalar a biblioteca Pandas é o seguinte:
(venv) $ pip install pandas
Este comando irá instalar a biblioteca Pandas e todos as bibliotecas dependentes. É aconselhável que você crie um ambiente virtual para instalar as bibliotecas, mas isso é assunto para um outro dia.
Utilizando Pandas
Iremos ver, passo a passo, como utilizar a biblioteca Pandas em nosso notebook Google Colab, passando pelos seus principais conceitos.
Carregando Pandas no notebook
A primeira coisa que devemos fazer é carregar a biblioteca em nosso notebook. Fazemos isso com o comando import
.
import pandas as pd
Tip
É considerado uma boa prática ao carregar a biblioteca sempre utilizar o alias pd
. Observe que todos os artigos na internet utilizam essa mesma referência.
Carregando os dados
Na aula 3, aprendemos que uma maneira de carregar os dados em um notebook on Google Colab é a seguinte:
import csv
pokemons = {}
with open('pokemons.csv', 'r', encoding='utf-8') as arquivo:
dados = csv.DictReader(arquivo)
for pokemon in dados:
for key, value in pokemon.items():
pokemons.setdefault(key,[]).append(value)
Utilizando pandas, isso será substituído por uma única linha:
pokemons = pd.read_csv('pokemons.csv')
Ficou bem mais fácil, não é mesmo? Da mesma forma, verificar o conteúdo que foi carregado também se simplifica - vai disso aqui:
for indice in range(0, 2): # executa um loop dos dois primeiros elementos do array
print(pokemons[indice]) # imprime o elemento
para isso aqui:
pokemons.head(2)
Number | Name | Type 1 | Type 2 | Abilities | HP | Att | Def | Spa | Spd | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Bulbasaur | Grass | Poison | ['Chlorophyll', 'Overgrow'] | 45 | 49 | 49 | 65 | 65 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 0.7 | 6.9 | 14.1 |
1 | 2 | Ivysaur | Grass | Poison | ['Chlorophyll', 'Overgrow'] | 60 | 62 | 63 | 80 | 80 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 13.0 | 13.0 |
2 rows × 44 columns
Mas não é só isso…
Outra grande vantagem de se utilizar pandas é que temos a nossa disposição um grande número de opções para carregar dados. Além do read_csv
, temos readers especializados: read_json
, read_excel
, e por aí vai… Aconselho a dar uma olhada no manual do pandas aqui.
Inspecionando os dados
Após termos os dados carregados, a nossa próxima atividade é inspecionar os dados. Além do comando que vimos acima head()
, que pode nos mostrar as primeiras linhas do DataFrame, temos outras funções, como:
-
tail()
- mostra as últimas linhas do DataFrame -
sample()
- mostra linhas aleatórias do DataFrame -
describe()
- mostra os valores de diversas medidas -
info()
- mostra os campos do DataFrame com seus tipos -
shape
- dá as dimensões (coluna, linha) do DataFrame
Vamos ver em detalhes cada uma destas funções.
head()
Esta função lista as primeiras linhas de dados. O pârametro é opcional, o que fará com que a função liste 10 linhas de dados.
pokemons.head(5)
Number | Name | Type 1 | Type 2 | Abilities | HP | Att | Def | Spa | Spd | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | Bulbasaur | Grass | Poison | ['Chlorophyll', 'Overgrow'] | 45 | 49 | 49 | 65 | 65 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 0.7 | 6.9 | 14.1 |
1 | 2 | Ivysaur | Grass | Poison | ['Chlorophyll', 'Overgrow'] | 60 | 62 | 63 | 80 | 80 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 13.0 | 13.0 |
2 | 3 | Venusaur | Grass | Poison | ['Chlorophyll', 'Overgrow'] | 80 | 82 | 83 | 100 | 100 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 2.0 | 100.0 | 25.0 |
3 | 3 | Mega Venusaur | Grass | Poison | ['Thick Fat'] | 80 | 100 | 123 | 122 | 120 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 2.4 | 155.5 | 27.0 |
4 | 4 | Charmander | Fire | NaN | ['Blaze', 'Solar Power'] | 39 | 52 | 43 | 60 | 50 | ... | 0.5 | 2.0 | 1.0 | 1.0 | 1.0 | 0.5 | 0.5 | 0.6 | 8.5 | 23.6 |
5 rows × 44 columns
tail()
Esta função lista as últimas linhas de dados. O pârametro é opcional, o que fará com que a função liste 10 linhas de dados.
pokemons.tail(5)
Number | Name | Type 1 | Type 2 | Abilities | HP | Att | Def | Spa | Spd | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1027 | 896 | Glastrier | Ice | NaN | ['Chilling Neigh'] | 100 | 145 | 130 | 65 | 110 | ... | 1.0 | 2.0 | 1.0 | 1.0 | 1.0 | 2.0 | 1.0 | 2.2 | 800.0 | 165.3 |
1028 | 897 | Spectrier | Ghost | NaN | ['Grim Neigh'] | 100 | 65 | 60 | 145 | 80 | ... | 0.5 | 1.0 | 2.0 | 1.0 | 2.0 | 1.0 | 1.0 | 2.0 | 44.5 | 11.1 |
1029 | 898 | Calyrex | Psychic | Grass | ['Unnerve'] | 100 | 80 | 80 | 80 | 80 | ... | 4.0 | 1.0 | 2.0 | 1.0 | 2.0 | 1.0 | 1.0 | 1.1 | 7.7 | 6.4 |
1030 | 898 | Calyrex Ice Rider | Psychic | Ice | ['As One'] | 100 | 165 | 150 | 85 | 130 | ... | 2.0 | 2.0 | 2.0 | 1.0 | 2.0 | 2.0 | 1.0 | 2.4 | 809.1 | 140.5 |
1031 | 898 | Calyrex Shadow Rider | Psychic | Ghost | ['As One'] | 100 | 85 | 80 | 165 | 100 | ... | 1.0 | 1.0 | 4.0 | 1.0 | 4.0 | 1.0 | 1.0 | 2.4 | 53.6 | 9.3 |
5 rows × 44 columns
sample()
Enquanto head()
e tail()
mostra o início e o fim do conjunto de dados, o sample()
traz linhas aleatórias do conjunto, o que pode ser bem interessante.
pokemons.sample(5)
Number | Name | Type 1 | Type 2 | Abilities | HP | Att | Def | Spa | Spd | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
519 | 438 | Bonsly | Rock | NaN | ['Rattled', 'Rock Head', 'Sturdy'] | 50 | 80 | 95 | 10 | 45 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 2.0 | 1.0 | 0.5 | 15.0 | 60.0 |
635 | 542 | Leavanny | Bug | Grass | ['Chlorophyll', 'Overcoat', 'Swarm'] | 75 | 103 | 80 | 70 | 80 | ... | 2.0 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.2 | 20.5 | 14.2 |
405 | 340 | Whiscash | Water | Ground | ['Anticipation', 'Hydration', 'Oblivious'] | 110 | 78 | 73 | 76 | 71 | ... | 1.0 | 0.5 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 0.9 | 23.6 | 29.1 |
502 | 422 | Shellos | Water | NaN | ['Sand Force', 'Sticky Hold', 'Storm Drain'] | 76 | 48 | 48 | 57 | 62 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 0.3 | 6.3 | 70.0 |
976 | 849 | Toxtricity | Electric | Poison | ['Plus', 'Punk Rock', 'Technician'] | 75 | 98 | 70 | 114 | 70 | ... | 0.5 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 0.5 | 1.6 | 40.0 | 15.6 |
5 rows × 44 columns
describe()
Esta função nos ajuda a ter uma idéia dos valores que temos em nosso conjunto de dados. A função lista todas as colunas numéricas e apresenta os resultados para os seguintes cálculos estatísticos: média, desvio padrão, valor mínimo, percentil 25%, 50%, 75% e valor máximo.
Isso nos dá uma idéia da variabilidade dos nossos dados, bem como permite uma análise de correlação superficial entre os valores numéricos.
pokemons.describe()
Number | HP | Att | Def | Spa | Spd | Spe | BST | Mean | Standard Deviation | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | ... | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 | 1032.000000 |
mean | 439.226744 | 69.906008 | 80.526163 | 74.609496 | 72.918605 | 72.139535 | 68.548450 | 438.648256 | 73.108043 | 20.028104 | ... | 1.002180 | 1.239826 | 1.025678 | 0.974806 | 1.074855 | 0.992006 | 1.094234 | 1.286822 | 71.879845 | 136.735756 |
std | 261.871350 | 26.189155 | 32.542374 | 30.905972 | 32.773495 | 27.625876 | 30.219526 | 120.675545 | 20.112591 | 10.830298 | ... | 0.613111 | 0.699361 | 0.577269 | 0.378040 | 0.475292 | 0.511859 | 0.535159 | 1.391501 | 132.872741 | 3111.666658 |
min | 1.000000 | 1.000000 | 5.000000 | 5.000000 | 10.000000 | 20.000000 | 5.000000 | 175.000000 | 29.166667 | 0.000000 | ... | 0.250000 | 0.250000 | 0.000000 | 0.000000 | 0.250000 | 0.250000 | 0.250000 | 0.100000 | 0.100000 | 0.000000 |
25% | 211.750000 | 50.000000 | 55.000000 | 50.000000 | 50.000000 | 50.000000 | 45.000000 | 330.000000 | 55.000000 | 12.801910 | ... | 0.500000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.500000 | 1.000000 | 0.600000 | 9.000000 | 18.600000 |
50% | 434.500000 | 67.000000 | 78.000000 | 70.000000 | 65.000000 | 70.000000 | 65.000000 | 459.000000 | 76.500000 | 18.484228 | ... | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 29.750000 | 28.350000 |
75% | 667.250000 | 83.000000 | 100.000000 | 90.000000 | 95.000000 | 90.000000 | 90.000000 | 515.000000 | 85.833333 | 24.835709 | ... | 1.000000 | 2.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.600000 | 71.275000 | 42.200000 |
max | 898.000000 | 255.000000 | 190.000000 | 230.000000 | 194.000000 | 230.000000 | 200.000000 | 780.000000 | 130.000000 | 103.215659 | ... | 4.000000 | 4.000000 | 4.000000 | 2.000000 | 4.000000 | 4.000000 | 4.000000 | 20.000000 | 999.900000 | 99990.000000 |
8 rows × 39 columns
info()
Outra função útil é a função info()
que traz a descrição da estrutura do DataFrame. Com esta função, você pode verificar os seguintes dados:
- lista colunas do DataFrame
- para cada coluna, quantos elementos não-nulos ela possui
- para cada coluna, seu tipo
- E ainda o número total de linhas e colunas no DataFrame
pokemons.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1032 entries, 0 to 1031
Data columns (total 44 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Number 1032 non-null int64
1 Name 1032 non-null object
2 Type 1 1032 non-null object
3 Type 2 548 non-null object
4 Abilities 1032 non-null object
5 HP 1032 non-null int64
6 Att 1032 non-null int64
7 Def 1032 non-null int64
8 Spa 1032 non-null int64
9 Spd 1032 non-null int64
10 Spe 1032 non-null int64
11 BST 1032 non-null int64
12 Mean 1032 non-null float64
13 Standard Deviation 1032 non-null float64
14 Generation 1032 non-null float64
15 Experience type 1032 non-null object
16 Experience to level 100 1032 non-null int64
17 Final Evolution 1032 non-null float64
18 Catch Rate 1032 non-null int64
19 Legendary 1032 non-null float64
20 Mega Evolution 1032 non-null float64
21 Alolan Form 1032 non-null float64
22 Galarian Form 1032 non-null float64
23 Against Normal 1032 non-null float64
24 Against Fire 1032 non-null float64
25 Against Water 1032 non-null float64
26 Against Electric 1032 non-null float64
27 Against Grass 1032 non-null float64
28 Against Ice 1032 non-null float64
29 Against Fighting 1032 non-null float64
30 Against Poison 1032 non-null float64
31 Against Ground 1032 non-null float64
32 Against Flying 1032 non-null float64
33 Against Psychic 1032 non-null float64
34 Against Bug 1032 non-null float64
35 Against Rock 1032 non-null float64
36 Against Ghost 1032 non-null float64
37 Against Dragon 1032 non-null float64
38 Against Dark 1032 non-null float64
39 Against Steel 1032 non-null float64
40 Against Fairy 1032 non-null float64
41 Height 1032 non-null float64
42 Weight 1032 non-null float64
43 BMI 1032 non-null float64
dtypes: float64(29), int64(10), object(5)
memory usage: 354.9+ KB
shape
Esta não é uma função, mas sim uma propriedade, que retorna uma tupla com as dimensões de linha e coluna do DataFrame.
pokemons.shape
(1032, 44)
Filtrando os dados
Agora vamos aprender como executar uma das tarefas mais comuns de manipular DataFrames com o objetivo de realizar análises: filtragem dos dados. A versão atual do pandas trouxe algumas funções que nos facilitam enormemente o processo. Mas, com o objetivo de equipa-los com o máximo de informação possível, vamos também aprender os métodos mais conhecidos.
Mas, antes de mostrarmos como realizar os filtros, vamos explicar alguns conceitos básicos, mas bem relevantes para a operação de filtragem dos dados.
Acessando uma coluna do DataFrame
Pode parecer óbvio, mas para acessar a coluna de um DataFrame, basta fazer o seguinte:
tipos = pokemons["Type 1"]
Assim, a variável tipos conterá o que chamamos de uma Series, que é um array numpy
(biblioteca especializada para criação de arrays numéricos) que contém todas as linhas daquela coluna. Para comprovar isso, vamos imprimir o conteúdo.
tipos
0 Grass
1 Grass
2 Grass
3 Grass
4 Fire
...
1027 Ice
1028 Ghost
1029 Psychic
1030 Psychic
1031 Psychic
Name: Type 1, Length: 1032, dtype: object
Parece interessante. Mas e se quisessemos criar um novo DataFrame apenas com as colunas Number, Name e Type 1? Parece simples, basta enviar ao DataFrame um array com o nome das colunas que quero extrair.
sub_df = pokemons[["Number", "Name", "Type 1"]]
sub_df
Number | Name | Type 1 | |
---|---|---|---|
0 | 1 | Bulbasaur | Grass |
1 | 2 | Ivysaur | Grass |
2 | 3 | Venusaur | Grass |
3 | 3 | Mega Venusaur | Grass |
4 | 4 | Charmander | Fire |
... | ... | ... | ... |
1027 | 896 | Glastrier | Ice |
1028 | 897 | Spectrier | Ghost |
1029 | 898 | Calyrex | Psychic |
1030 | 898 | Calyrex Ice Rider | Psychic |
1031 | 898 | Calyrex Shadow Rider | Psychic |
1032 rows × 3 columns
Tip
Observe a questão da sintaxe de array: quando queremos apenas uma coluna a sintaxe de array não é necessária, ela se aplica apenas a múltiplos campos.
Mas, e como podemos acessar uma linha específica de um DataFrame? Se você está seguindo a linha de raciocínio, já imaginou que não é da maneira tradicional. Na verdade, é exatamente ao contrário do que estamos acostumados. Primeiro acessamos a coluna, e depois a linha, enquanto que em conjuntos de dados em formato tabular largo, primeiro acessamos a linha e depois a coluna.
pokemons["Name"][0]
'Bulbasaur'
Percorrendo um DataFrame
Interessante, certo? Mas isso levanta o seguinte questionamento: quando vou manipular os dados, como filtra-los ou fazer alguma modificação? Nos conjuntos de dados mais tradicionais, eu geralmente percorro o meu conjunto de dados linha a linha e faço os filtros e então altero as colunas. Como fazer isso em pandas?
for index, pokemon in pokemons.iterrows():
if index <= 2:
print(pokemon["Name"])
else:
break
Bulbasaur
Ivysaur
Venusaur
Como pode ser visto no código acima, basta usarmos a função iterrows()
e iremos manipular o DataFrame como uma estrutura de dados mais tradicional, como um array de dicionário de dados. No código acima, fizemos um filtro que pega apenas as linhas com index menor ou igual 2, e mostra apenas o nome. Se DataFrames fossem estruturas de dados tradicionais, isso seria a maneira mais lógica de executar esse comando. Mas com pandas, podemos fazer isso:
pokemons[pokemons["Number"] <= 3]["Name"]
0 Bulbasaur
1 Ivysaur
2 Venusaur
3 Mega Venusaur
Name: Name, dtype: object
Wow, em uma única linha fizemos o filtro de linha e a seleção de coluna, e a principal vantagem sendo que este comando continuou retornando um DataFrame, o que ainda nos permite continuar trabalhando de forma eficiente com os dados que eu escolhi!
Warning
Embora a utilização da função iterrows() pareça ser a forma mais natural e fácil de se trabalhar com Dataframes Pandas, é com a certeza a que apresenta a pior performance. Então, faça um esforço e aprenda muito bem os métodos mais “pandônicos” de manipular Dataframes, Cientista de Dados!
Esta linha também nos introduz ao primeiro jeito de realizar filtros de linha: adicionando a expressão lógica nos primeiros parenteses. Parece simples, embora para referenciar ao campo que será utilizado no filtro, eu ainda precise referenciar o próprio DataFrame. E esse foi apenas um filtro simples. Como seria utilizar mais de um campo no filtro? Vamos ver agora mesmo.
pokemons[(pokemons["Number"] < 11) & (pokemons["Type 1"] == "Bug")]["Name"]
13 Caterpie
Name: Name, dtype: object
Podemos perceber duas coisas: cada condição de filtro deve estar envolta em parenteses (vá em frente, se remover, teremos um erro), e em vez de usar o conector lógico tradicional AND ou OR, utilizamos & (AND) ou | (OR). E quanto mais condições, pior será para lermos com clareza nosso código.
Uma variação deste tipo de filtragem é a utilização da propriedade loc
. Ela nos permite acessar linhas diretamente, e utilizando a notação de manipulação de arrays, filtrar rapidamente o DataFrame. Vamos a um exemplo: gostaria de extrair 10 linhas do DataFrame, iniciando na linha 10.
pokemons.loc[10:19]
Number | Name | Type 1 | Type 2 | Abilities | HP | Att | Def | Spa | Spd | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
10 | 8 | Wartortle | Water | NaN | ['Rain Dish', 'Torrent'] | 59 | 63 | 80 | 65 | 80 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 1.0 | 22.5 | 22.5 |
11 | 9 | Blastoise | Water | NaN | ['Rain Dish', 'Torrent'] | 79 | 83 | 100 | 85 | 105 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 1.6 | 85.5 | 33.4 |
12 | 9 | Mega Blastoise | Water | NaN | ['Mega Launcher'] | 79 | 103 | 120 | 135 | 115 | ... | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 1.6 | 101.1 | 39.5 |
13 | 10 | Caterpie | Bug | NaN | ['Run Away', 'Shield Dust'] | 45 | 30 | 35 | 20 | 20 | ... | 1.0 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.3 | 2.9 | 32.2 |
14 | 11 | Metapod | Bug | NaN | ['Shed Skin'] | 50 | 20 | 55 | 25 | 25 | ... | 1.0 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.7 | 9.9 | 20.2 |
15 | 12 | Butterfree | Bug | Flying | ['Tinted Lens'] | 60 | 45 | 50 | 90 | 80 | ... | 0.5 | 4.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.1 | 32.0 | 26.4 |
16 | 13 | Weedle | Bug | Poison | ['Run Away', 'Shield Dust'] | 40 | 35 | 30 | 20 | 20 | ... | 0.5 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 0.3 | 3.2 | 35.6 |
17 | 14 | Kakuna | Bug | Poison | ['Shed Skin'] | 45 | 25 | 50 | 25 | 25 | ... | 0.5 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 0.6 | 10.0 | 27.8 |
18 | 15 | Beedrill | Bug | Poison | ['Sniper', 'Swarm'] | 65 | 90 | 40 | 45 | 80 | ... | 0.5 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.0 | 29.5 | 29.5 |
19 | 15 | Mega Beedrill | Bug | Poison | ['Adaptability'] | 65 | 150 | 40 | 15 | 80 | ... | 0.5 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5 | 1.4 | 40.5 | 20.7 |
10 rows × 44 columns
Bem prático. A propriedade loc
também pode entender o filtro anterior.
E agora, a última maneira pela qual podemos realizar filtros em nossos DataFrames e a mais recomendada devido a legibilidade do código gerado: vamos utilizar a função query()
. Esta função permite que escrevamos filtros para o DataFrame como se o mesmo fosse um banco de dados, deixando o código mais limpo, pois eliminamos a necessidade de referenciar o DataFrame a cada filtro, bem como parênteses redundantes. Vamos ver um exemplo:
pokemons.query("Number < 11 and `Type 1` == 'Bug'")
Number | Name | Type 1 | Type 2 | Abilities | HP | Att | Def | Spa | Spd | ... | Against Bug | Against Rock | Against Ghost | Against Dragon | Against Dark | Against Steel | Against Fairy | Height | Weight | BMI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
13 | 10 | Caterpie | Bug | NaN | ['Run Away', 'Shield Dust'] | 45 | 30 | 35 | 20 | 20 | ... | 1.0 | 2.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.3 | 2.9 | 32.2 |
1 rows × 44 columns
Assim ficou bem mais limpo. Uma única observação é quanto ao uso do caracter “`” para campos com nomes compostos.
Agregando os dados
Em muitos casos, não queremos apenas filtrar os dados, mas também realizar totalizações, calcular valores médios, ou até mesmo cálculos mais complexos, de dados que devem ser agregados em um ou mais níveis.
Como exemplo, vamos supor que quisessemos totalizar o número de pokemons de acordo com o seu tipo.
pokemons.groupby(['Type 1'])["Name"].count().reset_index(name="Pokemons")
Type 1 | Pokemons | |
---|---|---|
0 | Bug | 81 |
1 | Dark | 46 |
2 | Dragon | 42 |
3 | Electric | 59 |
4 | Fairy | 22 |
5 | Fighting | 42 |
6 | Fire | 64 |
7 | Flying | 8 |
8 | Ghost | 41 |
9 | Grass | 91 |
10 | Ground | 41 |
11 | Ice | 38 |
12 | Normal | 114 |
13 | Poison | 40 |
14 | Psychic | 77 |
15 | Rock | 59 |
16 | Steel | 36 |
17 | Water | 131 |
Mas quanta coisa nova naquela linha, não é mesmo? Vamos explicar passo a passo:
- A primeira função é o groupby, onde especificamos por qual coluna ou colunas iremos fazer o agrupamento. No nosso exemplo, utilizamos a coluna ‘Type 1’
- Em seguida, especificamos que, além da coluna ‘Type 1’, queremos apenas a coluna ‘Name’ nos nossos resultados
- Logo após, indicamos que o valor original da coluna ‘Name’ será substituído pelo resultado da contagem de quantas linhas do DataFrame tem aquele valor específico da coluna ‘Type 1’
- E por último, utilizamos uma função que irá trocar o nome da coluna ‘Name’ por um nome mais significativo
Ufa, e tudo isso em apenas uma linha!
A fórmula para a agregação é sempre a mesma: groupby() e tipo de calculo (sum, count, mean, …). Por exemplo, no código abaixo, vamos agrupar também pela geração.
pokemons.groupby(['Generation', 'Type 1'])['Name'].count().reset_index(name='Pokemons')
Generation | Type 1 | Pokemons | |
---|---|---|---|
0 | 1.0 | Bug | 12 |
1 | 1.0 | Dragon | 3 |
2 | 1.0 | Electric | 9 |
3 | 1.0 | Fairy | 2 |
4 | 1.0 | Fighting | 7 |
... | ... | ... | ... |
129 | 8.0 | Poison | 4 |
130 | 8.0 | Psychic | 11 |
131 | 8.0 | Rock | 4 |
132 | 8.0 | Steel | 5 |
133 | 8.0 | Water | 9 |
134 rows × 3 columns
E se quisermos saber a média de pontos de vida por geração de pokemon? Parece simples…
pokemons.groupby(['Generation'])['HP'].mean().reset_index(name='Average HP')
Generation | Average HP | |
---|---|---|
0 | 1.0 | 64.211921 |
1 | 2.0 | 70.980000 |
2 | 3.0 | 65.326087 |
3 | 4.0 | 72.775862 |
4 | 5.0 | 71.601227 |
5 | 6.0 | 73.323308 |
6 | 7.0 | 69.793103 |
7 | 8.0 | 72.808696 |
Encerrando
Neste artigo, conhecemos um pouco mais a respeito da biblioteca Pandas e como ela pode nos ajudar a carregar e analisar conjuntos de dados que podem ser utilizados em nossas visualizações, de forma simplificada e eficiente.
Dentro do processo de utilização de Pandas, aprendemos as executar as principais tarefas:
- aprender sobre os metadados do conjunto de dados, utilizando: describe, info e shape
- listar conteúdo com head, tail, sample e iterrows
- acessar células diretamente
- realizar filtros em cima do DataFrame utilizando o método colunar, utilizando loc ou utilizando a função query
- agregar os dados para sumarizar a informação e facilitar a análise.
Se você quiser saber mais sobre Pandas, eis aqui alguns links que podem ajudar:
- Pandas - Documentação Oficial
- Tutorial Pandas - W3 Schools
- Tutorial Pandas - Kaggle
- Vídeo sobre Pandas - Hashtag Treinamentos
Um abraço e até a próxima,
Walter.
Top comments (0)