Neste projeto, analiso a educação do município de Paulista–PE ao longo de oito anos, com dados coletados bienalmente. O objetivo é avaliar a evolução da rede municipal, identificar pontos críticos que afetam a qualidade do ensino e destacar escolas com melhores e piores desempenhos em Língua Portuguesa, Matemática e distorção idade-série. A relevância do estudo está em fornecer informações que subsidiem decisões e políticas educacionais mais eficazes, considerando também os impactos da pandemia na aprendizagem.
Serão analisadas 160 planilhas obtidas no portal QEdu, contendo dados detalhados de matrículas, aprovação, abandono, aprendizagem, fluxo escolar e outros indicadores. Embora o site do QEdu apresente análises gerais, meu objetivo é ir além: cruzar variáveis específicas, comparar escolas individualmente, reconstruir séries históricas limpas e calcular métricas que não aparecem prontas na plataforma, como somatórios reais de abandono, distorção agregada e evolução percentual por série. A metodologia envolve limpeza dos dados, criação de tabelas consolidadas e visualização gráfica para destacar pontos críticos e mudanças ao longo dos anos.
Para enfrentar o problema, adoto uma análise quantitativa ano a ano usando um dataset extenso composto por todas as escolas públicas do município entre 2015 e 2023. A abordagem consiste em calcular indicadores-chave (aprovação, reprovação, abandono, distorção idade-série, matrículas, desempenho) e observar sua evolução ao longo do tempo, separando por série e consolidando por ano. Diferente das métricas agregadas do QEdu, que mostram apenas médias gerais, meu tratamento usa os microdados brutos de cada escola e soma ponderada dos valores. Isso permite identificar exatamente onde os piores índices se concentram — se em anos específicos, séries específicas ou escolas isoladas — e não apenas a média municipal. A técnica deve revelar padrões e gargalos que os indicadores oficiais, por serem muito resumidos, não deixam evidente.
A análise ajuda gestores municipais, escolas e pesquisadores ao mostrar claramente onde estão os gargalos educacionais do município. Com indicadores separados por série, ano e comportamento ao longo do tempo, fica mais fácil priorizar políticas públicas, direcionar recursos e identificar quais intervenções são mais urgentes. O resultado permite decisões mais precisas, baseadas em dados concretos, e não apenas em percepções gerais ou médias agregadas que escondem problemas locais.
# Pacote de funções
library(tidyverse)
# Manipulação e limpeza
library(dplyr)
library(tidyr)
library(stringr)
# Leitura de dados
library(readr)
library(readxl)
library(scales)
# Visualização
library(ggplot2)
library(plotly)
# Organização do relatório
library(knitr)
library(rmarkdown)
knitr::opts_chunk$set(warning = FALSE)
pasta <- "C:/Users/davia/Downloads/analise/"
arquivos <- list.files(pasta, full.names = TRUE, pattern = "\\.csv$")
ler_csv_completo <- function(arq) {
read_csv(arq, col_types = cols(.default = "c"))
}
dados_gerais <- arquivos %>%
map_dfr(ler_csv_completo) %>% # junta todos os CSVs
mutate(across(everything(), ~ type.convert(., as.is = TRUE))) %>%
distinct() %>% # remove duplicados
filter(!dependencia_id %in% c(1, 2, 4, 5)) # mantém só quem NÃO está nesses grupos
dplyr – usado para filtrar, agrupar, resumir e transformar dados de forma intuitiva. É o núcleo da manipulação.
tidyr – organiza dados desestruturados, facilita pivotagem, separação e junção de colunas, deixando tudo no formato “tidy”.
stringr – simplifica qualquer operação com textos, como padronizar nomes, extrair padrões ou corrigir inconsistências.
readr – leitura rápida e limpa de arquivos .csv, mantendo nomes de colunas e codificação corretos.
readxl – leitura de arquivos Excel sem precisar do Excel instalado, essencial para importar os dados originais.
ggplot2 – cria gráficos estáticos bem estruturados, com controle total sobre estética, cores, escalas e temas.
plotly – transforma gráficos em versões interativas (zoom, hover, seleção). Útil para dashboards e apresentações.
knitr – responsável pela renderização dos chunks, controle do código e aparência do relatório.
rmarkdown – converte o documento final em HTML, PDF ou Word e integra texto, tabelas e visualizações num único arquivo.
dados_gerais %>%
dplyr::sample_n(10) %>%
knitr::kable(caption = "Amostra de 10 linhas do conjunto de dados final após limpeza")
| ano | inep_id | dependencia_id | localizacao_id | ef_1ano | ef_2ano | ef_3ano | ef_4ano | ef_5ano | ef_6ano | ef_7ano | ef_8ano | ef_9ano | ef_total_ai | ef_total_af | ef_total | em_1ano | em_2ano | em_3ano | em_4ano | em_total | ibge_id | ciclo_id | ideb | fluxo | aprendizado | nota_mt | nota_lp | lp_adequado | mt_adequado | lp_insuficiente | lp_basico | lp_proficiente | lp_avancado | mt_insuficiente | mt_basico | mt_proficiente | mt_avancado | matriculas | serie_id | aprovados | reprovados | abandonos | Variável | Descrição | Códigos |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2017 | 26116260 | 3 | 1 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 0 | 8 | 0.0 | 0.0 | 0 | NA | NA | NA |
| 2023 | NA | 3 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 2610707 | AI | NA | NA | NA | NA | NA | 0.4021 | 0.2712 | 0.2670 | 0.3309 | 0.2808 | 0.1213 | 0.3523 | 0.3766 | 0.2116 | 0.0596 | NA | NA | NA | NA | NA | NA | NA | NA |
| 2015 | NA | 3 | 1 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 2610707 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 602 | 9 | 85.8 | 11.2 | 3 | NA | NA | NA |
| 2019 | 26182556 | 3 | 1 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 0 | 7 | 0.0 | 0.0 | 0 | NA | NA | NA |
| 2019 | 26188759 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | AI | NA | NA | NA | NA | NA | 0.2246 | 0.2246 | 0.2653 | 0.5101 | 0.2042 | 0.0204 | 0.4082 | 0.3673 | 0.2246 | 0.0000 | 51 | NA | NA | NA | NA | NA | NA | NA |
| 2019 | 26116120 | 3 | 1 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 0 | 10 | 0.0 | 0.0 | 0 | NA | NA | NA |
| 2023 | 26116880 | 3 | 1 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 1 | 100.0 | 0.0 | 0 | NA | NA | NA |
| 2019 | 26188600 | 3 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | AI | 4.9 | 0.9422 | 5.2393 | 202.28 | 187.82 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
| 2019 | 26188724 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | AI | NA | NA | NA | NA | NA | 0.4855 | 0.2855 | 0.1715 | 0.3430 | 0.2856 | 0.2000 | 0.3144 | 0.4001 | 0.2283 | 0.0571 | 37 | NA | NA | NA | NA | NA | NA | NA |
| 2021 | 26116790 | 3 | 1 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 6 | 100.0 | 0.0 | 0 | NA | NA | NA |
Explicação
Muitas colunas da base não estão preenchidas de forma consistente em todos os arquivos. Cada planilha traz um conjunto diferente de informações, variando por ano e por etapa. Por isso, quando tudo é unificado, várias linhas ficam com NA simplesmente porque aquela informação não existia naquele ano ou naquele ciclo. Como os dados não podem ser misturados entre anos diferentes, essas linhas não podem ser descartadas em massa, senão iria perder registros válidos para aquele ano específico. Em resumo: os NAs aparecem porque as planilhas não têm o mesmo padrão de preenchimento ao longo do tempo.
As variáveis utilizadas na análise foram selecionadas a partir do dicionário oficial presente nos arquivos do QEdu. Cada coluna do dataset consolidado mantém o significado original definido pelo Inep. Entre as variáveis de maior interesse estão:
inep_id — identificador único de cada escola, usado para garantir que as análises fossem feitas para as escolas corretas ao longo dos anos.
ideb — Índice de Desenvolvimento da Educação Básica, calculado pelo Inep a partir dos indicadores de fluxo escolar e do desempenho dos alunos, sendo o principal índice de qualidade educacional do país.
ano — ano de referência da observação, permitindo comparações temporais entre ciclos bianuais.
serie — etapa de ensino analisada (anos iniciais, anos finais ou ensino médio).
distorcao — percentual de alunos com dois ou mais anos de atraso na relação idade-série.
abandono — taxa de abandono escolar registrada para cada escola e ano.
aprovacao — taxa de aprovação dos estudantes.
Essas variáveis foram usadas por serem consistentes, sem problemas estruturais nos arquivos originais, e suficientes para realizar todos os cruzamentos e comparações descritos ao longo do relatório.
inep_ids <- dados_gerais %>%
filter(!is.na(inep_id)) %>%
distinct(inep_id) %>%
arrange(inep_id)
inep_ids
## # A tibble: 82 × 1
## inep_id
## <int>
## 1 26115530
## 2 26115700
## 3 26115883
## 4 26115913
## 5 26115964
## 6 26115972
## 7 26115999
## 8 26116014
## 9 26116057
## 10 26116081
## # ℹ 72 more rows
Busquei analisar os resultados do municipio de 2015 a 2023 (dados registrados de 2 em 2 anos), sobre como se comportou, se a pandemia de alguma forma impactou e quais pontos apresentam piores indices.
colunas_interesse <- c(
"lp_adequado",
"mt_adequado",
"lp_insuficiente",
"lp_basico",
"lp_proficiente",
"lp_avancado",
"mt_insuficiente",
"mt_basico",
"mt_proficiente",
"mt_avancado"
)
dados_filtrados <- dados_gerais %>%
filter(
inep_id %in% inep_ids$inep_id,
if_any(all_of(colunas_interesse), ~ !is.na(.))
) %>%
select(inep_id, ano, all_of(colunas_interesse))
dados_filtrados
## # A tibble: 320 × 12
## inep_id ano lp_adequado mt_adequado lp_insuficiente lp_basico
## <int> <int> <dbl> <dbl> <dbl> <dbl>
## 1 26115530 2015 0.161 0.035 0.496 0.343
## 2 26115530 2015 0.275 0.137 0.345 0.380
## 3 26115883 2015 0.348 0.140 0.148 0.504
## 4 26115913 2015 0.142 0.0383 0.273 0.585
## 5 26115964 2015 0.283 0.0459 0.272 0.444
## 6 26115972 2015 0.217 0.0408 0.140 0.643
## 7 26115999 2015 0.343 0.0917 0.172 0.485
## 8 26116057 2015 0.0801 0.0383 0.197 0.723
## 9 26116057 2015 0.185 0.106 0.489 0.327
## 10 26116081 2015 0.213 0.0989 0.356 0.432
## # ℹ 310 more rows
## # ℹ 6 more variables: lp_proficiente <dbl>, lp_avancado <dbl>,
## # mt_insuficiente <dbl>, mt_basico <dbl>, mt_proficiente <dbl>,
## # mt_avancado <dbl>
colunas_interesse <- c(
"lp_adequado",
"mt_adequado",
"lp_insuficiente",
"lp_basico",
"lp_proficiente",
"lp_avancado",
"mt_insuficiente",
"mt_basico",
"mt_proficiente",
"mt_avancado"
)
medias_ano_indicador <- dados_filtrados %>%
select(ano, all_of(colunas_interesse)) %>%
pivot_longer(
cols = all_of(colunas_interesse),
names_to = "indicador",
values_to = "valor"
) %>%
group_by(ano, indicador) %>%
summarise(media = mean(valor, na.rm = TRUE), .groups = "drop") %>%
arrange(ano, indicador)
medias_ano_indicador
## # A tibble: 40 × 3
## ano indicador media
## <int> <chr> <dbl>
## 1 2015 lp_adequado 0.247
## 2 2015 lp_avancado 0.0350
## 3 2015 lp_basico 0.499
## 4 2015 lp_insuficiente 0.254
## 5 2015 lp_proficiente 0.212
## 6 2015 mt_adequado 0.0999
## 7 2015 mt_avancado 0.00625
## 8 2015 mt_basico 0.495
## 9 2015 mt_insuficiente 0.405
## 10 2015 mt_proficiente 0.0936
## # ℹ 30 more rows
# Filtrar apenas indicadores de LP
lp_medias <- medias_ano_indicador %>%
filter(indicador %in% c(
"lp_adequado", "lp_insuficiente", "lp_basico",
"lp_proficiente", "lp_avancado"
))
ggplot(lp_medias, aes(x = ano, y = media * 100, group = indicador, color = indicador)) +
geom_line(linewidth = 1.2) +
geom_point(size = 3) +
theme_minimal(base_size = 14) +
labs(
title = "Evolução do Aprendizado em Língua Portuguesa no Município",
x = "Ano",
y = "Percentual (%)",
color = "Indicador"
) +
scale_color_discrete(labels = c(
"lp_adequado" = "Adequado",
"lp_insuficiente" = "Insuficiente",
"lp_basico" = "Básico",
"lp_proficiente" = "Proficiente",
"lp_avancado" = "Avançado"
)) +
scale_x_continuous(breaks = c(2015, 2017, 2019, 2023)) +
scale_y_continuous(breaks = c(0,5,10,15,20,25,30,35,40,45,50)) +
theme(
plot.title = element_text(face = "bold", size = 16),
legend.position = "right"
)
Legenda:
Adequado: percentual de estudantes com aprendizagem considerada suficiente para a etapa. Representa domínio sólido dos conteúdos esperados.
Básico: percentual de estudantes que demonstram conhecimentos fundamentais, mas ainda abaixo do nível esperado. Indica aprendizado parcial e necessidade de reforço.
Insuficiente: percentual de estudantes com desempenho abaixo dos requisitos mínimos. Aponta dificuldades significativas na leitura, escrita e interpretação.
Proficiente: estudantes que demonstram domínio consistente das habilidades avaliadas, acima do nível básico. É um patamar de bom desempenho.
Avançado: estudantes que apresentam desempenho superior, demonstrando compreensão aprofundada e habilidades além do esperado para a etapa.
Conclusão
Houve uma redução consistente na proporção de alunos no nível básico, acompanhada por um aumento igualmente forte no nível adequado. O avanço no nível proficiente reforça a tendência de melhoria contínua do desempenho no município.
Os demais indicadores apresentaram variações pequenas ou pouco relevantes do ponto de vista interpretativo.
# Filtrar apenas indicadores de MT
mt_medias <- medias_ano_indicador %>%
filter(indicador %in% c(
"mt_adequado", "mt_insuficiente", "mt_basico",
"mt_proficiente", "mt_avancado"
))
ggplot(mt_medias, aes(x = ano, y = media * 100, group = indicador, color = indicador)) +
geom_line(linewidth = 1.2) +
geom_point(size = 3) +
theme_minimal(base_size = 14) +
labs(
title = "Evolução do Aprendizado em Matemática no Município",
x = "Ano",
y = "Percentual (%)",
color = "Indicador"
) +
scale_color_discrete(labels = c(
"mt_adequado" = "Adequado",
"mt_insuficiente" = "Insuficiente",
"mt_basico" = "Básico",
"mt_proficiente" = "Proficiente",
"mt_avancado" = "Avançado"
)) +
scale_x_continuous(breaks = c(2015, 2017, 2019, 2023)) +
scale_y_continuous(breaks = c(0,5,10,15,20,25,30,35,40,45,50)) +
theme(
plot.title = element_text(face = "bold", size = 16),
legend.position = "right"
)
Legenda:
Adequado: percentual de estudantes com aprendizagem considerada suficiente para a etapa. Representa domínio sólido dos conteúdos esperados.
Básico: percentual de estudantes que demonstram conhecimentos fundamentais, mas ainda abaixo do nível esperado. Indica aprendizado parcial e necessidade de reforço.
Insuficiente: percentual de estudantes com desempenho abaixo dos requisitos mínimos. Aponta dificuldades significativas na leitura, escrita e interpretação.
Proficiente: estudantes que demonstram domínio consistente das habilidades avaliadas, acima do nível básico. É um patamar de bom desempenho.
Avançado: estudantes que apresentam desempenho superior, demonstrando compreensão aprofundada e habilidades além do esperado para a etapa.
Conclusão
Observa-se uma oscilação contínua na proporção de alunos nos níveis insuficiente e básico, que, ainda assim, representam a maior parte dos estudantes abaixo do esperado para o ano. Por outro lado, os níveis adequado e proficiente mostraram crescimento ao longo do período, com um leve recuo no último ano da avaliação.
indicadores_comp <- c(
"lp_basico", "lp_insuficiente",
"mt_basico", "mt_insuficiente"
)
comparacao <- medias_ano_indicador %>%
filter(indicador %in% indicadores_comp) %>%
mutate(
disciplina = ifelse(str_detect(indicador, "^lp_"), "Língua Portuguesa", "Matemática"),
nivel = case_when(
str_detect(indicador, "basico") ~ "Básico",
str_detect(indicador, "insuficiente") ~ "Insuficiente"
)
)
ggplot(comparacao, aes(
x = factor(ano),
y = media * 100, # já está em proporção, só converter pra porcentagem
fill = nivel
)) +
geom_col(position = "stack") +
facet_wrap(~ disciplina, nrow = 1) +
theme_minimal(base_size = 14) +
labs(
title = "Distribuição dos Níveis Básico e Insuficiente",
subtitle = "Comparação entre Língua Portuguesa e Matemática",
x = "Ano",
y = "Percentual (%)",
fill = "Nível"
) +
scale_x_discrete(limits = c("2015","2017","2019","2023")) +
scale_y_continuous(breaks = seq(0, 100, 10)) +
scale_fill_manual(
values = c(
"Básico" = "#1f77b4",
"Insuficiente" = "#d62728"
)
) +
theme(
plot.title = element_text(face = "bold", size = 16),
axis.title.x = element_text(margin = margin(t = 10)),
axis.title.y = element_text(margin = margin(r = 10)),
legend.position = "right"
)
Os percentuais de aprendizado em matemática mostram uma leve melhora, mas ainda indicam resultados preocupantes na rede pública da cidade, com o pior desempenho em 2015, quando cerca de 90% dos estudantes apresentavam conhecimento insuficiente. Já o ensino de Língua Portuguesa apresenta desempenho um pouco melhor, mas ainda assim preocupante.
Nesta análise, examinarei o registro da presença dos estudantes ao longo dos anos, focando em sua permanência, conclusão do ano letivo e se estão na série adequada.
desistiram <- dados_gerais |>
dplyr::filter(
!is.na(matriculas),
!is.na(abandonos)
) |>
dplyr::select(
inep_id,
ano,
matriculas,
abandonos
) |>
dplyr::distinct()
desistiram
## # A tibble: 1,140 × 4
## inep_id ano matriculas abandonos
## <int> <int> <int> <dbl>
## 1 26115530 2015 14 14.3
## 2 26115530 2015 19 0
## 3 26115530 2015 24 15
## 4 26115530 2015 25 4.1
## 5 26115530 2015 35 18.1
## 6 26115530 2015 108 2
## 7 26115530 2015 113 12.6
## 8 26115530 2015 110 5.7
## 9 26115530 2015 99 9.3
## 10 26115530 2015 0 0
## # ℹ 1,130 more rows
pizza_abandono <- desistiram |>
dplyr::mutate(
abandonos_reais = matriculas * (abandonos / 100)
) |>
dplyr::group_by(ano) |>
dplyr::summarise(
total_matriculas = sum(matriculas, na.rm = TRUE),
total_abandonos = sum(abandonos_reais, na.rm = TRUE)
) |>
dplyr::mutate(
total_nao_abandonaram = total_matriculas - total_abandonos
)
for (ano_atual in sort(unique(pizza_abandono$ano))) {
df_ano <- pizza_abandono %>%
filter(ano == ano_atual) %>%
pivot_longer(
cols = c(total_abandonos, total_nao_abandonaram),
names_to = "tipo",
values_to = "quantidade"
) %>%
mutate(
tipo = factor(tipo, levels = c("total_abandonos", "total_nao_abandonaram"),
labels = c("Estudantes que abandonaram", "Estudantes que continuaram")),
pct = quantidade / sum(quantidade) # para mostrar % na fatia
)
p <- ggplot(df_ano, aes(x = "", y = quantidade, fill = tipo)) +
geom_col(width = 1, color = "white", show.legend = TRUE) +
coord_polar(theta = "y") +
geom_text(aes(label = paste0(round(pct*100, 1), "%")),
position = position_stack(vjust = 0.5), size = 4) +
scale_fill_manual(values = c("#c0392b", "#2ecc71")) + # cores fixas (vermelho=abandono, verde=permanência)
labs(
title = paste("Distribuição de Abandono Escolar –", ano_atual),
fill = "Situação dos Estudantes"
) +
theme_void() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "right"
)
print(p)
}
Conclusão
Embora os dados de 2023 ainda não tenham sido divulgados, observa-se uma melhora contínua ao longo dos anos, com um leve recuo em 2021. A presença e a continuidade dos estudantes em sala de aula mostram-se consistentes, indicando que políticas públicas têm impacto positivo na permanência escolar.
A análise da distorção será realizada ano a ano apenas no ensino fundamental, uma vez que a oferta de ensino médio na rede pública de Paulista não está distribuída de forma abrangente por toda a cidade.
cols_distorcao <- paste0("ef_", 1:9, "ano")
dg_clean <- dados_gerais %>%
mutate(across(all_of(cols_distorcao), ~na_if(trimws(.), ""))) %>% # "" vira NA
mutate(across(all_of(cols_distorcao), ~as.numeric(str_replace(., ",", "."))))
distoados <- dg_clean %>%
filter(ano >= 2015) %>%
select(ano, all_of(cols_distorcao)) %>%
pivot_longer(cols = starts_with("ef_"),
names_to = "serie",
values_to = "distorcao") %>%
filter(!is.na(distorcao)) %>%
mutate(
serie = as.numeric(str_extract(serie, "\\d+"))
) %>%
group_by(ano, serie) %>%
summarise(distorcao = mean(distorcao, na.rm = TRUE),
.groups = "drop") %>%
arrange(ano, serie)
for (s in sort(unique(distoados$serie))) {
temp <- distoados %>% filter(serie == s)
p <- ggplot(temp, aes(x = ano, y = distorcao)) +
geom_col(fill = "#2c3e50") +
geom_text(aes(label = round(distorcao,1)), vjust = -0.5) +
labs(
title = paste0("Distorção Idade-Série – ", s, "º Ano"),
x = "Ano Letivo",
y = "Percentual (%)"
) +
scale_x_continuous(breaks = c(2015, 2017, 2019, 2021, 2023))
theme_minimal(base_size = 10)
print(p)
}
Conclusão
Durante os anos da pandemia de COVID-19, os anos finais do ensino fundamental (6º ao 9º ano) registraram piora expressiva nos resultados, com aumento significativo nos números, e, mesmo após o término da pandemia, os percentuais ainda não retornaram aos níveis anteriores. Já os anos iniciais (1º ao 5º ano) apresentaram flutuações nos percentuais, mas sem variações tão expressivas.
ggplot(distoados, aes(x = ano, y = distorcao, color = factor(serie))) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
labs(
title = "Evolução da Distorção Idade-Série (1º ao 9º ano)",
x = "Ano Letivo",
y = "Percentual (%)",
color = "Ano"
) +
scale_x_continuous(breaks = c(2015, 2017, 2019, 2021, 2023)) +
scale_y_continuous(breaks = c(1,5,10,15,20,25,30,35)) +
theme_minimal(base_size = 14)
Conclusão
Alguns anos específicos, como observado nos gráficos individualizados, apresentaram pioras muito expressivas no quesito de distorção, destacando-se significativamente em relação aos demais anos.
# anos fixos que você sempre quer ver
anos_desejados <- c(2015, 2017, 2019, 2021)
# --- 1) preparar matriculas por ano/serie (a partir de dados_gerais) ---
matriculas_ano_serie <- dados_gerais %>%
# garantir tipos / limpar strings vazias
mutate(
serie_id = as.character(serie_id) |> trimws(),
matriculas = as.numeric(str_replace(as.character(matriculas), ",", ".")),
ano = as.numeric(as.character(ano))
) %>%
filter(!is.na(serie_id), !is.na(matriculas), !is.na(ano)) %>%
mutate(serie = as.integer(str_extract(serie_id, "\\d+"))) %>%
filter(!is.na(serie)) %>%
group_by(ano, serie) %>%
summarise(total_matriculas = sum(matriculas, na.rm = TRUE), .groups = "drop") %>%
filter(ano %in% anos_desejados)
if (nrow(matriculas_ano_serie) == 0) {
matriculas_ano_serie <- dados_gerais %>%
mutate(
matriculas = as.numeric(str_replace(as.character(matriculas), ",", ".")),
ano = as.numeric(as.character(ano))
) %>%
filter(!is.na(matriculas), !is.na(ano)) %>%
group_by(ano) %>%
summarise(total_matriculas = sum(matriculas, na.rm = TRUE), .groups = "drop") %>%
filter(ano %in% anos_desejados) %>%
mutate(serie = NA_integer_)
}
# --- 2) preparar distoados (garantir tipos, filtrar anos desejados) ---
distoados_clean <- distoados %>%
mutate(
ano = as.numeric(as.character(ano)),
serie = as.integer(serie),
distorcao = as.numeric(distorcao) # percent (ex: 12.5)
) %>%
filter(ano %in% anos_desejados)
joined <- distoados_clean %>%
left_join(matriculas_ano_serie, by = c("ano", "serie"))
matriculas_por_ano <- matriculas_ano_serie %>%
group_by(ano) %>%
summarise(total_matriculas_ano = sum(total_matriculas, na.rm = TRUE), .groups = "drop")
joined <- joined %>%
left_join(matriculas_por_ano, by = "ano") %>%
mutate(
matriculas_usadas = if_else(!is.na(total_matriculas), total_matriculas, total_matriculas_ano)
)
if (all(is.na(joined$matriculas_usadas))) {
warning("Nenhuma contagem de matriculas por serie/ano encontrada. Resultado usará média simples das distorções por ano (não ponderada).")
resumo_ano <- joined %>%
group_by(ano) %>%
summarise(
distorcao_percent = mean(distorcao, na.rm = TRUE),
total_matriculas = NA_real_,
metodo = "media_simples",
.groups = "drop"
)
} else {
# calcula abandonos reais = distorcao(%) /100 * matriculas_usadas
resumo_ano <- joined %>%
filter(!is.na(distorcao)) %>%
mutate(abandonos_reais = (distorcao / 100) * matriculas_usadas) %>%
group_by(ano) %>%
summarise(
total_abandonos = sum(abandonos_reais, na.rm = TRUE),
total_matriculas = sum(matriculas_usadas, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(
distorcao_percent = if_else(total_matriculas > 0,
(total_abandonos / total_matriculas) * 100,
NA_real_),
metodo = "ponderada"
)
}
# Garante a ordem dos anos (sempre mostrar 2015..2023)
resumo_ano <- resumo_ano %>%
filter(ano %in% anos_desejados) %>%
mutate(ano = factor(ano, levels = anos_desejados))
ggplot(resumo_ano, aes(x = ano, y = distorcao_percent)) +
geom_col(fill = "#2c3e50", width = 0.6) +
geom_text(aes(label = ifelse(is.na(distorcao_percent), "NA", paste0(round(distorcao_percent,1), "%"))),
vjust = -0.5, size = 4) +
scale_y_continuous(limits = c(0, 50), expand = expansion(mult = c(0, 0.05))) +
labs(
title = "Distorção (percentual ponderado) por Ano Letivo",
x = "Ano",
y = "Distorção (%)"
) +
theme_minimal(base_size = 14) +
theme(plot.title = element_text(face = "bold")) +
scale_x_discrete(drop = FALSE)
**Conclusão*
Observando o panorama geral das distorções ao longo dos anos letivos, fica evidente que a pandemia provocou piora nos índices. Como os dados de matrícula de 2023 ainda não foram divulgados, não foi possível calcular a porcentagem de alunos em distorção para esse ano, mas os dados individuais dos anos anteriores indicam tendência de piora nesse indicador.
Nessa sessão irei buscar pelas melhores e piores escolas considerando indices positivos de aprendizado em lingua portuguesa e matemática e as menores taxas de distorção.
anos_validos <- c(2015, 2017, 2019, 2021, 2023)
# LIMPEZA E PREPARAÇÃO
dados_clean <- dados_gerais %>%
mutate(
inep_id = as.character(inep_id),
ano = as.numeric(ano)
) %>%
filter(
!is.na(inep_id),
!is.na(ano),
ano %in% anos_validos,
ciclo_id %in% c("AI", "AF")
) %>%
filter(!is.na(nota_mt), !is.na(nota_lp)) %>%
mutate(
nota_mt = as.numeric(nota_mt),
nota_lp = as.numeric(nota_lp)
) %>%
group_by(inep_id, ano) %>%
summarise(
nota_mt = mean(nota_mt, na.rm = TRUE),
nota_lp = mean(nota_lp, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(Score = (nota_mt + nota_lp) / 2)
# GERA TABELA FINAL
final_list <- lapply(anos_validos, function(a) {
ano_df <- dados_clean %>% filter(ano == a)
media_ano <- mean(ano_df$Score, na.rm = TRUE)
melhor <- ano_df %>% arrange(desc(Score)) %>% slice(1) %>% mutate(tipo = "MELHOR")
pior <- ano_df %>% arrange(Score) %>% slice(1) %>% mutate(tipo = "PIOR")
linha_media <- tibble(
tipo = "MEDIA_ANO",
inep_id = paste0("MEDIA_", a),
ano = a,
nota_mt = NA_real_,
nota_lp = NA_real_,
Score = media_ano
)
bind_rows(linha_media, melhor, pior)
})
tabela_final <- bind_rows(final_list)
tabela_final
## # A tibble: 15 × 6
## tipo inep_id ano nota_mt nota_lp Score
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 MEDIA_ANO MEDIA_2015 2015 NA NA 190.
## 2 MELHOR 26136549 2015 218. 212. 215.
## 3 PIOR 26116235 2015 173. 159. 166.
## 4 MEDIA_ANO MEDIA_2017 2017 NA NA 203.
## 5 MELHOR 26116251 2017 228. 233. 230.
## 6 PIOR 26116820 2017 175. 169. 172.
## 7 MEDIA_ANO MEDIA_2019 2019 NA NA 206.
## 8 MELHOR 26116804 2019 255. 247. 251.
## 9 PIOR 26188759 2019 187. 176. 181.
## 10 MEDIA_ANO MEDIA_2021 2021 NA NA 201.
## 11 MELHOR 26116804 2021 251. 258. 254.
## 12 PIOR 26116766 2021 182. 170. 176.
## 13 MEDIA_ANO MEDIA_2023 2023 NA NA 206.
## 14 MELHOR 26116804 2023 259. 261. 260.
## 15 PIOR 26188589 2023 182. 168. 175.
tabela_escolas <- tibble(
inep_id = c("26116804", "26188589", "26116766", "26188759","26116251","26136549","26116235","26116820"),
nome_escola = c("
ESCOLA MUNICIPAL DRA GELDA AMORIM", "ESCOLA MUNICIPAL PROFESSORA IRACEMA CASTRO", "
ESCOLA MUNICIPAL ABELARDO SALES DE SIQUEIRA","
ESCOLA MUNICIPAL PROFESSORA EDNA MARINHO DA SILVA", "
ESCOLA MUNICIPAL JOAO F DE ALBUQUERQUE","
ESCOLA MUL PROF MARIA CONCEICAO DA PAZ","
ESCOLA MUNICIPAL ZULIMA PINHO ALVES","
ESCOLA MUNICIPAL HEINZ HERING")
)
anos_validos <- c(2015, 2017, 2019, 2021, 2023)
# tabela_final: já criada antes
# tabela_escolas: tabela com inep_id e nome_escola
tabela_final_nomes <- tabela_final %>%
mutate(inep_id = as.character(inep_id)) %>%
left_join(tabela_escolas, by = "inep_id") %>%
mutate(
tipo = factor(tipo, levels = c("MEDIA_ANO", "MELHOR", "PIOR")),
nome_plot = ifelse(tipo == "MEDIA_ANO",
paste0("Média ", ano),
nome_escola)
)
for (a in anos_validos) {
df_plot <- tabela_final_nomes %>% filter(ano == a)
print(
ggplot(df_plot, aes(x = tipo, y = Score, fill = tipo)) +
geom_col() +
geom_text(
aes(label = paste0(nome_plot, "\n", round(Score, 2))),
vjust = 3,
size = 3
) +
labs(
title = paste("Ano", a, "- Média, Melhor e Pior Escola"),
x = "",
y = "Score"
) +
theme_minimal(base_size = 12) +
theme(legend.position = "none")
)
}
Conclusão
Ao analisar os dados, fica claro que o desempenho bruto das escolas em Língua Portuguesa e Matemática não explica sozinho o nível de “aprendizado” nem o resultado final do IDEB. As notas disponibilizadas nas planilhas representam valores absolutos, enquanto o indicador “aprendizado” utiliza notas padronizadas, que passam por transformação estatística antes de gerar o valor final.
Por isso, é comum observar casos em que uma escola apresenta médias superiores em LP e MT, mas não alcança os maiores índices de aprendizado. Além disso, o IDEB depende também do fluxo escolar — aprovação, reprovação e abandono — o que faz com que escolas com desempenho intermediário possam superar outras com notas mais altas, desde que apresentem fluxo muito eficiente.
Em resumo: melhores notas não garantem melhor aprendizado, e melhor aprendizado não garante o melhor IDEB, pois cada indicador responde a componentes diferentes e não lineares do processo educacional.
# Calcular média de IDEB por ano, ignorando NAs
ideb_ano <- dados_gerais %>%
group_by(ano) %>%
summarise(ideb = mean(ideb, na.rm = TRUE)) %>%
ungroup() %>%
filter(!is.na(ideb)) # Remove anos que ficaram NA após a média
# Previsão para 2025 com base nos anos válidos
ideb_media <- mean(ideb_ano$ideb, na.rm = TRUE)
ideb_previsto_2025 <- data.frame(
ano = 2025,
ideb = ideb_media
)
# Combinar dados históricos e previsão
dados_plot <- bind_rows(ideb_ano, ideb_previsto_2025) %>%
mutate(tipo = ifelse(ano == 2025, "Previsão", "Histórico"))
# Plot com linha de tendência
ggplot(dados_plot, aes(x = factor(ano), y = ideb, group = 1)) +
geom_col(aes(fill = tipo), width = 0.5) +
geom_text(aes(label = round(ideb,1)), vjust = -0.5) +
geom_line(data = filter(dados_plot, tipo == "Histórico"), aes(y = ideb),
color = "#2980b9", size = 1, linetype = "dashed") +
geom_point(aes(color = tipo), size = 3) +
scale_fill_manual(values = c("Histórico" = "#2c3e50", "Previsão" = "#e74c3c")) +
scale_color_manual(values = c("Histórico" = "#2c3e50", "Previsão" = "#e74c3c")) +
scale_y_continuous(limits = c(0, 8), expand = expansion(mult = c(0, 0.05)))
labs(
title = "IDEB: Histórico e previsão para 2025",
x = "Ano",
y = "IDEB",
fill = "",
color = ""
) +
theme_minimal(base_size = 14) +
theme(plot.title = element_text(face = "bold"))
## NULL
Conclusão
Mesmo considerando a previsão matemática, o município continua distante das metas ideais do IDEB, com resultados muito abaixo da média do estado (5,7 nos anos iniciais e 5,0 nos anos finais) e da média nacional (6,0 nos anos iniciais e 5,0 nos anos finais). Isso evidencia a necessidade de investimentos e estudos aprofundados voltados à melhoria da qualidade da educação na rede municipal.
O problema abordado neste projeto é a baixa qualidade do aprendizado dos estudantes da rede pública de Paulista, evidenciada por altos índices de distorção idade-série e pelo baixo desempenho em Língua Portuguesa e Matemática. A investigação busca compreender a evolução desses indicadores ao longo dos anos, identificar os impactos da pandemia e destacar escolas com melhores e piores resultados, fornecendo subsídios para políticas públicas que melhorem a aprendizagem e a permanência dos alunos na escola.
A declaração do problema foi abordada por meio da análise de dados educacionais da rede pública de Paulista, abrangendo os anos letivos de 2015 a 2023. Foram utilizados indicadores de aprendizado em Língua Portuguesa e Matemática, taxas de distorção idade-série e registros de presença dos alunos. A metodologia empregada envolveu o tratamento e organização dos dados no R, cálculo de médias e percentuais por ano e escola, além da visualização dos resultados por meio de gráficos de barras, linhas e comparativos, permitindo identificar tendências, oscilações e as melhores e piores escolas em cada indicador.
A análise revelou que, embora alguns indicadores de aprendizado apresentem leve melhora ao longo dos anos, a maioria dos estudantes ainda se encontra nos níveis insuficiente e básico, especialmente em Matemática. Os anos finais do ensino fundamental (6º ao 9º) foram mais afetados durante a pandemia, com piora significativa na distorção idade-série e no desempenho escolar, sendo poucos os dados que apresentaram melhora expressiva após esse período. Observou-se também que nem sempre as escolas com melhores notas individuais registram um IDEB mais alto, indicando que o desempenho médio não reflete completamente a qualidade de aprendizagem ou a progressão de todos os alunos. Além disso, a comparação entre escolas permitiu identificar unidades com melhores resultados e menor distorção, reforçando a importância de políticas públicas que favoreçam a permanência e o aprendizado dos estudantes.
As implicações desta análise para gestores e formuladores de políticas educacionais incluem a identificação de escolas que necessitam de atenção prioritária, bem como práticas que podem ser replicadas nas unidades com melhores resultados. Os dados permitem monitorar a evolução do aprendizado e da distorção idade-série ao longo do tempo, ajudando a direcionar recursos e estratégias pedagógicas de forma mais eficiente. Além disso, evidenciam que altos índices de desempenho em avaliações isoladas não garantem necessariamente um IDEB elevado, reforçando a necessidade de abordagens integradas que considerem tanto a aprendizagem quanto a permanência dos alunos na escola.
A análise apresenta algumas limitações. Embora o foco no ensino fundamental tenha sido proposital, já que a rede municipal de Paulista oferece praticamente apenas esse nível de ensino, os dados de 2023 ainda não estavam disponíveis por completo, o que impede avaliar completamente a evolução mais recente. Além disso, os indicadores utilizados não capturam aspectos qualitativos do aprendizado, como engajamento ou metodologia pedagógica.
Para aprimorar ou expandir a análise, seria interessante incluir dados futuros assim que disponíveis, incorporar variáveis socioeconômicas, qualitativas e verba de cada escola, e utilizar modelos estatísticos ou de aprendizado de máquina para prever tendências e identificar fatores que mais impactam o desempenho e a permanência dos alunos.
Além disso, a maioria dos modelos testados não encontrou padrões ou relações significativas nos dados disponíveis, indicando que os indicadores atuais podem ser insuficientes para capturar todos os fatores que influenciam o desempenho e a permanência dos alunos.