Skip to content

processing.link_trips

Module for linking trips.

Trip Linker Module.

ModeType to AccessEgressMode mapping for transit access/egress.

Maps travel mode types to access/egress mode categories used in transit trip analysis. This mapping is used when aggregating linked trips to classify non-transit segments as access or egress modes for transit journeys.

link_trips(
    unlinked_trips: pl.DataFrame,
    change_mode_enum: str,
    transit_mode_enums: list[str],
    max_dwell_time: float = 120,
    dwell_buffer_distance: float = 100,
    *,
    split_on_occupancy: bool
) -> dict[str, pl.DataFrame]

Link unlinked trip segments into complete journey records.

Detects mode changes and aggregates trip chains by validating spatial and temporal continuity across consecutive trips.

Parameters:

Name Type Description Default
unlinked_trips pl.DataFrame

Individual trip records with person_id, day_id, depart_time, arrive_time, o/d locations, o/d purposes, mode_type.

required
change_mode_enum str

Enum label indicating a mode change purpose.

required
transit_mode_enums list[str]

List of enum labels that count as transit modes.

required
max_dwell_time float

Maximum time gap between trips to link them, in minutes (default: 120).

120
dwell_buffer_distance float

Maximum spatial distance between trips to link, in meters (default: 100).

100
split_on_occupancy bool

Refuse to link two segments whose reported party size differs. A change of occupancy at a stop is evidence that somebody was picked up or dropped off, which makes the stop an activity rather than a transfer. Required, and deliberately without a default: either answer changes what a linked trip is, so the choice belongs to whoever configured the run rather than to this signature.

required

Returns:

Type Description
dict[str, pl.DataFrame]

Dictionary containing: - unlinked_trips: Original trips with added linked_trip_id column - linked_trips: Aggregated journey records with combined attributes

Algorithm
  1. Sort unlinked trips by person, day, and departure time
  2. For each person-day sequence:
    • If previous trip's destination purpose is change_mode_enum, continue current linked trip
    • Validate spatial/temporal continuity:
      • Time gap between trips ≤ max_dwell_time minutes
      • Distance between previous destination and current origin ≤ dwell_buffer_distance meters
      • With split_on_occupancy, the reported party size is unchanged
    • Otherwise, start a new linked trip
  3. Assign globally unique linked_trip_id: the day_id followed by a two-digit sequence number
  1. Group unlinked trips by linked_trip_id
  2. For each linked trip, aggregate:
    • Origin/Destination: First trip's origin, last trip's destination
    • Timing: First depart_time, last arrive_time
    • Distance: Sum of all trip distances
    • Duration: Sum of all trip durations (including dwell time)
    • Purposes: First origin purpose, last destination purpose
    • Mode Logic:
      • If any segment uses a transit mode → the mode of the longest-duration transit segment
      • Otherwise, the mode of the longest-duration segment
    • Transit Details: Count boarding/alighting, aggregate access/egress modes
    • Driver/Passenger: Aggregate from component trips
Notes
  • Links trips when travelers make intermediate stops for mode changes or transfers
  • Preserves full trip detail in unlinked_trips while creating journey-level linked_trips
  • Transit detection ensures multi-modal journeys classified correctly
  • Access/egress mode mapping converts trip modes to transit-specific codes
  • Spatial/temporal thresholds prevent false linkages across separate journeys
link_trip_ids(
    unlinked_trips: pl.DataFrame,
    change_mode_enum: str,
    max_dwell_time: float = 120,
    dwell_buffer_distance: float = 100,
    *,
    split_on_occupancy: bool
) -> pl.DataFrame

Link trips based on purpose and mode in time sequence.

Logic: For each person's day of trips: - Sort trips by departure time - If previous trip's destination purpose is 'change_mode', continue the linked trip; else start a new linked trip. - Assign linked trip IDs accordingly. Linked trip IDs are made globally unique by combining day_id and local linked trip index.

Parameters:

Name Type Description Default
unlinked_trips pl.DataFrame

DataFrame containing trip data

required
change_mode_enum str

Enum label indicating a mode change

required
max_dwell_time float

Maximum time gap between trips to link them (minutes)

120
dwell_buffer_distance float

Maximum distance between trips to link (meters)

100
split_on_occupancy bool

Also break the link where the reported party size changes between segments. Required; see link_trips.

required

Returns:

Type Description
pl.DataFrame

DataFrame with linked_trip_id column added

Raises:

Type Description
ValueError

split_on_occupancy is set but the trips carry no num_travelers column to compare.

aggregate_linked_trips(
    unlinked_trips: pl.DataFrame, transit_mode_enums: list[str]
) -> pl.DataFrame

Aggregate linked trips into single records, summarizing key info.

Logic: For each linked trip: - keep the first trips depart_ and o_ fields - keep the last trips arrive_ and d_ fields - Mode is based on hierarchy. Simple case: If: transit is involved in any trip segment, use transit mode. Else: Use mode of longest duration trip segment.

Parameters:

Name Type Description Default
unlinked_trips pl.DataFrame

DataFrame with linked_trip_id column

required
transit_mode_enums list[str]

List of mode enums that count as transit

required

Returns:

Type Description
pl.DataFrame

Aggregated DataFrame with one row per linked trip