-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathexample_sim_call_center_series.py
More file actions
165 lines (124 loc) · 6.81 KB
/
example_sim_call_center_series.py
File metadata and controls
165 lines (124 loc) · 6.81 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
153
154
155
156
157
158
159
160
161
162
163
164
165
# Different aspects of a complex call center model
# Importing modules
# Data collection
import pandas as pd
# Plotting modules
import matplotlib.pyplot as plt
import matplotlib.ticker as formater
# Simulation
from queuesim import SimProcess, run_parallel
from queuesim.models import impatience_and_retry_model_build
# Analytic calculation
from queuesim.analytic import erlang_c_table, erlang_c_ext_table
# Defining general plot style
plt.style.use('seaborn-v0_8')
percent_formater = formater.PercentFormatter(xmax=1, decimals=0)
# Impatience of the callers
# Mean inter-arrival time
mean_i = 100
# Mean service time
mean_s_range = range(60, 121, 2)
# Mean waiting time tolerance range
mean_wt = 300
# Number of operators
c = 1
# Number of arrivals to be simulated
count = 100_000
if __name__ == '__main__':
# Simulation
models, simulators = run_parallel([SimProcess(impatience_and_retry_model_build(mean_i, mean_s, mean_wt, 0, 1, c, count)) for mean_s in mean_s_range])
# Processing results
mu = [1 / model['meanS'] for model in models]
rho_offered = [model['meanS'] / model['meanI'] / model['c'] for model in models]
ENQ = [model['Process'].statistic_queue_length.mean for model in models]
EN = [model['Process'].statistic_wip.mean for model in models]
EW = [model['Dispose'].statistic_client_waiting.mean for model in models]
EV = [model['Dispose'].statistic_client_residence.mean for model in models]
PA = [1 - model['Process'].statistic_success.data['Success'] / model['Process'].statistic_success.count for model in models]
results = pd.DataFrame({'mu': mu, 'rho_offered': rho_offered, 'E[N_Q]': ENQ, 'E[N]': EN, 'E[W]': EW, 'E[V]': EV, 'P(A)': PA})
print("")
print("Impatience of the callers")
print(results)
# Calculating Erlang C formula results
erlang_c_results = erlang_c_table([(1 / mean_i, mu, c) for mu in results[results["rho_offered"] < 1]["mu"]])
erlang_c_ext_results = erlang_c_ext_table([(1 / mean_i, mu, 1 / mean_wt, c, c * 100) for mu in results["mu"]])
# Plotting results
fig, ax = plt.subplots(figsize=(16, 9))
ax.plot(results['rho_offered'], results['E[W]'], 'r', label="E[W] simulation results")
ax.plot(erlang_c_results['rho'], erlang_c_results['E[W]'], 'k', label="E[W] Erlang C formula results")
ax.plot(erlang_c_ext_results['rho_offered'], erlang_c_ext_results['E[W]'], 'b', label="E[W] extended Erlang C formula results")
ax.xaxis.set_major_formatter(percent_formater)
ax.set_ylim(0, max(results['E[W]'] * 1.5))
ax.set_xlabel("Offered utilization")
ax.set_ylabel("Mean waiting time E[W]")
lines1, labels1 = ax.get_legend_handles_labels()
ax2 = ax.twinx()
ax2.plot(results['rho_offered'], results['P(A)'], 'r:', label="P(A) simulation results")
ax2.plot(erlang_c_ext_results['rho_offered'], erlang_c_ext_results['P(A)'], 'b:', label="P(A) extended Erlang C formula results")
ax2.yaxis.set_major_formatter(percent_formater)
ax2.set_ylabel("Waiting cancel probability P(A)")
lines2, labels2 = ax2.get_legend_handles_labels()
ax.set_title("Mean waiting time as a function of the utilization")
ax.legend(lines1 + lines2, labels1 + labels2)
plt.show()
# Calculating E[W] using the simple Erlang C formula (not respecting the impatience) gives completely wrong results.
# So the effect of impatience cannot be ignored. The extended Erlang C formula and the simulation results have a good match.
# We see the waiting time cancellation probability P(A) is increasing at an increasing offered utilization.
# The mean waiting time E[W] is also increasing but not in the same magnitude as it would without impatience.
# If there are waiting time cancellations, the system can also be operated with an offered utilization of more than 100%.
# Retry
# Mean inter-arrival time
mean_i = 100
# Mean service time
mean_s_range = range(60, 121, 2)
# Mean waiting time tolerance range
mean_wt = 300
# Retry
retry_probability = 0.2
mean_retry_delay = 600
# Number of operators
c = 1
# Number of arrivals to be simulated
count = 100_000
if __name__ == '__main__':
# Simulation
models, simulators = run_parallel([SimProcess(impatience_and_retry_model_build(mean_i, mean_s, mean_wt, retry_probability, mean_retry_delay, c, count)) for mean_s in mean_s_range])
# Processing results
mu = [1 / model['meanS'] for model in models]
rho_offered = [model['meanS'] / model['meanI'] / model['c'] for model in models]
ENQ = [model['Process'].statistic_queue_length.mean for model in models]
EN = [model['Process'].statistic_wip.mean for model in models]
EW = [model['Dispose'].statistic_client_waiting.mean for model in models]
EV = [model['Dispose'].statistic_client_residence.mean for model in models]
PA = [1 - model['Process'].statistic_success.data['Success'] / model['Process'].statistic_success.count for model in models]
results = pd.DataFrame({'mu': mu, 'rho_offered': rho_offered, 'E[N_Q]': ENQ, 'E[N]': EN, 'E[W]': EW, 'E[V]': EV, 'P(A)': PA})
print("")
print("Retry")
print(results)
# Calculating Erlang C formula results
erlang_c_ext_results = erlang_c_ext_table([(1 / mean_i, mu, 1 / mean_wt, c, c * 100) for mu in results["mu"]])
# Using the simple Erlang C formula was already in the model above no good idea anymore.
# So when adding also retry, we have left of the simple Erlang C formula here.
# The extended Erlang C formula can handle impatience but no retry.
# So deviations between the simulation and the formula results can be expected.
# Plotting results
_, ax = plt.subplots(figsize=(16, 9))
ax.plot(results['rho_offered'], results['E[W]'], 'r', label="E[W] simulation results")
ax.plot(erlang_c_ext_results['rho_offered'], erlang_c_ext_results['E[W]'], 'b', label="E[W] extended Erlang C formula results")
ax.xaxis.set_major_formatter(percent_formater)
ax.set_ylim(0, max(results['E[W]'] * 1.5))
ax.set_xlabel("Offered utilization")
ax.set_ylabel("Mean waiting time E[W]")
lines1, labels1 = ax.get_legend_handles_labels()
ax2 = ax.twinx()
ax2.plot(results['rho_offered'], results['P(A)'], 'r:', label="P(A) simulation results")
ax2.plot(erlang_c_ext_results['rho_offered'], erlang_c_ext_results['P(A)'], 'b:', label="P(A) extended Erlang C formula results")
ax2.yaxis.set_major_formatter(percent_formater)
ax2.set_ylabel("Waiting cancel probability P(A)")
lines2, labels2 = ax2.get_legend_handles_labels()
ax.set_title("Mean waiting time as a function of the utilization")
ax.legend(lines1 + lines2, labels1 + labels2)
plt.show()
# Only 20% of the callers who have canceled waiting are staring a new call attempt later.
# This increases the average waiting times as can be seen significantly compared to the
# results of the extended Erlang C formula (which does not map retry).