Source code for passengersim.summaries.demand_to_come

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np
import pandas as pd

from passengersim.database import common_queries
from passengersim.utils.nested_dict import from_nested_dict

from .generic import DatabaseTableItem, GenericSimulationTables, SimulationTableItem
from .tools import aggregate_by_concat_dataframe, combine_sigmas

if TYPE_CHECKING:
    from passengersim import Simulation

    from . import SimulationTables


[docs] def extract_demand_to_come_summary(sim: Simulation) -> pd.DataFrame: """Extract demand-to-come summary data from a Simulation.""" eng = sim.eng raw = eng.summary_demand_to_come() df = ( from_nested_dict(raw, ["segment", "days_prior", "metric"]) .sort_index(ascending=[True, False]) .rename(columns={"mean": "mean_future_demand", "stdev": "stdev_future_demand"}) ) return df
[docs] def aggregate_demand_to_come_summary( summaries: list[SimulationTables], ) -> pd.DataFrame | None: frames = [] for s in summaries: frame = getattr(s, "_raw_demand_to_come_summary", None) if isinstance(frame, Exception): raise frame if frame is not None: frames.append((frame, s.n_total_samples)) while len(frames) > 1: df, df_n = frames[0] other, other_n = frames.pop(1) df["stdev_future_demand"] = np.sqrt( combine_sigmas( df["stdev_future_demand"], other["stdev_future_demand"], df["mean_future_demand"], other["mean_future_demand"], df_n, other_n, ddof=1, ) ) df["mean_future_demand"] = (df["mean_future_demand"] * df_n + other["mean_future_demand"] * other_n) / ( df_n + other_n ) frames[0] = (df, df_n + other_n) if frames: return frames[0][0] return None
[docs] class SimTabDemandToCome(GenericSimulationTables): """Container for summary tables and figures extracted from a Simulation. This class is a subclass of GenericSimulationTables, which is defined in the generic module. It lists the items that are available in the SimulationTables class, and provides type hints and (optionally, but ideally) documentation for the data that is stored in each item. """ demand_to_come_summary: pd.DataFrame = SimulationTableItem( aggregation_func=aggregate_demand_to_come_summary, extraction_func=extract_demand_to_come_summary, doc="Demand-to-come summary data.", ) demand_to_come: pd.DataFrame = DatabaseTableItem( aggregation_func=aggregate_by_concat_dataframe("demand_to_come"), query_func=common_queries.demand_to_come, doc="Demand-to-come data.", )
[docs] def aggregate_demand_history(self, by_segment: bool = True) -> pd.Series: """ Total demand by sample, aggregated over all markets. Parameters ---------- by_segment : bool, default True Aggregate by segment. If false, segments are also aggregated. Returns ------- pandas.Series Total demand, indexed by trial, sample, and segment (e.g. business/leisure). """ groupbys = ["trial", "sample"] if by_segment: groupbys.append("segment") df = self.demand_to_come if isinstance(df, Exception): raise df if not isinstance(df, pd.DataFrame): raise ValueError("demand_to_come is not a DataFrame") return df.iloc[:, 0].groupby(groupbys, observed=False).sum()