1 Introdução

O ensino superior costuma ser apresentado como um investimento: o estudante paga mensalidades, assume custos de permanência, dedica anos de estudo e espera obter melhores oportunidades de renda no futuro. Porém, esse investimento não é igual para todos. Instituições públicas, privadas sem fins lucrativos e privadas com fins lucrativos podem apresentar diferenças expressivas de custo, seletividade, permanência, conclusão de curso, endividamento e renda posterior dos estudantes.

O problema investigado neste relatório é: quais características das instituições de ensino superior estão associadas a melhores resultados acadêmicos e financeiros para os estudantes? A pergunta é relevante porque ajuda estudantes, famílias, gestores educacionais e formuladores de políticas públicas a comparar instituições de forma mais informada. Não basta saber se uma faculdade é cara ou famosa; é necessário observar se os estudantes permanecem, concluem o curso, assumem dívidas sustentáveis e alcançam renda compatível após a formação.

Para responder a essa questão, utilizo dados públicos do College Scorecard, mantido pelo U.S. Department of Education. A análise combina dados institucionais, como localização, tipo de controle, custo, taxa de admissão, taxa de conclusão e renda mediana após 10 anos, com dados por área de estudo, como dívida mediana e ganhos um ano após a conclusão. A metodologia adotada é exploratória: importação de dados diretamente da web, seleção de variáveis relevantes, padronização de nomes e tipos, tratamento de valores ausentes, criação de variáveis derivadas, junção entre tabelas e construção de gráficos e tabelas para identificar padrões.

A análise não pretende provar causalidade. O objetivo é construir uma narrativa baseada em evidências descritivas: entender como custo, seletividade, tipo de instituição e área de estudo se relacionam com indicadores de resultado. Isso pode auxiliar potenciais usuários da análise a fazer perguntas melhores antes de escolher uma instituição ou avaliar políticas educacionais.

2 Pacotes requeridos

Pacote Propósito no projeto
tidyverse Importação, transformação, sumarização e visualização dos dados com readr, dplyr, tidyr, stringr e ggplot2.
janitor Padronização dos nomes das variáveis para formato limpo e consistente.
lubridate Registro e manipulação simples de datas usadas no relatório.
scales Formatação de valores monetários, percentuais e números nos gráficos e tabelas.
DT Criação de tabelas interativas pesquisáveis e ordenáveis.
plotly Conversão de gráficos ggplot2 em visualizações interativas.
knitr Geração de tabelas e integração entre texto, código e resultados.
kableExtra Melhoria visual de tabelas estáticas no HTML final.
htmltools Ajustes de elementos HTML usados nas tabelas interativas.

Todos os pacotes são carregados no início do documento para que a análise seja reprodutível. O bloco de configuração também instala pacotes ausentes automaticamente, o que facilita a renderização em uma instalação nova do RStudio.

3 Preparação dos dados

3.1 Fonte original

Os dados foram obtidos no portal oficial do College Scorecard, mantido pelo U.S. Department of Education. A atualização citada no portal de dados utilizada aqui é de 23 de março de 2026.

Foram usadas duas bases:

  1. Most Recent Institution-Level Data: arquivo com informações agregadas por instituição de ensino superior.
  2. Most Recent Data by Field of Study: arquivo com informações por instituição, nível de credencial e área de estudo.

Esses dados foram originalmente coletados para aumentar a transparência sobre instituições de ensino superior nos Estados Unidos, permitindo comparar custos, matrícula, seletividade, permanência, conclusão, dívida estudantil, pagamento de empréstimos e ganhos posteriores. A base institucional contém milhares de instituições e centenas de variáveis. A base por área de estudo contém milhares de combinações entre instituição, curso, nível de credencial, dívida e renda.

Algumas peculiaridades importantes da fonte são:

  1. Muitos valores ausentes aparecem como campos vazios, NULL ou PrivacySuppressed.
  2. Algumas variáveis numéricas são importadas como texto por causa desses códigos especiais.
  3. Variáveis percentuais aparecem como proporções entre 0 e 1.
  4. O tipo de instituição é codificado numericamente e precisa ser transformado em categoria interpretável.
  5. A análise exige junção entre tabelas usando o identificador institucional UNITID.

