-
Notifications
You must be signed in to change notification settings - Fork 8
/
simulation.py
128 lines (105 loc) · 5.48 KB
/
simulation.py
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
from numpy import random
from employee import Employee
from result import Result
class Simulation:
def __init__(self, num_simulations, attrition, iterations_per_simulation, promotion_bias,
num_positions_at_level, bias_favors_this_gender):
self.num_simulations = num_simulations
self.attrition = attrition
self.iterations_per_simulation = iterations_per_simulation
self.promotion_bias = promotion_bias
self.num_positions_at_level = num_positions_at_level
self.bias_favors_this_gender = bias_favors_this_gender
self.num_employee_levels = len(num_positions_at_level)
self.init_employees()
self.hire()
def init_employees(self):
"""Build up mapping of levels with an empty array, which will
eventually be populated with Employees"""
self.levels_to_employees = {}
for i in range(0, self.num_employee_levels):
self.levels_to_employees[i] = []
def hire(self):
"""Populates levels_to_employees with 50% male/50% female employees"""
gender = ['men', 'women']
#Randomly assign gender to first employee
next_gender = random.choice(gender)
level = 0
for positions in self.num_positions_at_level:
employee_list_at_level = self.levels_to_employees.get(level)
append = employee_list_at_level.append
if employee_list_at_level is not None:
while len(employee_list_at_level) < positions:
append(Employee(next_gender))
if next_gender == "women":
next_gender = "men"
else:
next_gender = "women"
level += 1
def run(self):
"""Run simulation"""
for _ in xrange(0, self.iterations_per_simulation):
self.attrit()
self.talent_review()
self.promote()
self.hire()
def talent_review(self):
"""Looks at each employee object in dictionary, checks gender and gives
random performance rating"""
for employee_list in self.levels_to_employees.values():
for employee in employee_list:
new_rating = random.normal(10, 1)
bias = (self.promotion_bias/100.0) + 1
# print "bias calculation", bias
if employee.gender == self.bias_favors_this_gender:
previous_rating = employee.rating/2
# Saves updated rating to Employee object
employee.rating =previous_rating + (new_rating * bias)
else:
previous_rating = employee.rating/2
# Saves updated rating to Employee object
employee.rating = previous_rating + new_rating
def attrit(self):
"""Looks at each employee in dictionary and randomly retains employees
based on global attrition rate"""
for level in range(self.num_employee_levels):
employee_list_at_level = self.levels_to_employees.get(level)
num_employees_at_level = len(employee_list_at_level)
num_employees_to_retain = int(num_employees_at_level * ((100 - self.attrition)/100.0))
indices_to_retain = random.choice(range(num_employees_at_level), num_employees_to_retain)
retained_employees = []
for i in indices_to_retain:
retained_employees.append(employee_list_at_level[i])
self.levels_to_employees[level] = retained_employees
def promote(self):
"""Starts at highest level and checks for open positions, then removes the top
employees from the level below to fill the open positions. Continues this process through
each lower level. Only the entry level will have open positions at the end of this method."""
for i in range(self.num_employee_levels - 1, 0, -1):
promote_to_level = i
promote_from_level = i - 1
promote_from_employees = self.levels_to_employees.get(promote_from_level)
promote_to_employees = self.levels_to_employees.get(promote_to_level)
promote_from_employees.sort(key=lambda x: x.rating, reverse=True)
num_candidates = len(promote_from_employees)
total_positions = self.num_positions_at_level[promote_to_level]
filled_positions = len(promote_to_employees)
open_positions = total_positions - filled_positions
num_promotions = min(num_candidates, open_positions)
candidates_to_promote = promote_from_employees[:num_promotions]
# Saves revised data back to the dictionary
self.levels_to_employees[promote_from_level] = promote_from_employees[num_promotions:]
self.levels_to_employees[promote_to_level] = promote_to_employees + candidates_to_promote
def get_result(self):
"""Counts number of men and women at each level and saves totals to
the corresponding list."""
total_men_at_level = [0] * self.num_employee_levels
total_women_at_level = [0] * self.num_employee_levels
for level in range(self.num_employee_levels):
employee_list = self.levels_to_employees.get(level)
for employee in employee_list:
if employee.gender == "men":
total_men_at_level[level] += 1
else:
total_women_at_level[level] += 1
return Result(total_men_at_level, total_women_at_level)