laurel.pipelines.describe_dwells package
Submodules
laurel.pipelines.describe_dwells.nodes module
Kedro pipeline nodes for the describe_dwells pipeline (Model Module 2 — Augment dwell data).
Transforms the raw telematics trip records into a clean, shift-annotated
DwellSet that is ready for the electrification simulation. This pipeline
implements the second part of Model Module 2 (Augment Dwell Data): coalescing
brief return trips that break a dwell without meaningfully changing the
vehicle’s location, and marking the start of each new FMCSA-compliant driver
shift.
Pipeline overview
format_trips_columns — Parses timestamps, converts H3 hex strings to integers, recodes the vehicle-ID column as a compact integer category, and optionally persists the Dask DataFrame in memory.
calc_derived_trip_cols — Computes trip duration in hours from start and end timestamps.
create_dwells — Converts the trip-oriented DataFrame into a
DwellSet(one row per dwell event) usingDwellSet.from_trips.coalesce_interrupted_dwells — Merges dwells that are separated by a short “circle trip” (same origin and destination, below distance and duration thresholds) into a single, longer dwell.
mark_vehicle_shifts — Marks dwell events that start a new driver shift (dwell duration >=
min_refresh_hrs, per FMCSA HOS regulations) and assigns a monotonically increasing shift ID within each vehicle.calc_rolling_dwell_ratios — Computes a rolling time-window dwell ratio for each (vehicle, location) pair as the fraction of the vehicle’s total dwell time spent at that location.
map_location_groups — Joins freight-activity-class labels from the
describe_locationspipeline onto each dwell row.
Key design decisions
Coalescing via ``accum_masked``: Rather than a forward-fill or group-join, coalescing is implemented by propagating the latest end-time of the interrupted dwell sequence backwards through the masked rows and then dropping the non-masked rows. This avoids a sort-dependent join and is compatible with Dask partitioned DataFrames.
Reset column neutralisation: The
DwellSet.resetcolumn has special semantics insideaccum_masked. Setting it toFalsebefore callingaccum_maskedand restoring it afterwards prevents the coalescing logic from treating existing shift boundaries as accumulation barriers.FMCSA 6.9-hour threshold: The
min_refresh_hrsparameter encodes the FMCSA 8-hour off-duty rest requirement. A slightly lower threshold of 6.9 hours is used to accommodate GPS rounding in the telematics data.Deprecated rolling-ratio functions:
calc_inter_visit_stats,calc_inter_visit_times,describe_veh_loc_pairs,filter_substantial_dwells, andcluster_veh_loc_pairsare preserved for potential future use but are not connected to the active pipeline as of 2025-10-20.
References
Passow, F., & Rajagopal, R. (2026). Identifying indicators to inform proactive substation upgrades for charging electric heavy-duty trucks. Applied Energy.
Federal Motor Carrier Safety Administration. Hours of Service Regulations, 49 CFR Part 395.
- laurel.pipelines.describe_dwells.nodes.calc_derived_trip_cols(trips, params)[source]
Compute trip duration in hours from start and end timestamp columns.
- Parameters:
trips (
DataFrame) – Trips DataFrame with UTC-aware timestamp columns.params (
dict) –Pipeline parameters dict with keys:
time_cols(dict): sub-keystrip_startandtrip_endnaming the timestamp columns.persist(bool): ifTrue, trigger Dask computation and pin the result in distributed memory.
- Return type:
DataFrame- Returns:
The trips DataFrame with a new
trip_hrscolumn (float).
- laurel.pipelines.describe_dwells.nodes.calc_inter_visit_stats(dw)[source]
Compute inter-visit time and mileage for each (vehicle, location) pair. :rtype:
DwellSetDeprecated since version Not: connected to the active pipeline as of 2025-10-20. Preserved for potential future use.
- laurel.pipelines.describe_dwells.nodes.calc_inter_visit_times(grp, hex_col, end_col, start_col)[source]
Calculate inter-visit times for a single vehicle’s dwells, sorted by time.
- Return type:
DataFrame
- laurel.pipelines.describe_dwells.nodes.calc_rolling_dwell_ratios(dw, params)[source]
Compute each dwell’s maximum rolling location-specific dwell ratio.
For each dwell, the rolling ratio is the fraction of the vehicle’s total dwell time (within a rolling time window) that was spent at the same hexagon. The maximum of this ratio across all windows in the observation period is recorded for each (vehicle, location) pair and then mapped back onto individual dwell rows.
Dispatches to
_calc_rolling_dwell_ratios_partviamap_partitionsfor Dask-backedDwellSetinstances, or calls it directly for pandas.- Parameters:
dw (
DwellSet) – Shift-annotatedDwellSet(output ofmark_vehicle_shifts).params (
dict) –Pipeline parameters dict with keys:
output_ratio_col(str): name of the output rolling-ratio column.rolling_kwargs(dict): keyword arguments forwarded topd.Series.rolling(e.g.,window,min_periods).
- Return type:
DwellSet- Returns:
The
DwellSetwithparams["output_ratio_col"]added todw.data.
- laurel.pipelines.describe_dwells.nodes.cluster_veh_loc_pairs(veh_locs, params)[source]
Cluster (vehicle, location) pairs using HDBSCAN on log-scaled, standardised features.
Deprecated since version Not: connected to the active pipeline as of 2025-10-20. Preserved for potential future use.
Selects
params["feature_cols"], optionally subsamples, applieslog10transformation (skipping ratio columns), standardises withStandardScaler, and fits HDBSCAN with a minimum cluster size ofn_obs / min_cluster_size_denom.- Parameters:
veh_locs (
DataFrame) – (vehicle, location) summary DataFrame (output ofdescribe_veh_loc_pairs).params (
dict) –Pipeline parameters dict with keys:
feature_cols(list[str]): feature columns for clustering.sample(dict):active(bool),n(int),seed(int) for optional subsampling.min_cluster_size_denom(int): denominator for deriving HDBSCAN’smin_cluster_sizefromn_obs.cluster_col(str): output cluster-label column name.
- Return type:
DataFrame- Returns:
The input DataFrame with a categorical
cluster_colcolumn added.
- laurel.pipelines.describe_dwells.nodes.coalesce_interrupted_dwells(dw, params)[source]
Merge dwells that are split by short circle trips into single, continuous dwells.
A “circle trip” is a trip whose origin and destination hexagon are identical and whose distance and duration both fall below configurable thresholds. Such trips most likely represent GPS noise or brief vehicle movements within a depot and should not break the surrounding dwell into two separate events.
The algorithm proceeds as follows:
Mark non-circle trips with
is_not_short_circle = True.Temporarily set
dw.resettoFalsefor all rows to prevent shift boundaries from interrupting the accumulation.Use
DwellSet.accum_maskedwithCumAggFunc.MAXto propagate the latest end time backwards through each circle-trip gap (reverse=True), so that the dwell preceding the circle trip absorbs the end time of the dwell following it.Drop rows marked as circle trips.
Restore
dw.resetfrom the saved copy and rename accumulated columns back to their original names.
The distances and durations of the dropped circle trips are not accumulated into the surviving dwell (they are treated as negligible).
- Parameters:
dw (
DwellSet) – InputDwellSetfreshly created from trips.params (
dict) –Pipeline parameters dict with keys:
max_short_dist_miles(float): maximum trip distance (miles) for a trip to qualify as a circle trip.max_short_dur_hrs(float): maximum trip duration (hours) for a trip to qualify as a circle trip.
- Return type:
DwellSet- Returns:
The
DwellSetwith circle trips coalesced;dw.datahas the same schema as the input but with fewer rows.
- laurel.pipelines.describe_dwells.nodes.create_dwells(trips, params)[source]
Convert a trip-oriented DataFrame into a
DwellSetof dwell events.A dwell event represents the period during which a vehicle is stationary at a location between two consecutive trips.
DwellSet.from_tripsinfers each dwell’s start time, end time, and hexagon from the surrounding trip records.An optional debug subsample limits computation to the first
nrows of the Dask DataFrame for rapid iteration. Ifload_into_memoryis set, the entire Dask DataFrame is materialised before conversion, which can be faster when working on smaller datasets.- Parameters:
trips (
DataFrame) – Trips Dask DataFrame (output ofcalc_derived_trip_cols).params (
dict) –Pipeline parameters dict with keys:
debug_subsample(dict):active(bool) andn(int) to limit the input to the firstnrows.load_into_memory(bool): ifTrue, call.compute()before conversion.drop_cols(list[str]): trip columns to remove before conversion.col_renamer(dict[str, str]): additional column renames to apply before conversion.from_trips_cols(dict): column-name keyword arguments forwarded toDwellSet.from_trips(e.g.,veh,hex,start,end).verify_sorting(bool): ifTrue,DwellSet.from_tripsverifies that trips are sorted by (vehicle, time).set_index_kwargs(dict): additional keyword arguments for setting the index inDwellSet.from_trips.
- Return type:
DwellSet- Returns:
A
DwellSetwith one dwell record per stationary event, sorted by (vehicle, start time).
- laurel.pipelines.describe_dwells.nodes.describe_veh_loc_pairs(dw)[source]
Summarise each (vehicle, location) pair with visit counts, dwell hours, and inter-visit statistics. :rtype:
DataFrameDeprecated since version Not: connected to the active pipeline as of 2025-10-20. Preserved for potential future use.
- laurel.pipelines.describe_dwells.nodes.filter_substantial_dwells(dw, params)[source]
Drop dwells shorter than a duration threshold, accumulating trip stats across gaps. :rtype:
DwellSetDeprecated since version Not: connected to the active pipeline as of 2025-10-20. Preserved for potential future use.
- laurel.pipelines.describe_dwells.nodes.format_trips_columns(trips, params)[source]
Parse timestamps, encode H3 hex strings, and recode vehicle IDs in the raw trips DataFrame.
Categorising the vehicle-ID column before converting timestamp columns prevents a known Dask issue in which
dd.to_datetimesilently converts integer category codes tofloat64. H3 hexagon strings are converted touint64integers for memory efficiency and join performance.- Parameters:
trips (
DataFrame) – Raw trips Dask DataFrame as loaded from the01_rawcatalog.params (
dict) –Pipeline parameters dict with keys:
category_columns(list[str]): columns to categorise before timestamp conversion.time_columns(list[str]): columns to convert to UTC-awaredatetime64.h3_columns(list[str]): H3 hexagon string columns to convert touint64.col_renamer(dict[str, str]): mapping from raw to internal column names (applied after all other transformations).veh_id_col(str): vehicle identifier column; category codes are cast toint64after renaming.persist(bool): ifTrue, trigger Dask computation and pin the result in distributed memory.
- Return type:
DataFrame- Returns:
A Dask DataFrame with parsed timestamps, integer hex IDs, compact integer vehicle IDs, and columns renamed to internal names.
- laurel.pipelines.describe_dwells.nodes.map_location_groups(dw, hex_corresp, params)[source]
Join freight-activity-class labels from the hex correspondence table onto dwells.
Maps the cluster/group label for each hexagon (computed by the
describe_locationspipeline) onto theDwellSetrows by hexagon ID, viamerge_dataframes_node. That helper preservesdw.data’s index (veh_id) across the merge and, for Dask-backedDwellSetinstances, performs a partition-wise broadcast merge rather thanSeries.map(), which would otherwise embed the correspondence table as an unmanaged, unspillable graph literal and can stall workers under memory pressure.- Parameters:
dw (
DwellSet) –DwellSetwith a hex-ID column.hex_corresp (
DataFrame) – DataFrame indexed by hexagon ID with a location-group column.params (
dict) –Pipeline parameters dict with keys:
location_group_col(str): name of the group column inhex_correspand the output column added todw.data.missing_values(dict): sub-keys:fill_missing(bool): whether to fill NaN group labels.fill_value: value used to fill unmapped hexagons (e.g.,"undeveloped").
- Return type:
DwellSet- Returns:
The
DwellSetwith a new location-group column added todw.data.
- laurel.pipelines.describe_dwells.nodes.mark_vehicle_shifts(dw, params)[source]
Identify driver shift boundaries and assign shift IDs.
A dwell whose duration meets or exceeds
min_refresh_hrsis classified as a shift-refresh dwell (the driver’s mandatory off-duty rest period under FMCSA 49 CFR Part 395). The shift ID for the following dwell is incremented, so that the refresh dwell itself is the last event of the preceding shift. Shift IDs are vehicle-local (i.e., they restart at 0 for each vehicle).- Parameters:
dw (
DwellSet) – CoalescedDwellSet(output ofcoalesce_interrupted_dwells).params (
dict) –Pipeline parameters dict with keys:
columns(dict): sub-keys:dur(str): temporary column name for dwell duration.refresh(str): boolean column name marking shift-refresh dwells.shift_id(str): integer shift-ID column name.
min_refresh_hrs(float): minimum dwell duration in hours to classify as a shift refresh (typically 6.9 hours).
- Return type:
DwellSet- Returns:
The
DwellSetwithrefreshandshift_idcolumns added todw.data.
laurel.pipelines.describe_dwells.pipeline module
Kedro pipeline definition for the describe_dwells pipeline.
Wires the nodes from laurel.pipelines.describe_dwells.nodes into a single Pipeline object.
For full documentation of each node’s inputs, outputs, and algorithm,
see laurel.pipelines.describe_dwells.nodes.
Module contents
This is a boilerplate pipeline ‘describe_dwells’ generated using Kedro 1.0.0