Use cases
This package is built for two main purposes: simulation and revenue management.
Each case typically correspond to different types of users:
In simulation contexts, researchers evaluate new algorithms aiming at better pricing strategies given specific demand patterns.
In production contexts, historic and real-time data is periodically uploaded to servers where it is processed for optimization.
Simulation
The typical simulation worfklow consists on the following steps:
Create and upload to server customers models to emulate customers behavior.
Create and upload to server pricing models to evaluate carriers pricing strategies.
Create core static items and schedules, possibly with multiple carriers competing for the same routes.
Create parametric filters to associate previous models with previous static items.
Upload parametric filters to server.
Upload core static items and schedules to server.
Trigger simulation, possibly stopping at specific dates of interest before its full termination.
Fetch flight data from server to analyze results
Following script is a fully working example that loads local data, triggers a simulation and fetches the results.
import asyncio, os, pathlib
import pandas as pd
from rmlab import API
_DataSamplesPath = "[DATA-SAMPLES-PATH]"
async def main():
# ---- Create an API context manager.
# Will read environment variables: RMLAB_WORKGROUP, RMLAB_USERNAME, RMLAB_PASSWORD
async with API() as api:
# ---- Print which scenarios we can access
print(f"Available scenarios: {api.scenarios}")
# ---- We'll work on the first scenario
scen_id = api.scenarios[0]
# ---- Remove any previous data, so we are sure that we work on a clean scenario
await api.remove_data_full(scen_id)
# ---- Upload customers models to server
await api.upload_batch_customers_models(
scen_id,
request_models=[
_DataSamplesPath + "/basic/customers_request.poisson_bl1.json"
],
choice_models=[
_DataSamplesPath + "/basic/customers_choice.mnl_bl1.json"
],
)
# ---- Upload pricing models to server
await api.upload_batch_pricing_models(
scen_id,
range_models=[_DataSamplesPath + "/basic/pricing_range.sample.json"],
behavior_models=[
_DataSamplesPath + "/basic/pricing_behavior.sample.json"
],
optimizer_models=[
_DataSamplesPath + "/basic/pricing_optimizer.sample.json"
],
)
# ---- Upload parametric filters to bind previous models
await api.upload_parametric_filters(
scen_id, _DataSamplesPath + "/basic/table.pfilter.json"
)
# ---- Upload core items to server
await api.upload_batch_core(
scen_id,
aircraft_items=_DataSamplesPath + "/basic/table.aircraft.csv",
airline_items=_DataSamplesPath + "/basic/table.airline.csv",
airport_items=_DataSamplesPath + "/basic/table.airport.csv",
city_items=_DataSamplesPath + "/basic/table.city.csv",
country_items=_DataSamplesPath + "/basic/table.country.csv",
schedule_items=_DataSamplesPath + "/basic/table.schedule.csv",
)
# ---- Print the status after loading data
(
scen_dates,
scen_items_count,
scen_schedules_count,
scen_flights_count,
) = await api.fetch_info(scen_id)
print(f"DATES {scen_dates}")
print(f"ITEMS COUNT {scen_items_count}")
print(f"SCHEDULES COUNT {scen_schedules_count}")
print(f"FLIGHTS COUNT {scen_flights_count}")
# ---- Start running the simulation on our scenario
print("Running simulation...")
await api.trigger_simulation(scen_id)
print("Simulation finished")
# ---- Analyze the results of simulation on a specific citysector
# ---- Fetch all the citysectors of the simulation
csc_items = await api.fetch_citysectors(scen_id)
# ---- We'll focus on the first citysector
csc = csc_items[0]
print(f"Looking at citysector {csc}")
# ---- Fetch all flights ids of that citysector
flights_ids = await api.fetch_flights_ids(scen_id, citysector_id=csc.id)
# ---- Fetch the books of the first three flights
some_flights_ids = flights_ids[0:3]
print(f"Fetching data of flights {some_flights_ids}")
flights_data = await api.fetch_flights_data_historic(
scen_id, some_flights_ids, citysector_id=csc.id
)
# Once we don't need to interact with API anymore
# we can get out of the indented context
# ---- We'll analyze the results of the first flight
flight_books = flights_data[0]
print(f"Looking at books of flight {flight_books.id}")
occupation_data = pd.DataFrame(
data={
"Time": flight_books.timestamps_array,
"Cumulated occupation": flight_books.cumulated_seats_array,
}
).set_index("Time")
print(f"Occupation data:\n{occupation_data}")
revenue_data = pd.DataFrame(
data={
"Time": flight_books.timestamps_array,
"Cumulated revenue (cents)": flight_books.cumulated_revenue_array,
}
).set_index("Time")
print(f"Revenue data:\n{revenue_data}")
if __name__ == "__main__":
asyncio.run(main())
The input data files can be downloaded from RMLab Samples.
Assessing optimization in production
It's good to be scientific in order to assess the performance of the optimization algorithms in production, so we may want to take the following steps:
Design an experiment by separating flights in two groups ("test" and "control") with similar characteristics. For instance, looking at flights in separate markets (so they don't interact) but with similar demand and competition patterns):
Flights in test group will be applied rmlab algorithms.
Flights in control group will behave as normal.
The experiment period ends when the last flight departs.
Regarding flights in test group, upload their historic data (ie: equivalent flights in previous years). Better discard COVID-19 data.
Assign pricing models to flights of test group that proved to work well on equivalent simulation scenarios (with demand models reflecting real patterns, and competition scenarios).
Do once every day until experiment ends:
4a. At each day, manually trigger (or schedule) optimization passes at the end of each day for the test group.
4b. Gather the results of daily optimization passes and commit proposed changes to Navitaire (eg: modify fare prices, fare thresholds, ...).
After all test and control flights departed, compare the average revenues of both groups under some measure of statistical significance.
NOTE: The greater the number of flights of the groups, the more reliable will be the results.
NOTE: Experimenters can take step 4b manually, so that they are more confident that nothing horribly wrong is being committed to Navitaire. However, steps 4 can be automated in single python program using rmlab.API
commands running in the background without requiring any intervention from the experimenter.
Automated revenue management in production
Once the performance of optimization algorithms has been validated, following steps could be taken:
Upload historic data of multiple flights. This would be done once, at the beginning, using our Upload API.
Establish a optimization schedule to periodically upload daily data on a daily basis to our servers. This could be done in a background program that periodically uses our Upload API once or several times every day.
Use our Operations API in a program that runs once every night, so that fresh daily data previously loaded is processed for optimization.
Fetch the results (using our Fetch API) of the optimization runs, optionally committing them to Navitaire after verification.