Manipulando grandes volumes de dados com o Jupyter
Hoje, resolvi trazer um assunto um pouco diferente. Vamos conversar sobre Data Science.
Isso se deve pelo número crescente de dados nas empresas, além de que uma das etapas do DevSecOps é permitir que um código esteja íntegro e que gere qualidade para a empresa, de maneira rápida e com geração de report.
Então, vamos utilizar o Jupyter, melhor ambiente para manipular dados com Python, pois permite criar manipular dados utilizando blocos, trabalhando com eles ainda em execução.
Além disso, utilizaremos bastante a biblioteca Pandas. Essa biblioteca nos permite manipular e analisar dados de uma maneira bem fácil e intuitiva. Além de possuir uma biblioteca incrível!
Vamos aos insights e aprendizados! :D
Existem diversas maneiras de instalar o Jupyter na nossa máquina. Para exemplo de estudo, estou utilizando docker.
docker run -p 8888:8888 jupyter/minimal-notebook
Além disso, usaremos uma amostra com 5 milhões de dados disponíveis em um CSV, proveniente desse site aqui:
Após iniciarmos nosso Jupyter, vocês verão algo parecido com a imagem abaixo. Utilize essa URL para poder utilizar o Jupyter.
Agora, iremos adicionar esse arquivo ao Jupyter, fazendo o upload.
Aqui, podemos ter acesso ao nosso primeiro notebook!
Agora, iremos manipular o nosso CSV para ele gastar o mínimo de memória possível, pois caso o seu Dataset seja enorme, ele provavelmente será muito pesado e será muito difícil de manipulá-lo.
Então, usaremos uma técnica conhecida como chunksize. Isso nos permitirá criarmos pequenos fragmentos de dados extremamente leves.
df_chunk = pd.read_csv (‘./sales-record.csv’, chunksize = 1000000)
Além disso, outras técnicas interessantes, para entendermos mais do nosso Dataset, consiste em:
- Observar quais colunas realmente são necessárias para análise e assim, podemos filtrar o que realmente importa
- Podemos observar o tipo de dado presente no Dataset. Caso ele seja do tipo 'Object', podemos convertê-lo para o tipo mais adequado, como int ou string, por exemplo.
df_test.info()
Caso precisarmos converter o tipo da variável, podemos fazer isso utilizando o comando abaixo:
df_test['Order ID'].astype(str).astype(int)
Para filtrarmos nosso Dataset, utilizando o chunksize, podemos realizar da seguinte maneira:
- Criamos uma função com as colunas que queremos realmente utilizar
def chunk_preprocessing(chunk,column):
return chunk[[column,'Country','Unit Price','Order ID','Order Date']]
- Para criarmos o Dataframe:
def create_df(df_chunk,chunk_list,i):
for chunk in df_chunk:
chunk_filter = chunk_preprocessing(chunk,’Region’)
i+=100000
print(i)
chunk_list.append(chunk_filter)
return chunk_list
chunk_list = create_df(df_chunk,chunk_list,i)data_df = pd.concat(chunk_list)
print(data_df)
Uma outra sugestão, também, é sempre criarmos funções. Muitas pessoas que estão na área de Data Science, esquecem disso. Isso ocorre porque o Jupyter consegue rodar em blocos, sem precisar criar um debug, por exemplo. Isso torna mais fácil de saber o resultado de um dado que está sendo manipulado.
Por outro lado, quando pensamos em uma Dataset com grande volume de dados, precisamos criar funções. Não apenas porque poderemos ter processos repetitivos, mas no momento que precisamos "salvar memória", ao criarmos funções, aquele processo somente existirá quando a função for executada. Quando terminarmos, as variáveis que foram usadas dentro das funções, não serão armazenadas. Isso nos ajuda, principalmente, quando temos Dataframes pesados.
Portanto, lembre-se de criar funções :)
Outras funções que também são interessantes:
- A função type() ajuda muito quando se está programando. Dessa forma, você consegue saber qual é o tipo da variável e se faz sentido no processo que está sendo manipulado
- Quando precisamos manipular certas colunas no Dataframe, os dois métodos seguintes demonstrados ajudam bastante! Podemos mudar para o formato de data escolhida e o formato necessário quando lidamos com números, respectivamente.
- %%capture é um comando especial do Jupyter (ele possui outros, inclusive) e dessa maneira, permite que eu não precise mostrar o output do bloco
type(data_df_ge2)data_df_ge['Order Date'] = data_df['Order Date'].dt.strftime("%Y-%m-%d")data_df_ge['Unit Price'] = data_df_ge['Unit Price'].map('{:.4f}'.format)%%capture
Um outro ponto bem interessante, que encontrei recentemente e é super útil quando estamos querendo comparar Datasets, sem precisar criarmos diversas funções para isso, é a utilização de um framework.
Nesse caso, existem vários que fazem isso, mas o great expectations é muito bom! Funciona em diversas linguagens e você não precisa utilizar um docker para rodá-lo separadamente. Justamente porque ele possui uma biblioteca e uma documentação muito boa!
- No exemplo abaixo, eu comparei 2 colunas e 2 listas, onde eu pude perceber se a coluna (Country) existente no dataset (data_df_ge) , estava presente no data_df_ge2 e pudemos observar que sim!
- É possível obter o resultado em diferentes formatos, mas o resultado "COMPLETE" , te traz todas as informações sem você precisar criar diversas funções extras para exibir o seu resultado
data_df_ge2.expect_column_values_to_be_in_set(‘Country’,list(data_df_ge.Country),result_format={‘result_format’:’COMPLETE’})
Aqui na documentação, você pode ver muitas outras funções pré-existentes nele!
Mais exemplos aqui:
E meu github com o exemplo completo:
👩💻
Espero que gostem! :D
Referência extra: