Skip to content

Laboratório 2 - Pré-processamento e Análise de Dados


Para realizar os exemplos vamos utilizar Notebooks, pois permite uma utilização clara, dinâmica e eficiente para desenvolvimento de soluções de Machine Learning em Pyhton, permitindo a integração de textos estruturados, imagens e códigos em um documento. Podemos utilizar tanto o jupyter-notebook quando o Visual Studio Code para desenvolvimento local, e Google Colab para ambientes online.

Para o laboratório de hoje serão utilizadas as bibliotecas pandas e numpy. Portanto, caso você não tenha instalado, você pode fazer isso utilizando o comando:

pip install pandas numpy
Após a instalação das bibliotecas, podemos importa-las no código da seguinte forma:
import pandas as pd
import numpy as np

A seguir é apresentado um exemplo das estruturas de dados utilizadas pelo pandas, sendo elas a series e dataframes.

Séries

Series são matrizes unidimensionais rotuladas capazes de armazenar dados de qualquer tipo (inteiro, string, float, objetos python, etc.). Os rótulos dos eixos são chamados coletivamente de índices. Fazendo um paralelo com uma planilha uma série representa uma coluna em uma planilha do Excel. Uma Serie suporta tanto a indexação inteira, quanto a baseada em rótulo e fornece uma série de métodos para executar operações envolvendo o índice. Podemos criar séries de diversas formas, incluindo listas, dicionários, e ndarrays, como mostrados nos exemplos a seguir:

#baseada em listas
serie_1 = pd.Series(['A', 'B', 'C', 'D', 'E'])
serie_2 = pd.Series([20, 30, 40, 50, 60])

#baseada em ndarray
s1 = pd.Series(np.ones(5))

#baseada em dicionários
s2 = pd.Series({'a': 1, 'b': 3, 'c':50})

DataFrames

É uma estrutura de dados bidimensional com os dados alinhados de forma tabular em linhas e colunas, mutável em tamanho e potencialmente heterogênea. A diferença essencial é que os nomes de colunas e os números de linha são conhecidos como índice de coluna e linha, no caso do DataFrame. As colunas possuem nomes (índice da coluna) e, as linhas, podem ter nomes referentes a colunas e as linhas podem ter nomes (índices textuais) ou podem, por padrão, ser numeradas (Índice numérico).

df = pd.DataFrame({'Numeros' : serie_2,
                   'Letras'  : serie_1,
                   'ndarray' : np.ones(5),
                   'lista'   : ['a', 'b', 'c', 'd', 'e']},
                 )
df

Acessando Atributos de um DataFrame

Podemos acessar os atributos do dataframe de duas formas:

  • df['atributo']: referenciando o nome do atributo entre colchetes e aspas
  • df.atributo: referenciando com .atributo, porém caso o atributo possua um "espaço" em seu nome este método não funciona
#Exemplos de acesso 
df['Numeros']
df.Numeros
df.Letras[0]
df.Letras[1]

Alterando Indices

  • df.set_index(atributo, inplace)
    • atributo: informa o nome do atributo que será o novo indice do dataframe
    • inplace: {True, False}, aplica as alterações direto no dataframe original
df.set_index('Numeros', inplace=True)
df

.loc e .iloc

São métodos alternativos para acessar linhas e/ou elementos de um dataframe, onde as principais diferenças entre eles são:

  • .iloc[idx]: retorna a linha idx do dataframe

    df.iloc[0]
    

  • .loc[idx]: retorna a linha do dataframe na qual o indice seja igual a idx

    df.loc[20]
    

Criando DataFrame com Arquivo .csv

Para criar um dataframe baseado em um arquivo .csv utilizamos o seguinte método:

  • pd.read_csv(file, sep, names)
    • file: é o caminho para o arquivo incluindo seu nome
    • sep: é o separador utilizado no arquivo csv para separar os dados
    • names: não é obrigatório, mas é utilizado para atribuir nome as colunas do dataframe. Assim, deve ser informado uma lista com o nome de cada coluna/atributo
df = pd.read_csv('Iris.csv', sep=',')
df

Visualizando Amostras do Dataset

Para visualizar as amostras iniciais ou finais dos dados utilizamos o método .head() e .tail():

  • df.head(quantidade)

    • quantidade: informa a quantidade de linhas superiores a serem mostradas do dataframe
  • df.tail(quantidade)

    • quantidade: informa a quantidade de linhas inferiores a serem mostradas do dataframe

Identificando Colunas, Indices, Valores

Podemos obter arrays contendo as colunas, indices e até mesmo os valores do dataframe para executar determinadas operações, como por exemplo: (i) re-ordenar as colunas do dataframe; (ii) desenvolver métodos para iterar sobres colunas específicas; (iii) operações com indices; (iv) aplicar os valores em um modelo; etc. Assim, podemos obter essas informações da seguinte forma:

#apresenta colunas do dataframe
df.columns
#apresenta indices do dataframe
df.index
#apresenta tipos dos dados do dataframe
df.dtypes
#apresenta valores do dataframe
df.values

Obtendo Tamanho e Formato do Dataset

É possível obter o tamanho do dataframe utilizando a mesma abordagen das listas. Além disso, é possível obter o formato do dataframe utilizando a mesma abordagen dos ndarrays

#retorna o tamanho do DataFrame (linhas)
len(df)
#retorna o formato do DataFrame (linhas, colunas)
df.shape

Informações Detalhadas

Todas essas informações préviamente apresentadas podem ser obtidas de forma resumida utilizando o método:

#apresenta todas as informações
df.info()

Descrição Estatística e Correlações dos Dados

