1 Introdução
1.1 O problema
O burnout se caracteriza como um fenômeno ocupacional de esgotamento profissional reconhecido pela Organização Mundial da Saúde (OMS) desde 2019, tornando-se um dos temas mais discutidos dentro da saúde organizacional (World Health Organization (2019)). Em especial desde a pandemia de COVID-19, que mudou significativamente os padrões de trabalho, as discussões sobre o burnout se tornaram cada vez mais relevantes no que diz respeito à retenção de talentos, considerando que pesquisas apontam que o burnout é um fator importante na intenção de funcionários deixarem os seus empregos (Trinkenreich et al. (2023)).
Segundo estimativas de um estudo de referência do Instituto Americano de Estresse, o estresse ocupacional custa às empresas norte-americanas mais de US$ 300 bilhões por ano em fatores como rotatividade e falta de desempenho (American Institute of Stress (2024)). No Brasil, o burnout foi reconhecido na portaria GM/MS Nº 1.999 como doença ocupacional em 2022, impondo responsabilidades legais crescentes às empresas (Brasil. Ministério da Saúde (2023)).
Tais questões levantam a seguinte pergunta de pesquisa:
Quais fatores organizacionais e individuais estão mais associados ao risco de burnout, e como eles se combinam para elevar a probabilidade de um funcionário deixar a empresa?
Responder a esta pergunta se torna relevante para: equipes e gestores de RH, que precisam identificar perfis de risco antes que o funcionário solicite demissão; executivos que necessitam entender quais departamentos e cargos concentram maior esgotamento; e pesquisadores interessados nos fatores comportamentais e organizacionais do burnout em larga escala.
1.2 Abordagem e metodologia
Para responder à pergunta de pesquisa, foram conduzidas duas etapas de análise.
Na primeira etapa, o Synthetic Employee Dataset, disponibilizado publicamente no Hugging Face com 850.000 perfis sintéticos de funcionários, foi importado, limpo e organizado. O dataset contém variáveis numéricas (scores de estresse, carga de trabalho, desempenho), categóricas (departamento, cargo, arquétipo comportamental), booleanas (saída da empresa) e textuais (feedbacks). A limpeza incluiu padronização de nomes, conversão de tipos, tratamento de valores ausentes, desaninhamento de listas de habilidades e criação de variáveis derivadas, entre elas, um índice de engajamento e um score de desequilíbrio esforço-recompensa inspirado no modelo de Siegrist (1996).
Na segunda etapa, apresentada neste documento,
conduz-se a análise exploratória dos dados (EDA). Primeiro examina-se a
distribuição global do burnout; em seguida investiga-se como ele varia
entre departamentos, níveis de cargo e perfis comportamentais; depois
explora-se a relação entre burnout e as variáveis de esforço (horas
extras, carga de trabalho) e recompensa (salário, satisfação); e por fim
constrói-se um perfil do funcionário em risco severo de saída por
burnout. Os resultados são apresentados por meio de gráficos estáticos,
tabelas interativas (pacote DT) e um gráfico interativo
(pacote plotly).
2 Pacotes necessários
library(tidyverse) # Manipulação e visualização de dados (dplyr, ggplot2, etc.)
library(janitor) # Padronização de nomes de colunas
library(arrow) # Leitura de arquivos no formato Parquet
library(knitr) # Geração de tabelas formatadas no relatório
library(kableExtra) # Estilização de tabelas estáticas
library(skimr) # Sumários estatísticos compactos
library(plotly) # Gráficos interativos
library(DT) # Tabelas interativas com busca e ordenação
library(jsonlite) # Carregamento de dados em JSON
3 Preparação dos dados
3.1 Fonte e descrição
Os dados foram obtidos do repositório público do Hugging Face:
BrotherTony. (2025). Synthetic Employee Dataset: 800K+ Records for HR Analytics. Hugging Face. https://huggingface.co/datasets/BrotherTony/employee-burnout-turnover-prediction-800k
3.1.1 Contexto e propósito original
O dataset corresponde a um conjunto de dados sintéticos, construído com base em padrões observados em ambientes corporativos reais. Seu propósito original é servir como base para modelos preditivos de burnout e análises de contextos de RH.
3.1.2 Características do dataset bruto
| Atributo | Valor |
|---|---|
| Total de registros | ~850.000 |
| Número de variáveis | 31 |
| Formato original | Parquet/Arquivo direto |
| Idioma | Inglês |
| Licença | CC-BY-NC-ND 4.0 (uso permitido em contexto não comercial e com atribuição) |
3.1.3 Variáveis originais
| Variável | Tipo | Descrição |
|---|---|---|
| employee_id | character | Identificador único |
| role | character | Cargo (diversas variações) |
| job_level | character | Nível: Entry, Mid, Senior, Manager, Lead |
| department | character | Departamento (38 divisões) |
| tenure_months | integer | Tempo de empresa em meses |
| salary | double | Salário anual em USD |
| performance_score | double | Score de desempenho (0–1) |
| satisfaction_score | double | Score de satisfação (0–1) |
| workload_score | double | Carga de trabalho (0–1) |
| team_sentiment | double | Sentimento da equipe (0–1) |
| recent_feedback | character | Texto livre de avaliação do funcionário |
| communication_patterns | character | Estilo de comunicação baseado em persona |
| project_completion_rate | double | Taxa de conclusão de projetos (0–1) |
| overtime_hours | double | Horas extras |
| training_participation | double | Participação em treinamentos (0–1) |
| collaboration_score | double | Score de colaboração (0–1) |
| technical_skills | list | Habilidades técnicas por funcionário |
| soft_skills | list | Habilidades interpessoais por funcionário |
| email_sentiment | double | Sentimento do e-mail (0–1) |
| slack_activity | double | Atividade no Slack (0–1) |
| meeting_participation | double | Participação em reuniões (0–1) |
| goal_achievement_rate | double | Taxa de atingimento de metas (0–1) |
| stress_level | double | Nível de estresse (0–1) |
| burnout_risk | double | Risco de burnout (0–1) |
| left_company | logical | Saiu da empresa? (TRUE/FALSE) |
| turnover_reason | character | Motivo da saída (quando aplicável) |
| risk_factors_summary | character | Resumo do risco: assume apenas ‘Low Risk’ e ‘Severe Burnout Risk’ no dataset |
| turnover_probability_generated | double | Probabilidade de saída gerada por ML |
| persona_name | character | Arquétipo comportamental (12 tipos) |
| role_complexity_score | double | Complexidade do cargo (0–1) |
| career_progression_score | double | Progressão de carreira (0–1) |
3.2 Importação dos dados
O dataset é distribuído no formato Parquet, um
formato binário que preserva os tipos de cada coluna e ocupa muito menos
espaço que um CSV equivalente. O pacote arrow permite ler
arquivos Parquet diretamente de uma URL. O primeiro shard contém
aproximadamente 95.000 registros com os 31 atributos
originais. Nos primeiros testes, este arquivo foi importado. Contudo,
para esta versão, importa-se o arquivo JSON com todos os registros
disponibilizados na fonte (como citado acima, cerca de 850.000
observações).
url_parquet <- paste0(
"https://huggingface.co/datasets/BrotherTony/",
"employee-burnout-turnover-prediction-800k/",
"resolve/refs%2Fconvert%2Fparquet/default/train/0000.parquet"
)
arquivo_local <- "0000.parquet"
if (!file.exists(arquivo_local)) {
download.file(url_parquet, destfile = arquivo_local, mode = "wb")
}
#df_bruto <- read_parquet(arquivo_local) # Primeira possibilidade: importação de parte dos dados via Parquet
df_bruto <-fromJSON("synthetic-employee-dataset.json") # Importação completa dos dados
3.3 Etapas de limpeza e organização
3.3.1 Padronização dos nomes das variáveis
A função clean_names() do pacote janitor
converte todos os nomes de colunas para snake_case, sem
espaços ou caracteres especiais.
df <- clean_names(df_bruto)
names(df)
## [1] "employee_id" "role"
## [3] "job_level" "department"
## [5] "tenure_months" "salary"
## [7] "performance_score" "satisfaction_score"
## [9] "workload_score" "team_sentiment"
## [11] "recent_feedback" "communication_patterns"
## [13] "project_completion_rate" "overtime_hours"
## [15] "training_participation" "collaboration_score"
## [17] "technical_skills" "soft_skills"
## [19] "email_sentiment" "slack_activity"
## [21] "meeting_participation" "goal_achievement_rate"
## [23] "stress_level" "burnout_risk"
## [25] "left_company" "turnover_reason"
## [27] "risk_factors_summary" "turnover_probability_generated"
## [29] "persona_name" "role_complexity_score"
## [31] "career_progression_score"
3.3.2 Conversão de tipos de dados
job_level precisa se tornar um fator ordenado para
permitir a comparação entre níveis, e tenure_months é
convertido explicitamente para inteiro.
df$job_level <- factor(df$job_level,
levels = c("Entry", "Mid", "Senior", "Manager", "Lead"),
ordered = TRUE)
df$tenure_months <- as.integer(df$tenure_months)
glimpse(df)
## Rows: 849,999
## Columns: 31
## $ employee_id <chr> "SYN_00000000", "SYN_00000001", "SYN_00…
## $ role <chr> " ", " Customer Success Manager", " Adm…
## $ job_level <ord> Mid, Manager, Entry, Manager, Mid, Mid,…
## $ department <chr> "Research & Development", "Research & D…
## $ tenure_months <int> 169, 54, 1, 31, 131, 34, 100, 77, 107, …
## $ salary <dbl> 79704.58, 29694.29, 62208.47, 236066.57…
## $ performance_score <dbl> 0.6324821, 0.5385873, 0.6246560, 0.9593…
## $ satisfaction_score <dbl> 0.6237460, 0.9825562, 0.7672001, 0.1858…
## $ workload_score <dbl> 0.7581168, 0.7884164, 0.6976166, 0.4931…
## $ team_sentiment <dbl> 0.6623349, 0.9346612, 0.8885591, 0.7321…
## $ recent_feedback <chr> "Close to home and good shift time give…
## $ communication_patterns <chr> "Exhibits communication style typical o…
## $ project_completion_rate <dbl> 0.5241873, 0.5582056, 0.5668491, 0.7674…
## $ overtime_hours <dbl> 0.000000, 0.000000, 0.000000, 9.591680,…
## $ training_participation <dbl> 0.04437344, 0.31485786, 0.74490049, 0.2…
## $ collaboration_score <dbl> 0.4921307, 0.9813943, 0.7011382, 0.3396…
## $ technical_skills <list> <"REST APIs", "Python (Pandas, NumPy, …
## $ soft_skills <list> <"Communication", "Creativity", "Teamw…
## $ email_sentiment <dbl> 0.6328913, 1.0000000, 0.7580049, 0.2025…
## $ slack_activity <dbl> 0.4921307, 0.9813943, 0.7011382, 0.3396…
## $ meeting_participation <dbl> 0.4921307, 0.9813943, 0.7011382, 0.3396…
## $ goal_achievement_rate <dbl> 0.6324821, 0.5385873, 0.6246560, 0.9593…
## $ stress_level <dbl> 0.9089919, 0.3633211, 0.6643785, 1.0000…
## $ burnout_risk <dbl> 0.8666426, 0.2189959, 0.5415310, 1.0000…
## $ left_company <lgl> FALSE, FALSE, TRUE, FALSE, FALSE, FALSE…
## $ turnover_reason <chr> "Not Applicable", "Not Applicable", "Pe…
## $ risk_factors_summary <chr> "Severe Burnout Risk", "Low Risk", "Low…
## $ turnover_probability_generated <dbl> 0.2909790, 0.1560017, 0.2338970, 0.3516…
## $ persona_name <chr> "ChangeResistor", "NewEnthusiast", "New…
## $ role_complexity_score <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2,…
## $ career_progression_score <dbl> 1.0000000, 1.0000000, 0.8364946, 1.0000…
3.3.3 Tratamento de valores ausentes e strings especiais
turnover_reason usa a string
"Not Applicable" para funcionários ainda ativos. Para o
contexto do R, converte-se para NA. Em adição, padroniza-se
a capitalização de risk_factors_summary e remove-se espaços
extras nas colunas de texto.
df$turnover_reason[df$turnover_reason == "Not Applicable"] <- NA
df$turnover_reason[df$turnover_reason == ""] <- NA
df$risk_factors_summary <- str_to_title(trimws(df$risk_factors_summary))
df$risk_factors_summary <- factor(
df$risk_factors_summary,
levels = c("Low Risk", "Medium Risk", "High Risk", "Severe Burnout Risk"),
ordered = TRUE
)
df$persona_name <- trimws(df$persona_name)
df$role <- trimws(df$role)
df$department <- trimws(df$department)
df$communication_patterns <- trimws(df$communication_patterns)
df$recent_feedback <- trimws(df$recent_feedback)
n_nas <- colSums(is.na(df))
n_nas <- n_nas[n_nas > 0]
if (length(n_nas) > 0) {
na_tabela <- data.frame(
Variável = names(n_nas),
N_ausentes = as.integer(n_nas)
)
kable(na_tabela,
caption = "Valores ausentes por variável (após tratamento)",
col.names = c("Variável", "Nº de ausentes"),
row.names = FALSE) |>
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
} else {
cat("Nenhum valor ausente detectado.\n")
}
| Variável | Nº de ausentes |
|---|---|
| turnover_reason | 604246 |
| risk_factors_summary | 44784 |
3.3.4 Processamento das colunas de habilidades
As colunas technical_skills e soft_skills
contêm, em cada célula, uma lista de habilidades do funcionário.
Contam-se as habilidades por funcionário e criam-se datasets expandidos
para análise de frequência.
df$n_tech_skills <- sapply(df$technical_skills, length)
df$n_soft_skills <- sapply(df$soft_skills, length)
df_tech_skills <- df[, c("role", "job_level", "department",
"persona_name", "burnout_risk",
"left_company", "technical_skills")]
df_tech_skills <- tidyr::unnest(df_tech_skills, technical_skills)
names(df_tech_skills)[names(df_tech_skills) == "technical_skills"] <- "skill"
df_tech_skills$skill <- trimws(as.character(df_tech_skills$skill))
df_soft_skills <- df[, c("role", "job_level", "department",
"persona_name", "burnout_risk",
"left_company", "soft_skills")]
df_soft_skills <- tidyr::unnest(df_soft_skills, soft_skills)
names(df_soft_skills)[names(df_soft_skills) == "soft_skills"] <- "skill"
df_soft_skills$skill <- trimws(as.character(df_soft_skills$skill))
3.3.5 Criação de variáveis derivadas
Criam-se novas variáveis a partir das existentes. Em especial, desenvolve-se um score composto inspirado no modelo de desequilíbrio esforço-recompensa de Siegrist (1996), que identifica a combinação de alta demanda e baixa recompensa como base do esgotamento ocupacional. A operacionalização com pesos numéricos (0.4/0.3/0.3) é uma adaptação exploratória para as variáveis disponíveis no dataset, e não reproduz diretamente nenhuma fórmula original do autor.
High Effort (extrinsic: demands/obligations + intrinsic: need for control) <-> Low Reward (money + esteem + status control)
Em adição, cria-se uma nova variável burnout_level, à
despeito da risk_factors_summary, a partir dos valores de
burnout_risk. A razão disto é que, nesta última, a fonte só
trouxe ocorrências de “Low Risk” e “Severe Burnout Risk”.
# 1. Faixa salarial
df$faixa_salarial <- cut(
df$salary,
breaks = c(0, 50000, 80000, 120000, 200000, Inf),
labels = c("< 50K", "50K-80K", "80K-120K", "120K-200K", "> 200K"),
right = FALSE
)
# 2. Tempo de empresa em anos
df$tenure_anos <- df$tenure_months / 12
# 3. Grupos de tenure (fases de carreira)
df$grupo_tenure <- cut(
df$tenure_anos,
breaks = c(0, 1, 3, 5, 10, Inf),
labels = c("< 1 ano", "1-3 anos", "3-5 anos", "5-10 anos", "> 10 anos"),
right = FALSE
)
# 4. Índice de engajamento: média entre satisfação, colaboração e sentimento da equipe
df$indice_engajamento <- (df$satisfaction_score +
df$collaboration_score +
df$team_sentiment) / 3
# 5. Score de desequilíbrio esforço-recompensa (inspirado em Siegrist, 1996)
df$score_desequilibrio <- (df$workload_score * 0.4) +
(df$overtime_hours / max(df$overtime_hours, na.rm = TRUE) * 0.3) +
((1 - df$satisfaction_score) * 0.3)
# 6. Flag de alto risco de burnout (acima do percentil 75)
df$alto_risco_burnout <- df$burnout_risk >= quantile(df$burnout_risk, 0.75,
na.rm = TRUE)
# 7. Flag: saída associada ao burnout
df$saida_por_burnout <- df$left_company & df$alto_risco_burnout
# 8. Variável de nível de risco de burnout baseada no score contínuo
# A variável original risk_factors_summary assume apenas dois valores no dataset
# (Low Risk e Severe Burnout Risk), sem granularidade suficiente para análise.
# Cria-se aqui uma variável própria a partir de burnout_risk, um dado contínuo,
# dividida em quatro faixas.
df$burnout_level <- cut(
df$burnout_risk,
breaks = c(0, 0.3, 0.6, 0.8, 1.01),
labels = c("Low", "Medium", "High", "Severe"),
right = FALSE,
include.lowest = TRUE
)
df$burnout_level <- factor(df$burnout_level,
levels = c("Low", "Medium", "High", "Severe"),
ordered = TRUE)
3.3.6 Remoção de colunas desnecessárias
df$technical_skills <- NULL
df$soft_skills <- NULL
df$employee_id <- NULL
cat(sprintf("Dataset limpo final: %d linhas × %d colunas\n", nrow(df), ncol(df)))
## Dataset limpo final: 849999 linhas × 38 colunas
3.3.7 Verificação de duplicatas
n_dup <- sum(duplicated(df))
cat(sprintf("Registros duplicados: %d\n", n_dup))
## Registros duplicados: 0
if (n_dup > 0) {
df <- unique(df)
cat(sprintf("Após remoção: %d linhas\n", nrow(df)))
}
3.4 Visão do dataset final
df[1:10, c("role", "job_level", "department", "tenure_anos",
"stress_level", "burnout_risk", "satisfaction_score",
"overtime_hours", "left_company",
"risk_factors_summary", "burnout_level", "persona_name")] |>
kable(caption = "Amostra do dataset limpo (10 primeiras observações)",
digits = 3) |>
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
font_size = 11, full_width = TRUE) |>
scroll_box(width = "100%", height = "380px")
| role | job_level | department | tenure_anos | stress_level | burnout_risk | satisfaction_score | overtime_hours | left_company | risk_factors_summary | burnout_level | persona_name |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mid | Research & Development | 14.083 | 0.909 | 0.867 | 0.624 | 0.000 | FALSE | Severe Burnout Risk | Severe | ChangeResistor | |
| Customer Success Manager | Manager | Research & Development | 4.500 | 0.363 | 0.219 | 0.983 | 0.000 | FALSE | Low Risk | Low | NewEnthusiast |
| Administrative Assistant | Entry | HR | 0.083 | 0.664 | 0.542 | 0.767 | 0.000 | TRUE | Low Risk | Medium | NewEnthusiast |
| Senior Manager | Manager | Research & Development | 2.583 | 1.000 | 1.000 | 0.186 | 9.592 | FALSE | Severe Burnout Risk | Severe | OverachievingSprinter |
| Anonymous Employee | Mid | Research & Development | 10.917 | 0.723 | 0.615 | 0.567 | 0.000 | FALSE | Low Risk | High | SeasonedExpert |
| Anonymous Employee | Mid | Dairy | 2.833 | 0.362 | 0.217 | 0.909 | 0.000 | FALSE | Low Risk | Low | QuietAchiever |
| Anonymous Employee | Mid | Procurement | 8.333 | 0.457 | 0.309 | 0.895 | 0.000 | FALSE | Low Risk | Medium | QuietAchiever |
| Senior Business Analyst | Senior | Meats | 6.417 | 0.599 | 0.464 | 0.708 | 0.000 | FALSE | Low Risk | Medium | SocialCatalyst |
| Senior Manager | Manager | Sales & Marketing | 8.917 | 0.955 | 0.934 | 0.616 | 0.000 | FALSE | Severe Burnout Risk | Severe | StrugglingLearner |
| Sales | Mid | Procurement | 14.083 | 0.401 | 0.254 | 1.000 | 0.000 | FALSE | Low Risk | Low | QuietAchiever |
3.5 Sumário das variáveis de interesse
vars_foco <- c("stress_level", "burnout_risk", "satisfaction_score",
"workload_score", "overtime_hours",
"indice_engajamento", "score_desequilibrio")
resumo <- data.frame(
Variável = vars_foco,
Mínimo = round(sapply(df[vars_foco], min, na.rm = TRUE), 3),
Mediana = round(sapply(df[vars_foco], median, na.rm = TRUE), 3),
Média = round(sapply(df[vars_foco], mean, na.rm = TRUE), 3),
Máximo = round(sapply(df[vars_foco], max, na.rm = TRUE), 3),
Desvio_Padrao = round(sapply(df[vars_foco], sd, na.rm = TRUE), 3),
Pct_Ausentes = round(sapply(df[vars_foco],
function(x) mean(is.na(x)) * 100), 1),
row.names = NULL
)
kable(resumo,
caption = "Sumário estatístico das variáveis contínuas de interesse",
col.names = c("Variável", "Mínimo", "Mediana", "Média",
"Máximo", "Desvio Padrão", "% Ausentes")) |>
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) |>
column_spec(1, bold = TRUE, monospace = TRUE)
| Variável | Mínimo | Mediana | Média | Máximo | Desvio Padrão | % Ausentes |
|---|---|---|---|---|---|---|
| stress_level | 0.000 | 0.942 | 0.791 | 1.000 | 0.265 | 0 |
| burnout_risk | 0.000 | 0.914 | 0.736 | 1.000 | 0.316 | 0 |
| satisfaction_score | 0.050 | 0.583 | 0.582 | 1.000 | 0.276 | 0 |
| workload_score | 0.007 | 0.614 | 0.599 | 1.000 | 0.200 | 0 |
| overtime_hours | 0.000 | 0.000 | 3.133 | 73.954 | 6.094 | 0 |
| indice_engajamento | 0.041 | 0.570 | 0.574 | 0.999 | 0.169 | 0 |
| score_desequilibrio | 0.013 | 0.377 | 0.378 | 0.693 | 0.107 | 0 |
Destaques do sumário:
- O
burnout_riskmédio da amostra é 0.74 (escala 0–1). - 28.5% dos funcionários saíram da empresa.
- Funcionários com nível de risco Severe representam 56.2% da amostra.
- O máximo de
overtime_hoursé 73.9535736h.
3.5.1 Distribuição das variáveis categóricas principais
risco_tab <- as.data.frame(table(df$burnout_level))
risco_tab$Pct <- round(risco_tab$Freq / sum(risco_tab$Freq) * 100, 1)
names(risco_tab) <- c("Nível de Risco", "N", "%")
kable(risco_tab,
caption = "Distribuição por nível de risco de burnout",
row.names = FALSE) |>
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
| Nível de Risco | N | % |
|---|---|---|
| Low | 121311 | 14.3 |
| Medium | 153101 | 18.0 |
| High | 98040 | 11.5 |
| Severe | 477547 | 56.2 |
persona_tab <- as.data.frame(table(df$persona_name))
persona_tab <- persona_tab[order(-persona_tab$Freq), ]
persona_tab$Pct <- round(persona_tab$Freq / sum(persona_tab$Freq) * 100, 1)
names(persona_tab) <- c("Persona", "N", "%")
kable(persona_tab,
caption = "Distribuição por arquétipo comportamental (persona)",
row.names = FALSE) |>
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
| Persona | N | % |
|---|---|---|
| StrugglingLearner | 86714 | 10.2 |
| CorporateNavigator | 69639 | 8.2 |
| SteadyEddy | 69611 | 8.2 |
| NewEnthusiast | 69566 | 8.2 |
| QuietAchiever | 69525 | 8.2 |
| 9-to-5Clockwatcher | 69434 | 8.2 |
| ChangeResistor | 69337 | 8.2 |
| SeasonedExpert | 69293 | 8.2 |
| SocialCatalyst | 69273 | 8.1 |
| OverachievingSprinter | 69243 | 8.1 |
| BurntOutStar | 69209 | 8.1 |
| AmbitiousClimber | 69155 | 8.1 |
4 Análise Exploratória dos Dados
A análise parte de uma pergunta central: Quais fatores organizacionais e individuais estão mais associados ao risco de burnout, e como eles se combinam para elevar a probabilidade de um funcionário deixar a empresa? Para responder, a exploração é organizada: (1) distribuição geral do risco de burnout; (2) variação por cargo e departamento; e (3) relação com esforço e recompensa.
4.1 Distribuição geral do risco de burnout
Inicialmente, busca-se entender como o risco de burnout se distribui
na população listada. Como a variável original
risk_factors_summary assume apenas dois valores no dataset
(Low Risk e Severe Burnout Risk), utiliza-se a variável derivada
burnout_level, criada a partir do score contínuo
burnout_risk com quatro faixas: Low (< 0.3), Medium
(0.3–0.6), High (0.6–0.8) e Severe (≥ 0.8), que permite uma leitura mais
específica do risco.
risco_dist <- as.data.frame(table(df$burnout_level)) # Contar e calcular proporções por categoria de risco
risco_dist$pct <- risco_dist$Freq / sum(risco_dist$Freq) * 100
risco_dist$label <- paste0(round(risco_dist$pct, 1), "%")
names(risco_dist)[1] <- "categoria"
cores_risco <- c("Low" = "#2ecc71", #Paleta para os riscos
"Medium" = "#f39c12",
"High" = "#e74c3c",
"Severe" = "#7b241c")
p1 <- ggplot(risco_dist, aes(x = categoria, y = pct, fill = categoria)) +
geom_col(width = 0.55, alpha = 1) +
geom_text(aes(label = label), vjust = -0.6, size = 4, fontface = "bold",
color = COR_TEXTO) +
scale_fill_manual(values = CORES_RISCO) +
scale_y_continuous(limits = c(0, 75),
labels = function(x) paste0(x, "%")) +
labs(title = "Distribuição dos funcionários por nível de risco de burnout",
subtitle = paste0("Total de ", format(nrow(df), big.mark = "."),
" funcionários analisados"),
x = NULL,
y = "Proporção (%)") +
theme(legend.position = "none",
axis.text.x = element_text(face = "bold"))
print(p1)
As faixas Low e Medium concentram funcionários com
burnout_risk abaixo de 0.6, enquanto High e Severe reúnem
os casos de maior atenção. O dado mais relevante é que 67.7% dos
funcionários estão em High ou Severe: ou seja, com risco de
burnout acima de 0.6. Desses, 477.547 estão no nível mais crítico
(Severe, burnout_risk ≥ 0.8), o que representa os
casos de maior urgência de intervenção.
4.2 Burnout por nível hierárquico
Uma das perguntas mais práticas para gestores de RH pode ser
traduzida em: o risco de burnout aumenta com o nível do
cargo? O boxplot abaixo permite comparar a distribuição de
burnout_risk entre os cinco níveis hierárquicos.
p2 <- ggplot(df, aes(x = job_level, y = burnout_risk, fill = job_level)) +
geom_boxplot(outlier.size = 0.5, outlier.alpha = 0.25,
outlier.color = COR_PRIMARIA, alpha = 0.85) +
scale_fill_manual(values = c(
"Entry" = "#C8DDD9",
"Mid" = "#F2C14E",
"Senior" = "#E07B54",
"Manager" = "#C0533A",
"Lead" = "#7B3F35"
)) +
labs(title = "Risco de burnout por nível hierárquico",
subtitle = "Cada caixa representa 50% central dos funcionários naquele nível",
x = "Nível hierárquico",
y = "Risco de Burnout (0–1)",
fill = "Nível") +
theme(legend.position = "none")
print(p2)
# Tabela de médias por nível
nivel_resumo <- aggregate(burnout_risk ~ job_level, data = df, FUN = mean)
nivel_resumo$burnout_risk <- round(nivel_resumo$burnout_risk, 3)
nivel_resumo$n <- as.integer(table(df$job_level))
nivel_resumo$pct_alto_risco <- round(
tapply(df$alto_risco_burnout, df$job_level, mean) * 100, 1)
names(nivel_resumo) <- c("Nível", "Burnout médio", "N", "% em alto risco")
kable(nivel_resumo,
caption = "Burnout médio e proporção em alto risco por nível hierárquico",
row.names = FALSE) |>
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
| Nível | Burnout médio | N | % em alto risco |
|---|---|---|---|
| Entry | 0.740 | 101211 | 46.4 |
| Mid | 0.732 | 609887 | 45.0 |
| Senior | 0.739 | 41347 | 46.0 |
| Manager | 0.758 | 78219 | 48.8 |
| Lead | 0.758 | 19335 | 48.7 |
Contrariamente ao senso comum, os níveis hierárquicos superiores não concentram necessariamente o maior risco. Isso é coerente com a teoria de Siegrist: trabalhadores de entrada (Entry) podem enfrentar alta carga sem contrapartida adequada de estabilidade ou reconhecimento, enquanto líderes (Lead) têm maior controle sobre seu trabalho.
4.3 Os departamentos mais críticos
Qual departamento concentra os funcionários com maior risco? O gráfico abaixo apresenta os 15 departamentos com maior média de burnout, permitindo priorização de ações de RH.
# Calcular média por departamento e selecionar os 15 maiores
dept_media <- aggregate(burnout_risk ~ department, data = df, FUN = mean)
dept_media <- dept_media[order(-dept_media$burnout_risk), ]
dept_top15 <- dept_media[1:15, ]
dept_top15$department <- factor(dept_top15$department,
levels = rev(dept_top15$department))
dept_top15$burnout_risk <- round(dept_top15$burnout_risk, 3)
p3 <- ggplot(dept_top15, aes(x = department, y = burnout_risk, fill = burnout_risk)) +
geom_col(alpha = 1) +
geom_text(aes(label = burnout_risk), hjust = -0.15, size = 3,
color = COR_TEXTO, fontface = "bold") +
scale_fill_gradient(low = "#F2C14E", high = "#7B3F35") +
coord_flip() +
labs(title = "Top 15 departamentos com maior risco de burnout",
subtitle = "Média do score de burnout_risk por departamento",
x = NULL,
y = "Burnout médio",
fill = "Burnout") +
theme(legend.position = "none") +
scale_y_continuous(limits = c(0, 0.8))
print(p3)
A tabela interativa abaixo permite explorar todos os departamentos:
dept_completo <- aggregate(burnout_risk ~ department, data = df, FUN = mean)
dept_completo$n <- as.integer(table(df$department))
dept_completo$pct_saida <- round(
tapply(df$left_company, df$department, mean, na.rm = TRUE) * 100, 1)
dept_completo$burnout_risk <- round(dept_completo$burnout_risk, 3)
dept_completo <- dept_completo[order(-dept_completo$burnout_risk), ]
names(dept_completo) <- c("Departamento", "Burnout médio", "N", "% que saiu")
datatable(dept_completo,
caption = "Risco de burnout e rotatividade por departamento",
rownames = FALSE,
options = list(pageLength = 10, language = list(
search = "Buscar:",
lengthMenu = "Mostrar _MENU_ registros",
info = "Mostrando _START_–_END_ de _TOTAL_",
paginate = list(previous = "Anterior", `next` = "Próximo")
))) |>
formatStyle("Burnout médio",
background = styleColorBar(c(0, 1), "#f1948a"),
backgroundSize = "100% 90%",
backgroundRepeat = "no-repeat",
backgroundPosition = "center")
4.4 A relação entre esforço, recompensa e burnout
Abaixo, busca-se verificar se o desequilíbrio entre esforço (carga e
horas extras) e recompensa (satisfação e salário) está de fato associado
ao risco de burnout. O gráfico interativo abaixo cruza o
score_desequilibrio com o burnout_risk,
colorido pelo nível de risco classificado:
set.seed(42)
df_amostra <- df[sample(nrow(df), 3000), ] #Amostra
p4 <- plot_ly(
data = df_amostra,
x = ~score_desequilibrio,
y = ~burnout_risk,
color = ~burnout_level,
colors = c("Low" = "#8BBFB0", "Medium" = "#F2C14E",
"High" = "#E07B54", "Severe" = "#7B3F35"),
type = "scatter",
mode = "markers",
marker = list(size = 5, opacity = 0.6),
text = ~paste0("Departamento: ", department,
"<br>Cargo: ", role,
"<br>Nível: ", job_level,
"<br>Desequilíbrio: ", round(score_desequilibrio, 2),
"<br>Burnout risk: ", round(burnout_risk, 2)),
hoverinfo = "text"
) |>
layout(
title = list(text = "Desequilíbrio esforço-recompensa vs. risco de burnout",
font = list(size = 14)),
xaxis = list(title = "Score de desequilíbrio (0–1)"),
yaxis = list(title = "Risco de burnout (0–1)"),
legend = list(title = list(text = "Nível de risco"))
)
p4
Os pontos classificados como Severe (vermelho escuro) concentram-se no quadrante superior direito. Nota-se que, quanto maior o risco de burnout, maior a probabilidade de cair em extremo ou em mínimo desequilíbrio, de acordo com os dados. As observações mais equilibradas são aquelas que apresentam menor risco de burnout, estando mais próximos da mediana dos valores de desequilíbrio. Passe o cursor sobre os pontos para identificar departamento, cargo e nível de cada funcionário.
4.5 Horas extras e burnout
O boxplot abaixo revela a distribuição: mediana, intervalo central e valores extremos em cada faixa de jornada extra.
df$faixa_overtime <- cut( # Criar faixas de horas extras
df$overtime_hours,
breaks = c(-Inf, 0, 15, 30, 50, Inf),
labels = c("Sem extras", "1–15h", "16–30h", "31–50h", "> 50h")
)
n_faixas <- table(df$faixa_overtime) # Calcular N por faixa
p5 <- ggplot(df, aes(x = faixa_overtime, y = burnout_risk, fill = faixa_overtime)) +
geom_boxplot(outlier.size = 0.4,
outlier.alpha = 0.2,
alpha = 0.9,
width = 0.55) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "white", color = COR_TEXTO) +
scale_fill_manual(values = c("Sem extras" = "#8BBFB0",
"1\u201315h" = "#F2C14E",
"16\u201330h" = "#E07B54",
"31\u201350h" = "#C0533A",
"> 50h" = "#7B3F35")) +
labs(title = "Distribuição do risco de burnout por faixa de horas extras",
subtitle = "Losango branco = média; linha central = mediana",
x = "Horas extras semanais",
y = "Risco de Burnout (0–1)") +
theme(legend.position = "none")
print(p5)
overtime_tab <- data.frame(
Faixa = levels(df$faixa_overtime),
N = as.integer(n_faixas),
Mediana = round(tapply(df$burnout_risk, df$faixa_overtime,
median, na.rm = TRUE), 3),
Media = round(tapply(df$burnout_risk, df$faixa_overtime,
mean, na.rm = TRUE), 3),
Pct_alto = round(tapply(df$alto_risco_burnout, df$faixa_overtime,
mean, na.rm = TRUE) * 100, 1),
row.names = NULL
)
names(overtime_tab) <- c("Faixa de horas extras", "N",
"Burnout mediano", "Burnout médio", "% em alto risco")
kable(overtime_tab,
caption = "Risco de burnout por faixa de horas extras",
row.names = FALSE) |>
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
| Faixa de horas extras | N | Burnout mediano | Burnout médio | % em alto risco |
|---|---|---|---|---|
| Sem extras | 583707 | 0.751 | 0.668 | 36.7 |
| 1–15h | 213253 | 1.000 | 0.884 | 65.2 |
| 16–30h | 48433 | 1.000 | 0.883 | 64.9 |
| 31–50h | 4462 | 1.000 | 0.883 | 65.1 |
| > 50h | 144 | 1.000 | 0.878 | 65.3 |
O boxplot revela que, a partir da ocorrência de horas extras, ocorre uma concentração maior de observações em níveis maiores de burnout, mantendo-se, contudo, de modo visualmente equilibrado a partir das 15h extras de trabalhp
5 Conclusões
5.1 O problema abordado
Este projeto investigou quais fatores organizacionais e individuais estão mais associados ao risco de burnout em ambientes de trabalho e como eles elevam a probabilidade de um funcionário deixar a empresa, uma questão com implicações diretas para a saúde ocupacional e para os custos organizacionais.
5.2 Como o problema foi abordado
Utilizou-se o Synthetic Employee Dataset (BrotherTony, 2025), com ~850.000 registros e 31 variáveis. Após importação via arquivo limpeza dos dados, conduziu-se uma análise exploratória organizada em quatro eixos: distribuição global do risco, variação por cargo e departamento e relação com esforço e recompensa.
5.3 Observações
Inicialmente, observa-se que o Burnout não respeita a hierarquia de
modo linear. O risco é distribuído de maneira similar entre os níveis
Entry a Lead, sugerindo que fatores de cargo específico (carga,
autonomia, reconhecimento) importam mais que o nível em si. Com relação
aos departamentos, os 15 com maior burnout médio apresentam scores
expressivamente acima da média geral, indicando que contextos
organizacionais específicos amplificam o risco. Em adição, o
desequilíbrio esforço-recompensa pode se mostrar um preditor
consistente, dado que a correlação entre
score_desequilibrio e burnout_risk foi visível
mesmo numa amostra aleatória de 3.000 pontos. Com respeito às horas
extras dos funcionários, por fim,a distribuição de burnout sobe em
mediana e em variabilidade à medida que a jornada extra aumenta, com os
funcionários acima de 50h semanais concentrando grandes valores de
risco.
5.4 Implicações
Os departamentos identificados como críticos e o limiar de horas extras podem ser levados como pontos de partida para intervenções focadas: monitoramento de carga, programas de suporte e revisão de remuneração nos grupos de alto risco. Já no âmbito executivo, a análise demonstra que burnout não é problema individual, mas organizacional. Departamentos específicos concentram risco, e a estrutura de esforço e recompensa (não apenas o salário) determina quem fica e quem sai. Em termos de peqsuisa, o score de desequilíbrio composto (inspirado em Siegrist, 1996) mostrou-se um possível preditor analítico útil quando outros dados diretos de escalas psicométricas não estão disponíveis.
5.5 Limitações e possíveis extensões
- Os dados são sintéticos, com base em distribuições reais, mas não representam nenhuma organização específica. Os padrões encontrados não devem ser generalizados sem validação em dados reais.
- O dataset não permite analisar a evolução do burnout ao longo do tempo. Um dataset temporal/longitudinal permitiria identificar trajetórias de risco e pontos de intervenção com maior precisão.
- Os pesos 0.4/0.3/0.3, pensados para o score de desequilíbrio, são arbitrários. Uma extensão natural seria estimar os pesos por regressão ou análise de componentes principais, tornando o score mais robusto.
- A coluna
recent_feedbackcontém texto livre que não foi explorado. Uma análise de sentimentos sobre esses textos poderia ser de grande valor para enriquecer os achados qualitativos.
6 Referências
Licença dos dados: CC-BY-NC-ND 4.0. BrotherTony (2025), Hugging Face