Detect Joint Trips
Detect joint trips among household members using similarity matching.
Identifies trips where multiple household members traveled together by comparing origin-destination-time similarity using either strict buffer thresholds or Mahalanobis distance.
Algorithm
Phase 1: Household Pre-filtering
- Filter to households with 2+ members who took trips
- Reduces search space to only households where joint trips are possible
Phase 2: Pairwise Distance Calculation
- Within each multi-person household, compute pairwise distances between
all trip combinations using 4D space:
- Origin coordinates (o_lon, o_lat)
- Destination coordinates (d_lon, d_lat)
- Departure time
- Arrival time
- Store distances in condensed matrix format for efficiency
Phase 3: Similarity Filtering
Buffer Method:
- Filter trip pairs where:
- Spatial distance (haversine) ≤ space_threshold_meters for both origin AND destination
- Absolute time difference ≤ time_threshold_minutes for both departure AND arrival
- Simple, interpretable thresholds
Mahalanobis Method:
- Calculate statistical distance using covariance matrix:
- Accounts for correlated variations in space/time
- Keeps pairs whose distance is below a cutoff set by
confidence_level (see
JointTripConfig.get_distance_threshold)
- More sophisticated, calibrated to actual joint trip patterns
- Can capture joint trips more flexibly than fixed thresholds
Phase 4: Clique Detection
- Build graph where nodes = trips, edges = similar trip pairs
- Detect maximal cliques (groups of mutually-similar trips)
- Handle overlapping cliques by selecting disjoint set with maximum coverage
- Each clique represents one joint trip event
- Ensures transitivity: if A travels with B, and B with C, then A,B,C form one joint trip
Phase 5: Joint Trip Aggregation
- Assign unique joint_trip_id to each clique
- Create joint_trips table, one row per joint trip:
- joint_trip_id, hh_id, travel_date
- num_joint_travelers: number of member linked trips
- survey_complete: true only if every member trip is complete
- Mark each member linked trip with its joint_trip_id
- Validate against reported num_travelers field if available
Notes
- Only compares trips within one household date (joint trips across households, or across the survey's day boundary, not detected)
- Mahalanobis method requires calibrated covariance matrix (see scripts/calibrate_joint_trip_covariance.py)
- Clique detection prevents false positives from partial or coincidental matches
- Handles survey reporting errors where respondents over/under-report number of travelers
- Non-joint trips retain joint_trip_id = NULL
Example
- name: detect_joint_trips
params:
method: mahalanobis
covariance: [10000, 10000, 100, 100] # 100 m, 100 m, 10 min, 10 min std devs
confidence_level: 0.90
Settings
| Key | Type | Default | Meaning |
|---|---|---|---|
method |
buffer or mahalanobis |
required | Detection method: buffer (strict) or mahalanobis (statistical) |
time_threshold_minutes |
number, ≥ 0 | 15.0 |
Maximum time difference in minutes for buffer method |
space_threshold_meters |
number, ≥ 0 | 100.0 |
Maximum spatial distance in meters for buffer method |
covariance |
list or null | required | Diagonal (4 values) or full (4x4) covariance matrix in units [origin_m^2, dest_m^2, depart_min^2, arrive_min^2]. Required for mahalanobis; null for buffer. The empirical variance of BATS 2023 household joint trips is about diagonal [7000, 7000, 20, 20]: origin ~84m std dev (7142 m² var), dest ~84m (7089 m²), depart ~4.7min (22 min²), arrive ~4.6min (21 min²). |
confidence_level |
number, > 0, < 1 | required | Strictness of the mahalanobis cutoff (mahalanobis method only). Higher = stricter. It reads backward from the usual meaning of confidence and is not the share of joint pairs kept. See get_distance_threshold. |
log_discrepancies |
true / false | false |
If True, log DEBUG-level details when detected joint trip size differs from reported num_travelers |
Configuration models and calibration utilities for joint trip detection.
ALGORITHM DESIGN NOTES:
This module implements joint trip detection using two methods:
-
BUFFER METHOD (debugging/calibration): Strict AND across four dimensions: origin distance, destination distance, departure time, arrival time. Uses Haversine for spatial matching and absolute time differences. All four must pass thresholds.
-
MAHALANOBIS METHOD (production): Statistical distance on 4D vector [Δorigin_m, Δdest_m, Δdepart_min, Δarrive_min]. Supports diagonal covariance (assumes independence) or full covariance matrix (captures correlations). More robust to GPS noise via compensation.
CALIBRATION WORKFLOW:
- Run detection with method='buffer' to find high-confidence joint trips
- Use estimate_covariance_from_detected_pairs() to analyze buffer results
- Helper scans threshold range and recommends optimal distance_threshold
- Copy diagonal or full covariance to config for production runs
- Run detection with method='mahalanobis' using calibrated parameters
TEMPORAL OVERLAP:
Trips must have overlapping time windows to be joint (travelers physically together). Pre-filter: max(depart_A, depart_B) <= min(arrive_A, arrive_B)
Code: processing.joint_trips