3.2 Importação dos dados

dir.create("data/raw", recursive = TRUE, showWarnings = FALSE)

institution_url <- "https://ed-public-download.scorecard.network/downloads/Most-Recent-Cohorts-Institution_03232026.zip"
field_url <- "https://ed-public-download.scorecard.network/downloads/Most-Recent-Cohorts-Field-of-Study_03232026.zip"

institution_zip <- "data/raw/Most-Recent-Cohorts-Institution_03232026.zip"
field_zip <- "data/raw/Most-Recent-Cohorts-Field-of-Study_03232026.zip"

download_if_missing <- function(url, destination) {
  if (!file.exists(destination)) {
    download.file(url, destination, mode = "wb", quiet = TRUE)
  }
}

download_if_missing(institution_url, institution_zip)
download_if_missing(field_url, field_zip)

extract_first_csv <- function(zip_path, output_dir) {
  csv_name <- unzip(zip_path, list = TRUE) |>
    as_tibble() |>
    filter(str_detect(Name, "\\.csv$")) |>
    slice(1) |>
    pull(Name)

  output_path <- file.path(output_dir, basename(csv_name))
  if (!file.exists(output_path)) {
    unzip(zip_path, files = csv_name, exdir = output_dir, overwrite = TRUE)
    extracted_path <- file.path(output_dir, csv_name)
    if (!identical(normalizePath(extracted_path, mustWork = FALSE), normalizePath(output_path, mustWork = FALSE))) {
      file.rename(extracted_path, output_path)
    }
  }

  output_path
}

institution_csv <- extract_first_csv(institution_zip, "data/raw")
field_csv <- extract_first_csv(field_zip, "data/raw")

O código acima baixa os arquivos compactados apenas se eles ainda não existirem localmente. Isso torna a primeira execução mais demorada, mas as execuções seguintes ficam mais rápidas.

3.3 Seleção, limpeza e padronização

Antes de iniciar a limpeza, defini os códigos que devem ser tratados como valores ausentes. Na base original, valores faltantes podem aparecer de formas diferentes, como campo vazio, NULL ou PrivacySuppressed. Se esses códigos não forem informados na leitura, o R pode interpretar números como texto e prejudicar cálculos de média, mediana e proporção.

na_codes <- c("", "NA", "NULL", "PrivacySuppressed")

institution_raw <- readr::read_csv(
  institution_csv,
  na = na_codes,
  show_col_types = FALSE
)

field_raw <- readr::read_csv(
  field_csv,
  na = na_codes,
  show_col_types = FALSE
)

Em seguida, reduzi a base institucional para as variáveis ligadas à pergunta do projeto. A base completa possui muitas colunas, mas nem todas são necessárias para analisar custo, permanência, conclusão, dívida e renda. Também usei janitor::clean_names() para transformar nomes como ADM_RATE em adm_rate, facilitando a leitura do código.

institution <- institution_raw |>
  janitor::clean_names() |>
  select(any_of(c(
    "unitid", "instnm", "city", "stabbr", "region", "control", "locale", "highdeg", "preddeg",
    "adm_rate", "sat_avg", "ugds", "costt4_a", "tuitionfee_in", "tuitionfee_out",
    "avgfacsal", "pftfac", "pctpell", "c150_4", "ret_ft4", "debt_mdn",
    "md_earn_wne_p10", "rpy_3yr_rt", "curroper", "openadmp"
  )))

Depois converti as variáveis numéricas. Esse passo é importante porque algumas colunas vêm misturadas com códigos especiais de ausência. Por exemplo, uma coluna de dívida pode ter valores como 12000, campos vazios e valores suprimidos por privacidade. Ao usar parse_number(), mantemos apenas a parte numérica e transformamos o restante em NA, permitindo calcular medianas corretamente.

institution <- institution |>
  mutate(
    across(
      c(adm_rate, sat_avg, ugds, costt4_a, tuitionfee_in, tuitionfee_out,
        avgfacsal, pftfac, pctpell, c150_4, ret_ft4, debt_mdn,
        md_earn_wne_p10, rpy_3yr_rt),
      ~ readr::parse_number(as.character(.x))
    )
  )

