Contrasting Runs¶
Often when we run PassengerSim, we’re not really interested in just the result of a simulation run
– we are interested in how the results change across two or more slightly different runs. For
this kind of analysis, PassengerSim offers a
Contrast tool, which bundles multiple
SimulationTables objects together,
and provides comparison figures mirroring many of the single-simulation visualizations.
import passengersim as pax
from passengersim.contrast import Contrast
summary0 = pax.from_file(pax.demo_output("US25/baseline"))
summary1 = pax.from_file(pax.demo_output("US25/high-demand"))
comps = Contrast({"Baseline": summary0, "High Demand": summary1})
Carrier Revenues¶
Contrast.fig_carrier_revenues.
Display the average revenues by carrier.
comps.fig_carrier_revenues()
Carrier Load Factors¶
Contrast.fig_carrier_load_factors.
Display the average system load factors by carrier. The system load factor is calculated based on ASM and RPM figures for the carrier, which weights larger capacity vehicles and longer distance legs more heavily, to reflect their larger relative importance in evaluating carrier performance.
comps.fig_carrier_load_factors()
comps.fig_carrier_load_factors(load_measure="avg_leg_lf")
Carrier Yields¶
Display the average yield (revenue per passenger mile) by carrier.
comps.fig_carrier_yields()
Fare Class Mix¶
Display the fare class mix by carrier.
comps.fig_fare_class_mix()
/Users/jpn/Git/pax/packages/passengersim-public/src/passengersim/reporting/__init__.py:66: UserWarning: error in getting data from 'Baseline': "['fare_class', 'brand'] not in index"
fig = func(*args, **kwargs)
/Users/jpn/Git/pax/packages/passengersim-public/src/passengersim/reporting/__init__.py:66: UserWarning: error in getting data from 'High Demand': "['fare_class', 'brand'] not in index"
fig = func(*args, **kwargs)
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[7], line 1
----> 1 comps.fig_fare_class_mix()
File ~/Git/pax/packages/passengersim-public/src/passengersim/reporting/__init__.py:66, in report_figure.<locals>.report_figure_wrapper(report, trace, *args, **kwargs)
64 @wraps(func)
65 def report_figure_wrapper(*args, report=None, trace=None, **kwargs):
---> 66 fig = func(*args, **kwargs)
67 if report is not None:
68 report.add_figure(fig)
File ~/Git/pax/packages/passengersim-public/src/passengersim/contrast/__init__.py:1344, in fig_fare_class_mix(summaries, raw_df, also_df, label_threshold, group_by)
1309 @report_figure
1310 def fig_fare_class_mix(
1311 summaries: dict[str, SimulationTables],
(...) 1316 group_by: str = "",
1317 ) -> alt.Chart | pd.DataFrame | tuple[alt.Chart, pd.DataFrame]:
1318 """Generate a figure contrasting carrier fare class mix for one or more runs.
1319
1320 Displays stacked bar charts of average seats sold per booking class for
(...) 1342 ``(chart, dataframe)`` tuple if ``also_df`` is True.
1343 """
-> 1344 df = _assemble(summaries, "fare_class_mix")
1345 source_order = list(summaries.keys())
1347 sub_title = ""
File ~/Git/pax/packages/passengersim-public/src/passengersim/contrast/__init__.py:338, in _assemble(summaries, base, **kwargs)
335 summaries_[k] = v
336 if len(summaries_) == 0:
337 # no data recovered, re-raise last exception
--> 338 raise last_exception
339 return pd.concat(summaries_, names=["source"]).reset_index(level="source")
File ~/Git/pax/packages/passengersim-public/src/passengersim/contrast/__init__.py:324, in _assemble(summaries, base, **kwargs)
322 if (fun := getattr(v, f"fig_{base}", None)) is not None:
323 try:
--> 324 summaries_[k] = fun(raw_df=True, **kwargs)
325 except Exception as err:
326 last_exception = err
File ~/Git/pax/packages/passengersim-public/src/passengersim/reporting/__init__.py:66, in report_figure.<locals>.report_figure_wrapper(report, trace, *args, **kwargs)
64 @wraps(func)
65 def report_figure_wrapper(*args, report=None, trace=None, **kwargs):
---> 66 fig = func(*args, **kwargs)
67 if report is not None:
68 report.add_figure(fig)
File ~/Git/pax/packages/passengersim-public/src/passengersim/summaries/fare_class_mix.py:165, in SimTabFareClassMix.fig_fare_class_mix(self, raw_df, also_df, label_threshold, title, group_by)
163 df = self.fare_class_mix / self.n_total_samples
164 df = df.rename(columns={"sold": "avg_sold"})
--> 165 df = df.reset_index()[["carrier", "fare_class", "booking_class", "brand", "avg_sold"]]
166 else:
167 return None
File ~/Git/pax/.venv/lib/python3.12/site-packages/pandas/core/frame.py:4119, in DataFrame.__getitem__(self, key)
4115 indexer = [indexer]
4116 else:
4117 if is_iterator(key):
4118 key = list(key)
-> 4119 indexer = self.columns._get_indexer_strict(key, "columns")[1]
4120
4121 # take() does not accept boolean indexers
4122 if getattr(indexer, "dtype", None) == bool:
File ~/Git/pax/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:6212, in Index._get_indexer_strict(self, key, axis_name)
6209 else:
6210 keyarr, indexer, new_indexer = self._reindex_non_unique(keyarr)
-> 6212 self._raise_if_missing(keyarr, indexer, axis_name)
6214 keyarr = self.take(indexer)
6215 if isinstance(key, Index):
6216 # GH 42790 - Preserve name from an Index
File ~/Git/pax/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:6264, in Index._raise_if_missing(self, key, indexer, axis_name)
6261 raise KeyError(f"None of [{key}] are in the [{axis_name}]")
6263 not_found = list(ensure_index(key)[missing_mask.nonzero()[0]].unique())
-> 6264 raise KeyError(f"{not_found} not in index")
KeyError: "['fare_class', 'brand'] not in index"
Bookings by Timeframe¶
Contrast.fig_bookings_by_timeframe.
Display the bookings by timeframe, optionally filtered by carrier and broken down by fare class.
comps.fig_bookings_by_timeframe(by_carrier="Bison")
comps.fig_bookings_by_timeframe(by_carrier="Bison", by_class=True, source_labels=True)
Demand to Come¶
Display the demand to come (total demand, not just bookings) over time, showing how anticipated demand evolves as the departure date approaches. This figure can show the mean, standard deviation, or both.
comps.fig_demand_to_come(["mean", "std"])
Bid Prices¶
Contrast.fig_bid_price_history.
Display the bid price history over time, showing how bid prices evolve as the departure date approaches across different simulation scenarios.
comps_3mkt.fig_bid_price_history()
Displacement Costs¶
Contrast.fig_displacement_history.
Display the displacement cost history over time, showing how displacement costs evolve as the departure date approaches across different simulation scenarios. Note that not all bid price based RM algorithms compute a displacement cost, in which case these values will be reported as zeros.
comps_3mkt.fig_displacement_history()