-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_data.py
More file actions
152 lines (132 loc) · 6.26 KB
/
Copy pathgenerate_data.py
File metadata and controls
152 lines (132 loc) · 6.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
generate_data.py
-----------------
Generates a realistic SYNTHETIC dataset for the Student Placement Salary
Prediction project.
Why synthetic data, and why it's still meaningful:
A public, clean, feature-rich "placement salary" dataset with the exact
feature set an interviewer would expect (CGPA, DSA score, communication
score, etc.) does not reliably exist. Instead of using a toy/unrealistic
random dataset, this script builds salary as an EXPLICIT linear
combination of the input features (each with a defensible real-world
weight), then adds Gaussian noise and a few realistic non-idealities
(placement status gating, backlog penalty, diminishing returns on
attendance, a mild non-linear interaction between CGPA and coding score).
This keeps the ground-truth relationship close to linear (so Linear/Ridge/
Lasso are legitimately the right model family to try -- not a strawman),
while still requiring real preprocessing, scaling and regularization to
recover it well, which is exactly what the project needs to demonstrate.
Assumptions used to generate salary (documented for the README / interviews):
- Base salary starts around 3.0 LPA (typical fresher floor at tier-2/3
engineering colleges in India).
- CGPA is the strongest driver (weight reflects how strongly recruiters at
this college tier filter/anchor on CGPA).
- Coding/DSA score and technical interview score are the next strongest
drivers, reflecting product/service-based company hiring bars.
- Internships, projects and certifications have smaller but positive,
additive effects (differentiators, not primary filters).
- Backlogs apply a MULTIPLICATIVE penalty (each active backlog reduces
salary by ~4%), because backlogs are a gating factor in real placement
cells, not a smooth linear one.
- Placement Status is a downstream (not causal) label: students below a
composite eligibility threshold are marked 'Not Placed' and their salary
is set to 0, mirroring how real placement datasets look.
- Gaussian noise (mean 0) is added to reflect interviewer variance, panel
subjectivity, and company-specific pay bands that features can't fully
explain.
"""
import numpy as np
import pandas as pd
import os
RANDOM_STATE = 42
N_SAMPLES = 1500
def generate_dataset(n_samples: int = N_SAMPLES, random_state: int = RANDOM_STATE) -> pd.DataFrame:
rng = np.random.default_rng(random_state)
# ---- Academic features ----
tenth_pct = np.clip(rng.normal(78, 8, n_samples), 45, 99)
twelfth_pct = np.clip(tenth_pct + rng.normal(0, 6, n_samples), 40, 99)
cgpa = np.clip(
4 + (tenth_pct + twelfth_pct) / 100 * 3 + rng.normal(0, 0.6, n_samples),
4.5, 10.0
)
# ---- Skill / activity features ----
internships = rng.poisson(1.1, n_samples).clip(0, 5)
projects = rng.poisson(2.5, n_samples).clip(0, 8)
certifications = rng.poisson(2.0, n_samples).clip(0, 10)
hackathons = rng.poisson(0.8, n_samples).clip(0, 6)
coding_score = np.clip(
30 + cgpa * 5 + rng.normal(0, 12, n_samples), 0, 100
)
aptitude_score = np.clip(rng.normal(65, 15, n_samples), 0, 100)
communication_score = np.clip(rng.normal(68, 14, n_samples), 0, 100)
dsa_score = np.clip(
20 + coding_score * 0.6 + rng.normal(0, 10, n_samples), 0, 100
)
technical_interview_score = np.clip(
0.4 * coding_score + 0.3 * dsa_score + 0.3 * aptitude_score
+ rng.normal(0, 8, n_samples), 0, 100
)
backlogs = rng.poisson(0.4, n_samples).clip(0, 6)
attendance_pct = np.clip(rng.normal(82, 9, n_samples), 50, 100)
placement_training_hours = np.clip(rng.normal(40, 18, n_samples), 0, 120)
work_experience_months = rng.poisson(1.5, n_samples).clip(0, 24)
# ---- Ground-truth salary generation (mostly linear + documented noise) ----
salary = (
3.0
+ 0.75 * (cgpa - 5) # strongest driver
+ 0.022 * coding_score
+ 0.020 * dsa_score
+ 0.018 * technical_interview_score
+ 0.30 * internships
+ 0.15 * projects
+ 0.10 * certifications
+ 0.20 * hackathons
+ 0.010 * aptitude_score
+ 0.008 * communication_score
+ 0.006 * attendance_pct
+ 0.012 * placement_training_hours
+ 0.06 * work_experience_months
)
# Backlog penalty: multiplicative, ~4% reduction per backlog
salary = salary * (0.96 ** backlogs)
# Mild non-linear interaction: strong coders with high CGPA get an extra bump
salary += 0.004 * (cgpa - 7).clip(0, None) * coding_score
# Noise reflecting real-world variance
salary += rng.normal(0, 1.1, n_samples)
salary = np.clip(salary, 2.5, 45)
# ---- Placement status (downstream label, not a feature used for prediction) ----
eligibility_score = (
0.5 * cgpa + 0.02 * technical_interview_score + 0.02 * dsa_score
- 0.8 * backlogs
)
threshold = np.percentile(eligibility_score, 12) # ~12% not placed
placed = eligibility_score >= threshold
salary_final = np.where(placed, salary, 0.0)
df = pd.DataFrame({
"CGPA": np.round(cgpa, 2),
"Tenth_Percentage": np.round(tenth_pct, 2),
"Twelfth_Percentage": np.round(twelfth_pct, 2),
"Internships": internships.astype(int),
"Projects": projects.astype(int),
"Certifications": certifications.astype(int),
"Coding_Score": np.round(coding_score, 2),
"Aptitude_Score": np.round(aptitude_score, 2),
"Communication_Score": np.round(communication_score, 2),
"Technical_Interview_Score": np.round(technical_interview_score, 2),
"Backlogs": backlogs.astype(int),
"Attendance_Percentage": np.round(attendance_pct, 2),
"Hackathons_Participated": hackathons.astype(int),
"DSA_Score": np.round(dsa_score, 2),
"Work_Experience_Months": work_experience_months.astype(int),
"Placement_Training_Hours": np.round(placement_training_hours, 2),
"Placement_Status": np.where(placed, "Placed", "Not Placed"),
"Salary_LPA": np.round(salary_final, 2),
})
return df
if __name__ == "__main__":
df = generate_dataset()
os.makedirs("data", exist_ok=True)
out_path = os.path.join("data", "student_placement_salary.csv")
df.to_csv(out_path, index=False)
print(f"Saved {len(df)} rows to {out_path}")
print(df.describe(include='all').T)