dlpena84/ons-hidro-explorer
💧 ONS Hidrológico Explorer
Toolbox em Streamlit para análise de dados operativos hidráulicos das usinas do Sistema Interligado Nacional (SIN) brasileiro, consumindo os dados abertos do ONS — Operador Nacional do Sistema Elétrico em formato Parquet.
A aplicação tem entry point único (app.py), com uma landing page de cards e navegação entre módulos via st.navigation. Cada módulo é um arquivo Python na raiz, executado pelo entry point.
Módulos
1. Gráficos Operativos · graficos_operativos.py
Visualização interativa de séries temporais para uma usina selecionada, com escolha entre base horária ou diária, eixo duplo (vazões em m³/s + nível/volume), linhas de referência configuráveis e exportação.
- Bases:
- Diária — parquets anuais, desde 01/01/2000
- Horária — parquets mensais, desde 01/01/2010
- Visualização com Plotly:
- Linhas contínuas no eixo Y principal (vazões em m³/s)
- Linha pontilhada no eixo Y secundário (nível em m ou volume útil em %)
- Hover unificado mostrando todos os valores no ponto selecionado
- Seletor de período recolhível (slider com handles + date inputs sincronizados) para ajuste fino sem recarregar dados
- Controles min/máx por eixo Y
- Linhas de referência horizontais customizáveis (outorga, alerta, etc.)
- Variáveis:
- Base horária — Y1: Vazão Afluente, Defluente, Turbinada, Vertida; Y2: Nível Montante (m), Volume Útil (%)
- Base diária — Y1: as 4 acima + Vazão Natural; Y2: Nível Montante (m), Volume Útil (%)
- Volume Útil (%) disponível no eixo Y2 apenas para usinas do tipo RCU e ITAIPU; demais usinas exibem somente Nível (m)
2. Histórico Dia/Mês · dia_historico.py
Análise comparativa de um mesmo dia/mês ao longo de um intervalo de anos: gráfico de colunas + rankings dos maiores e menores valores.
- Entrada: usina, intervalo de anos (2000+), dia/mês, variável (Nível ou Volume Útil)
- Gráfico de barras com rótulos de valor no topo (formato BR), uma barra por ano
- Eixo Y: zero-anchored para Volume Útil (%); escala livre para Nível (m)
- Linhas de referência horizontais customizáveis sobrepostas ao gráfico de barras
- Lógica especial para 29/02: em anos não-bissextos, usa 28/02 e marca essas barras em azul-claro com aviso explicativo
- Ranking com Top N configurável em duas tabelas lado a lado (maiores / menores). Ano vigente destacado em âmbar quando está no range
- Carregamento inteligente: dados são baixados uma vez para todas as usinas do período. Mudança de usina ou variável atualiza render instantaneamente; só ano e dia/mês exigem novo carregamento
3. Projeção por Analogia · projecao_analogia.py
Projeção do restante do ciclo (civil ou hidrológico) usando um ou mais anos históricos análogos, com offset de continuidade no ponto de emenda.
- Entrada: usina, horizonte temporal (ano civil jan–dez ou hidrológico out–set), variável (Nível ou Volume Útil)
- Ciclo hidrológico: identificado pelo ano de encerramento (ex: "2024–2025" termina em set/2025)
- Método de projeção: a partir do último dia com dado real, os valores restantes são substituídos pelos do ano análogo, com um offset constante calculado no ponto de emenda para eliminar saltos
- Múltiplos análogos: seleção via multiselect; cada análogo recebe uma cor distinta no gráfico; botão ↺ Reset volta ao padrão
- Ranking de ciclos: tabelas dos 10 piores e 10 melhores ciclos históricos por média de Vazão Natural, com destaque em amarelo para os análogos selecionados
- Linhas de referência horizontais customizáveis sobrepostas ao gráfico
- Exportação CSV da série resultante (dados reais + projeção), compatível com Excel BR
4. Permanência Mensal · curva_permanencia_mensal.py
Curva de permanência de vazões naturais médias mensais para qualquer usina do SIN, com base na série histórica 1931–2024 do ONS e extensão automática para o ano corrente via base diária.
- Entrada: usina (inicia em branco), tipo de ano (civil jan–dez ou hidrológico out–set) e um ou mais anos/ciclos de destaque via multiselect com botão ↺ de reset ao ano/ciclo vigente
- Ciclo hidrológico: mesma convenção do módulo Projeção por Analogia — ciclo Y termina em set/Y (ex: "2024–2025" = out/2024 → set/2025). Eixo X reordenado para Out→Set, e cada ciclo combina Out/Nov/Dez do ano calendário Y-1 com Jan–Set do ano Y
- Estatísticas mensais calculadas sobre a série completa do arquivo XLS (série invariante 1931–2024):
- Máxima histórica, Permanência de 10% (P10), Média histórica, Permanência de 90% (P90) e Mínima histórica
- P10 = valor excedido em 10% do tempo (alta); P90 = valor excedido em 90% do tempo (baixa)
- Gráfico: cinco linhas contínuas de 3 px com cores distintas; anos/ciclos de destaque em linhas com marcadores (símbolo único: círculo, quadrado, triângulo, diamante, etc.)
- Cores dos destaques: ano/ciclo vigente sempre preto; se apenas um selecionado, também preto; demais recebem cores da paleta em sequência
- Anos/ciclos de destaque: série do XLS (1931–2024) ou médias mensais da base DI para anos além do XLS (2025+); em modo hidrológico, o pré-fetch DI considera os dois anos calendário que compõem o ciclo. Carregamento em paralelo via
ThreadPoolExecutor - Troca de ciclo (civil ↔ hidrológico): reseta a seleção e força remount do multiselect (mesmo padrão do módulo de Analogia)
- Tabela: pivotada — meses nas colunas (na ordem do ciclo escolhido), métricas nas linhas; anos/ciclos de destaque aparecem abaixo das métricas em ordem cronológica
- Arquivo de dados:
data/vazoes_mensais.xls— série de Vazões Médias Mensais do ONS (versionado no repo) - Mapeamento de usinas (
usinas_map.py): normalização automática de nomes (strip de acentos, pontuação) + 43 entradas manuais para divergências de nomenclatura entre a base DI/HO e o XLS histórico - Média parcial do ciclo/ano vigente: quando o ciclo vigente está nos destaques, exibe a média acumulada dos meses já encerrados (ex:
janeiro de 2026 a maio de 2026 (parcial até o dia 17): 2.304,5 m³/s) - Ranking — Média da Vazão Natural Média Mensal: tabelas dos N piores e N melhores anos/ciclos da série XLS por média anual; slider configurável de posições (1 a N total); ciclos/anos selecionados no multiselect aparecem destacados em amarelo; exclui o ciclo vigente (incompleto)
- Exportação CSV das estatísticas e séries de destaque, compatível com Excel BR
5. Comparador de Ciclos · comparador_ciclos.py
Sobreposição da evolução diária de uma variável para múltiplos anos ou ciclos hidrológicos em um único gráfico, permitindo comparação visual da trajetória de cada período.
- Entrada: usina, tipo de ciclo (civil jan–dez ou hidrológico out–set), variável, anos/ciclos a comparar (multiselect com botão ↺ Reset)
- Variáveis: Nível Montante (m), Volume Útil (%) para usinas RCU/Itaipu, Vazão Natural, Afluente, Defluente, Turbinada, Vertida e Transferida (m³/s) — base DI desde 2000
- Gráfico: todas as séries com eixo X em formato DD/MM referenciado a um ano fixo (civil → 2000; hidrológico → 1999/2000) para sobreposição precisa; ciclo vigente sempre em preto
- Controle de período (eixo X): slider + date inputs sincronizados para exibir subperíodos dentro do ciclo
- Linhas de referência horizontais customizáveis (nome + valor); até 6 linhas, cores cíclicas, edição inline
- Ajuste do eixo Y: slider + inputs com min/máx configuráveis; Nível com escala livre, demais variáveis com zero-anchored
- Aparência: toggle com 4 controles de tamanho de fonte (ticks, labels, legenda, título)
- Exportação PNG via modebar, com nome contextual contendo usina, ciclo e variável
6. Séries de Dados · exportador_ho.py
Filtra e exporta séries horárias (HO) e diárias (DI) do ONS para CSV.
- Bases: Horária (desde jan/2010, parquets mensais) ou Diária (desde jan/2000, parquets anuais)
- Entrada: uma ou mais usinas (multiselect com botões Todas/Nenhuma), período via seletores de data, separador e opção de BOM UTF-8
- Download paralelo dos arquivos necessários via
ThreadPoolExecutor - Filtros aplicados: período exato e usinas selecionadas; dados ordenados por usina e instante
- Formato de saída: CSV com todas as colunas do parquet original; decimal vírgula automático quando separador é
;com BOM (formato PT-BR para Excel) - Pré-visualização das primeiras 500 linhas antes do download
Funcionalidades compartilhadas
- Seleção dinâmica de usinas carregada do arquivo mais recente disponível, com fallback automático
- Linhas de referência: presente nos módulos 1, 2, 3 e 5 — nome + valor configuráveis, até 6 cores cíclicas, edição inline, invalidação automática ao trocar usina ou variável
- Exportação:
- CSV compatível com Excel BR (separador
;, decimal,, BOM UTF-8, datas pt-BR) - PNG via barra de ferramentas do Plotly (client-side, com anotação da fonte)
- Performance:
- Dados servidos do Cloudflare R2 (cache intermediário) — fallback automático para o S3 do ONS se necessário
- Download paralelo de parquets via
ThreadPoolExecutor - Cache via
@st.cache_data— trocar parâmetros leves é instantâneo - Cache-busting automático para dados do período corrente (15 min para HO, 1 hora para DI)
- Leitura seletiva de colunas com fallback de schema (parquets antigos sem todas as colunas)
Fluxo de dados
cron-job.org (HO + DI) ──► repository_dispatch ──┐
├──► GitHub Actions ──► ONS (S3 público) ──► Cloudflare R2
GitHub Actions cron (histórico semanal) ──────────┘ │
Streamlit App
(R2-first, fallback ONS)Os dados são espelhados do ONS para um bucket Cloudflare R2 por jobs independentes:
HO e DI usam repository_dispatch disparado por um serviço externo (cron-job.org) porque o GitHub Actions não garante crons em repositórios gratuitos — disparos podem atrasar mais de 1 hora. O histórico semanal permanece no GitHub Actions pois atraso de 1h numa tarefa semanal é irrelevante.
O app lê sempre do R2 primeiro; se o arquivo não estiver disponível, cai silenciosamente no S3 público do ONS. Isso protege o portal de dados abertos do ONS contra thundering herd em cache misses simultâneos.
Armazenamento no R2: ~320 MB (222 parquets), plano gratuito (10 GB).
Portal oficial ONS: https://dados.ons.org.br/
Stack
- Streamlit ≥ 1.36 — interface web (requer
st.navigation) - Pandas ≥ 2.2 — manipulação de dados
- PyArrow ≥ 15 — leitura de Parquet
- Plotly ≥ 5.22 — gráficos interativos
- Boto3 ≥ 1.34 — acesso ao Cloudflare R2 (API compatível com S3)
- xlrd ≥ 1.2 — leitura do arquivo XLS histórico de vazões mensais
Como rodar localmente
Requisitos: Python 3.11+.
git clone https://github.com/dlpena/ons-hidro-explorer.git
cd ons-hidro-explorer
python -m venv venv
# Windows
.\venv\Scripts\activate
# Linux/macOS
source venv/bin/activate
pip install -r requirements.txtO app funciona sem credenciais R2 — nesse caso lê direto do S3 do ONS (comportamento original). Para usar o cache R2, crie .streamlit/secrets.toml:
[r2]
account_id = "seu_account_id"
access_key_id = "sua_access_key"
secret_access_key = "sua_secret_key"
bucket = "ons-data-lake"streamlit run app.pyA aplicação abre em http://localhost:8501 na landing page, com sidebar para navegar entre os módulos.
Estrutura do projeto
ons-hidro-explorer/
├── app.py # entry point: page_config, CSS, st.navigation, landing
├── graficos_operativos.py # módulo 1 — séries temporais operativas
├── dia_historico.py # módulo 2 — comparativo histórico dia/mês
├── projecao_analogia.py # módulo 3 — projeção por analogia hidrológica
├── curva_permanencia_mensal.py # módulo 4 — permanência de vazões mensais
├── comparador_ciclos.py # módulo 5 — comparador de anos/ciclos hidrológicos
├── exportador_ho.py # módulo 6 — séries de dados (horária + diária)
├── usinas_map.py # mapeamento de nomes ONS ↔ XLS histórico (auto + 43 manuais)
├── utils.py # fetch_parquet compartilhado (R2-first + fallback ONS)
├── ingest_ons.py # script de ingestão ONS → R2 (usado pelo GitHub Actions)
├── requirements.txt
├── data/
│ └── vazoes_mensais.xls # série histórica ONS 1931–2024
├── .streamlit/
│ ├── config.toml # tema (light, azul-marinho #1A237E)
│ └── secrets.toml # credenciais R2 — não versionado
├── .github/
│ └── workflows/
│ ├── ingest_ho.yml # HO: repository_dispatch (cron-job.org, 15 em 15 min)
│ └── ingest_ons.yml # DI (cron-job.org, 3×/dia) + histórico completo (cron semanal)
├── .gitignore
└── README.mdpage_config, GA4 e CSS são definidos uma única vez em app.py. Os arquivos dos módulos não chamam st.set_page_config — são carregados via st.navigation. A função fetch_parquet em utils.py é o único ponto de acesso aos dados, garantindo cache compartilhado entre módulos e leitura R2-first com fallback automático para o ONS.
Deploy
Otimizado para deploy no Streamlit Community Cloud:
- Faça fork ou clone do repositório no seu GitHub
- Acesse https://share.streamlit.io e conecte com o GitHub
- Aponte para
app.pyna branchmain - Em App Settings → Secrets, adicione as credenciais R2 (opcional — sem elas o app lê direto do ONS)
- Deploy automático a cada push
Autor
Diego Liz Pena — diegoicc@gmail.com
Licença
MIT
