Esta vinheta apresenta análises prontas do mercado de trabalho formal
usando o datacaged. Cada caso de uso é independente — você
pode executar apenas o trecho de interesse.
Dados necessários (executar uma vez):
# Novo CAGED 2023
caged_load(years = 2023, months = seq_len(12L), db_path = "caged.duckdb")
# Para casos com série histórica, adicionar:
caged_load(years = 2020:2022, months = seq_len(12L), db_path = "caged.duckdb")
caged_load(years = 2015:2019, months = seq_len(12L), db_path = "caged.duckdb")
caged_adjustments_load(years = 2015:2019, months = seq_len(12L), db_path = "caged.duckdb")Análise do fluxo mensal de entrada e saída no mercado de trabalho formal.
fluxo <- tbl(con, "caged_mov") |>
group_by(competenciamov) |>
summarise(
admissoes = sum(saldomovimentacao == 1, na.rm = TRUE),
desligamentos = sum(saldomovimentacao == -1, na.rm = TRUE),
saldo = sum(saldomovimentacao, na.rm = TRUE)
) |>
collect() |>
mutate(
data = ym(as.character(competenciamov))
) |>
arrange(data)ggplot(fluxo, aes(x = data)) +
geom_col(aes(y = admissoes), fill = "#2196F3", alpha = 0.8) +
geom_col(aes(y = -desligamentos), fill = "#F44336", alpha = 0.8) +
geom_line(aes(y = saldo), color = "#212121", linewidth = 1.2) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey40") +
scale_y_continuous(labels = label_number(scale = 1e-6, suffix = "M")) +
scale_x_date(date_breaks = "2 months", date_labels = "%b/%y") +
labs(
title = "Fluxo de Emprego Formal — Novo CAGED 2023",
subtitle = "Barras azuis = admissões | barras vermelhas = desligamentos | linha = saldo",
x = NULL,
y = "Movimentações",
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Identifica quais setores geraram e destruíram mais empregos formais no período.
# Dicionário de seções da CNAE 2.0
cnae_secoes <- tibble::tribble(
~secao, ~descricao,
"A", "Agricultura",
"B", "Indústrias Extrativas",
"C", "Indústria de Transformação",
"D", "Eletricidade e Gás",
"E", "Água e Saneamento",
"F", "Construção",
"G", "Comércio",
"H", "Transporte",
"I", "Alojamento e Alimentação",
"J", "Informação e Comunicação",
"K", "Atividades Financeiras",
"L", "Atividades Imobiliárias",
"M", "Profissionais e Técnicas",
"N", "Atividades Administrativas",
"O", "Adm. Pública",
"P", "Educação",
"Q", "Saúde",
"R", "Artes e Cultura",
"S", "Outras Atividades",
"T", "Serviços Domésticos",
"U", "Organismos Internacionais"
)
setor <- tbl(con, "caged_mov") |>
filter(!is.na(secao)) |>
group_by(secao) |>
summarise(saldo = sum(saldomovimentacao, na.rm = TRUE)) |>
collect() |>
left_join(cnae_secoes, by = "secao") |>
mutate(
descricao = coalesce(descricao, paste("Setor", secao)),
cor = if_else(saldo >= 0, "#2196F3", "#F44336")
) |>
arrange(saldo)ggplot(setor, aes(x = saldo, y = reorder(descricao, saldo), fill = cor)) +
geom_col(show.legend = FALSE) +
geom_vline(xintercept = 0, color = "grey30") +
scale_fill_identity() +
scale_x_continuous(labels = label_number(scale = 1e-3, suffix = "k")) +
labs(
title = "Saldo de Empregos por Setor Econômico — 2023",
x = "Saldo (mil vínculos)",
y = NULL,
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11) +
theme(panel.grid.major.y = element_blank())Visualização de qual UF e em qual mês o emprego formal foi mais aquecido.
# Dicionário de UFs (código IBGE -> sigla)
data("uf_codigos")
uf_mensal <- tbl(con, "caged_mov") |>
group_by(uf, competenciamov) |>
summarise(saldo = sum(saldomovimentacao, na.rm = TRUE)) |>
collect() |>
left_join(uf_codigos, by = c("uf" = "codigo")) |>
filter(!is.na(sigla)) |>
mutate(
data = ym(as.character(competenciamov)),
mes = format(data, "%b")
)ggplot(uf_mensal, aes(x = mes, y = reorder(sigla, saldo), fill = saldo)) +
geom_tile(color = "white", linewidth = 0.3) +
scale_fill_gradient2(
low = "#F44336",
mid = "white",
high = "#2196F3",
midpoint = 0,
labels = label_number(scale = 1e-3, suffix = "k")
) +
labs(
title = "Saldo de Empregos por UF e Mês — 2023",
x = NULL,
y = NULL,
fill = "Saldo",
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 10) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "right"
)Caracterização demográfica dos novos vínculos formais por sexo, escolaridade e raça/cor.
# Dicionários de categorias
grau_instrucao <- tibble::tribble(
~graudeinstrucao, ~escolaridade,
1L, "Analfabeto",
2L, "Fundamental incompleto",
3L, "Fundamental completo",
4L, "Médio incompleto",
5L, "Médio completo",
6L, "Superior incompleto",
7L, "Superior completo",
8L, "Mestrado",
9L, "Doutorado"
)
raca_dic <- tibble::tribble(
~racacor, ~raca,
1L, "Indígena",
2L, "Branca",
4L, "Preta",
6L, "Amarela",
8L, "Parda",
9L, "Não informada"
)
# Admissões por escolaridade e sexo
perfil_escol <- tbl(con, "caged_mov") |>
filter(saldomovimentacao == 1, !is.na(graudeinstrucao)) |>
group_by(graudeinstrucao, sexo) |>
summarise(n = n()) |>
collect() |>
left_join(grau_instrucao, by = "graudeinstrucao") |>
mutate(
sexo_label = if_else(sexo == 1, "Masculino", "Feminino"),
escolaridade = factor(escolaridade, levels = grau_instrucao$escolaridade)
)ggplot(perfil_escol, aes(x = escolaridade, y = n, fill = sexo_label)) +
geom_col(position = "dodge") +
scale_y_continuous(labels = label_number(scale = 1e-3, suffix = "k")) +
scale_fill_manual(values = c("Masculino" = "#1565C0", "Feminino" = "#AD1457")) +
labs(
title = "Admissões por Escolaridade e Sexo — 2023",
x = NULL,
y = "Admissões (mil)",
fill = NULL,
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11) +
theme(
axis.text.x = element_text(angle = 40, hjust = 1),
legend.position = "top"
)# Admissões por raça/cor
perfil_raca <- tbl(con, "caged_mov") |>
filter(saldomovimentacao == 1, !is.na(racacor)) |>
group_by(racacor) |>
summarise(n = n()) |>
collect() |>
left_join(raca_dic, by = "racacor") |>
mutate(pct = n / sum(n))ggplot(perfil_raca, aes(x = reorder(raca, n), y = n, fill = raca)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = percent(pct, accuracy = 0.1)), hjust = -0.1, size = 3.5) +
coord_flip() +
scale_y_continuous(
labels = label_number(scale = 1e-6, suffix = "M"),
expand = expansion(mult = c(0, 0.15))
) +
scale_fill_brewer(palette = "Set2") +
labs(
title = "Distribuição de Admissões por Raça/Cor — 2023",
x = NULL,
y = "Admissões",
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11)Análise dos salários declarados nas novas contratações.
salarios <- tbl(con, "caged_mov") |>
filter(
saldomovimentacao == 1,
!is.na(salario),
salario > 0,
salario < 30000 # remove outliers extremos
) |>
select(salario, sexo, graudeinstrucao) |>
collect() |>
mutate(
sexo_label = if_else(sexo == 1, "Masculino", "Feminino"),
log_salario = log10(salario)
)
# Percentis salariais
salarios |>
group_by(sexo_label) |>
summarise(
p10 = quantile(salario, 0.10),
mediana = median(salario),
media = mean(salario),
p90 = quantile(salario, 0.90)
)ggplot(salarios, aes(x = salario, fill = sexo_label)) +
geom_histogram(
aes(y = after_stat(density)),
bins = 60,
alpha = 0.6,
position = "identity"
) +
geom_vline(
data = salarios |>
group_by(sexo_label) |>
summarise(med = median(salario)),
aes(xintercept = med, color = sexo_label),
linewidth = 1.2, linetype = "dashed"
) +
scale_x_continuous(
labels = label_dollar(prefix = "R$", big.mark = ".", decimal.mark = ","),
limits = c(0, 10000)
) +
scale_fill_manual(values = c("Masculino" = "#1565C0", "Feminino" = "#AD1457")) +
scale_color_manual(values = c("Masculino" = "#0D47A1", "Feminino" = "#880E4F")) +
labs(
title = "Distribuição Salarial das Admissões por Sexo — 2023",
subtitle = "Linhas tracejadas indicam a mediana salarial de cada grupo",
x = "Salário de admissão (R$)",
y = "Densidade",
fill = NULL,
color = NULL,
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11) +
theme(legend.position = "top")Construção de uma série histórica longa unindo CAGED antigo (até 2019) e Novo CAGED (2020 em diante).
# CAGED antigo: competencia + saldomovimentacao
antigo <- tbl(con, "caged_antigo") |>
filter(!is.na(competencia)) |>
group_by(competencia) |>
summarise(saldo = sum(saldomovimentacao, na.rm = TRUE)) |>
collect() |>
mutate(serie = "CAGED Antigo")
# Ajustes: somados ao antigo para corrigir a série
ajustes <- tbl(con, "caged_ajustes") |>
filter(!is.na(competencia)) |>
group_by(competencia) |>
summarise(saldo_ajuste = sum(saldomovimentacao, na.rm = TRUE)) |>
collect()
antigo_corrigido <- left_join(antigo, ajustes, by = "competencia") |>
mutate(
saldo = saldo + coalesce(saldo_ajuste, 0L),
serie = "CAGED Antigo (corrigido)"
) |>
select(competencia, saldo, serie)
# Novo CAGED: competenciamov + saldomovimentacao
novo <- tbl(con, "caged_mov") |>
group_by(competencia = competenciamov) |>
summarise(saldo = sum(saldomovimentacao, na.rm = TRUE)) |>
collect() |>
mutate(serie = "Novo CAGED")
# União das séries
serie_longa <- bind_rows(antigo_corrigido, novo) |>
filter(!is.na(competencia)) |>
mutate(data = ym(as.character(competencia))) |>
filter(data >= ym("201501")) |>
arrange(data)ggplot(serie_longa, aes(x = data, y = saldo, color = serie)) +
geom_line(linewidth = 1) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey50") +
annotate("rect",
xmin = ym("202001"), xmax = ym("202012"),
ymin = -Inf, ymax = Inf,
alpha = 0.08, fill = "orange"
) +
annotate("text",
x = ym("202006"), y = Inf, vjust = 1.5,
label = "Pandemia\nCOVID-19", size = 3, color = "darkorange"
) +
scale_y_continuous(labels = label_number(scale = 1e-6, suffix = "M")) +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
scale_color_manual(values = c(
"CAGED Antigo (corrigido)" = "#795548",
"Novo CAGED" = "#1565C0"
)) +
labs(
title = "Saldo de Empregos Formais — Série Histórica 2015-2023",
subtitle = "Antigo CAGED corrigido pelos Ajustes + Novo CAGED",
x = NULL,
y = "Saldo mensal",
color = NULL,
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 12) +
theme(legend.position = "top")Taxa de rotatividade (desligamentos / estoque estimado) por setor econômico.
# cnae_secoes também definido aqui para que o Caso 7 possa ser executado de forma independente
cnae_secoes <- tibble::tribble(
~secao, ~descricao,
"A", "Agricultura", "B", "Indústrias Extrativas",
"C", "Ind. de Transformação", "D", "Eletricidade e Gás",
"E", "Água e Saneamento", "F", "Construção",
"G", "Comércio", "H", "Transporte",
"I", "Alojamento e Alimentação", "J", "Informação e Comunicação",
"K", "Atividades Financeiras", "L", "Atividades Imobiliárias",
"M", "Profissionais e Técnicas", "N", "Atividades Administrativas",
"O", "Adm. Pública", "P", "Educação",
"Q", "Saúde", "R", "Artes e Cultura",
"S", "Outras Atividades", "T", "Serviços Domésticos",
"U", "Organismos Internacionais"
)
rotatividade <- tbl(con, "caged_mov") |>
filter(!is.na(secao)) |>
group_by(secao, competenciamov) |>
summarise(
admissoes = sum(saldomovimentacao == 1, na.rm = TRUE),
desligamentos = sum(saldomovimentacao == -1, na.rm = TRUE)
) |>
collect() |>
left_join(cnae_secoes, by = "secao") |>
mutate(descricao = coalesce(descricao, paste("Setor", secao))) |>
group_by(descricao) |>
summarise(
total_admissoes = sum(admissoes),
total_desligamentos = sum(desligamentos),
rotatividade_pct = total_desligamentos / (total_admissoes + total_desligamentos)
) |>
arrange(desc(rotatividade_pct))ggplot(
rotatividade |> filter(!is.na(descricao)),
aes(x = total_admissoes, y = total_desligamentos,
size = rotatividade_pct, color = rotatividade_pct,
label = descricao)
) +
geom_point(alpha = 0.7) +
geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "grey50") +
ggrepel::geom_text_repel(size = 3, max.overlaps = 12) +
scale_x_continuous(labels = label_number(scale = 1e-6, suffix = "M")) +
scale_y_continuous(labels = label_number(scale = 1e-6, suffix = "M")) +
scale_size_continuous(range = c(2, 10), guide = "none") +
scale_color_gradient(low = "#2196F3", high = "#F44336", labels = percent) +
labs(
title = "Rotatividade por Setor Econômico — 2023",
subtitle = "Pontos acima da diagonal = mais desligamentos que admissões",
x = "Admissões",
y = "Desligamentos",
color = "Rotatividade",
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11)Análise de qual porte de empresa concentra mais geração de empregos.
porte_dic <- tibble::tribble(
~tamestabjan, ~porte,
0L, "Não informado",
1L, "Até 4 vínculos",
2L, "5 a 9",
3L, "10 a 19",
4L, "20 a 49",
5L, "50 a 99",
6L, "100 a 249",
7L, "250 a 499",
8L, "500 a 999",
9L, "1000 ou mais"
)
porte <- tbl(con, "caged_mov") |>
filter(!is.na(tamestabjan)) |>
group_by(tamestabjan) |>
summarise(
saldo = sum(saldomovimentacao, na.rm = TRUE),
admissoes = sum(saldomovimentacao == 1, na.rm = TRUE),
desligamentos = sum(saldomovimentacao == -1, na.rm = TRUE)
) |>
collect() |>
left_join(porte_dic, by = "tamestabjan") |>
mutate(porte = factor(porte, levels = porte_dic$porte))ggplot(porte |> filter(!is.na(porte)), aes(x = porte)) +
geom_col(aes(y = admissoes), fill = "#1565C0", alpha = 0.8) +
geom_col(aes(y = -desligamentos), fill = "#B71C1C", alpha = 0.8) +
geom_point(aes(y = saldo), color = "#212121", size = 3) +
geom_hline(yintercept = 0, color = "grey30") +
scale_y_continuous(labels = label_number(scale = 1e-6, suffix = "M")) +
labs(
title = "Movimentação por Porte de Estabelecimento — 2023",
subtitle = "Azul = admissões | Vermelho = desligamentos | Ponto = saldo",
x = "Faixa de tamanho (vínculos em jan/ano)",
y = "Movimentações",
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11) +
theme(axis.text.x = element_text(angle = 40, hjust = 1))Comparação do mercado de trabalho formal entre as 5 regiões do Brasil.
data("uf_codigos")
regiao_mensal <- tbl(con, "caged_mov") |>
group_by(uf, competenciamov) |>
summarise(saldo = sum(saldomovimentacao, na.rm = TRUE)) |>
collect() |>
left_join(uf_codigos, by = c("uf" = "codigo")) |>
filter(!is.na(regiao)) |>
group_by(regiao, competenciamov) |>
summarise(saldo = sum(saldo), .groups = "drop") |>
mutate(data = ym(as.character(competenciamov)))ggplot(regiao_mensal, aes(x = data, y = saldo, color = regiao, fill = regiao)) +
geom_line(linewidth = 1) +
geom_area(alpha = 0.1) +
geom_hline(yintercept = 0, linetype = "dashed", color = "grey40") +
scale_y_continuous(labels = label_number(scale = 1e-3, suffix = "k")) +
scale_x_date(date_breaks = "2 months", date_labels = "%b/%y") +
scale_color_brewer(palette = "Set1") +
scale_fill_brewer(palette = "Set1") +
facet_wrap(~regiao, scales = "free_y", ncol = 2) +
labs(
title = "Saldo de Empregos Formais por Região — 2023",
x = NULL,
y = "Saldo (mil vínculos)",
color = NULL,
fill = NULL,
caption = "Fonte: MTE/CAGED via datacaged"
) +
theme_minimal(base_size = 11) +
theme(
legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1),
strip.text = element_text(face = "bold")
)Conjunto de indicadores-chave em um painel consolidado.
# KPIs gerais do ano
kpis <- tbl(con, "caged_mov") |>
summarise(
admissoes = sum(saldomovimentacao == 1, na.rm = TRUE),
desligamentos = sum(saldomovimentacao == -1, na.rm = TRUE),
saldo = sum(saldomovimentacao, na.rm = TRUE),
salario_medio = round(mean(salario[salario > 0], na.rm = TRUE), 2),
pct_mulheres = round(mean(sexo == 3, na.rm = TRUE) * 100, 1),
pct_superior = round(mean(graudeinstrucao >= 7, na.rm = TRUE) * 100, 1)
) |>
collect()
cat(glue::glue("
=== PAINEL MERCADO DE TRABALHO FORMAL 2023 ===
Admissões: {scales::number(kpis$admissoes, big.mark = '.')}
Desligamentos: {scales::number(kpis$desligamentos, big.mark = '.')}
Saldo: {scales::number(kpis$saldo, big.mark = '.')}
Salário médio: R$ {scales::number(kpis$salario_medio, big.mark = '.', decimal.mark = ',')}
Mulheres (%): {kpis$pct_mulheres}%
Com nível sup.: {kpis$pct_superior}%
"))# Exportar qualquer resultado para Parquet via caged_to_parquet()
caged_to_parquet(
"caged.duckdb",
output_dir = "~/exports_caged"
)
# Ou particionado por UF para análises regionais
caged_to_parquet(
"caged.duckdb",
output_dir = "~/exports_caged",
tables = "caged_mov",
partition_by = "uf"
)Nota sobre os dados: os dados do CAGED estão sujeitos a revisões pelo MTE. Para análises que exigem precisão histórica, considere incorporar também os ajustes via
caged_adjustments_load().