Goal
From HR data, answer:
- Attrition rate
- Attrition by department/job role
- Salary and tenure differences
Analysis pipeline
flowchart LR A["Raw HR data
(attrition 0/1)"] --> B["Rate
(overall attrition %)"] B --> C["Segment
(department, role)"] C --> D["Visualize
(attrition by segment)"] D --> E["Conclude
(interventions)"]
Step 1: Load
import pandas as pd
df = pd.read_csv("data/hr.csv")
print(df.head())Step 2: Attrition rate
rate = df["attrition"].mean() # assuming attrition is 0/1
print("Attrition rate:", rate)Step 3: Bar plots by segment
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 4))
sns.barplot(data=df, x="department", y="attrition")
plt.title("Attrition rate by department")
plt.xticks(rotation=20)
plt.tight_layout()
plt.show()Step 4: Box plots for numeric features
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(7, 4))
sns.boxplot(data=df, x="attrition", y="salary")
plt.title("Salary vs attrition")
plt.tight_layout()
plt.show()Visualize it
Deliverable
A dashboard should include:
- Attrition KPI
- Top segments with high attrition
- Suggested interventions (training, compensation, career path)
Why attrition rate is a mean
Attrition is a yes-or-no outcome for each employee. If you store it as 0 for "stayed" and 1 for "left", then the mean of the column is exactly the share of people who left, which is the attrition rate. A mean of 0.16 means 16 percent left. This trick is used everywhere in analytics: a 0/1 column makes rates, conversion and churn one mean() away, and groupby turns it into a rate per segment.
Real HR files often store the column as the text "Yes" and "No". Convert it first, or the mean will fail:
import pandas as pd
df = pd.DataFrame({
"department": ["Sales", "Sales", "IT", "IT", "HR", "HR"],
"attrition": ["Yes", "No", "No", "No", "Yes", "Yes"],
"salary": [50, 60, 80, 90, 55, 52],
})
df["attrition"] = (df["attrition"] == "Yes").astype(int)
print(df["attrition"].mean()) # 0.5
print(df.groupby("department")["attrition"].mean())
# HR 1.0
# IT 0.0
# Sales 0.5Reading the segment comparison
The overall rate hides differences between groups. A company at 16 percent overall may have one department at 4 percent and another at 30 percent, and only the second needs action. Group by each dimension you care about (department, job role, tenure band) and sort from highest to lowest so the segments needing attention appear first.
Be careful with small groups. A department with three employees can show 100 percent attrition after one person leaves. Always print the group size next to the rate:
summary = df.groupby("department")["attrition"].agg(rate="mean", people="count")
print(summary.sort_values("rate", ascending=False))Interpreting the charts
sns.barplot with a 0/1 target draws the mean per group, which is the rate, and adds a confidence interval line. A long interval signals a small or noisy group. The box plot of salary by attrition compares the middle 50 percent of pay for people who stayed and left. If the "left" box sits lower, pay may be a factor, but that is a hint and not proof, since other variables such as role and tenure can explain the gap.
Common mistakes
- Averaging a text column. Convert "Yes/No" to 1/0 first.
- Ignoring group size, and reporting big percentages from tiny groups.
- Claiming a cause. Lower pay among leavers does not prove pay made them leave.
- Using different definitions of attrition in different charts, such as voluntary in one and all exits in another.
- Charts without labels, which make a dashboard unreadable to managers.
Turning it into a dashboard
Start with the headline KPI, then show the two or three segments with the highest rate, then the drivers (salary, tenure, overtime), and finish with recommendations. Every chart should answer one question that an HR manager would actually ask.
Check your understanding
Why is the attrition rate a mean?
The mean of a 0/1 column is a rate.
Show answer
A โ With 0 for stayed and 1 for left, the mean is the share who left โ The mean of a 0/1 column is a rate.
The raw column holds the text Yes and No. What must you do before averaging?
The mean of text fails, so convert first.
Show answer
A โ Convert it to 1 and 0, for example (df["attrition"] == "Yes").astype(int) โ The mean of text fails, so convert first.
Why print the group size next to each attrition rate?
Rates from tiny groups are unreliable, so show people count alongside rate.
Show answer
A โ A tiny group can show 100 percent attrition after one person leaves โ Rates from tiny groups are unreliable, so show people count alongside rate.
Lower pay among leavers is observed. What is the correct conclusion?
The lesson warns against claiming a cause from an association.
Show answer
A โ It does not prove pay caused them to leave โ The lesson warns against claiming a cause from an association.
๐งช Try It Yourself
Exercise 1 โ Overall attrition rate
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 2 โ Attrition by department
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 3 โ Two-way attrition pivot table
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.