Sumário

1 - Introdução e descrição do dataset

O Censo Escolar é a principal investigação estatística da educação básica no Brasil, realizada anualmente pelo Inep, uma autarquia vinculada ao Ministério da Educação (MEC).

Iremos trabalhar especificamente com o recorte das escolas em Pernambuco, a fim de realizar cruzamentos de dados e análises aprofundadas.

  • Fonte: arquivo MicroDadosPEFinal.csv presente na pasta do projeto.
  • Objetivo: Explorar, limpar e visualizar informações sobre escolas e infraestrutura.
# Pacotes necessários
if (!requireNamespace("pacman", quietly = TRUE)) install.packages("pacman")
pacman::p_load(dplyr, stringr, ggplot2, plotly, tidyr, scales)

# Carregar dados (ajuste o encoding se necessário)
educacao_data <- read.csv2("MicroDadosPEFinal.csv", fileEncoding = "latin1", stringsAsFactors = FALSE,
                          na.strings = c("", "NA", "N/A", "na", "n/a", "NULL"))

# Rápido diagnóstico
str(educacao_data)
## 'data.frame':    9935 obs. of  426 variables:
##  $ NU_ANO_CENSO                  : int  2024 2024 2024 2024 2024 2024 2024 2024 2024 2024 ...
##  $ NO_REGIAO                     : chr  "Nordeste" "Nordeste" "Nordeste" "Nordeste" ...
##  $ CO_REGIAO                     : int  2 2 2 2 2 2 2 2 2 2 ...
##  $ NO_UF                         : chr  "Pernambuco" "Pernambuco" "Pernambuco" "Pernambuco" ...
##  $ SG_UF                         : chr  "PE" "PE" "PE" "PE" ...
##  $ CO_UF                         : int  26 26 26 26 26 26 26 26 26 26 ...
##  $ NO_MUNICIPIO                  : chr  "Abreu e Lima" "Abreu e Lima" "Abreu e Lima" "Abreu e Lima" ...
##  $ CO_MUNICIPIO                  : int  2600054 2600054 2600054 2600054 2600054 2600054 2600054 2600054 2600054 2600054 ...
##  $ NO_REGIAO_GEOG_INTERM         : chr  "Recife" "Recife" "Recife" "Recife" ...
##  $ CO_REGIAO_GEOG_INTERM         : int  2601 2601 2601 2601 2601 2601 2601 2601 2601 2601 ...
##  $ NO_REGIAO_GEOG_IMED           : chr  "Recife" "Recife" "Recife" "Recife" ...
##  $ CO_REGIAO_GEOG_IMED           : int  260001 260001 260001 260001 260001 260001 260001 260001 260001 260001 ...
##  $ NO_MESORREGIAO                : chr  "Metropolitana de Recife" "Metropolitana de Recife" "Metropolitana de Recife" "Metropolitana de Recife" ...
##  $ CO_MESORREGIAO                : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ NO_MICRORREGIAO               : chr  "Recife" "Recife" "Recife" "Recife" ...
##  $ CO_MICRORREGIAO               : int  17 17 17 17 17 17 17 17 17 17 ...
##  $ NO_DISTRITO                   : chr  "Abreu e Lima" "Abreu e Lima" "Abreu e Lima" "Abreu e Lima" ...
##  $ CO_DISTRITO                   : int  260005405 260005405 260005405 260005405 260005405 260005405 260005405 260005405 260005405 260005405 ...
##  $ NO_ENTIDADE                   : chr  "EM - CENTRO COMU E EDUC ISAAC MARTINS RODRIGUES" "EM - CENTRO COM EDUCACIONAL WILIBALDO DE FRANCA SEIXAS" "ESCOLA DE REFERENCIA EM ENSINO FUNDAMENTAL GENERAL ABREU E LIMA" "ESCOLA JESUS MENINO" ...
##  $ CO_ENTIDADE                   : int  26106450 26106477 26106582 26106590 26106612 26106620 26106647 26106655 26106671 26106680 ...
##  $ TP_DEPENDENCIA                : int  3 3 2 4 2 4 3 3 3 3 ...
##  $ TP_CATEGORIA_ESCOLA_PRIVADA   : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ TP_LOCALIZACAO                : int  1 1 1 1 1 1 2 1 1 2 ...
##  $ TP_LOCALIZACAO_DIFERENCIADA   : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ DS_ENDERECO                   : chr  "3ª TRAVESSA TRINTA E UM DE MARCO" "AVENIDA A" "AV DUQUE DE CAXIAS" "RUA CENTO E TRINTA E OITO" ...
##  $ NU_ENDERECO                   : chr  "S/N" "S/N" "660" "46" ...
##  $ DS_COMPLEMENTO                : chr  NA NA NA NA ...
##  $ NO_BAIRRO                     : chr  "PLANALTO" "CAETES II" "CENTRO" "CAETES I" ...
##  $ CO_CEP                        : int  53550798 53540010 53510050 53530240 53590000 53525030 53590000 53570564 53570720 53580365 ...
##  $ NU_DDD                        : int  81 81 81 81 81 81 81 81 81 81 ...
##  $ NU_TELEFONE                   : int  NA 985350133 31812930 35422467 31814898 35421128 999247440 NA 87761925 984472988 ...
##  $ TP_SITUACAO_FUNCIONAMENTO     : int  1 1 1 2 1 2 1 1 1 1 ...
##  $ CO_ORGAO_REGIONAL             : int  3 3 3 3 3 3 3 3 3 3 ...
##  $ DT_ANO_LETIVO_INICIO          : chr  "05FEB2024:00:00:00" "05FEB2024:00:00:00" "01FEB2024:00:00:00" NA ...
##  $ DT_ANO_LETIVO_TERMINO         : chr  "20DEC2024:00:00:00" "20DEC2024:00:00:00" "20DEC2024:00:00:00" NA ...
##  $ IN_VINCULO_SECRETARIA_EDUCACAO: int  1 1 1 NA 1 NA 1 1 1 1 ...
##  $ IN_VINCULO_SEGURANCA_PUBLICA  : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_VINCULO_SECRETARIA_SAUDE   : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_VINCULO_OUTRO_ORGAO        : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_PODER_PUBLICO_PARCERIA     : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ TP_PODER_PUBLICO_PARCERIA     : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_TERMO_COLABORA  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_TERMO_FOMENTO   : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ACORDO_COOP     : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_PRESTACAO_SERV  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_COOP_TEC_FIN    : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_CONSORCIO_PUB   : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_MU_TERMO_COLAB  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_MU_TERMO_FOMENTO: int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_MU_ACORDO_COOP  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_MU_PREST_SERV   : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_MU_COOP_TEC_FIN : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_MU_CONSORCIO_PUB: int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ES_TERMO_COLAB  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ES_TERMO_FOMENTO: int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ES_ACORDO_COOP  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ES_PREST_SERV   : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ES_COOP_TEC_FIN : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_FORMA_CONT_ES_CONSORCIO_PUB: int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIVADA_EMP    : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIVADA_ONG    : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIVADA_OSCIP  : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIV_ONG_OSCIP : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIVADA_SIND   : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIVADA_SIST_S : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_MANT_ESCOLA_PRIVADA_S_FINS : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ NU_CNPJ_ESCOLA_PRIVADA        : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ NU_CNPJ_MANTENEDORA           : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ TP_REGULAMENTACAO             : int  2 1 1 NA 1 NA 1 1 1 1 ...
##  $ TP_RESPONSAVEL_REGULAMENTACAO : int  2 2 2 NA 2 NA 2 3 2 2 ...
##  $ CO_ESCOLA_SEDE_VINCULADA      : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ CO_IES_OFERTANTE              : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_LOCAL_FUNC_PREDIO_ESCOLAR  : int  1 1 1 NA 1 NA 1 1 1 1 ...
##  $ TP_OCUPACAO_PREDIO_ESCOLAR    : int  1 1 3 NA 1 NA 1 1 1 1 ...
##  $ IN_LOCAL_FUNC_SOCIOEDUCATIVO  : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_LOCAL_FUNC_UNID_PRISIONAL  : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_LOCAL_FUNC_PRISIONAL_SOCIO : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_LOCAL_FUNC_GALPAO          : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ TP_OCUPACAO_GALPAO            : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IN_LOCAL_FUNC_SALAS_OUTRA_ESC : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_LOCAL_FUNC_OUTROS          : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_PREDIO_COMPARTILHADO       : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_AGUA_POTAVEL               : int  1 1 1 NA 1 NA 1 1 1 1 ...
##  $ IN_AGUA_REDE_PUBLICA          : int  1 1 1 NA 1 NA 0 1 1 1 ...
##  $ IN_AGUA_POCO_ARTESIANO        : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_AGUA_CACIMBA               : int  0 0 0 NA 0 NA 1 0 0 1 ...
##  $ IN_AGUA_FONTE_RIO             : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_AGUA_INEXISTENTE           : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_AGUA_CARRO_PIPA            : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_ENERGIA_REDE_PUBLICA       : int  1 1 1 NA 1 NA 1 1 1 1 ...
##  $ IN_ENERGIA_GERADOR_FOSSIL     : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_ENERGIA_RENOVAVEL          : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_ENERGIA_INEXISTENTE        : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_ESGOTO_REDE_PUBLICA        : int  0 0 0 NA 1 NA 0 0 1 0 ...
##  $ IN_ESGOTO_FOSSA_SEPTICA       : int  0 0 0 NA 0 NA 1 1 0 0 ...
##  $ IN_ESGOTO_FOSSA_COMUM         : int  1 1 1 NA 0 NA 0 0 0 1 ...
##  $ IN_ESGOTO_FOSSA               : int  1 1 1 NA 0 NA 1 1 0 1 ...
##  $ IN_ESGOTO_INEXISTENTE         : int  0 0 0 NA 0 NA 0 0 0 0 ...
##  $ IN_LIXO_SERVICO_COLETA        : int  1 1 1 NA 1 NA 1 1 1 1 ...
##   [list output truncated]
head(educacao_data)

