Report
Report
SUPERVISOR
Dr. Khaled Almghari
January 2026
TABLE OF CONTENT
1. Introduction ............................................................................................................................................... 5
2. Problems ................................................................................................................................................... 5
3. Data Preparation, Exploratory Analysis, and Statistical Evaluation ......................................................... 5
3.1 Data Loading....................................................................................................................................... 5
3.2 Data Understanding ............................................................................................................................ 5
3.3 Missing Value & Duplicate Analysis.................................................................................................. 5
3.4 Irrelevant Columns / Cleaning ............................................................................................................ 5
3.5 Exploratory Data Analysis (EDA) ...................................................................................................... 6
3.6 Feature Selection (Selected Variables) ............................................................................................. 24
3.7 Point Of View ................................................................................................................................... 24
4. Python codes with interpretation for each line........................................................................................ 25
LIST OF TABLES
Table 1: Univariate Analysis - Numerical Features .................................................................... 7
Table 2: Univariate Analysis - Categorical Features ................................................................. 10
Table 3: Univariate Analysis - Ordinal Categorical Feature ..................................................... 14
Table 4: Bivariate Analysis - Numerical vs Attrition ................................................................ 16
Table 5: Bivariate Analysis - Categorical vs Attrition............................................................... 20
Table6 : Bivariate Analysis - Ordinal Categorical Feature vs Attrition ................................... 23
LIST OF FIGURES
2. Problems
- The data structure was examined using the info() function. This step provided information about the
number of features, their data types, and the presence of any missing values. The dataset contains 26
numerical features and 9 categorical features.
- Verifying missing values, duplicate rows, and duplicate columns ensures data quality and reliability.
Clean and unique features are essential for accurate exploratory data analysis (EDA) and predictive
modeling, reducing the risk of bias or redundant information.
1. Missing Values:
- Checking for missing values showed that all columns are complete. No imputation is needed, so
the dataset is ready for analysis.
2. Duplicate Rows:
- Checking for duplicate rows showed that no duplicate rows exist. This indicates that the dataset
is clean in terms of row duplication.
3. Duplicate Columns:
- Checking duplicate columns showed that no duplicate columns exist. Each feature provides
unique information for analysis.
- A set of 20 columns were identified as irrelevant for predicting employee attrition. These columns were
excluded because they either serve as identifiers, are constant across all employees, or showed weak,
inconsistent, or unclear relationships with attrition during initial inspection. Removing them helps reduce
noise, improve model clarity, and focus the analysis on the most meaningful features.
- We selected 16 initial variables from a total of 35 for further analysis based on their anticipated
importance in employee attrition, according to the specialized knowledge and type of information they
provide. These variables include the following:
- These features were chosen because they capture key aspects of an employee's work environment,
career progression, satisfaction, and demographic characteristics, which are known to influence attrition.
- Understanding distribution helps in detecting outliers, distribution patterns, deciding scaling, and
preparing for modeling.
Note:
Outliers: Found mainly in variants with Right Skew.
- Detecting class imbalance and rare categories is essential for modeling and interpreting
attrition factors.
JobRole Some job roles contain very few employees compared to others.
(Class Imbalance)
EducationField Fields such as Life Sciences are overrepresented, while others have
low frequency. (Class Imbalance)
Gender Male and Female categories are relatively evenly distributed. (No
Class Imbalance)
- Based on the exploratory data analysis (EDA), a focused set of variables was selected for further
analysis due to their clear and consistent relationship with employee attrition. The selection aimed to
concentrate on variables that showed meaningful patterns and potential influence on employee turnover.
1. OverTime:
Justification: Selected because employees who work overtime show noticeably higher attrition
rates, indicating work pressure as a key factor.
2. JobSatisfaction
Justification: Selected because lower satisfaction levels are strongly associated with higher
employee attrition, reflecting employees’ attitudes toward their jobs.
3. WorkLifeBalance
Justification: Selected due to its clear impact on attrition, where employees with poor work-life
balance are more likely to leave.
4. MonthlyIncome
Justification: Selected because employees with lower income tend to leave more frequently,
and the variable shows meaningful distribution differences across attrition classes.
5. YearsSinceLastPromotion
Justification: Selected as fewer promotion opportunities are linked to higher attrition,
indicating the importance of career growth.
6. TotalWorkingYears
Justification: Selected because employees with less experience are more likely to leave,
showing a relationship between experience level and retention.
7. YearsAtCompany
Justification: Selected since shorter tenure at the company is associated with higher
attrition rates.
8. YearsWithCurrManager
Justification: Selected because weaker or shorter relationships with managers appear to
increase the likelihood of attrition.
- The following variables were not selected for further analysis as they showed weak, inconsistent, or
unclear relationships with employee attrition: Gender, Age, YearsInCurrentRole, EducationField,
JobRole, YearsInCurrentRole, Department, MaritalStatus. Excluding these variables reduces noise
and allows the analysis to focus on the features most relevant to predicting and understanding attrition.
1. Skewed Variables:
MonthlyIncome, TotalWorkingYears, YearsSinceLastPromotion, YearsAtCompany, and
YearsWithCurrManager exhibit positive (right) skewness. This indicates the presence of a small
number of employees with very high values, and these variables may require transformation (e.g.,
log or square root) before modeling.
2. Outliers:
Outliers are mainly observed in MonthlyIncome and experience-related variables
(TotalWorkingYears, YearsAtCompany, YearsWithCurrManager). These outliers correspond to
high-income and highly experienced employees and may be handled using capping or
winsorization if predictive modeling is applied.
3. Class Imbalance:
The target variable Attrition (Yes/No) is imbalanced, with significantly more employees staying
than leaving. This imbalance should be considered during modeling using techniques such as
SMOTE or undersampling to avoid biased predictions.
4. Workload and Satisfaction Factors:
OverTime, JobSatisfaction, and WorkLifeBalance show a strong association with attrition.
Employees who work overtime, report low job satisfaction, or have poor work-life balance are
more likely to leave the organization, making these variables critical for feature selection.
5. Career Progression and Compensation Factors:
MonthlyIncome and YearsSinceLastPromotion are strongly related to attrition. Employees with
lower income levels or longer periods without promotion exhibit higher attrition, indicating that
compensation and career growth significantly influence turnover decisions.
6. Experience and Tenure Factors:
TotalWorkingYears, YearsAtCompany, and YearsWithCurrManager demonstrate that employees
with less experience, shorter organizational tenure, or limited time with their current manager are
more prone to attrition.
7. Feature Relevance:
The selected eight variables collectively capture key aspects of employee experience, including
workload, satisfaction, compensation, career growth, and tenure, making them suitable candidates
for further statistical analysis and predictive modeling.
8. Data Quality Considerations:
The dataset shows high data quality, with no missing values or duplicates. Irrelevant or constant
columns (EmployeeNumber, EmployeeCount, StandardHours, Over18) were identified and
excluded to reduce noise and improve analysis reliability.
Data loading:
df = pd.read_csv(r"C:\Users\AfnanMoq\Desktop\python\WA_Fn-UseC_-HR-Employee-
Attrition (7).csv")
- This loads the dataset from a CSV file using the read_csv function from the Pandas library &the dataset
is stored in a DataFrame object named df, which represents the data in a tabular line.
Data understanding:
print([Link])
print([Link]())
print([Link]())
print([Link]())
• shape(): The function returns the dimensions of the dataset in terms of number of rows, number
of columns
• head(): The function displays the first five rows of the dataset
• info(): The function provides a concise summary of the dataset, including:
o Column names.
o Number of non-null values in each column.
o Data types of each variable.
o Identify categorical and numerical features.
OUTPUT:
• describe(): The function generates descriptive statistics for numerical variables such as:
o Mean (average).
o Standard deviation.
o Minimum value.
o Maximum value.
o 25th percentile (Q1).
o 50th percentile (Median).
o 75th percentile (Q3).
o Count of non-null values.
OUTPUT:
- This only gives us the columns whose value is True (i.e., the duplicates), and we store
the duplicate columns in a variable called dup_cols and print them as a list.
OUTPUT:
rel_cols = [
'Age', 'MonthlyIncome', 'TotalWorkingYears', 'YearsAtCompany',
'YearsInCurrentRole', 'YearsSinceLastPromotion', 'YearsWithCurrManager',
'OverTime', 'JobRole', 'Department', 'EducationField', 'MaritalStatus',
'Gender', 'JobSatisfaction', 'WorkLifeBalance', 'Attrition' ]
- It defines a list of variables (List) named rel_cols, which contains the names of the columns that we will
use in the analysis.
df_rel = df[rel_cols].copy()
print("Selected dataset shape:", df_rel.shape)
• copy(): function ensures that the new DataFrame is an independent copy of the original
dataset, which prevents modifications to the original data.
• print(): Print the number of new rows and columns) (Only here).
- Here we create a new Dataset called df_rel containing only the important columns we defined in
rel_cols, and out of the 35 columns we have, we will only store 16 in df_rel .
df_rel['Attrition_bin'] = df_rel['Attrition'].map({'Yes': 1, 'No': 0})
print(df_rel[['Attrition', 'Attrition_bin']].head())
• map(): to convert the categorical target variable Attrition into a binary numerical format, named:
Encoding:
▪ If the value = "Yes" → Return 1
▪ If the value = "No" → Return 0
- here, we replaced the original column containing "yes" and "no" with numeric values and stored them in
a variable called df_rel[attrition_bin]. Then, in the head function, we printed the first 5 rows of the main
columns and the numeric columns to ensure that the conversion was done correctly.
OUTPUT:
attr_counts = df_rel['Attrition'].value_counts()
attr_perc = df_rel['Attrition'].value_counts( normalize=True) * 100
print("Attrition counts: \n", attr_counts)
print("Attrition percentages (%):\n", attr_perc.round(2))
OUTPUT:
num_features = [
'Age', 'MonthlyIncome', 'TotalWorkingYears', 'YearsAtCompany',
'YearsInCurrentRole', 'YearsSinceLastPromotion', 'YearsWithCurrManager' ]
- This code defines a list called num_features that contains the names of all numerical used in the
analysis.
- This code plots the count plot for each variable to show the number of each category, the distribution of
the variables, and which category is most frequent. First, we defined the variables in a list and placed
them inside a variable called cat_features. Then, we'll iterate over them using a for loop, creating a new
shape for each variable using the figure function. Next, we'll start plotting with the countplot function,
which shows how many times each category appears and plots them as ordered columns. Inside the
function, we have order, a variable where we put the values counts() and index to sort the categories in
the plot from most frequent to least frequent, in order to easily interpret the shape.
OUTPUT:
Figure 8: Attrition Univariate Analysis.
Figure 9: OverTime Univariate Analysis.
Figure 10: JobRole Univariate Analysis.
Figure 11: Department Univariate Analysis.
Figure 12: EducationField Univariate Analysis.
Figure 13: MaritalStatus Univariate Analysis.
Figure 14: Gender Univariate Analysis.
Figure 15: JobSatisfaction Univariate Analysis.
Figure 16: WorkLifeBalance Univariate Analysis.
Bivariate Boxplots:
for col in num_features:
[Link](figsize=(8, 4))
[Link](x='Attrition', y=col, data=df_rel)
[Link](f"{col} vs Attrition")
plt.tight_layout()
[Link]()
- This code is responsible for drawing a boxplot and analyzing the relationship between the variables we
chose and the attraction to compare employees who will stay and those who will leave. This is a bivariate
EDA, and it will produce 7 images. To generate these 7 images, we start with a for loop that iterates
through all the variables, and the first function is figure to generate a shape for each variable. Then we
start drawing the boxplot using the boxplot function, placing the attraction on the x-axis and the variables
we want to place on the y-axis. The for loop will place a variable on the y-axis each time, and the title is
to show the output the title of the image so that the user can understand the image from it.
OUTPUT:
Figure 17 – Age VS Attrition.
Figure 18 – MonthlyIncome VS Attrition.
Figure 19 – TotalWorkingYears VS Attrition.
Figure 20 – YearsAtCompany VS Attrition.
Figure 21 – YearsInCurrentRole VS Attrition.
Figure 22 – YearsSinceLastPromotion VS Attrition.
- here, we'll also analyze the relationship between variables and attraction by drawing a countplot
to understand how the categories of employees who left and those who stayed differ. For each
variable, we'll generate 8 images. These 8 images will be generated using a for loop that iterates
through each variable. Then, we'll generate a new shape using the figure function and draw the
countplot inside the shape using the countplot function. In the variable x, we'll place the desired
variable that the forloop will loop through, and the hue will be used for attraction. We'll then divide
the variable into two parts, i.e., two columns: a yes column and a no column, so we can compare the
two groups. Next, we'll add a title to the shape for clarity and to facilitate understanding and
familiarity with the diagram. We'll format the shape using the tight_layout function, and then
display the shape and the palette. This palette is used to assign a different color to yes and another
color to no.
OUTPUT:
Figure 24 – OverTime VS Attrition.
Figure 25 – JobRole VS Attrition.
Figure 26 – Department VS Attrition.
Figure 27 – EducationField VS Attrition.
Figure 28 – MaritalStatus VS Attrition.
Figure 29 – Gender VS Attrition.
Figure 30 – JobSatisfaction VS Attrition.
Figure 31 – WorkLifeBalance VS Attrition.
Statistical Analysis:
Features that were selected:
cat_vs_attr_select = [
'OverTime', 'JobSatisfaction',
'WorkLifeBalance'
]
num_features_selected = [
'MonthlyIncome', 'TotalWorkingYears', 'YearsAtCompany', 'YearsSinceLastPromotion',
'YearsWithCurrManager'
]
Numerical vs Attrition:
print('--- Statistical Analysis: Numerical vs Attrition ---')
for col in num_features_selected:
x = df_rel[col].astype(float)
y = df_rel['Attrition_bin'].astype(int)
r, p = [Link](y, x)
print(f"{col}: point-biserial r = {r:.3f}, p-value = {p:.4f}")
group_yes = x[df_rel['Attrition'] == 'Yes']
group_no = x[df_rel['Attrition'] == 'No']
t, p2 = stats.ttest_ind(group_yes, group_no, equal_var=False)
print(f" t-test: t = {t:.3f}, p-value = {p2:.4f}")
OUTPUT:
--- Statistical Analysis: Numerical vs Attrition ---
MonthlyIncome: point-biserial r = -0.160, p-value = 0.0000
t-test: t = -7.483, p-value = 0.0000
TotalWorkingYears: point-biserial r = -0.171, p-value = 0.0000
t-test: t = -7.019, p-value = 0.0000
YearsAtCompany: point-biserial r = -0.134, p-value = 0.0000
t-test: t = -5.283, p-value = 0.0000
YearsSinceLastPromotion: point-biserial r = -0.033, p-value = 0.2058
t-test: t = -1.288, p-value = 0.1987
YearsWithCurrManager: point-biserial r = -0.156, p-value = 0.0000
t-test: t = -6.633, p-value = 0.0000
Categorical vs Attrition:
print('--- Statistical Analysis: Categorical vs Attrition')
for col in cat_vs_attr_select:
table = [Link](df_rel[col], df_rel['Attrition'])
chi2, p, dof, expected = stats.chi2_contingency(table)
print(f"{col}: chi2 = {chi2:.3f}, dof = {dof}, p-value = {p:.4f}")
n = table.to_numpy().sum)(
k = min([Link])
cramers_v = [Link](chi2 / (n * (k - 1)))
print(f" Cramer's V = {cramers_v:.3f}")
• crosstab(): Creates a contingency table showing the frequency distribution between two
categorical variables.
• chi2_contingency(): Performs the Chi-square test to determine whether two categorical variables
are statistically related.
• to_numpy(): Converts a Pandas DataFrame into a NumPy array for numerical computations.
• min([Link]): Returns the smallest dimension of the table (rows or columns), required for
computing Cramer's V.
- Here we will perform a statistical analysis between the categorical variables and the attrition using two
tests: Cramers' V (effect size) and the chi-square test. We will start by iterating over the variables we
selected using a for loop to analyze each variable against its attrition. Then we used the Pandas library
and took the Crosstab function, which creates a table showing the number of cases for each variable in
relation to the attrition. We stored this in the table variable. Next, from the stats library, we called
chi2_contingency. This performs a test to determine whether the variable and the attrition are independent
or related. It returns the test statistic value, and the larger the value, the greater the difference between the
true and expected values. We put this value in the chi2 variable. Then we measure p. If p is less than 0.05,
there is a relationship between the variable and the attrition. The DOF variable is the number of values
allowed to change, which determines the shape of the statistical distribution. The table of expected values
indicates how many attrition values each variable will have if attrition is independent of overtime. And
how much is 'no'? Then we print the chi-square value. Next, we take the variable 'n', which is the actual
sample size, and add it to the table. Using the To_numpy function, we convert the table into a number
array. Then we sum all the numbers using the sum function. Because Cramer's V test requires the smallest
possible dimension, we use the min function and put it inside the variable k. To calculate the strength of
the relationship in Cramer's V test, we use the equation in the code, which has a range from 0 to 1. It's
strong at 0.5 and weak down to 0.1. Then we print the value of Cramer's V.
OUTPUT:
--- Statistical Analysis: Categorical vs Attrition ---
OverTime: chi2 = 87.564, dof = 1, p-value = 0.0000
Cramer's V = 0.244
JobSatisfaction: chi2 = 17.505, dof = 3, p-value = 0.0006
Cramer's V = 0.109
WorkLifeBalance: chi2 = 16.325, dof = 3, p-value = 0.0010
Cramer's V = 0.105
- The statistical analysis largely confirms the patterns observed in the EDA phase and supports the
relevance of most of the selected features in explaining employee attrition.
- Numerical features such as MonthlyIncome, TotalWorkingYears, YearsAtCompany, and
YearsWithCurrManager show statistically significant negative correlations with attrition, indicating that
employees who leave tend to have lower income, less experience, shorter tenure, and weaker relationships
with their managers.
- Categorical features tested with Chi-square, including OverTime, JobSatisfaction, and
WorkLifeBalance, also show significant associations with attrition. Employees working overtime, with
low job satisfaction, or poor work-life balance are more likely to leave.
- YearsSinceLastPromotion, however, did not show a statistically significant relationship with attrition (p
> 0.05), suggesting that promotion timing alone may not be a decisive factor in employee turnover,
although it may still hold practical relevance.
- Overall, the statistical results validate most of the patterns seen in EDA and justify the selection of the
key variables for further analysis.
corr = df_rel[num_features].corr(method='pearson')
[Link](figsize=(10, 6))
[Link](corr, annot=True, fmt=".2f")
[Link]("Correlation Heatmap (Numerical Features)")
plt.tight_layout()
[Link]()
• corr(method='pearson'): The Pearson method is used to measure the strength and direction of the
linear relationship between numerical variables.
- In this code, we calculate the Pearson correlation coefficient between each pair of numerical variables in
num_features using the Pearson method because the data is numerical and the analysis focuses on linear
relationships.
Then, we set the size of the heatmap figure. Next, we draw the heatmap using the heatmap() function and
display the numerical values inside each cell using annot=True.
After that, we add a title to the figure using title() and adjust the spacing between elements using
tight_layout() to prevent overlap. Finally, we display the figure.
OUTPUT:
- This heatmap presents the Pearson correlation matrix between the selected numerical variables. Each
cell represents the correlation coefficient between two variables, ranging from -1 to +1. Values close to
+1 indicate strong positive relationships, values close to -1 indicate strong negative relationships, and
values close to 0 indicate weak relationships. The diagonal values are equal to 1 because each variable is
perfectly correlated with itself.
- From the heatmap, strong positive correlations can be observed between experience-related variables.
For example, TotalWorkingYears and MonthlyIncome show a high correlation of 0.77, indicating that
employees with more experience tend to have higher income. In addition, YearsAtCompany and
YearsWithCurrManager also show a strong correlation of 0.77, reflecting that employees who stay
longer in the company usually remain longer with the same manager. Furthermore, YearsAtCompany
and TotalWorkingYears have a correlation of 0.63, showing a moderate to strong relationship between
overall experience and tenure in the company.
- Other relationships such as between MonthlyIncome and YearsAtCompany (0.51) and
YearsSinceLastPromotion and YearsAtCompany (0.62) indicate moderate associations.
- Overall, this heatmap highlights important relationships between numerical features and helps identify
potential multicollinearity among experience-related variables, which is useful for feature selection before
building predictive models.