Manipulando dados com Pandas

Como vimos anteriormente, criar Séries e DataFrame do zero é legal, mas normalmente faremos a importação dos dados no formato de um arquivo .csv ou planilha.

O Pandas permite a importação de dados de maneira fácil por meio de funções como pd.read_csv() e pd.read_excel().

Por exemplo, vamos obter as informações deste documento do Google Sheet:

Google Sheet

Depois de exportá-lo como um arquivo .csv, podemos agora importá-lo no Pandas com pd.read_csv() e criar o nosso DataFrame:

Importando dados

Agora temos os mesmos dados da planilha disponíveis em um DataFrame Pandas. Isso permite que você aproveite todas as funcionalidades do Pandas para explorar os dados.

Anatomia de um DataFrame

Abaixo vemos um resumo dos principais componentes de um DataFrame:

Anatomia DataFrame

Explorando os dados

Uma das primeiras tarefas que fazemos logo após importar os dados para um DataFrame Pandas é começar a explorá-lo. O Pandas possui muitas funções internas que nos permitem obter informações descritivas dos dados, .dtypes por exemplo nos mostra que tipo de dados cada coluna contém:

Dtypes

df.dtypes

Fabricante       object
Cor              object
Quilometragem     int64
Portas            int64
Preco            object
dtype: object

Note que a coluna Preco não é um número inteiro como Quilometragem e Portas. Não se preocupe isso é fácil de consertar e faremos isso em outro momento.

Describe

A função .describe() nos mostra uma visão estatística de todas as colunas numéricas:

df.describe()
Quilometragem Portas
count 10.000000 10.000000
mean 78601.400000 4.000000
std 61983.471735 0.471405
min 11179.000000 3.000000
25% 35836.250000 4.000000
50% 57369.000000 4.000000
75% 96384.500000 4.000000
max 213095.000000 5.000000

Info

Outra função muito utilizada é a .info(), que mostra quantas linhas existem, se há valores ausentes e os tipos de dados de cada coluna:

df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 5 columns):
 #   Column         Non-Null Count  Dtype 
---  ------         --------------  ----- 
 0   Fabricante     10 non-null     object
 1   Cor            10 non-null     object
 2   Quilometragem  10 non-null     int64 
 3   Portas         10 non-null     int64 
 4   Preco          10 non-null     object
dtypes: int64(2), object(3)
memory usage: 528.0+ bytes

Mean e Sum

Pandas também disponibiliza várias funções estatísticas e matemáticas como .mean() e .sum() que podem ser aplicadas diretamente em um DataFrame ou Séries.

valores = pd.Series([3000, 3500, 11250])
valores.mean()

5916.666666666667
valores.sum()

17750

Columns

Usar essas funções em um DataFrame inteiro pode não fazer muito sentido, nesse caso podemos direcionar a função individualmente para uma coluna. O método .columns mostrará todas as colunas de um DataFrame:

df.columns

Index(['Fabricante', 'Cor', 'Quilometragem', 'Portas', 'Preco'], dtype='object')

Selecionando uma coluna com dados numéricos podemos utilizar a função estatística .mean() novamente:

df["Quilometragem"].mean()

78601.4

Visualizando e selecionando dados

Na prática, realizar alterações nos dados e visualizá-los constantemente é uma tarefa de rotina. Nem sempre precisamos alterar todos os dados de um DataFrame, portanto veremos maneiras diferentes de selecionar.

Para visualizar as primeiras 5 linhas do seu DataFrame existe a função .head() que é muito utilizada:

df.head()
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043 4 R$ 24,000.00
1 Honda Vermelho 87899 4 R$ 25,000.00
2 Toyota Azul 32549 3 R$ 27,000.00
3 BMW Preto 11179 5 R$ 122,000.00
4 Nissan Branco 213095 4 R$ 13,500.00

Para selecionar mais de 5, você pode passar o valor desejado como argumento na função, por exemplo: .head(7).

Tail

Outro método muito utilizado é o .tail() que seleciona as últimas 5 linhas do seu DataFrame:

df.tail()
Fabricante Cor Quilometragem Portas Preco
5 Toyota Verde 99213 4 R$ 14,500.00
6 Honda Azul 45698 4 R$ 17,500.00
7 Honda Azul 54738 4 R$ 27,000.00
8 Toyota Branco 60000 4 R$ 26,250.00
9 Nissan Branco 31600 4 R$ 19,700.00

Loc e Iloc