2 - Limpeza e padronização de dados

Transformações aplicadas para normalizar textos, indicadores e tratar valores missing.

library(stringr)

# Função para limpar textos: trim, collapse spaces, normalizar acentos e Title Case
clean_text <- function(x) {
  x <- as.character(x)
  x[trimws(x) == ""] <- NA
  x[x %in% c("NA", "N/A", "na", "n/a", "NULL")] <- NA
  x <- ifelse(is.na(x), NA, trimws(x))
  x <- ifelse(is.na(x), NA, str_squish(x))
  idx <- which(!is.na(x))
  if (length(idx) > 0) {
    res_utf8 <- iconv(x[idx], from = "UTF-8", to = "ASCII//TRANSLIT")
    na_idx <- which(is.na(res_utf8) & !is.na(x[idx]))
    if (length(na_idx) > 0) {
      res_lat <- iconv(x[idx][na_idx], from = "latin1", to = "ASCII//TRANSLIT")
      res_utf8[na_idx] <- res_lat
    }
    x[idx] <- res_utf8
  }
  x <- ifelse(is.na(x), NA, tolower(x))
  x <- ifelse(is.na(x), NA, str_to_title(x))
  x
}

# Função para normalizar colunas indicador (IN_)
clean_indicator <- function(x) {
  if (is.numeric(x)) {
    x <- as.integer(x)
    return(x)
  }
  x <- as.character(x)
  x[trimws(tolower(x)) == ""] <- NA
  x[ x %in% c("NA", "N/A", "na", "n/a", "NULL") ] <- NA
  x2 <- tolower(trimws(x))
  x_out <- ifelse(is.na(x2), NA,
                  ifelse(x2 %in% c("1","sim","s","yes","y","true","t"), 1,
                         ifelse(x2 %in% c("0","nao","não","n","no","false","f"), 0, NA)))
  as.integer(x_out)
}

