O objetivo deste projeto é demonstrar como processar eficientemente um arquivo de dados massivo contendo 1 bilhão de linhas (~15GB), especificamente para calcular estatísticas (Incluindo agregação e ordenação que são operações pesadas) utilizando Python.
Este desafio foi inspirado no The One Billion Row Challenge, originalmente proposto para Java.
O arquivo de dados consiste em medições de temperatura de várias estações meteorológicas. Cada registro segue o formato <string: nome da estação>;<double: medição>
, com a temperatura sendo apresentada com precisão de uma casa decimal.
Aqui estão dez linhas de exemplo do arquivo:
Hamburg;12.0
Bulawayo;8.9
Palembang;38.8
St. Johns;15.2
Cracow;12.6
Bridgetown;26.9
Istanbul;6.2
Roseau;34.4
Conakry;31.2
Istanbul;23.0
O desafio é desenvolver um programa Python capaz de ler esse arquivo e calcular a temperatura mínima, média (arredondada para uma casa decimal) e máxima para cada estação, exibindo os resultados em uma tabela ordenada por nome da estação.
station | min_temperature | mean_temperature | max_temperature |
---|---|---|---|
Abha | -31.1 | 18.0 | 66.5 |
Abidjan | -25.9 | 26.0 | 74.6 |
Abéché | -19.8 | 29.4 | 79.9 |
Accra | -24.8 | 26.4 | 76.3 |
Addis Ababa | -31.8 | 16.0 | 63.9 |
Adelaide | -31.8 | 17.3 | 71.5 |
Aden | -19.6 | 29.1 | 78.3 |
Ahvaz | -24.0 | 25.4 | 72.6 |
Albuquerque | -35.0 | 14.0 | 61.9 |
Alexandra | -40.1 | 11.0 | 67.9 |
... | ... | ... | ... |
Yangon | -23.6 | 27.5 | 77.3 |
Yaoundé | -26.2 | 23.8 | 73.4 |
Yellowknife | -53.4 | -4.3 | 46.7 |
Yerevan | -38.6 | 12.4 | 62.8 |
Yinchuan | -45.2 | 9.0 | 56.9 |
Zagreb | -39.2 | 10.7 | 58.1 |
Zanzibar City | -26.5 | 26.0 | 75.2 |
Zürich | -42.0 | 9.3 | 63.6 |
Ürümqi | -42.1 | 7.4 | 56.7 |
İzmir | -34.4 | 17.9 | 67.9 |
Para executar os scripts deste projeto, você precisará das seguintes bibliotecas:
- pandas:
^2.2.2
- duckdb:
^1.0.0
- dask:
^2024.7.1
- polars:
^1.3.0
- fireducks:
^1.2.1
- streamlit:
^1.37.0
- Apache Airflow (via Astro CLI)
Os testes foram realizados em um Laptop com Ubuntu 24.04.2 LTS (Lenovo ThinkPad E14 Gen 3) equipado com um processador AMD Ryzen™ 7 5700U with Radeon™ Graphics × 16 e 16GB de RAM. As implementações utilizaram abordagens puramente Python, Pandas, Dask, Polars, Fireducks e DuckDB. Os resultados de tempo de execução para processar o arquivo de 1 bilhão de linhas são apresentados abaixo:
Implementação | Tempo |
---|---|
Bash + awk | 25 minutos |
Python | 20 minutos |
Python + Pandas | 358.78 seg |
Python + Fireducks | 324.40 seg |
Python + Dask | 242.49 seg |
Python + Duckdb | 67.78 seg |
Python + Polars | 36.57 seg |
Obrigado por Koen Vossen pela implementação em Polars e Arthur Julião pela implementação em Python e Bash
Este desafio destacou claramente a eficácia de diversas bibliotecas Python na manipulação de grandes volumes de dados. Métodos tradicionais como Bash, Python puro e até mesmo o Pandas/Fireducks demandaram uma série de táticas para implementar o processamento em "lotes", enquanto bibliotecas como Dask, Polars e DuckDB provaram ser excepcionalmente eficazes, requerendo menos linhas de código devido à sua capacidade inerente de distribuir os dados em "lotes em streaming" de maneira mais eficiente.
🏆 O Polars alcançou o menor tempo de execução (36.57 segundos), talvez devido a uma combinação de características arquiteturais e estratégias de processamento superiores:
- Lazy Execution: Permite fusão de múltiplas operações em um único plano otimizado.
- Multi-threading: Aproveita todos os núcleos de CPU disponíveis de forma eficiente.
- Arquitetura em Rust: O código base em Rust oferece performance nativa e gerenciamento eficiente de memória. Alta performance, memória otimizada e paralelismo nativo.
O DuckDB alcançou o segundo menor tempo de execução (67.78 segundos), talvez graças à sua estratégia de execução e processamento de dados, que inclui:
-
Query Execution Model:
- Processamento vetorizado que quebra operações em blocos otimizados
- Implementação de um otimizador SQL que reduz operações desnecessárias
- Compilação interna de queries para acelerar o processamento
-
Processamento de Dados Incremental:
- Suporte a arquivos Parquet com leitura seletiva de colunas
- Streaming eficiente de dados, processando blocos de linhas em formato vetorizado
- Cache inteligente que reutiliza resultados para evitar reprocessamento
-
Multi-threading Eficiente:
- Aproveitamento automático de múltiplos núcleos da CPU
- Paralelização inteligente de queries em grandes arquivos
- Redução significativa do tempo de resposta através de execução paralela
Esses resultados reforçam a importância de escolher a ferramenta adequada para análise de dados em larga escala. Polars e DuckDB se destacaram por combinar execução colunar, gerenciamento eficiente de memória e paralelização automática. O Polars, em particular, demonstrou como uma implementação em Rust com lazy evaluation pode superar significativamente abordagens tradicionais. Isso evidencia que Python, munido das bibliotecas certas, continua sendo uma opção poderosa para big data.
Para executar este projeto e reproduzir os resultados:
- Clone esse repositório
- Definir a versão do Python usando o
pyenv local 3.12.3
- Execute os comandos:
poetry env use 3.12.3 poetry install --no-root poetry lock --no-update
- Execute o comando
python src/create_measurements.py
para gerar o arquivo de teste (caso queira aumentar ou diminuir o tamanho do arquivo, altere o valor da variavel num_rows_to_create=1_000_000_000) - Aguarde alguns minutos para a geração completa do arquivo
- Execute os scripts:
python src/etl_python.py python src/etl_pandas.py python src/etl_fireducks.py python src/etl_dask.py python src/etl_polars.py python src/etl_duckdb.py
Este projeto utiliza o Apache Airflow para gerenciar e automatizar os fluxos de processamento de dados. Para iniciar o Airflow, siga os passos abaixo:
- Navegue até o diretório do Airflow:
cd src/airflow
- Inicie o ambiente com Astro CLI:
astro dev start
- Acesse a interface web do Airflow em
http://localhost:8080
- Para verificar os DAGs disponíveis e iniciar a execução, ative os DAGs necessários na interface
Caso queira parar a execução do Airflow, utilize:
astro dev stop
Para rodar o script Bash descrito, você precisa seguir alguns passos simples. Primeiro, assegure-se de que você tenha um ambiente Unix-like, como Linux ou macOS, que suporta scripts Bash nativamente. Além disso, verifique se as ferramentas utilizadas no script (wc
, head
, pv
, awk
, e sort
) estão instaladas em seu sistema. A maioria dessas ferramentas vem pré-instalada em sistemas Unix-like, mas pv
(Pipe Viewer) pode precisar ser instalado manualmente.
Se você não tem o pv
instalado, pode facilmente instalá-lo usando o gerenciador de pacotes do seu sistema. Por exemplo:
-
No Ubuntu/Debian:
sudo apt-get update sudo apt-get install pv
-
No macOS (usando Homebrew):
brew install pv
-
Dê permissão de execução para o arquivo script. Abra um terminal e execute:
chmod +x process_measurements.sh
-
Rode o script. Abra um terminal e execute:
./src/using_bash_and_awk.sh 1000
Neste exemplo, apenas as primeiras 1000 linhas serão processadas.
Ao executar o script, você verá a barra de progresso (se pv estiver instalado corretamente) e, eventualmente, a saída esperada no terminal ou em um arquivo de saída, se você decidir modificar o script para direcionar a saída.
Para dúvidas, sugestões ou feedbacks:
- Thiago Silva - Linkedin