Descrição dos dados apresenta informações estatísticas sobre os atributos do dataframe, incluindo: (i) total de registros; (ii) média; (iii) desvio padrão; (iv) valor mínimo; (v) primeiro quartil; (vi) segundo quartil; (vii) terceiro quartil; e (viii) valor máximo. Para isso, utilizamos o método:

df.describe().T

Podemos análisar as correlações dos atributos para entender correlações positivas ou negativas entre os dados. Para isso, utilizamos o seguinte método:

df.corr()

Tratando Valores Nulos

É possivel identificar valores nulos utilizando diferentes abordagens com o pandas. Exemplo delas são:

  • .isna(): retorna um dataframe com valores True e False para todos os dados do dataframe. Para os valores que não são nulos, este dataframe é preenchido com False, caso contrário o dataframe é preenchido para True (apenas onde os valores são nulos)
  • .isnull(): Retorna um resumo dos atributos com a quantidade de valores nulos em cada atributo

Podemos utilizados da seguinte forma:

df.isna()
df.isnull().sum()

Preenchendo ou Removendo Valores Nulos

Podemos preencher valores nulos com algum valor pré-definido. Isto é útil durante o processamento dos dados. Uma das abordagens mais comum quando existem valores nulos é o prenchimento com a média dos valores daquele atributo. Portanto, podemos preencher esses valores usando:

  • df['atributo'].fillna(valor, inplace)
    • valor: representa o valor a ser substituido pelo valor nulo
    • inplace: {True, False} realiza a alteração no dataframe original
media = df['SepalWidthCm'].mean()
df['SepalWidthCm'].fillna(media, inplace=True)
print(media)
df

Para remover os valores nulos podemos utilizar o seguinte método:

df.dropna(inplace=True)

Operações Aritiméticas com os Dados

Operações de agregação podem ser executadas diretamente do DataFrame, para calcular métricas de um atributo específico. Essas operações incluem:

  • df['atributo'].mean(): retorna a médida dos valores do atributo
  • df['atributo'].sum(): retorna a soma dos valores do atributo
  • df['atributo'].std(): retorna o desvio padrão dos valores do atributo
  • df['atributo'].min(): retorna o valor mínimo do atributo
  • df['atributo'].max(): retorna o valor mínimo do atributo

Exemplo:

mean   = df['SepalLengthCm'].mean()
soma   = df['SepalLengthCm'].sum()
minimo = df['SepalLengthCm'].min()
maximo = df['SepalLengthCm'].max()

print(f'media: {mean}, soma: {soma}, minimo:{minimo}, maximo:{maximo}')

Adicionando Colunas

Para adicionar colunas ou atributos no DataFrame é preciso definir uma 'chave' que represente o nome do atributo e atribuir os dados para compor aquele atributo. Por exemplo, df['novo atributo'] = dados. Dessa forma, os dados podem ser:

  • Valor: o valor especificado é atribuido a todas as linhas do DataFrame para o novo atributo
  • Lista: precisa possuir o mesmo tamanho que a quantidade de linhas do DataFrame
  • ndarray: precisa possuir o mesmo tamanho que a quantidade de linhas do DataFrame
  • serie: pode ter tamanho inferior a quantidade de linhas do dataframe, nesse caso para as linhas excedentes do dataframe o valor NaN é atribuido
#valores iguais
df['teste']    = True
#baseado em um ndarray
df['nprandom'] = np.random.rand(146)
#Baseado em um array com tamanho menor que o dataset
df['series_']  = pd.Series(np.random.rand(100))
df

Removendo Colunas

Para remover colunas do dataframe utilizamos o método:

  • df.drop(atributo, axis, inplace)
    • atributo: informa o nome da coluna a ser deletada
    • axis: informa o eixo onde está o atributo (0 para linhas, 1 para colunas)
    • inplace: {True, False}, aplica as alterações diretamente no dataframe original
df.drop('teste', axis=1, inplace=True)
df.drop(['nprandom', 'series_'], axis=1, inplace=True)

Filtrando Dados

Podemos filtrar dados utilizando as condições de filtragem diratamente nos atributos que queremos filtrar. Dessa forma, quando a condição for verdadeira será retornado True para cada linha do dataframe, caso contrário False. Assim, com o resultado, podemos filtrar diretamente do DataFrame original e obter um DataFrame filtrado baseado na condição estabelecida.

filtro = df['PetalLengthCm'] > 3
df[filtro]

Fracionando Dados

Fracionar os dados permite utilizar apenas uma porcentagem dos dados. Este fracionamento é útil para realizar testes iniciais nos dados quando temos datasets muito grandes onde o processamento é custo. Assim, podemos utilizar uma fração pequena dos dados para testar os modelos desenvolvidos, em seguida, após a validação dos teste o modelo pode ser aplicado no conjuto total de dados. Um exemplo de fracionamento é apresentado a seguir:

#retorna 20% dos dados aleatóriamente
df.sample(frac=0.2)

Função apply()

A função apply() é utilizada para aplicar uma funções sobre alguma coluna do DataFrame. Esse tipo de função pode ser útil no processamento dos dados ou até para criar novos atributos para o dataset baseado em um atributo de referência.

df['atibuto novo'] = df['PetalLengthCm'].apply(lambda x: x**2)

Um outro exemplo é utilizando funções

def soma_1(x):
    return x + 1

df['atibuto novo'] = df['PetalLengthCm'].apply(soma_1)
df

Exportanto o Dataset

Podemos salvar o dataset após as modificações (i.e., por exemplo, após um préprocessamento onde foi removido valores nulos e novos atributos foram criados). Exportando o dataset para um novo arquivo evita que todo o processamento seja feito novamente durante o próximo uso dos dados. Dessa forma, podemos exportar os dados da seguinte forma

df.to_csv('dataset_iris_alterado.csv', index=False)