# Aplicar limpeza às colunas de texto alvo
text_cols <- c("NO_MESORREGIAO", "NO_MUNICIPIO")
text_cols_present <- intersect(text_cols, names(educacao_data))
if (length(text_cols_present) > 0) {
  educacao_data <- educacao_data %>%
    mutate(across(all_of(text_cols_present), clean_text))
}

# Limpar demais colunas caractere
char_cols <- names(educacao_data)[sapply(educacao_data, is.character)]
other_char_cols <- setdiff(char_cols, text_cols_present)
if (length(other_char_cols) > 0) {
  educacao_data <- educacao_data %>%
    mutate(across(all_of(other_char_cols), ~ {x <- as.character(.x); x[trimws(x) == ""] <- NA; str_squish(trimws(x))}))
}

# Normalizar colunas de indicador que começam com 'IN_'
indicator_cols <- names(educacao_data)[grepl("^IN_", names(educacao_data))]
if (length(indicator_cols) > 0) {
  educacao_data <- educacao_data %>%
    mutate(across(all_of(indicator_cols), clean_indicator))
}

# Resumo rápido após limpeza
cat("Limpeza concluída. Resumo de NA por coluna:\n")
## Limpeza concluída. Resumo de NA por coluna:
print(sort(sapply(educacao_data, function(x) sum(is.na(x))), decreasing = TRUE)[1:20])
##           CO_LINGUA_INDIGENA_3       CO_ESCOLA_SEDE_VINCULADA 
##                           9935                           9932 
##           CO_LINGUA_INDIGENA_2   IN_FORMA_CONT_ES_TERMO_COLAB 
##                           9930                           9925 
## IN_FORMA_CONT_ES_TERMO_FOMENTO   IN_FORMA_CONT_ES_ACORDO_COOP 
##                           9925                           9925 
##    IN_FORMA_CONT_ES_PREST_SERV  IN_FORMA_CONT_ES_COOP_TEC_FIN 
##                           9925                           9925 
## IN_FORMA_CONT_ES_CONSORCIO_PUB           CO_LINGUA_INDIGENA_1 
##                           9925                           9923 
##               CO_IES_OFERTANTE             TP_OCUPACAO_GALPAO 
##                           9918                           9900 
##             TP_INDIGENA_LINGUA   IN_FORMA_CONT_MU_TERMO_COLAB 
##                           9774                           9761 
## IN_FORMA_CONT_MU_TERMO_FOMENTO   IN_FORMA_CONT_MU_ACORDO_COOP 
##                           9761                           9761 
##    IN_FORMA_CONT_MU_PREST_SERV  IN_FORMA_CONT_MU_COOP_TEC_FIN 
##                           9761                           9761 
## IN_FORMA_CONT_MU_CONSORCIO_PUB      TP_PODER_PUBLICO_PARCERIA 
##                           9761                           9751
cat("Valores únicos (amostra) para 'NO_MUNICIPIO':\n")
## Valores únicos (amostra) para 'NO_MUNICIPIO':
if ("NO_MUNICIPIO" %in% names(educacao_data)) print(unique(na.omit(head(educacao_data$NO_MUNICIPIO, 50))))
## [1] "Abreu E Lima"