Na sequência, traduzi códigos numéricos para categorias compreensíveis. Por exemplo, na base original control == 1 representa instituição pública, control == 2 representa privada sem fins lucrativos e control == 3 representa privada com fins lucrativos. Fazer essa tradução melhora a interpretação das tabelas e dos gráficos.

institution <- institution |>
  mutate(
    tipo_controle = case_when(
      control == 1 ~ "Publica",
      control == 2 ~ "Privada sem fins lucrativos",
      control == 3 ~ "Privada com fins lucrativos",
      TRUE ~ "Nao informado"
    ),
    regiao = case_when(
      region == 1 ~ "Nova Inglaterra",
      region == 2 ~ "Meio-Leste",
      region == 3 ~ "Grandes Lagos",
      region == 4 ~ "Plains",
      region == 5 ~ "Sudeste",
      region == 6 ~ "Sudoeste",
      region == 7 ~ "Montanhas Rochosas",
      region == 8 ~ "Extremo Oeste",
      region == 9 ~ "Territorios/EUA exterior",
      TRUE ~ "Nao informado"
    ),
    nivel_predominante = case_when(
      preddeg == 1 ~ "Certificado",
      preddeg == 2 ~ "Associado",
      preddeg == 3 ~ "Bacharelado",
      preddeg == 4 ~ "Pos-graduacao",
      TRUE ~ "Outro/nao classificado"
    )
  )

Também criei categorias analíticas novas. A variável porte resume o tamanho da instituição com base no número de estudantes de graduação (ugds). A variável seletividade transforma a taxa de admissão em faixas interpretáveis. Isso ajuda a comparar grupos, em vez de analisar apenas números soltos.

institution <- institution |>
  mutate(
    porte = case_when(
      is.na(ugds) ~ "Nao informado",
      ugds < 1000 ~ "Pequeno",
      ugds < 5000 ~ "Medio",
      ugds < 15000 ~ "Grande",
      TRUE ~ "Muito grande"
    ),
    seletividade = case_when(
      is.na(adm_rate) ~ "Nao informada",
      adm_rate < 0.30 ~ "Alta seletividade",
      adm_rate < 0.70 ~ "Seletividade moderada",
      TRUE ~ "Baixa seletividade"
    )
  )

Por fim, criei indicadores diretamente ligados ao problema do projeto. A variável custo_liquido_aprox usa o custo anual quando disponível e, na ausência dele, usa a mensalidade interna. A razão renda_dez_anos / custo_liquido_aprox gera a variável razao_renda_custo, que indica quanto a renda mediana após 10 anos representa em relação ao custo anual aproximado. Assim, se a renda for US$ 50.000 e o custo for US$ 25.000, a razão será 2, isto é, a renda equivale a duas vezes o custo anual aproximado.

institution <- institution |>
  mutate(
    custo_liquido_aprox = coalesce(costt4_a, tuitionfee_in),
    renda_dez_anos = md_earn_wne_p10,
    razao_renda_custo = renda_dez_anos / custo_liquido_aprox,
    taxa_conclusao = c150_4,
    taxa_retencao = ret_ft4,
    taxa_pagamento_emprestimo = rpy_3yr_rt
  ) |>
  filter(curroper == 1, !is.na(tipo_controle), !is.na(ugds), ugds > 0)

Na segunda base, por área de estudo, selecionei variáveis sobre curso, nível de credencial, dívida mediana e ganho mediano um ano após a conclusão. Como o College Scorecard pode alterar alguns nomes entre versões, usei any_of() e depois padronizei nomes equivalentes. Por exemplo, debt_all_stgp_any_mdn foi renomeada para debtmedian, deixando o restante do código mais claro.

field <- field_raw |>
  janitor::clean_names() |>
  select(any_of(c(
    "unitid", "instnm", "cipcode", "cipdesc", "credlev", "creddesc",
    "debtmedian", "debt_all_stgp_any_mdn", "earn_mdn_hi_1yr", "earn_mdn_1yr",
    "count_ed", "ipedscount1", "count_nwne_1yr", "earn_count_nwne_hi_1yr"
  ))) |>
  rename(
    debtmedian = any_of(c("debtmedian", "debt_all_stgp_any_mdn")),
    count_ed = any_of(c("count_ed", "ipedscount1")),
    count_nwne_1yr = any_of(c("count_nwne_1yr", "earn_count_nwne_hi_1yr"))
  ) |>
  mutate(earn_mdn_hi_1yr = coalesce(earn_mdn_hi_1yr, earn_mdn_1yr))

