Summary Outputs¶
When you run a simulation in PassengerSim, you will get a SimulationTables
object.
This objects embeds a variety of summary infomation from the run.
import passengersim as pax
pax.versions()
passengersim 0.51.dev11+g921f2a8 passengersim.core 0.51.dev2+g02d3ce20
cfg = pax.Config.from_yaml(pax.demo_network("3MKT/08-untrunc-em"))
cfg.simulation_controls.num_samples = 300
cfg.simulation_controls.num_trials = 2
sim = pax.Simulation(cfg)
summary = sim.run()
Task Completed after 3.08 seconds
The simple repr
of this object has a bit of information about what data is in there.
You can view this in a Jupyter notebook by putting the object as the last line of a cell
(or just by itself):
summary
<passengersim.summaries.SimulationTables created on 2025-02-21> * bid_price_history (256 row DataFrame) * carriers (2 row DataFrame) * carrier_history2 (800 row DataFrame) * forecast_accuracy (NoneType) * demand_to_come_summary (34 row DataFrame) * demands (6 row DataFrame) * displacement_history (68 row DataFrame) * fare_class_mix (12 row DataFrame) * legbuckets (48 row DataFrame) * legs (8 row DataFrame) * pathclasses (72 row DataFrame) * path_legs (16 row DataFrame) * paths (12 row DataFrame) * segmentation_by_timeframe (304 row DataFrame) <*>
We can see here there are a variety of tables stored as pandas DataFrames. We
can access the raw values of any of these dataframes directly in Python as an
attribute on the SimulationTables
object.
summary.carriers
control | avg_rev | avg_sold | truncation_rule | avg_leg_lf | asm | rpm | ancillary_rev | avg_local_leg_pax | avg_total_leg_pax | cp_sold | cp_revenue | avg_price | yield | rasm | sys_lf | local_pct_leg_pax | local_pct_bookings | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
carrier | ||||||||||||||||||
AL1 | leg | 90920.1875 | 285.9675 | 3 | 86.790729 | 590302.497298 | 519508.421436 | 0.0 | 188.8825 | 383.0525 | 0.0 | 0.0 | 317.938883 | 0.175012 | 0.154023 | 88.007153 | 49.309820 | 66.050338 |
AL2 | leg | 90614.6250 | 285.3625 | 3 | 86.622292 | 590302.497298 | 519025.918143 | 0.0 | 188.3300 | 382.3950 | 0.0 | 0.0 | 317.542161 | 0.174586 | 0.153505 | 87.925415 | 49.250121 | 65.996759 |
There is also some metadata stored on the summary, which can be accessed via the metadata
method.
summary.metadata()
{'time.created': '2025-02-21T18:25:35.944776+00:00', 'machine.system': 'Darwin', 'machine.release': '24.3.0', 'machine.version': 'Darwin Kernel Version 24.3.0: Thu Jan 2 20:24:23 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T8122', 'machine.machine': 'arm64', 'machine.processor': 'arm', 'machine.architecture': ('64bit', ''), 'machine.node': 'Nightshade.local', 'machine.platform': 'macOS-15.3.1-arm64-arm-64bit', 'machine.python_version': '3.12.0', 'machine.cpu_count': 8, 'version.passengersim': '0.51.dev11+g921f2a8', 'version.passengersim_core': '0.51.dev2+g02d3ce20'}
You can access the metadata for a specific key by passing that key as an argument to the method.
summary.metadata("time")
{'created': '2025-02-21T18:25:35.944776+00:00'}
Saving and Restoring¶
Running a PassengerSim simulation on a practical network can take some time,
so it is desirable to save your results after a run. This allows you to come
back to the analyze those results later, or compare against other future scenario
permutations. Saving outputs is done efficiently in the pickle
format by
using the to_pickle
method. This will automatically also use LZ4 compression
if available, which will reduce the filesize considerably.
summary.to_pickle("saved-outputs/summary")
PosixPath('saved-outputs/summary.20250221-122536.pkl.lz4')
The method will automatically add the appropriate file extension and write the file to disk. By default, it will also add a timestamp, so that you will not overwrite existing similar files.
from passengersim.utils.show_dir import display_directory_contents
display_directory_contents("saved-outputs")
saved-outputs/ summary.20250221-122536.pkl.lz4
Restoring from this pickle file can be done, surprisingly enough, using the
from_pickle
method. You can call this method with the same filename as
to_pickle
, and it will load the file with the most recent timestamp if there
is one or more matching file(s) with various timestamps. To load a specific
pickle file that may or may not be the most recent, you can call this method
with the complete actual filename, including the timestamp and ".pkl" or
".pkl.lz4" suffix.
resummary = pax.SimulationTables.from_pickle("saved-outputs/summary")
resummary
<passengersim.summaries.SimulationTables created on 2025-02-21> * bid_price_history (256 row DataFrame) * carriers (2 row DataFrame) * carrier_history2 (800 row DataFrame) * forecast_accuracy (NoneType) * demand_to_come_summary (34 row DataFrame) * demands (6 row DataFrame) * displacement_history (68 row DataFrame) * fare_class_mix (12 row DataFrame) * legbuckets (48 row DataFrame) * legs (8 row DataFrame) * pathclasses (72 row DataFrame) * path_legs (16 row DataFrame) * paths (12 row DataFrame) * segmentation_by_timeframe (304 row DataFrame) <*>
Once loaded, you can confirm which file was loaded, as that is added to the metadata at load time:
resummary.metadata("loaded")
{'filename': 'saved-outputs/summary.20250221-122536.pkl.lz4', 'time': '2025-02-21T18:25:36.027438+00:00'}
Lazy Data Access¶
When it is time to load the data, the pickle format is an all-or-nothing file format:
if you only need a tiny bit of information from the file (e.g. what was AL1's revenue?)
you still need to read all the data in the file: legs, paths, etc. PassengerSim also
includes an alternative file format that stores the various data tables from the
SimulationTables
in independently loadable sections. This format otherwise works the
same as the pickle file, but uses to_file
and from_file
, and it creates files with
a "*.pxsim" extension.
summary.to_file("saved-outputs/summary")
display_directory_contents("saved-outputs")
saved-outputs/ summary.20250221-122536.pkl.lz4 summary.20250221-122536.pxsim
When opening the file, only the most basic metadata is loaded by the from_file
method,
and the rest is identified as available on demand from storage.
resummary2 = pax.SimulationTables.from_file("saved-outputs/summary")
resummary2
<passengersim.summaries.SimulationTables created on 2025-02-21> * bid_price_history (available in file storage) * carrier_history2 (available in file storage) * carriers (available in file storage) * demand_to_come_summary (available in file storage) * demands (available in file storage) * displacement_history (available in file storage) * fare_class_mix (available in file storage) * forecast_accuracy (available in file storage) * legbuckets (available in file storage) * legs (available in file storage) * path_legs (available in file storage) * pathclasses (available in file storage) * paths (available in file storage) * segmentation_by_timeframe (available in file storage) <*>
The metadata about this file is available under the "store" key instead of "loaded".
resummary2.metadata("store")
{'filename': 'saved-outputs/summary.20250221-122536.pxsim'}
Accessing data will load just that table from the file. this includes accessing a table explicity (by calling for it), or implicitly (e.g. by creating a figure using the data).
resummary2.carriers
control | avg_rev | avg_sold | truncation_rule | avg_leg_lf | asm | rpm | ancillary_rev | avg_local_leg_pax | avg_total_leg_pax | cp_sold | cp_revenue | avg_price | yield | rasm | sys_lf | local_pct_leg_pax | local_pct_bookings | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
carrier | ||||||||||||||||||
AL1 | leg | 90920.1875 | 285.9675 | 3 | 86.790729 | 590302.497298 | 519508.421436 | 0.0 | 188.8825 | 383.0525 | 0.0 | 0.0 | 317.938883 | 0.175012 | 0.154023 | 88.007153 | 49.309820 | 66.050338 |
AL2 | leg | 90614.6250 | 285.3625 | 3 | 86.622292 | 590302.497298 | 519025.918143 | 0.0 | 188.3300 | 382.3950 | 0.0 | 0.0 | 317.542161 | 0.174586 | 0.153505 | 87.925415 | 49.250121 | 65.996759 |
resummary2.fig_fare_class_mix()
We can see in the objects repr
that the carriers and fare_class_mix tables have been loaded,
but the rest are still only in the storage file.
resummary2
<passengersim.summaries.SimulationTables created on 2025-02-21> * carriers (2 row DataFrame) * fare_class_mix (12 row DataFrame) * bid_price_history (available in file storage) * carrier_history2 (available in file storage) * demand_to_come_summary (available in file storage) * demands (available in file storage) * displacement_history (available in file storage) * forecast_accuracy (available in file storage) * legbuckets (available in file storage) * legs (available in file storage) * path_legs (available in file storage) * pathclasses (available in file storage) * paths (available in file storage) * segmentation_by_timeframe (available in file storage) <*>