3 - Seleção e Preparação das Variáveis

Aqui escolhemos variáveis de interesse para análises (ex.: indicadores de infraestrutura) e preparamos possíveis métricas/composições.

# Exemplo: seleção de features de infraestrutura
infra_features <- c(
  "IN_LABORATORIO_CIENCIAS",
  "IN_BIBLIOTECA",
  "IN_REFEITORIO",
  "IN_QUADRA_ESPORTES",
  "IN_INTERNET",
  "IN_COZINHA",
  "IN_PISCINA"
)
infra_present <- intersect(infra_features, names(educacao_data))
cat("Colunas de infraestrutura detectadas:", paste(infra_present, collapse = ", "), "\n")
## Colunas de infraestrutura detectadas: IN_LABORATORIO_CIENCIAS, IN_BIBLIOTECA, IN_REFEITORIO, IN_QUADRA_ESPORTES, IN_INTERNET, IN_COZINHA, IN_PISCINA
# Criar resumo por feature
if (length(infra_present) > 0) {
  df_feat <- lapply(infra_present, function(f) {
    vals <- educacao_data[[f]]
    present <- sum(vals == 1, na.rm = TRUE)
    non_na <- sum(!is.na(vals))
    percent <- ifelse(non_na > 0, present / non_na, NA)
    data.frame(feature = f, present = present, non_na = non_na, percent = percent)
  }) %>% bind_rows() %>% arrange(desc(present))
  df_feat$label <- recode(df_feat$feature,
                          IN_LABORATORIO_CIENCIAS = "Laboratório de Ciências",
                          IN_BIBLIOTECA = "Biblioteca",
                          IN_REFEITORIO = "Refeitório",
                          IN_QUADRA_ESPORTES = "Quadra de Esportes",
                          IN_INTERNET = "Internet",
                          IN_COZINHA = "Cozinha",
                          IN_PISCINA = "Piscina",
                          .default = df_feat$feature)
  print(df_feat)
}
##                   feature present non_na    percent                   label
## 1             IN_INTERNET    7509   8013 0.93710221                Internet
## 2              IN_COZINHA    6892   8013 0.86010233                 Cozinha
## 3           IN_BIBLIOTECA    3798   8013 0.47397978              Biblioteca
## 4      IN_QUADRA_ESPORTES    2400   8013 0.29951329      Quadra de Esportes
## 5           IN_REFEITORIO    2164   8013 0.27006115              Refeitório
## 6 IN_LABORATORIO_CIENCIAS    1091   8013 0.13615375 Laboratório de Ciências
## 7              IN_PISCINA     297   8013 0.03706477                 Piscina