Por meio das instruções .loc[] e .iloc[] podemos selecionar dados de Séries e DataFrames, essas opções são muito utilizadas. Para ilustrar o uso em uma Série, vamos criar uma:

animais = pd.Series(["gato", "ave", "cachorro", "cobra", "leão", "cavalo"], 
                   index=[0, 3, 8, 9, 6, 3])
animais

0        gato
3         ave
8    cachorro
9       cobra
6        leão
3      cavalo
dtype: object

Agora vamos utilizar o .loc[] informando como valor de entrada um número inteiro que corresponde ao índice da nossa Série:

animais.loc[3]

3       ave
3    cavalo
dtype: object

Temos como retorno os valores que tem como índice o número 3.

Já com o .iloc[]:

animais.iloc[3]

cobra

O valor retornado é bem diferente cobra que tem como índice o número 9. Isso acontece pois o .iloc[] faz uma seleção pela posição dos dados na Série ou DataFrame. Vamos testar esses recursos agora no nosso DataFrame:

df.loc[3]

Fabricante                 BMW
Cor                      Preto
Quilometragem            11179
Portas                       5
Preco            R$ 122,000.00
Name: 3, dtype: object

df.iloc[3]

Fabricante                 BMW
Cor                      Preto
Quilometragem            11179
Portas                       5
Preco            R$ 122,000.00
Name: 3, dtype: object

Ambos .loc[] e .iloc[] retornaram o mesmo valor pois as informações no DataFrame exibidas estão em ordem tanto na posição quanto no índice.

Ainda podemos utilizar o slicing com .loc[] e .iloc[], selecionando dados em um range:

animais.iloc[:3]

0        gato
3         ave
8    cachorro
dtype: object

Lembre-se, utilize .loc[] quando estiver selecionando dados pelo índice e .iloc[] quando estiver referindo-se a posições no DataFrame.

Se quiser selecionar uma coluna em particular use ['NOME_DA_COLUNA']:

df["Fabricante"]

0    Toyota
1     Honda
2    Toyota
3       BMW
4    Nissan
5    Toyota
6     Honda
7     Honda
8    Toyota
9    Nissan
Name: Fabricante, dtype: object

Ao selecionar colunas podemos elaborar filtros condicionais entre colchetes, vejamos a sintaxe:

df[df["Quilometragem"] > 100000]
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043 4 R$ 24,000.00
4 Nissan Branco 213095 4 R$ 13,500.00

No exemplo estamos filtrando a coluna Quilometragem adicionando a condição para retornar apenas os carros que possuem uma quilometragem maior que 100.000.

Que tal elaborar um filtro para listar apenas os carros da Honda ?

df[df["Fabricante"] == "Honda"]
Fabricante Cor Quilometragem Portas Preco
1 Honda Vermelho 87899 4 R$ 25,000.00
6 Honda Azul 45698 4 R$ 17,500.00
7 Honda Azul 54738 4 R$ 27,000.00

Groupby

Para comparar mais colunas no contexto de outra coluna, podemos usar o .groupby(), por exemplo, podemos agrupar os dados por fabricante e calcular a média das colunas numéricas:

df.groupby(["Fabricante"]).mean()
Fabricante Quilometragem Portas
BMW 11179.000000 5.00
Honda 62778.333333 4.00
Nissan 122347.500000 4.00
Toyota 85451.250000 3.75

Replace

A coluna de preço no DataFrame ainda não foi convertida para um tipo numérico, vamos tratar isso. Primeiro precisamos elaborar uma expressão regular para eliminar os caracteres R$ e ,. O Pandas permite isso por meio do método str.replace, para persistir a mudança no DataFrame é comum atribuir a mudança ao próprio DataFrame:

df["Preco"] = df["Preco"].str.replace('[/R$\,]', '', regex=True)

Agora vejamos como ficaram os dados:

df.head(3)
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043 4 24000.00
1 Honda Vermelho 87899 4 25000.00
2 Toyota Azul 32549 3 27000.00

A coluna de preço já exibe os novos valores formatados, porém os dados ainda não são numéricos:

df.info()

RangeIndex: 10 entries, 0 to 9
Data columns (total 5 columns):
 #   Column         Non-Null Count  Dtype 
---  ------         --------------  ----- 
 0   Fabricante     10 non-null     object
 1   Cor            10 non-null     object
 2   Quilometragem  10 non-null     int64 
 3   Portas         10 non-null     int64 
 4   Preco          10 non-null     object
