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.
| 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.
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:
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:
NULL ou PrivacySuppressed.UNITID.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.
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"
)
)| 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.
| 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.
| 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_perfilO 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.
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.
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_retencaoA 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.
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_seletividadeInstituiçõ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.
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.
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_areasA 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.
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.
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:
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.
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.