4 - Análise exploratória (plots)

Vários plots para entender a distribuição espacial (por município/mesorregião) e infraestrutura.

# 1) Contagem por município (top N)
schools_by_muni <- educacao_data %>%
  filter(!is.na(NO_MUNICIPIO)) %>%
  group_by(NO_MUNICIPIO) %>%
  summarise(n_schools = n(), .groups = "drop") %>%
  arrange(desc(n_schools))

top_n <- 20
schools_by_muni_top <- head(schools_by_muni, top_n)

p_muni <- ggplot(schools_by_muni_top, aes(x = reorder(NO_MUNICIPIO, n_schools), y = n_schools)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(title = paste0("Top ", top_n, " municípios por número de escolas"), x = "Município", y = "Número de escolas") +
  theme_minimal()

p_muni

# 2) Contagem por mesorregião
schools_by_meso <- educacao_data %>%
  filter(!is.na(NO_MESORREGIAO)) %>%
  group_by(NO_MESORREGIAO) %>%
  summarise(n_schools = n(), .groups = "drop") %>%
  arrange(desc(n_schools))

p_meso <- ggplot(schools_by_meso, aes(x = reorder(NO_MESORREGIAO, n_schools), y = n_schools)) +
  geom_col(fill = "darkgreen") +
  coord_flip() +
  labs(title = "Escolas por Mesorregião", x = "Mesorregião", y = "Número de escolas") +
  theme_minimal()

p_meso

# 3) Gráficos de infraestrutura
if (length(infra_present) > 0) {
  df_feat <- df_feat
  p_infra_overall <- ggplot(df_feat, aes(x = reorder(label, present), y = present)) +
    geom_col(fill = "steelblue") +
    coord_flip() +
    geom_text(aes(label = paste0(present, " (", scales::percent(percent, accuracy = 0.1), ")")),
              hjust = -0.05, size = 3) +
    labs(title = "Número de escolas por tipo de infraestrutura",
         subtitle = "(valor absoluto e % sobre registros não-missing)", x = "Infraestrutura", y = "Número de escolas") +
    theme_minimal() +
    scale_y_continuous(expand = expansion(mult = c(0, 0.15)))

  p_infra_overall

  # Distribuição do número de infraestruturas presentes por escola
  infra_matrix <- educacao_data[, infra_present, drop = FALSE]
  infra_count <- rowSums(infra_matrix == 1, na.rm = TRUE)
  df_count <- data.frame(n_resources = infra_count)
  p_infra_count <- ggplot(df_count, aes(x = n_resources)) +
    geom_bar(fill = "coral") +
    labs(title = "Distribuição do número de infraestruturas presentes por escola",
         x = "Número de infraestruturas (marcadas)", y = "Número de escolas") +
    theme_minimal()

  p_infra_count
}