dtypes: int64(2), object(3)
memory usage: 528.0+ bytes

Numeric

Como podemos ver Preco continua sendo do tipo object. Podemos corrigir isso com a função to_numeric do Pandas:

df["Preco"] = pd.to_numeric(df["Preco"])
df.head(3)
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043 4 24000.0
1 Honda Vermelho 87899 4 25000.0
2 Toyota Azul 32549 3 27000.0

Se verificarmos o tipo novamente veremos que agora a coluna Preco é do tipo float64:

df.info()

RangeIndex: 10 entries, 0 to 9
Data columns (total 5 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   Fabricante     10 non-null     object 
 1   Cor            10 non-null     object 
 2   Quilometragem  10 non-null     int64  
 3   Portas         10 non-null     int64  
 4   Preco          10 non-null     float64
dtypes: float64(1), int64(2), object(2)
memory usage: 528.0+ bytes

Um desafio muito comum em Data Science é preencher dados ausentes. É provável que em algum momento você trabalhe com um DataFrame incompleto e terá que decidir como preencher os dados ausentes ou remover linhas de dados ausentes. Vamos verificar como fazer isso em uma versão do nosso DataFrame com valores ausentes:

df2 = pd.read_csv("venda-de-carros-dados-ausentes.csv")
df2
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043.0 4.0 24000.0
1 Honda Vermelho 87899.0 4.0 25000.0
2 Toyota Azul NaN 3.0 27000.0
3 BMW Preto 11179.0 5.0 122000.0
4 Nissan Branco 213095.0 4.0 13500.0
5 Toyota Verde NaN 4.0 14500.0
6 Honda NaN NaN 4.0 17500.0
7 Honda Azul NaN 4.0 NaN
8 Toyota Branco 60000.0 NaN NaN
9 NaN Branco 31600.0 4.0 19700.0

Fillna

Os valores ausentes são mostrados por NaN no Pandas, isso é considerado o equivalente ao None em Python. Vamos usar a função .fillna() para preencher a coluna Quilometragem com a média dos outros valores da mesma coluna, dessa vez não vamos reatribuir a coluna para persistir os dados no DataFrame, em vez disso, usaremos a opção inplace=True:

df2["Quilometragem"].fillna(df2["Quilometragem"].mean(), inplace=True)
df2
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043.000000 4.0 24000.0
1 Honda Vermelho 87899.000000 4.0 25000.0
2 Toyota Azul 92302.666667 3.0 27000.0
3 BMW Preto 11179.000000 5.0 122000.0
4 Nissan Branco 213095.000000 4.0 13500.0
5 Toyota Verde 92302.666667 4.0 14500.0
6 Honda NaN 92302.666667 4.0 17500.0
7 Honda Azul 92302.666667 4.0 NaN
8 Toyota Branco 60000.000000 NaN NaN
9 NaN Branco 31600.000000 4.0 19700.0

Dropna

Perfeito! Os valores ausentes da coluna Quilometragem foram preenchidos com o valor médio da mesma coluna. Digamos que você queira apenas remover todas as linhas com dados ausentes e trabalhar apenas com as linhas que possuem todos os valores preenchidos, podemos fazer isso usando o .dropna():

df2.dropna(inplace=True)
df2
Fabricante Cor Quilometragem Portas Preco
0 Toyota Branco 150043.000000 4.0 24000.0
1 Honda Vermelho 87899.000000 4.0 25000.0
2 Toyota Azul 92302.666667 3.0 27000.0
3 BMW Preto 11179.000000 5.0 122000.0
4 Nissan Branco 213095.000000 4.0 13500.0
5 Toyota Verde 92302.666667 4.0 14500.0

Mas e se você quisesse criar dados em vez de remover ou preencher ? Por exemplo, adicionando uma coluna nova chamada Assentos para o número de lugares disponíveis no carro. Pandas permite a criação simples de colunas extras no DataFrame usando Series, Lista ou uma coluna existente:

qtde_assentos = pd.Series([5, 5, 5, 5, 5, 5, 5, 5, 5, 5])
df["Assentos"] = qtde_assentos
df
Fabricante Cor Quilometragem Portas Preco Assentos
0 Toyota Branco 150043 4 24000.0 5
1 Honda Vermelho 87899 4 25000.0 5
2 Toyota Azul 32549 3 27000.0 5
3 BMW Preto 11179 5 122000.0 5
4 Nissan Branco 213095 4 13500.0 5
5 Toyota Verde 99213 4 14500.0 5
6 Honda Azul 45698 4 17500.0 5
7 Honda Azul 54738 4 27000.0 5
8 Toyota Branco 60000 4 26250.0 5
9 Nissan Branco 31600 4 19700.0 5

A coluna assentos foi criada por meio de uma Series, vamos criar outra coluna chamada Motor, dessa vez usando uma lista Python:

motor_lista = [1.3, 2.0, 3.0, 4.2, 1.6, 1, 2.0, 2.3, 2.0, 3.0]
df["Motor"] = motor_lista
df
Fabricante Cor Quilometragem Portas Preco Assentos Motor
0 Toyota Branco 150043 4 24000.0 5 1.3
1 Honda Vermelho 87899 4 25000.0 5 2.0
2 Toyota Azul 32549 3 27000.0 5 3.0
3 BMW Preto 11179 5 122000.0 5 4.2
4 Nissan Branco 213095 4 13500.0 5 1.6
5 Toyota Verde 99213 4 14500.0 5 1.0
6 Honda Azul 45698 4 17500.0 5 2.0
7 Honda Azul 54738 4 27000.0 5 2.3
8 Toyota Branco 60000 4 26250.0 5 2.0
9 Nissan Branco 31600 4 19700.0 5 3.0

Por fim, vamos criar mais uma coluna, dessa vez combinando diretamente os valores de outras colunas. Dividindo o preço do carro por sua quilometragem teremos o preço por quilômetro, esses serão os dados da nova coluna:

df["Preco por KM"] = df["Preco"] / df["Quilometragem"]
df

Gerando nova coluna

Drop

Esse tipo de criação de coluna é chamado de Feature Engineering. Se fabricante, cor e quantidade de portas são características dos dados, a criação de preço por quilômetro pode ser outra. Nesse exemplo não foi uma boa escolha, então vamos remover essa coluna. Para remover uma só coluna podemos usar o .drop('NOME_DA_COLUNA', axis=1):

df = df.drop("Preco por KM", axis=1)
df
Fabricante Cor Quilometragem Portas Preco Assentos Motor
0 Toyota Branco 150043 4 24000.0 5 1.3
1 Honda Vermelho 87899 4 25000.0 5 2.0
2 Toyota Azul 32549 3 27000.0 5 3.0
3 BMW Preto 11179 5 122000.0 5 4.2
4 Nissan Branco 213095 4 13500.0 5 1.6
5 Toyota Verde 99213 4 14500.0 5 1.0
6 Honda Azul 45698 4 17500.0 5 2.0
7 Honda Azul 54738 4 27000.0 5 2.3
8 Toyota Branco 60000 4 26250.0 5 2.0
9 Nissan Branco 31600 4 19700.0 5 3.0

Sample

Outro recurso interessante no Pandas é o de ordenação, digamos que você precise embaralhar a ordem do seu DataFrame para poder dividi-lo em conjuntos de treinamento, teste e validação para utilizar em um projeto de Machine Learning. Para isso temos o recurso .sample(frac=1), essa função pega diferentes linhas de um DataFrame e embaralha. O parâmetro frac configura a fração que será utilizada para embaralhar, onde 1 equivale a 100% das linhas.

df_sample = df.sample(frac=1)
df_sample
Fabricante Cor Quilometragem Portas Preco Assentos Motor
3 BMW Preto 11179 5 122000.0 5 4.2
8 Toyota Branco 60000 4 26250.0 5 2.0
9 Nissan Branco 31600 4 19700.0 5 3.0
6 Honda Azul 45698 4 17500.0 5 2.0
5 Toyota Verde 99213 4 14500.0 5 1.0
4 Nissan Branco 213095 4 13500.0 5 1.6
1 Honda Vermelho 87899 4 25000.0 5 2.0
0 Toyota Branco 150043 4 24000.0 5 1.3
7 Honda Azul 54738 4 27000.0 5 2.3
2 Toyota Azul 32549 3 27000.0 5 3.0

Reset index

Note como as linhas permanecem intactas, apenas a ordem é alterada e isso pode ser verificado pelos índices. Agora, e se você quisesse colocar os índices de volta em ordem ?

Nesse caso podemos utilizar o .reset_index():

df_sample.reset_index()

Sample reset index

Ao utilizar .reset_index() em um DataFrame, os números de índice retornam para o padrão, porém ele também cria uma nova coluna de índice contendo os valores anteriores.


WIP

results matching ""

    No results matching ""