This dataset examines employee satisfaction and workplace dynamics across 3,025 responses from a fictional company. It includes information on demographics, job role, work-life balance, and more. The goal is to identify factors that influence employee job satisfaction using statistical techniques such as Chi-square tests, ANOVA, and regression analysis.
The employee_survey dataset uses a synthetically made survey that a fictional company recorded the responses of their employees in regards to job satisfaction and overall happiness. Available here on kaggle:
https://www.kaggle.com/datasets/lainguyn123/employee-survey/data
library(readxl) # Load library for reading Excel files
library(dplyr) # Data manipulation
library(ggplot2) # Plotting
library(corrplot) # Correlation matrix visual
library(knitr) #Table rendering
library(gridExtra)
The employee_survey contains 23 answer columns and a total of 3,025 recorded responses. For this project I’ll be using just 8 answer columns as my variables
Gender (Gender) — Categorical variable representing
the gender the employee identifies as.
Age (Age) — Continuous variable measuring the age of
the employee.
Job Level (JbL) — Categorical variable indicating
the employee’s job level (e.g., Intern/Fresher, Junior, Mid, Senior,
Lead).
Experience (Exp) — Continuous variable measuring the
number of years of work experience the employee has.
Department (Dept) — Categorical variable indicating
the department where the employee works.
Work-Life Balance (WLB) — Ordinal variable (1–5)
indicating the employee’s perception of work-life balance, with 1 being
poor and 5 being excellent.
Work Environment (WkEnv) — Ordinal variable (1–5)
representing the perceived quality of the employee’s work environment,
from 1 (Very Poor) to 5 (Excellent).
Work Load (WkLd) — Ordinal variable (1–5) indicating
the employee’s perception of their workload:
Stress (Stress) — Ordinal variable (1–5)
representing the employee’s reported level of stress.
Sleep Hours (SleepH) — Continuous variable
representing the average number of hours of sleep the employee
reports.
Training Hours Per Year (THPY) — Continuous variable
measuring the number of training hours the employee receives
annually.
Job Satisfaction (JobSat) — Ordinal variable (1–5)
representing how satisfied an employee feels with their job.
#Loading the data set
emp_data <- read_excel("employee_survey.xlsx", sheet = "employee_survey (2)")
#Converting categorical variables to factors
emp_data$Gender <- as.factor(emp_data$Gender) # Unordered factor
emp_data$JbL <- factor(emp_data$JbL, ordered = TRUE, levels = c("Intern/Fresher", "Junior", "Mid", "Senior", "Lead"))
emp_data$Dept <- as.factor(emp_data$Dept) # Unordered factor (categorical department names)
emp_data$WLB <- factor(emp_data$WLB, levels = 1:5, ordered = TRUE) # 1 = Worst, 5 = Best
emp_data$WkEnv <- factor(emp_data$WkEnv, levels = 1:5, ordered = TRUE) # Work Environment Rating
emp_data$WkLd <- factor(emp_data$WkLd, levels = 1:5, ordered = TRUE) # Work Load
emp_data$Stress <- factor(emp_data$Stress, levels = 1:5, ordered = TRUE) # Stress Level
emp_data$JobSat <- factor(emp_data$JobSat, levels = 1:5, ordered = TRUE) # Job Satisfaction
#Creating a vector of all the columns I'm using
selected_cols <- emp_data %>%
select(EmpID, Gender, Age, JbL, Exp, Dept, WLB, WkEnv, WkLd, Stress, SleepH, THPY, JobSat)
#Creating vector of numeric columns
numeric_cols <- selected_cols %>%
select(Age, Exp, SleepH, THPY)
#selected_cols <- emp_data %>%
#select(Gender, Age, JbL, Exp, Dept, WLB, WkEnv, WkLd, Stress, SleepH, THPY, JobSat)
summary(selected_cols)
## EmpID Gender Age JbL
## Min. : 1 Female:1240 Min. :22.00 Intern/Fresher: 202
## 1st Qu.: 757 Male :1528 1st Qu.:27.00 Junior : 602
## Median :1513 Other : 257 Median :34.00 Mid : 766
## Mean :1513 Mean :35.74 Senior :1069
## 3rd Qu.:2269 3rd Qu.:43.00 Lead : 386
## Max. :3025 Max. :60.00
##
## Exp Dept WLB WkEnv WkLd Stress
## Min. : 0.000 IT :774 1:618 1:580 1:614 1:1809
## 1st Qu.: 3.000 Finance :637 2:554 2:604 2:618 2: 535
## Median : 8.000 Operations:455 3:549 3:631 3:610 3: 426
## Mean : 9.083 Marketing :316 4:633 4:568 4:621 4: 179
## 3rd Qu.:14.000 Sales :281 5:671 5:642 5:562 5: 76
## Max. :29.000 Legal :272
## (Other) :290
## SleepH THPY JobSat
## Min. : 4.000 Min. :10.0 1: 417
## 1st Qu.: 6.300 1st Qu.:23.5 2: 277
## Median : 7.000 Median :40.0 3: 566
## Mean : 7.006 Mean :37.3 4:1249
## 3rd Qu.: 7.700 3rd Qu.:46.5 5: 516
## Max. :10.000 Max. :64.5
##
# Round and print as table
cor_matrix <- cor(numeric_cols, use = "complete.obs")
# Plot the correlation matrix
corrplot(cor_matrix, method = "circle", type = "lower",
tl.col = "black", tl.srt = 45, addCoef.col = "black", number.cex = 0.7)
The correlation matrix reveals several positive relationships between Age, Experience, and TrainingHoursPerYear, indicating that older or more experienced employees tend to receive more training. However, SleepHours shows no significant linear relationship with any of the variables analyzed.
library(knitr)
# Creating a data frame with interpretation
cor_summary <- data.frame(
`Variable Pair` = c(
"Age vs Exp",
"Age vs THPY",
"Exp vs THPY",
"SleepH with others"
),
Correlation = c(0.69, 0.81, 0.74, "~0"),
Interpretation = c(
"Positive — older employees tend to have more experience",
"Very strong postive — older employees likely recive more training hours per year",
"Postive — more experienced staff likely receive more training",
"No linear relationship — sleep hours are independent of age, experience, and THPY."
)
)
# Display using kable
kable(cor_summary, caption = "Summary of Key Correlation Findings")
| Variable.Pair | Correlation | Interpretation |
|---|---|---|
| Age vs Exp | 0.69 | Positive — older employees tend to have more experience |
| Age vs THPY | 0.81 | Very strong postive — older employees likely recive more training hours per year |
| Exp vs THPY | 0.74 | Postive — more experienced staff likely receive more training |
| SleepH with others | ~0 | No linear relationship — sleep hours are independent of age, experience, and THPY. |
To determine if there is an association between these two categorical variables, a Chi-squared test will be used. The Null Hypothesis usually states there is no association between stress level and employee type.
#Summary of Job Level
summary(selected_cols$JbL)
## Intern/Fresher Junior Mid Senior Lead
## 202 602 766 1069 386
#summary of Stress Level
summary(selected_cols$Stress)
## 1 2 3 4 5
## 1809 535 426 179 76
ggplot(emp_data, aes(x = Stress, fill = JbL)) +
geom_bar(position = "dodge") +
labs(
title = "Distribution of Job Levels Across Stress Ratings",
x = "Stress Level (1 = Low, 5 = High)",
y = "Number of Employees",
fill = "Job Level"
) +
theme_minimal() +
scale_fill_brewer(palette = "Set2")
The bar plot suggests a potential association between Job Level and reported Stress levels. Most Senior and Mid-level employees report low stress, while Interns and Junior employees appear more evenly spread across the stress spectrum. A Chi-square test can confirm whether this observed pattern is statistically significant.
#Performing Chi-Square Test
chi_sq_test <- chisq.test(table(emp_data$Stress, emp_data$JbL), simulate.p.value = TRUE)
#Results
chi_sq_test
##
## Pearson's Chi-squared test with simulated p-value (based on 2000
## replicates)
##
## data: table(emp_data$Stress, emp_data$JbL)
## X-squared = 12.861, df = NA, p-value = 0.6977
With a p-value ≥ 0.05, we fail to reject the null hypothesis, suggesting that there is not a statistically significat association between Job Level and Stress Level.
ggplot(emp_data, aes(x = Stress, fill = JobSat)) +
geom_bar(position = "dodge") +
labs(
title = "Distribution of Job Satisfaction Across Stress Levels",
x = "Stress Level (1 = Low, 5 = High)",
y = "Number of Employees",
fill = "Job Satisfaction"
) +
theme_minimal() +
scale_fill_brewer(palette = "Spectral")
The barplot suggests a potential association between an employee’s percieved level of stress and job satisfaction.
#Performing Chi-Square test
chi_sq_jb <- chisq.test(table(emp_data$Stress, emp_data$JobSat), simulate.p.value = TRUE)
#Results
chi_sq_jb
##
## Pearson's Chi-squared test with simulated p-value (based on 2000
## replicates)
##
## data: table(emp_data$Stress, emp_data$JobSat)
## X-squared = 195.7, df = NA, p-value = 0.0004998
With a p-value < 0.05, this Chi-Square test indicates that we can reject the null hypothesis. Which means there is strong evidence to suggest a relationship or association exists between Stress and Job Satisfaction.
SleepH) across different gender groups
(Gender)?A One-way ANOVA test will be used to compare the
means of Sleep Hours (SleepH) across
different gender groups (Gender).
A One-way ANOVA uses one categorical (nominal or ordinal) independent variable and one continuous (interval or ratio) dependent variable to determine if there is a statistically significant difference in the means of the dependent variable across the different levels of the independent variable.
Independent Variable:
- Gender
- Categorical: It categorizes subjects into three
groups — Male, Female, and
Other.
- Satisfies the requirement of having at least two
levels.
Dependent Variable:
- SleepH (Sleep Hours)
- Continuous: It represents the average number of hours
an employee reports sleeping, ranging across a numerical scale.
This test will determine whether there is a statistically significant difference in average sleep duration across gender identities within the company.
# Visualize Data
ggplot(emp_data, aes(x = Gender, y = SleepH, fill = Gender)) +
geom_boxplot() +
labs(title = "Sleep Hours by Gender", x = "Gender", y = "Average Sleep Hours") +
theme_minimal()
Because the boxes do overlap much, the boxplot suggests there is no significant difference in SleepH between the groups.
For a one-way ANOVA, the The Null Hypothesis states that the mean sleep hours for all gender groups are equal, while the Alternative Hypothesis states at least one group mean is different from the others.
#Perform one-way ANOVA
anova_gender_sleep <- aov(SleepH ~ Gender, data = emp_data)
#Output the results
summary(anova_gender_sleep)
## Df Sum Sq Mean Sq F value Pr(>F)
## Gender 2 2 0.9887 0.983 0.374
## Residuals 3022 3038 1.0053
Based on the ANOVA output, the test produced an F-statistic of 0.983 with a corresponding p-value of 0.374.
Since the p-value is greater than 0.05, we
fail to reject the null hypothesis. This suggests that
there is no statistically significant difference in
average Sleep Hours (SleepH) across the three gender groups
(Gender).
In other words, the data does not provide sufficient evidence to conclude that gender identity is associated with different sleep durations among employees in this dataset.
A two-way ANOVA will be used to analyze whether the
average Stress (treated as numeric) differs based on
levels of WkEnv and WkLd. Both
WkLd and WkEnv are treated as categorical
grouping variables with 5 levels (1–5).
Dependent Variable: - Stress (treated
as numeric for this test)
Independent Variables: - WkLd (Work
Load, ordinal) - WkEnv (Work Environment, ordinal)
The two-way ANOVA tests: 1. Whether WkLd affects Stress
2. Whether WkEnv affects Stress 3. Whether there’s an
interaction effect between them ### Visualization
# Create the interaction plot (NOTE THAT STRESS IS BEING TREATED AS NUMERIC FOR THIS TEST)
interaction.plot(
x.factor = emp_data$WkEnv,
trace.factor = emp_data$WkLd,
response = as.numeric(emp_data$Stress),
fun = mean,
type = "b",
legend = TRUE,
xlab = "Work Environment (1 = Poor, 5 = Excellent)",
ylab = "Mean Stress Level",
trace.label = "Work Load Level",
col = rainbow(5),
pch = 16,
main = "Interaction Plot: Work Environment x Work Load on Stress"
)
The interaction plot suggests that stress levels are influenced by both WkLd and WkEnv, with strong evidence from crossing-lines suggesting a strong interaction event.
In a Two-Way ANOVA, we test several hypotheses to understand the effects of two factors and their interaction on a dependent variable. Here are the hypotheses involved:
-Null Hypotheses (H₀):
-There is no difference in mean stress levels across different levels of Work Load.
-There is no difference in mean stress levels across different levels of Work Environment.
-There is no interaction between Work Load and Work Environment on stress.
Alternative Hypotheses (H₁):
-At least one level of Work Load has a different mean stress level.
-At least one level of Work Environment has a different mean stress level.
-There is an interaction effect between Work Load and Work Environment on stress.
# Perform two way ANOVA
anova_stress <- aov(as.numeric(Stress) ~ WkLd * WkEnv, data = emp_data)
# Output findings
summary(anova_stress)
## Df Sum Sq Mean Sq F value Pr(>F)
## WkLd 4 2 0.473 0.417 0.796
## WkEnv 4 3 0.789 0.696 0.594
## WkLd:WkEnv 16 21 1.301 1.149 0.303
## Residuals 3000 3399 1.133
Since all p-values are greater than 0.05, we fail to reject the null hypotheses. This suggests that:
-There is no statistically significant difference in mean stress levels across levels of Work Load or Work Environment.
-Additionally, there is no significant interaction effect, meaning the relationship between Work Load and Stress does not depend on Work Environment in a statistically meaningful way.
A Multiple Linear Regression test will be used to model the relationship between JobSat, as the dependent variable, and for the predictors:
| Predictor | Type | Rationale |
|---|---|---|
| Stress | Ordinal → Numeric | Higher stress is likely to reduce job satisfaction. |
| WLB | Ordinal → Numeric | Better work-life balance often leads to higher satisfaction. |
| WkEnv | Ordinal → Numeric | Positive work environments are linked to satisfaction. |
| THPY | Continuous | Training may boost morale and long-term job fulfillment. |
| Gender | Categorical (factor) | Controls for potential gender-related differences. |
# Ensure JobSat is numeric for regression
emp_data$JobSatNum <- as.numeric(as.character(emp_data$JobSat))
emp_data$Stress <- as.numeric(as.character(emp_data$Stress))
emp_data$WLB <- as.numeric(as.character(emp_data$WLB))
emp_data$WkEnv <- as.numeric(as.character(emp_data$WkEnv))
# Fit the multiple linear regression model
mlr_model <- lm(JobSatNum ~ Stress + WLB + WkEnv + THPY + Gender, data = emp_data)
# Output model summary
summary(mlr_model)
##
## Call:
## lm(formula = JobSatNum ~ Stress + WLB + WkEnv + THPY + Gender,
## data = emp_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.9676 -0.7683 0.2322 0.7621 3.1082
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.5056855 0.0964067 25.991 <2e-16 ***
## Stress -0.2508591 0.0196025 -12.797 <2e-16 ***
## WLB 0.2256344 0.0144241 15.643 <2e-16 ***
## WkEnv 0.2178628 0.0147436 14.777 <2e-16 ***
## THPY -0.0004239 0.0015379 -0.276 0.783
## GenderMale -0.0380143 0.0438510 -0.867 0.386
## GenderOther 0.0129429 0.0786610 0.165 0.869
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.147 on 3018 degrees of freedom
## Multiple R-squared: 0.1738, Adjusted R-squared: 0.1721
## F-statistic: 105.8 on 6 and 3018 DF, p-value: < 2.2e-16
The regression analysis reveals that Stress, Work-Life Balance, and Work Environment are strong, statistically significant predictors of job satisfaction. In contrast, Training Hours and Gender (both Male and Other) do not appear to significantly influence satisfaction ratings.
This employee survey analysis explored how various workplace factors influence stress and job satisfaction. Multiple statistical tests—including ANOVAs and multiple linear regression—were performed to examine relationships among variables.
Overall Insights:
From Chi-Squared Tests there is significant association between stress and an employees perceived satisfaction with their job.
Results from Two-way ANOVA test suggests further study into other variables affecting employee work environment, as work load and work environment did not indicates a interaction, yet from our regression analysis Work Environment is shown as a significant predictor for satisfaction ratings.
As one would expect, older employee’s likely have more years of work experience