Tutorial: comparing scenarios with grids and DataFrames
Planning rarely uses a single set of assumptions. The Multi* estimators evaluate many scenarios at once, and results_to_dataframe turns the results into a tidy table you can sort, filter and plot. Every output below is the real output of the snippet.
Sweeping Erlang C service-level targets
from pyworkforce.queuing import MultiErlangC
from pyworkforce.utils import results_to_dataframe
param_grid = {
"transactions": [100], "aht": [3], "interval": [30],
"asa": [20 / 60], "shrinkage": [0.3],
}
multi = MultiErlangC(param_grid=param_grid, n_jobs=1)
results = multi.required_positions({"service_level": [0.8, 0.9], "max_occupancy": [0.85]})
df = results_to_dataframe(results, multi.required_positions_params)
print(df[["service_level", "service_level_result", "positions", "occupancy"]].round(4).to_string(index=False)) service_level service_level_result positions occupancy
0.8 0.8884 20 0.7143
0.9 0.9415 22 0.6667Note the two service_level columns: the input target stays under service_level, while the achieved value is preserved as service_level_result (pyworkforce suffixes result keys that would otherwise collide with an input name).
How patience changes Erlang A staffing
Here we sweep the patience parameter to see how customer impatience affects required staffing, using MultiErlangA.
from pyworkforce.queuing import MultiErlangA
from pyworkforce.utils import results_to_dataframe
grid = {
"transactions": [100], "aht": [3], "interval": [30],
"asa": [20 / 60], "patience": [3, 5, 10], "shrinkage": [0.3],
}
multi = MultiErlangA(param_grid=grid, n_jobs=1)
results = multi.required_positions(
{"service_level": [0.8], "max_occupancy": [0.85], "max_abandonment": [0.05]}
)
df = results_to_dataframe(results, multi.required_positions_params)
cols = ["patience", "raw_positions", "positions", "service_level_result", "abandonment_probability"]
print(df[cols].round(4).to_string(index=False)) patience raw_positions positions service_level_result abandonment_probability
3 13 19 0.8751 0.0322
5 13 19 0.8582 0.0250
10 13 19 0.8376 0.0165As customers become more patient, fewer abandon, so the achieved service level drops slightly for the same staffing while the abandonment probability falls — useful when deciding how conservative your staffing should be.
Scalar metrics across positions
The Multi* methods that return a single number (like service_level) also work with the grid. Each result lines up with the matching *_params entry.
from pyworkforce.queuing import MultiErlangC
from pyworkforce.utils import results_to_dataframe
multi = MultiErlangC(
param_grid={"transactions": [100], "aht": [3], "interval": [30],
"asa": [20 / 60], "shrinkage": [0.0]},
n_jobs=1,
)
sl = multi.service_level({"positions": [11, 12, 13, 14]})
df = results_to_dataframe(sl, multi.service_level_params)
print(df[["positions", "result"]].round(4).to_string(index=False)) positions result
11 0.3896
12 0.6402
13 0.7956
14 0.8884Scalar results are placed in a result column. From a DataFrame it is a short hop to plotting a staffing curve or exporting a planning table to CSV.