-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathexample_sim_call_center_series.py
166 lines (125 loc) · 6.81 KB
/
example_sim_call_center_series.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
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
166
# 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
import seaborn as sns
# Simulation
from queuesim import SimProcess, run_parallel
from queuesim.models import impatience_and_retry_model_build
# Analytic calcution
from queuesim.analytic import erlang_c_table, erlang_c_ext_table
# Defining general plot style
sns.set()
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 cancelation 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 cancelations, 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
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_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).