Depois tratei os tipos e padronizei textos. A descrição da área de estudo foi formatada com iniciais maiúsculas para melhorar a apresentação nas tabelas. Também criei retorno_curto_prazo, calculado como ganho mediano um ano após a conclusão dividido pela dívida mediana. A ideia é simples: quanto maior esse valor, maior o ganho inicial em relação à dívida assumida.

field <- field |>
  mutate(
    across(any_of(c("debtmedian", "earn_mdn_hi_1yr", "count_ed", "count_nwne_1yr")),
           ~ readr::parse_number(as.character(.x))),
    cipdesc = str_to_title(cipdesc),
    creddesc = str_to_sentence(creddesc),
    retorno_curto_prazo = earn_mdn_hi_1yr / debtmedian
  ) |>
  filter(!is.na(unitid), !is.na(cipdesc))

Como a base por área de estudo possui várias linhas para a mesma instituição, resumi essas informações por unitid. Assim, cada instituição passa a ter um número de áreas de estudo, uma dívida mediana por área, um ganho mediano por área e um retorno mediano por área. Esse resumo permite juntar a base de cursos com a base institucional sem duplicar instituições.

field_summary <- field |>
  group_by(unitid) |>
  summarise(
    n_areas_estudo = n_distinct(cipdesc),
    divida_mediana_area = median(debtmedian, na.rm = TRUE),
    ganho_mediano_1ano_area = median(earn_mdn_hi_1yr, na.rm = TRUE),
    retorno_mediano_area = median(retorno_curto_prazo, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(across(where(is.numeric), ~ if_else(is.infinite(.x), NA_real_, .x)))

O último passo da preparação foi unir as bases. A chave de junção é unitid, que identifica cada instituição no College Scorecard. Após a união, criei uma classificação simples de resultado, separando instituições acima ou abaixo das medianas de conclusão e retorno financeiro. Essa variável será usada apenas como apoio interpretativo, não como ranking oficial.

college <- institution |>
  left_join(field_summary, by = "unitid") |>
  mutate(
    classificacao_resultado = case_when(
      taxa_conclusao >= median(taxa_conclusao, na.rm = TRUE) &
        razao_renda_custo >= median(razao_renda_custo, na.rm = TRUE) ~ "Conclusao e retorno acima da mediana",
      taxa_conclusao >= median(taxa_conclusao, na.rm = TRUE) ~ "Conclusao acima da mediana",
      razao_renda_custo >= median(razao_renda_custo, na.rm = TRUE) ~ "Retorno acima da mediana",
      TRUE ~ "Abaixo das medianas analisadas"
    )
  )

3.4 Tamanho e aparência da base final

Base Linhas Colunas
Institucional original 6,322 3,308
Areas de estudo original 227,980 178
Base final limpa e unida 5,481 41

A base final atende ao requisito de ter mais de 5 mil linhas e mais de 10 colunas após limpeza e união dos dados. Para evitar imprimir uma tabela muito grande, abaixo está apenas uma amostra das variáveis centrais.

3.5 Resumo das variáveis de interesse

Indicador Valor
Instituicoes analisadas 5,481
Estados/territorios 59
Matricula mediana 564
Taxa mediana de admissao 78.0%
Taxa mediana de conclusao 52.3%
Taxa mediana de retencao 75.7%
Custo mediano aproximado US$24,902
Divida mediana US$9,500
Renda mediana apos 10 anos US$40,740
Razao renda/custo mediana 2

O conjunto limpo combina variáveis categóricas (tipo_controle, regiao, porte, seletividade), variáveis numéricas (ugds, custo_liquido_aprox, debt_mdn, renda_dez_anos) e variáveis proporcionais (adm_rate, taxa_conclusao, taxa_retencao, taxa_pagamento_emprestimo). Essa diversidade permite investigar o problema por vários ângulos, sem depender de uma única medida de desempenho.

4 Análise exploratória dos dados

4.1 Perfil das instituições analisadas

Tipo de instituicao Quantidade Percentual
Privada com fins lucrativos 2055 37.5%
Publica 1888 34.4%
Privada sem fins lucrativos 1538 28.1%
g_perfil <- perfil |>
  ggplot(aes(x = reorder(tipo_controle, n), y = n, fill = tipo_controle)) +
  geom_col(width = 0.72, show.legend = FALSE) +
  coord_flip() +
  scale_y_continuous(labels = scales::comma) +
  labs(
    title = "Distribuicao das instituicoes por tipo de controle",
    subtitle = "Instituicoes publicas, privadas sem fins lucrativos e privadas com fins lucrativos aparecem em proporcoes distintas",
    x = NULL,
    y = "Numero de instituicoes"
  )

g_perfil

O primeiro resultado mostra que a estrutura do ensino superior analisado não é homogênea. Existem instituições com modelos de governança diferentes, e isso justifica comparar os indicadores por tipo de controle, em vez de tratar todas as instituições como se pertencessem ao mesmo grupo.

4.2 Custo, renda e tipo de instituição

resumo_controle <- college |>
  group_by(tipo_controle) |>
  summarise(
    instituicoes = n(),
    matricula_mediana = median(ugds, na.rm = TRUE),
    custo_mediano = median(custo_liquido_aprox, na.rm = TRUE),
    divida_mediana = median(debt_mdn, na.rm = TRUE),
    renda_mediana_10anos = median(renda_dez_anos, na.rm = TRUE),
    conclusao_mediana = median(taxa_conclusao, na.rm = TRUE),
    retencao_mediana = median(taxa_retencao, na.rm = TRUE),
    razao_renda_custo_mediana = median(razao_renda_custo, na.rm = TRUE),
    .groups = "drop"
  ) |>
  arrange(desc(renda_mediana_10anos))

resumo_controle |>
  mutate(
    matricula_mediana = scales::comma(matricula_mediana, accuracy = 1),
    across(c(custo_mediano, divida_mediana, renda_mediana_10anos), ~ scales::dollar(.x, prefix = "US$", accuracy = 1)),
    across(c(conclusao_mediana, retencao_mediana), ~ scales::percent(.x, accuracy = 0.1)),
    razao_renda_custo_mediana = round(razao_renda_custo_mediana, 2)
  ) |>
  knitr::kable(col.names = c(
    "Tipo", "Instituicoes", "Matricula mediana", "Custo mediano", "Divida mediana",
    "Renda mediana 10 anos", "Conclusao mediana", "Retencao mediana", "Renda/custo"
  )) |>
  kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
Tipo Instituicoes Matricula mediana Custo mediano Divida mediana Renda mediana 10 anos Conclusao mediana Retencao mediana Renda/custo
Privada sem fins lucrativos 1538 808 US$44,211 US$16,000 US$53,187 57.4% 77.0% 1.12
Publica 1888 2,630 US$17,730 US$8,730 US$42,382 46.2% 75.6% 2.41
Privada com fins lucrativos 2055 149 US$30,868 US$8,265 US$29,833 50.0% 66.7% 1.33
plot_data <- college |>
  filter(
    !is.na(custo_liquido_aprox), !is.na(renda_dez_anos),
    custo_liquido_aprox > 0, renda_dez_anos > 0
  ) |>
  mutate(
    texto = paste0(
      "Instituicao: ", instnm,
      "<br>Tipo: ", tipo_controle,
      "<br>Estado: ", stabbr,
      "<br>Custo aprox.: ", scales::dollar(custo_liquido_aprox, prefix = "US$"),
      "<br>Renda 10 anos: ", scales::dollar(renda_dez_anos, prefix = "US$"),
      "<br>Conclusao: ", scales::percent(taxa_conclusao, accuracy = 0.1)
    )
  )

g_custo_renda <- plot_data |>
  ggplot(aes(
    x = custo_liquido_aprox,
    y = renda_dez_anos,
    color = tipo_controle,
    size = ugds,
    text = texto
  )) +
  geom_point(alpha = 0.55) +
  scale_x_continuous(labels = scales::dollar_format(prefix = "US$")) +
  scale_y_continuous(labels = scales::dollar_format(prefix = "US$")) +
  scale_size_continuous(labels = scales::comma, range = c(1.5, 9)) +
  labs(
    title = "Custo anual aproximado e renda mediana apos 10 anos",
    subtitle = "Cada ponto representa uma instituicao; o tamanho indica matricula de graduacao",
    x = "Custo anual aproximado",
    y = "Renda mediana apos 10 anos",
    color = "Tipo de instituicao",
    size = "Matricula"
  )

plotly::ggplotly(g_custo_renda, tooltip = "text")

O gráfico interativo permite observar que custo maior não significa automaticamente maior renda posterior. Existem instituições caras com renda futura moderada e instituições com custo menor que apresentam renda posterior competitiva. Esse é um ponto central para potenciais estudantes: o preço isolado pode ser uma medida enganosa de qualidade ou retorno.

4.3 Conclusão e retenção: permanência importa

g_retencao <- college |>
  filter(!is.na(taxa_conclusao), !is.na(taxa_retencao)) |>
  ggplot(aes(x = taxa_retencao, y = taxa_conclusao, color = tipo_controle)) +
  geom_point(alpha = 0.45) +
  geom_smooth(method = "lm", se = FALSE) +
  scale_x_continuous(labels = scales::percent) +
  scale_y_continuous(labels = scales::percent) +
  labs(
    title = "Instituicoes que retêm estudantes tendem a concluir mais",
    subtitle = "A relacao positiva sugere que permanencia no primeiro ano e conclusao estao conectadas",
    x = "Taxa de retencao em tempo integral",
    y = "Taxa de conclusao em 150% do tempo previsto",
    color = "Tipo de instituicao"
  )

g_retencao

A retenção aparece como um indicador de alerta. Instituições com baixa retenção tendem a apresentar baixa conclusão. Para estudantes e famílias, isso sugere que a decisão de matrícula deve considerar não apenas a entrada, mas também a capacidade da instituição de manter o estudante até a conclusão.

4.4 Seletividade e resultado financeiro

seletividade_resumo <- college |>
  filter(seletividade != "Nao informada") |>
  group_by(seletividade, tipo_controle) |>
  summarise(
    instituicoes = n(),
    renda_mediana_10anos = median(renda_dez_anos, na.rm = TRUE),
    conclusao_mediana = median(taxa_conclusao, na.rm = TRUE),
    custo_mediano = median(custo_liquido_aprox, na.rm = TRUE),
    .groups = "drop"
  )

g_seletividade <- seletividade_resumo |>
  ggplot(aes(x = seletividade, y = renda_mediana_10anos, fill = tipo_controle)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.68) +
  scale_y_continuous(labels = scales::dollar_format(prefix = "US$")) +
  labs(
    title = "Renda posterior por seletividade e tipo de instituicao",
    subtitle = "A seletividade pode sinalizar diferencas de perfil institucional, mas nao deve ser lida isoladamente",
    x = "Classe de seletividade",
    y = "Renda mediana apos 10 anos",
    fill = "Tipo"
  ) +
  theme(axis.text.x = element_text(angle = 15, hjust = 1))

g_seletividade

Instituições mais seletivas tendem a apresentar renda posterior maior, mas essa relação precisa ser interpretada com cautela. Parte do resultado pode refletir diferenças anteriores dos estudantes admitidos, localização, reputação institucional e áreas de estudo oferecidas. Ainda assim, a seletividade se mostra útil como variável de segmentação na análise exploratória.

4.5 Retorno por região

regiao_resumo <- college |>
  filter(!is.na(razao_renda_custo), is.finite(razao_renda_custo)) |>
  group_by(regiao) |>
  summarise(
    instituicoes = n(),
    renda_custo_mediana = median(razao_renda_custo, na.rm = TRUE),
    custo_mediano = median(custo_liquido_aprox, na.rm = TRUE),
    renda_mediana = median(renda_dez_anos, na.rm = TRUE),
    .groups = "drop"
  ) |>
  filter(instituicoes >= 20) |>
  arrange(desc(renda_custo_mediana))

regiao_resumo |>
  mutate(
    renda_custo_mediana = round(renda_custo_mediana, 2),
    across(c(custo_mediano, renda_mediana), ~ scales::dollar(.x, prefix = "US$", accuracy = 1))
  ) |>
  knitr::kable(col.names = c("Regiao", "Instituicoes", "Renda/custo mediana", "Custo mediano", "Renda mediana")) |>
  kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
Regiao Instituicoes Renda/custo mediana Custo mediano Renda mediana
Extremo Oeste 371 2.15 US$21,772 US$48,143
Sudoeste 277 2.14 US$21,064 US$42,375
Montanhas Rochosas 103 2.12 US$21,526 US$44,511
Plains 313 1.98 US$25,172 US$48,179
Grandes Lagos 452 1.81 US$28,016 US$47,858
Sudeste 818 1.77 US$24,756 US$40,280
Territorios/EUA exterior 89 1.71 US$14,487 US$24,632
Meio-Leste 494 1.61 US$30,414 US$54,516
Nova Inglaterra 184 1.33 US$42,258 US$55,213
regiao_resumo |>
  ggplot(aes(x = reorder(regiao, renda_custo_mediana), y = renda_custo_mediana)) +
  geom_col(fill = "#2c7fb8", width = 0.72) +
  coord_flip() +
  labs(
    title = "Razao entre renda apos 10 anos e custo por regiao",
    subtitle = "Valores maiores indicam renda posterior mais alta em relacao ao custo anual aproximado",
    x = NULL,
    y = "Renda/custo mediana"
  )

A comparação regional adiciona uma camada importante: o retorno financeiro não depende apenas da instituição, mas também do contexto econômico. Regiões com maior renda posterior podem refletir mercados de trabalho mais fortes, maior concentração de setores de alta remuneração ou composição diferente de cursos.

4.6 Áreas de estudo: dívida e ganho inicial

area_resumo <- field |>
  filter(!is.na(debtmedian), !is.na(earn_mdn_hi_1yr), debtmedian > 0, earn_mdn_hi_1yr > 0) |>
  group_by(cipdesc) |>
  summarise(
    registros = n(),
    divida_mediana = median(debtmedian, na.rm = TRUE),
    ganho_mediano_1ano = median(earn_mdn_hi_1yr, na.rm = TRUE),
    retorno_curto_prazo = median(retorno_curto_prazo, na.rm = TRUE),
    .groups = "drop"
  ) |>
  filter(registros >= 30) |>
  arrange(desc(retorno_curto_prazo))

area_resumo |>
  slice_head(n = 15) |>
  mutate(
    divida_mediana = scales::dollar(divida_mediana, prefix = "US$", accuracy = 1),
    ganho_mediano_1ano = scales::dollar(ganho_mediano_1ano, prefix = "US$", accuracy = 1),
    retorno_curto_prazo = round(retorno_curto_prazo, 2)
  ) |>
  DT::datatable(
    rownames = FALSE,
    options = list(pageLength = 10, scrollX = TRUE),
    caption = htmltools::tags$caption("Areas de estudo com maior ganho inicial em relacao a divida mediana")
  )
top_areas <- area_resumo |>
  slice_head(n = 12)

g_areas <- top_areas |>
  ggplot(aes(x = reorder(cipdesc, retorno_curto_prazo), y = retorno_curto_prazo, fill = ganho_mediano_1ano)) +
  geom_col(width = 0.72) +
  coord_flip() +
  scale_fill_viridis_c(labels = scales::dollar_format(prefix = "US$")) +
  labs(
    title = "Areas de estudo com melhor retorno financeiro inicial",
    subtitle = "Retorno calculado como ganho mediano um ano apos conclusao dividido pela divida mediana",
    x = NULL,
    y = "Ganho inicial / divida mediana",
    fill = "Ganho mediano"
  )

g_areas

A análise por área de estudo revela uma informação que a base institucional sozinha não mostra. O retorno pode variar fortemente conforme o campo de formação. Isso reforça que a escolha educacional envolve pelo menos duas decisões: onde estudar e o que estudar.

4.7 Instituições com melhor combinação de conclusão e retorno

ranking <- college |>
  filter(
    !is.na(taxa_conclusao), !is.na(razao_renda_custo), !is.na(renda_dez_anos),
    ugds >= 500, custo_liquido_aprox > 0
  ) |>
  mutate(
    score_conclusao = percent_rank(taxa_conclusao),
    score_retorno = percent_rank(razao_renda_custo),
    score_retencao = percent_rank(taxa_retencao),
    score_final = 0.45 * score_conclusao + 0.40 * score_retorno + 0.15 * score_retencao
  ) |>
  arrange(desc(score_final)) |>
  select(
    Instituicao = instnm,
    Estado = stabbr,
    Tipo = tipo_controle,
    Porte = porte,
    `Taxa de conclusao` = taxa_conclusao,
    `Taxa de retencao` = taxa_retencao,
    `Custo aproximado` = custo_liquido_aprox,
    `Renda 10 anos` = renda_dez_anos,
    `Renda/custo` = razao_renda_custo,
    `Score final` = score_final
  )

ranking |>
  slice_head(n = 25) |>
  DT::datatable(
    rownames = FALSE,
    filter = "top",
    options = list(pageLength = 10, scrollX = TRUE),
    caption = htmltools::tags$caption("Ranking exploratorio: conclusao, retencao e retorno financeiro")
  ) |>
  DT::formatPercentage(c("Taxa de conclusao", "Taxa de retencao", "Score final"), digits = 1) |>
  DT::formatCurrency(c("Custo aproximado", "Renda 10 anos"), currency = "US$", digits = 0) |>
  DT::formatRound("Renda/custo", digits = 2)

O ranking é uma construção exploratória e não deve ser interpretado como uma lista definitiva de melhores instituições. Ele combina três critérios: conclusão, retorno financeiro e retenção. O objetivo é demonstrar como variáveis podem ser criadas para transformar dados brutos em informação comparável. A ponderação escolhida dá maior peso à conclusão e ao retorno, pois esses dois elementos se conectam diretamente à pergunta do projeto.

5 Conclusões

Este projeto investigou como características das instituições de ensino superior se relacionam com resultados acadêmicos e financeiros dos estudantes. A análise foi construída a partir de dados públicos do College Scorecard, combinando informações institucionais com informações por área de estudo. O processo incluiu importação direta da web, limpeza, padronização de tipos, tratamento de valores ausentes, criação de variáveis e junção de bases.

Os principais insights encontrados foram:

  1. O ensino superior analisado é bastante heterogêneo: instituições públicas, privadas sem fins lucrativos e privadas com fins lucrativos apresentam perfis diferentes de custo, matrícula e resultados.
  2. Custo maior não implica necessariamente renda posterior maior. Comparar custo e renda é mais informativo do que observar apenas o preço da instituição.
  3. Retenção e conclusão parecem caminhar juntas: instituições que mantêm estudantes após o primeiro ano tendem a apresentar melhores taxas de conclusão.
  4. A seletividade está associada a diferenças de renda posterior, mas não pode ser interpretada como causa direta, pois pode refletir características prévias dos estudantes e da própria instituição.
  5. A área de estudo muda bastante o retorno financeiro inicial. Portanto, a escolha do curso é tão importante quanto a escolha da instituição.

Para potenciais estudantes e famílias, a implicação prática é clara: a decisão educacional deve considerar uma combinação de fatores. Uma instituição pode ser barata, mas ter baixa conclusão; outra pode ser cara, mas gerar renda posterior elevada; uma terceira pode ter bom resultado médio, mas oferecer cursos com retornos muito diferentes. Para gestores e formuladores de políticas, os dados indicam que permanência, conclusão e endividamento devem ser monitorados conjuntamente.

5.1 Limitações e melhorias futuras

A análise possui limitações. Primeiro, ela é descritiva e não estabelece causalidade. Instituições com maior renda posterior podem atender estudantes com perfil socioeconômico diferente, localização mais favorável ou maior concentração em áreas de alta remuneração. Segundo, alguns indicadores possuem valores ausentes ou suprimidos por privacidade, o que reduz a comparabilidade. Terceiro, o custo usado é uma aproximação e pode não representar exatamente o custo real enfrentado por cada estudante.

Em trabalhos futuros, seria possível melhorar a análise com modelos estatísticos controlando por região, porte, tipo de instituição e perfil dos estudantes. Também seria interessante comparar vários anos para avaliar tendências temporais, incluir variáveis de composição socioeconômica e construir uma aplicação Shiny para permitir que usuários filtrem instituições de acordo com seus próprios critérios.