Table Definitions#

The traveler.examples.mtc_mini.tables package contains table definitions for the MTC Mini example, including classes for households and tours, among others. These tables are implemented as subclasses of JaxTable and define the schema for each data table in the model.

from traveler.trees import JaxTable
from traveler.typing import TypeMaker

t = TypeMaker("household")

class Households(JaxTable):
    """
    A class representing households in the ActivityJAX framework.
    """

    household_id: t.Int32  # (3)
    """Unique identifier for the household."""  # (1)

    home_zone_id: t.Int32
    """Identifier for the home zone of the household."""

    income_in_thousands: t.Float32
    hhsize: t.Int8  # (2)
    non_family: t.Bool

    _household_idx_: t.Int32
  1. Docstrings can be added to each field to describe its purpose and any relevant details.

  2. The data type for hhsize is set to Int8, which is a small integer type suitable for household sizes. The validation tool can be set to safely coerce integer values to this type if the underlying data can be represented accurately.

  3. Type annotations are used to define the data types of each field in the Households class. The t.Int32 type is used for 32-bit integers, t.Float32 for 32-bit floating-point numbers, and t.Bool for boolean values. The TypeMaker utility is used to create type aliases for various numeric types, and also ensures that data arrays added to the table have appropriate dimensions (e.g. match the number of rows in the table, or are a scalar value that can be broadcast to the rows).

from traveler.trees import JaxTable
from traveler.typing import TypeMaker

t = TypeMaker("tour")
time_periods = ["EA", "AM", "MD", "PM", "EV"]

class Tours(JaxTable):
    """
    A class representing households in the ActivityJAX framework.
    """

    tour_id: t.Int32
    person_id: t.Int32
    household_id: t.Int32

    otaz: t.Int32
    dtaz: t.Int32

    out_period: t.Categorical(categories=time_periods)  # (3)
    """Skims time period for the outbound (first) half of the tour."""

    in_period: t.Categorical(categories=time_periods)
    """Skims time period for the inbound (second, return) half of the tour."""

    _household_idx_: t.Int32
    _person_idx_: t.Int32

    @lazy  # (1)
    def sov_available(self, store: AbstractStore) -> t.Bool:
        """Whether the SOV mode is available for this tour."""
        odt_skims = skims_data(store, self.otaz, self.dtaz, self.out_period)
        dot_skims = skims_data(store, self.dtaz, self.otaz, self.in_period)
        return (odt_skims("SOV_TIME") > 0) & (dot_skims("SOV_TIME") > 0)

    is_joint: Endog("tour_preprocessor", t.Bool)  # (2)
    """Whether this is a joint tour."""
  1. The lazy decorator is used to define a lazy-loaded property that computes a new table column only when demanded. This allows for efficient computation and avoids unnecessary calculations. The computed column is cached in the table, so it is only computed once per table row, and can be used in subsequent computations without recomputing it.

  2. The Endog annotation is used to define an endogenous variable that is computed by some step that is part of the model. Data validation will expect that the endogenous variable does not need to exist unless that step has been run, but if that step has been run, the variable will be validated for shape and dtype.

  3. The Categorical type is used to define a categorical variable with nominal or ordinal categories. This is useful for representing discrete choices or categories in the data, such as time periods in this case. Data validation will ensure that the categories in the data match the defined categories, and will raise an error if they do not match.