diff --git a/_shared_utils/requirements.txt b/_shared_utils/requirements.txt index b5b7faff7..d0f037083 100644 --- a/_shared_utils/requirements.txt +++ b/_shared_utils/requirements.txt @@ -1,4 +1,5 @@ -e . gtfs-segments==0.1.0 pyairtable==2.2.2 -great-tables==0.3.1 +great-tables==0.4.0 +polars==0.20.16 diff --git a/gtfs_digest/extra_cleaning.py b/gtfs_digest/extra_cleaning.py new file mode 100644 index 000000000..b27893639 --- /dev/null +++ b/gtfs_digest/extra_cleaning.py @@ -0,0 +1,28 @@ +# operators that need route names parsed +# probably just keep the long name portion +# also do titlecase? this is finicky, because some are CC (which we don't want titlecase) +operators_only_route_long_name = [ + "Antelope Valley Transit Authority Schedule", + "Bay Area 511 ACE Schedule", + "Bay Area 511 Caltrain Schedule", + "Bay Area 511 Emery Go-Round Schedule", + "Bay Area 511 Petaluma Schedule", + "Beach Cities GMV Schedule", + "Bear Schedule", + "Commerce Schedule", + "Elk Grove Schedule", + "Humboldt Schedule", + "LA DOT Schedule", + "Lawndale Beat GMV Schedule", + "Redding Schedule", + "Redwood Coast Schedule", + "Santa Maria Schedule", + "StanRTA Schedule", + "VCTC GMV Schedule", + "Victor Valley GMV Schedule", + "Visalia Schedule", + "Yolobus Schedule", +] + +# BruinBus Schedule - nothing shows up in route stats +# Why does "StanRTA Schedule" and "Tahoe Transportation District Schedule" appear to have similar route names? \ No newline at end of file diff --git a/gtfs_funnel/logs/download_data.log b/gtfs_funnel/logs/download_data.log index 9c4705a65..a08d08e94 100644 --- a/gtfs_funnel/logs/download_data.log +++ b/gtfs_funnel/logs/download_data.log @@ -318,3 +318,4 @@ 2024-03-14 11:43:06.291 | INFO | __main__:download_one_day:29 - # operators to run: 172 2024-03-14 11:43:06.291 | INFO | __main__:download_one_day:33 - *********** Download st data *********** 2024-03-14 11:44:27.599 | INFO | __main__:download_one_day:56 - execution time: 0:01:22.625555 +2024-03-19 16:50:51.742 | INFO | __main__:download_one_year:35 - execution time: 0:00:43.062868 diff --git a/gtfs_funnel/route_typologies.py b/gtfs_funnel/schedule_stats_by_route_direction.py similarity index 100% rename from gtfs_funnel/route_typologies.py rename to gtfs_funnel/schedule_stats_by_route_direction.py diff --git a/gtfs_funnel/stop_times_with_direction.py b/gtfs_funnel/stop_times_with_direction.py index e88c3a89d..63926ae65 100644 --- a/gtfs_funnel/stop_times_with_direction.py +++ b/gtfs_funnel/stop_times_with_direction.py @@ -22,15 +22,16 @@ def prep_scheduled_stop_times(analysis_date: str) -> gpd.GeoDataFrame: """ stops = helpers.import_scheduled_stops( analysis_date, - columns = ["feed_key", "stop_id", "geometry"], + columns = ["feed_key", "stop_id", "stop_name", "geometry"], crs = PROJECT_CRS, get_pandas = True ) stop_times = helpers.import_scheduled_stop_times( analysis_date, - columns = ["feed_key", "trip_id", "stop_id", "stop_sequence"] - ).compute() + columns = ["feed_key", "trip_id", "stop_id", "stop_sequence"], + get_pandas = True + ) trips = helpers.import_scheduled_trips( analysis_date, @@ -53,7 +54,7 @@ def prep_scheduled_stop_times(analysis_date: str) -> gpd.GeoDataFrame: stops, on = ["feed_key", "stop_id"], how = "inner" - ).drop(columns = ["feed_key", "trip_id"]) + ).drop(columns = ["trip_id"]) st_with_stop = gpd.GeoDataFrame( st_with_stop, geometry = "geometry", crs = PROJECT_CRS) @@ -83,13 +84,15 @@ def get_projected_stop_meters( return gdf -def find_prior_stop( +def find_prior_subseq_stop( stop_times: gpd.GeoDataFrame, trip_stop_cols: list ) -> gpd.GeoDataFrame: """ For trip-stop, find the previous stop (using stop sequence). Attach the previous stop's geometry. + This will determine the direction for the stop (it's from prior stop). + Add in subseq stop information too. """ prior_stop = stop_times[trip_stop_cols].sort_values( trip_stop_cols).reset_index(drop=True) @@ -105,6 +108,9 @@ def find_prior_stop( subseq_stop_id = (prior_stop.groupby("trip_instance_key") .stop_id .shift(-1)), + subseq_stop_name = (prior_stop.groupby("trip_instance_key") + .stop_name + .shift(-1)) ) # Merge in prior stop geom as a separate column so we can @@ -133,9 +139,11 @@ def find_prior_stop( ).astype({ "prior_stop_sequence": "Int64", "subseq_stop_sequence": "Int64" - }).fillna({"subseq_stop_id": ""}) - - + }).fillna({ + "subseq_stop_id": "", + "subseq_stop_name": "" + }) + # Create stop pair with underscores, since stop_id # can contain hyphens stop_times_with_prior_geom = stop_times_with_prior_geom.assign( @@ -143,8 +151,13 @@ def find_prior_stop( lambda x: str(x.stop_id) + "__" + str(x.subseq_stop_id), axis=1, - ) - ).drop(columns = ["subseq_stop_id"]) + ), + stop_pair_name = stop_times_with_prior_geom.apply( + lambda x: + x.stop_name + "__" + x.subseq_stop_name, + axis=1, + ), + ).drop(columns = ["subseq_stop_id", "subseq_stop_name"]) return stop_times_with_prior_geom @@ -167,9 +180,10 @@ def assemble_stop_times_with_direction( scheduled_stop_times = prep_scheduled_stop_times(analysis_date) - trip_stop_cols = ["trip_instance_key", "stop_sequence", "stop_id"] + trip_stop_cols = ["trip_instance_key", "stop_sequence", + "stop_id", "stop_name"] - scheduled_stop_times2 = find_prior_stop( + scheduled_stop_times2 = find_prior_subseq_stop( scheduled_stop_times, trip_stop_cols ) @@ -195,11 +209,6 @@ def assemble_stop_times_with_direction( rt_utils.primary_cardinal_direction)(prior_geom, current_geom) stop_distance = prior_geom.distance(current_geom) - # Create a column with normalized direction vector - # Add this because some bus can travel in southeasterly direction, - # but it's categorized as southbound or eastbound depending - # on whether south or east value is larger. - other_stops_no_geom = other_stops_no_geom.assign( stop_primary_direction = stop_direction, stop_meters = stop_distance, @@ -224,10 +233,7 @@ def assemble_stop_times_with_direction( end = datetime.datetime.now() print(f"execution time: {end - start}") - - del scheduled_stop_times, scheduled_stop_times2 - del other_stops_no_geom, scheduled_stop_times_with_direction, df - + return diff --git a/rt_segment_speeds/logs/cut_road_segments.log b/rt_segment_speeds/logs/cut_road_segments.log index e8a75dda2..fa0ce465a 100644 --- a/rt_segment_speeds/logs/cut_road_segments.log +++ b/rt_segment_speeds/logs/cut_road_segments.log @@ -1,6 +1,5 @@ -2023-12-11 18:49:41.963 | INFO | __main__::178 - execution time: 0:02:12.275031 -2023-12-12 12:38:25.692 | INFO | __main__::282 - cut local roads: 0:13:31.749643 -2023-12-12 12:38:25.740 | INFO | __main__::285 - execution time: 0:13:31.797462 -2023-12-12 12:43:57.178 | INFO | __main__::261 - cut primary/secondary roads: 0:03:51.547346 -2023-12-12 13:01:51.518 | INFO | __main__::61 - concatenate road segments: 0:00:51.244157 -2023-12-12 14:35:58.485 | INFO | __main__::62 - concatenate road segments: 0:00:45.026472 +2024-03-21 16:53:55.732 | INFO | __main__::236 - cut primary/secondary roads: 0:00:25.924757 +2024-03-21 17:03:42.871 | INFO | __main__::255 - cut local roads: 0:09:47.138963 +2024-03-21 17:03:42.871 | INFO | __main__::258 - execution time: 0:10:13.064427 +2024-03-22 14:00:01.019 | INFO | __main__::184 - execution time: 0:05:50.663597 +2024-03-22 14:19:25.092 | INFO | __main__::196 - execution time: 0:08:45.006011 diff --git a/rt_segment_speeds/scripts/cut_road_segments.py b/rt_segment_speeds/scripts/cut_road_segments.py index a5d46ea2c..1c5078221 100644 --- a/rt_segment_speeds/scripts/cut_road_segments.py +++ b/rt_segment_speeds/scripts/cut_road_segments.py @@ -1,30 +1,24 @@ """ Cut road segments. """ -import dask.dataframe as dd -import dask_geopandas as dg +#import dask.dataframe as dd import datetime import geopandas as gpd import pandas as pd import sys -from dask import delayed +from dask import delayed, compute from loguru import logger -from typing import Literal from calitp_data_analysis.sql import to_snakecase -from segment_speed_utils import helpers from segment_speed_utils.project_vars import (SHARED_GCS, PROJECT_CRS, ROAD_SEGMENT_METERS ) from calitp_data_analysis import geography_utils, utils -from shared_utils import rt_utils, geog_utils_to_add +from shared_utils import rt_utils -""" -TIGER -""" -def load_roads(filtering: tuple) -> gpd.GeoDataFrame: +def load_roads(**kwargs) -> gpd.GeoDataFrame: """ Load roads based on what you filter for MTFCC values (road types). Do some basic cleaning/dissolving. @@ -43,8 +37,8 @@ def load_roads(filtering: tuple) -> gpd.GeoDataFrame: # https://stackoverflow.com/questions/56522977/using-predicates-to-filter-rows-from-pyarrow-parquet-parquetdataset df = gpd.read_parquet( f"{SHARED_GCS}all_roads_2020_state06.parquet", - filters = filtering, columns=["LINEARID", "MTFCC", "FULLNAME", "geometry"], + **kwargs, ).to_crs(PROJECT_CRS).pipe(to_snakecase) # If a road has mutliple rows but the same @@ -92,122 +86,45 @@ def load_roads(filtering: tuple) -> gpd.GeoDataFrame: ) return df2 - - -""" -Primary/Secondary Roads -""" -def cut_primary_secondary_roads( - roads: gpd.GeoDataFrame, - segment_length_meters: int -) -> gpd.GeoDataFrame: - """ - Cut all primary and secondary roads. - Add a primary cardinal direction for the segment. - """ - roads_segmented = geography_utils.cut_segments( - roads, - group_cols = ["linearid", "mtfcc", "fullname"], - segment_distance = segment_length_meters, - ).sort_values( - ["linearid", "segment_sequence"] - ).reset_index(drop=True) - roads_segmented2 = add_segment_direction(roads_segmented) - - return roads_segmented2 - -""" -Local Roads (base) -""" def cut_segments( - gdf: gpd.GeoDataFrame, - group_cols: list, - segment_length_meters: int -) -> gpd.GeoDataFrame: - gdf["segment_geometry"] = gdf.apply( - lambda x: - geography_utils.create_segments(x.geometry, int(segment_length_meters)), - axis=1, - ) - - gdf2 = geog_utils_to_add.explode_segments( - gdf, - group_cols, - segment_col = "segment_geometry", - )[group_cols + ["segment_sequence", "geometry"] - ].set_geometry("geometry").set_crs(PROJECT_CRS) - - return gdf2 - - -def cut_local_roads( - roads: gpd.GeoDataFrame, + roads: gpd.GeoDataFrame, group_cols: list, segment_length_meters: int ) -> gpd.GeoDataFrame: - # If roads are less than our segment length - # keep these intact...they are freebies, already segmented for us (1 segment) short_roads = roads[ roads.road_length <= segment_length_meters - ].drop(columns = "road_length").reset_index(drop=True) - - short_roads = short_roads.assign( + ].drop(columns = "road_length").assign( segment_sequence = 0 - ).astype({"segment_sequence": "int16"}) - - long_roads = roads[ + ).astype({"segment_sequence": "int16"}).reset_index(drop=True) + + gdf = roads[ roads.road_length > segment_length_meters ].drop(columns = "road_length").reset_index(drop=True) - - - long_roads = long_roads.repartition(npartitions=50) - time1 = datetime.datetime.now() - - # Concatenate the short roads and the segmented roads - road_dtypes = long_roads[group_cols].dtypes.to_dict() + gdf["segment_geometry"] = gdf.apply( + lambda x: + geography_utils.create_segments(x.geometry, int(segment_length_meters)), + axis=1, + ) - segmented_long_roads = long_roads.map_partitions( - cut_segments, + gdf2 = geography_utils.explode_segments( + gdf, group_cols, - segment_length_meters, - meta = { - **road_dtypes, - "segment_sequence": "int16", - "geometry": "geometry" - }, - align_dataframes = False - ).persist() + segment_col = "segment_geometry", + )[ + group_cols + ["segment_sequence", "geometry"] + ].set_geometry("geometry").set_crs(PROJECT_CRS) - print("first persist") - print(segmented_long_roads.dtypes) - - gdf = dd.multi.concat( - [short_roads, segmented_long_roads], - axis=0 + segmented = pd.concat( + [short_roads, gdf2], axis=0 ).reset_index(drop=True) - gdf = gdf.repartition(npartitions=20) - - gdf_dtypes = gdf.dtypes.to_dict() + segmented = gpd.GeoDataFrame(segmented, geometry="geometry", crs=PROJECT_CRS) - gdf2 = gdf.map_partitions( - add_segment_direction, - meta = { - **gdf_dtypes, - "origin": "geometry", - "destination": "geometry", - "primary_direction": "object" - } - ).persist() - - time2 = datetime.datetime.now() - print(f"map partitions to cut segments and add direction: {time2 - time1}") - - return gdf2 + return segmented def add_segment_direction(df: gpd.GeoDataFrame) -> gpd.GeoDataFrame: @@ -217,6 +134,7 @@ def add_segment_direction(df: gpd.GeoDataFrame) -> gpd.GeoDataFrame: one running on other side, we need to distinguish between these 2 rows with same linearid-mtfcc-fullname. """ + df = df.dropna(subset="geometry") df = rt_utils.add_origin_destination(df) df = df.assign( @@ -228,8 +146,33 @@ def add_segment_direction(df: gpd.GeoDataFrame) -> gpd.GeoDataFrame: ) return df - + +def cut_road_segments( + group_cols: list, + segment_length_meters: int, + road_segment_str: str, + **kwargs +): + roads = delayed(load_roads)(**kwargs) + + road_segments = delayed(cut_segments)( + roads, + group_cols, + segment_length_meters + ).pipe(add_segment_direction) + + road_segments = compute(road_segments)[0] + + utils.geoparquet_gcs_export( + road_segments, + SHARED_GCS, + f"segmented_roads_{road_segment_str}_2020" + ) + + return + + if __name__ == '__main__': LOG_FILE = "../logs/cut_road_segments.log" @@ -242,42 +185,12 @@ def add_segment_direction(df: gpd.GeoDataFrame) -> gpd.GeoDataFrame: road_cols = ["linearid", "mtfcc", "fullname"] - # Always cut all the primary and secondary roads - road_type = "primarysecondary" - road_type_values = ["S1100", "S1200"] - - roads = load_roads(filtering = [("MTFCC", "in", road_type_values)]) - primary_secondary_roads = cut_primary_secondary_roads( - roads, ROAD_SEGMENT_METERS) - - utils.geoparquet_gcs_export( - primary_secondary_roads, - SHARED_GCS, - f"segmented_roads_2020_{road_type}" - ) - - del roads, primary_secondary_roads - time1 = datetime.datetime.now() - logger.info(f"cut primary/secondary roads: {time1 - start}") - - road_type = "local" - road_type_values = ["S1400"] - roads = delayed(load_roads)(filtering = [("MTFCC", "in", road_type_values)]) - - local_gddf = dd.from_delayed(roads).repartition(npartitions=50) - - local_roads = cut_local_roads( - local_gddf, road_cols, ROAD_SEGMENT_METERS - ).compute() - - utils.geoparquet_gcs_export( - local_roads, - SHARED_GCS, - f"segmented_roads_2020_{road_type}" - ) - - time2 = datetime.datetime.now() - logger.info(f"cut local roads: {time2 - time1}") + cut_road_segments(road_cols, ROAD_SEGMENT_METERS, "onekm") + + ''' + TWO_MILES_IN_METERS = 1_609*2 + cut_road_segments(road_cols, TWO_MILES_IN_METERS, "twomile") + ''' end = datetime.datetime.now() logger.info(f"execution time: {end - start}") \ No newline at end of file diff --git a/rt_segment_speeds/scripts/nearest_vp_to_road.py b/rt_segment_speeds/scripts/nearest_vp_to_road.py index fe69029e3..b2b85f4b9 100644 --- a/rt_segment_speeds/scripts/nearest_vp_to_road.py +++ b/rt_segment_speeds/scripts/nearest_vp_to_road.py @@ -1,247 +1,273 @@ """ """ -import dask.dataframe as dd import datetime import geopandas as gpd -import numpy as np import pandas as pd +import shapely -from segment_speed_utils import helpers, wrangle_shapes -from segment_speed_utils.project_vars import (SEGMENT_GCS, - CONFIG_PATH, PROJECT_CRS) - - -def filter_long_sjoin_results( - sjoin_long: dd.DataFrame, - sjoin_wide: pd.DataFrame, -) -> dd.DataFrame: - """ - Long results contain all sjoins. - Wide is filtered down to road linearids that have at least - 2 vp_idx for that trip_instance_key on the road. - """ - keep_cols = sjoin_long.columns.tolist() - - df = dd.merge( - sjoin_long, - sjoin_wide, - on = ["trip_instance_key", "linearid", "mtfcc"], - how = "inner" - )[keep_cols].drop_duplicates().reset_index(drop=True) - - return df +from dask import delayed, compute +from segment_speed_utils import helpers, neighbor, segment_calcs, wrangle_shapes +from segment_speed_utils.project_vars import SEGMENT_GCS, SHARED_GCS, PROJECT_CRS +import interpolate_stop_arrival -def merge_vp_to_crosswalk( - analysis_date: str, -) -> dd.DataFrame: - """ - """ - sjoin_long = dd.read_parquet( - f"{SEGMENT_GCS}vp_sjoin/vp_road_segments_{analysis_date}", +road_id_cols = ["linearid", "mtfcc", "primary_direction"] +segment_identifier_cols = road_id_cols + ["segment_sequence"] +segment_identifier_cols2 = ['linearid', 'mtfcc', + 'stop_primary_direction', 'segment_sequence'] +test_shapes = [ + 'e3435a4b882913d92a12563910f7193d', + 'dfae5639b824ed6ad87ed753575f5381', + '626724031caabc3abcf3f183f4c9c718', + 'b63ae7a27ecb677ac93c0373245ea21b', + '85a03eed08934ed37f204e9aae6cbd36', + '20af512ed1ada757a3b49beb36b623ad', + 'a1999da4d09bc81548fe3e2b0fb458b4', + '1e7115aaac1fcb58c6509c7a90d9741c', + '3d437ea82b56e9827d15527ce41c716a', + '40469843deb94fbf61ef5f38bb76a137', + '04353cab33c0b31d8e9575ace6f9f6da', + 'f165d1399f8880fc0011eb75b740ba24', + 'ad15c10f6bc86dd6d3c02bfe6daf7ad9', + '71b06c07dabcecf71ddc5a8f3638ccf1', + '4e2b8555bc9936d711c8748694d67a3e', + '2e00363dba250fae30b7c14596f18907', + '2fd717cc5df1495434b8170e294137a6', + '60057141822cc9d9ac4795a83b2f25f1', + '5c0a268639c22fc58c8c842741cf68e3', + '67bc9cc93630ea8a22783cd59d11d46b', + '81e5d878737a777ffea18b0c08f58118', + 'a91351f71fc4d2c0b457e1701a3ade64', + '67af448db16c21d09812f58cf065aac5', + 'bd66e7d4ffae3bc36888cfddaf5e2e44', + 'b9b803a342d42f72bbe25042f7328385' +] + +def get_shape_road_crosswalk( + analysis_date: str, **kwargs +) -> pd.DataFrame: + + # shapes to road crosswalk + shape_road_crosswalk = pd.read_parquet( + f"{SEGMENT_GCS}roads_staging/" + f"shape_road_crosswalk_{analysis_date}.parquet", + ) + + # link shapes to trip + shape_to_trip = helpers.import_scheduled_trips( + analysis_date, + columns = ["trip_instance_key", "shape_array_key"], + **kwargs + #filters = [[("shape_array_key", "==", one_shape)]] ) - sjoin_wide = pd.read_parquet( - f"{SEGMENT_GCS}vp_sjoin/vp_road_segments_wide_{analysis_date}.parquet", + shape_road_crosswalk = shape_road_crosswalk.merge( + shape_to_trip, + on = "shape_array_key", + how = "inner" ) - # these are the vps joined to roads that we will narrow down - # for interpolation - sjoin_vp_roads = filter_long_sjoin_results(sjoin_long, sjoin_wide) + return shape_road_crosswalk + + +def make_road_stops_long( + shape_road_crosswalk: pd.DataFrame +) -> gpd.GeoDataFrame: - # Pull the vp info for ones that join to road segments - vp_usable = dd.read_parquet( - f"{SEGMENT_GCS}vp_usable_{analysis_date}", - columns = ["trip_instance_key", "vp_idx", "x", "y"], + road_segments = gpd.read_parquet( + f"{SHARED_GCS}road_segments/", + columns = segment_identifier_cols + ["geometry"], ) - vp_with_roads = dd.merge( - vp_usable, - sjoin_vp_roads, - on = ["trip_instance_key", "vp_idx"], + road_segments2 = pd.merge( + road_segments, + shape_road_crosswalk, + on = ["linearid", "mtfcc", "segment_sequence"], how = "inner" ) - return vp_with_roads + # Beginning of road segment (1st coord) + road_segments0 = road_segments2.assign( + geometry = road_segments2.apply( + lambda x: shapely.Point(x.geometry.coords[0]), + axis=1), + ).assign(stop_type=0) + # End of road segment (last coord) + road_segments1 = road_segments2.assign( + geometry = road_segments2.apply( + lambda x: shapely.Point(x.geometry.coords[-1]), + axis=1), + ).assign(stop_type=1) -def expand_road_segments( - roads: pd.DataFrame, - cols_to_unpack: list -) -> pd.DataFrame: - """ - Convert roads from wide to long. - This set up makes roads segment endpoints look like bus stops. - Once we use roads full line geometry to project, we can - drop it and get it in a long format. - """ - roads_long = (roads.drop(columns = "geometry") - .explode(cols_to_unpack) - .reset_index(drop=True) - ) - - return roads_long - - -def make_wide(vp_with_projected: pd.DataFrame): - - vp_projected_wide = (vp_with_projected - .groupby(["trip_instance_key"] + road_id_cols) - .agg({ - "vp_idx": lambda x: list(x), - "shape_meters": lambda x: list(x)}) - .reset_index() - .rename(columns = { - "vp_idx": "vp_idx_arr", - "shape_meters": "shape_meters_arr" - }) - ) - - #https://stackoverflow.com/questions/42099024/pandas-pivot-table-rename-columns - narrowed_down_roads = (vp_with_projected - .pivot_table(index = ["trip_instance_key"] + road_id_cols, - aggfunc = {"shape_meters": ["min", "max"]}) - .pipe(lambda x: - x.set_axis(map('_'.join, x), axis=1) - # if all column names are strings - # ('_'.join(map(str, c)) for c in x) - # if some column names are not strings - ) - .reset_index() - ) - - vp_projected_wide2 = pd.merge( - vp_projected_wide, - narrowed_down_roads, - on = ["trip_instance_key"] + road_id_cols, - how = "inner" - ) + # Make long, similar to how stop_times is set up + # For roads, each segment has beginning and end stop + # We want to interpolate arrival times for both + # to calculate speed + road_segments_long = pd.concat( + [road_segments0, road_segments1], + axis=0 + ).sort_values( + ["linearid", "segment_sequence", "stop_type"] + ).rename( + columns = {"primary_direction": "stop_primary_direction"} + ).reset_index(drop=True) - return vp_projected_wide2 + return road_segments_long +def merge_nn_with_shape(results2): -if __name__ == "__main__": + results2 = results2.assign( + stop_geometry = results2.stop_geometry.to_crs(PROJECT_CRS), + vp_coords_trio = results2.vp_coords_trio.to_crs(PROJECT_CRS) + ) - from segment_speed_utils.project_vars import analysis_date + shapes = helpers.import_scheduled_shapes( + analysis_date, + columns = ["shape_array_key", "geometry"], + crs = PROJECT_CRS + ).dropna(subset="geometry") + + gdf = pd.merge( + results2, + shapes.rename(columns = {"geometry": "shape_geometry"}), + on = "shape_array_key", + how = "inner" + ) - start = datetime.datetime.now() + stop_meters_series = [] + stop_arrival_series = [] - road_id_cols = ["linearid", "mtfcc"] - segment_identifier_cols = road_id_cols + ["segment_sequence"] + for row in gdf.itertuples(): + + stop_meters, interpolated_arrival = interpolate_stop_arrival.project_points_onto_shape( + getattr(row, "stop_geometry"), + getattr(row, "vp_coords_trio"), + getattr(row, "shape_geometry"), + getattr(row, "location_timestamp_local_trio") + ) + + stop_meters_series.append(stop_meters) + stop_arrival_series.append(interpolated_arrival) + + results2 = gdf.assign( + stop_meters = stop_meters_series, + arrival_time = stop_arrival_series, + )[segment_identifier_cols2 + [ + "trip_instance_key", "shape_array_key", + "stop_type", + "stop_meters", "arrival_time"] + ].sort_values( + segment_identifier_cols2 + ["trip_instance_key", "stop_type", ] + ).reset_index(drop=True) - vp = merge_vp_to_crosswalk(analysis_date) - vp = vp.repartition(npartitions=150) + return results2 + + +def quick_calculate_speeds(results2): + grouped_df = results2.groupby(segment_identifier_cols2 + + ["trip_instance_key"]) + + min_arrival = grouped_df.agg({"arrival_time": "min"}).reset_index() + max_arrival = grouped_df.agg({"arrival_time": "max"}).reset_index() - subset_roads = vp.linearid.unique().compute().tolist() + # If min/max arrival are the same, remove + # The same trio of vp is attached to the road segment's + # beginning and end + min_max_arrival = pd.merge( + min_arrival, + max_arrival, + on = segment_identifier_cols2 + ["trip_instance_key"] + ).query('arrival_time_x != arrival_time_y') - roads = gpd.read_parquet( - f"{SEGMENT_GCS}segments_staging/" - f"roads_with_cutpoints_wide_{analysis_date}.parquet", - filters = [[("linearid", "in", subset_roads)]] + results3 = pd.merge( + results2, + min_max_arrival[segment_identifier_cols2 + ["trip_instance_key"]], + on = segment_identifier_cols2 + ["trip_instance_key"], + how = "inner" ) - road_dtypes = vp[road_id_cols].dtypes.to_dict() - - vp_projected = vp.map_partitions( - wrangle_shapes.project_vp_onto_segment_geometry, - roads, - grouping_cols = road_id_cols, - meta = { - "vp_idx": "int", - **road_dtypes, - "shape_meters": "float", - }, - align_dataframes = False - ).persist() - - vp2 = vp[["vp_idx", "trip_instance_key"] + segment_identifier_cols] - - vp_with_projected = dd.merge( - vp2, - vp_projected, - on = ["vp_idx"] + road_id_cols, - how = "inner" - ).compute() + results3 = segment_calcs.convert_timestamp_to_seconds( + results3, ["arrival_time"] + ).sort_values( + segment_identifier_cols2 + ["trip_instance_key"] + ).reset_index(drop=True) + + trip_cols = segment_identifier_cols2 + ["trip_instance_key"] - vp_with_projected.to_parquet( - f"{SEGMENT_GCS}projection/vp_projected_roads_{analysis_date}.parquet" + results3 = results3.assign( + subseq_arrival_time_sec = (results3.groupby(trip_cols, + observed=True, group_keys=False) + .arrival_time_sec + .shift(-1) + ), + subseq_stop_meters = (results3.groupby(trip_cols, + observed=True, group_keys=False) + .stop_meters + .shift(-1) + ) ) - ''' - vp_projected_wide2 = make_wide(vp_with_projected) - - road_cutpoints = expand_road_segments( - roads, - ["road_meters_arr", "segment_sequence_arr", "road_direction_arr"] - ).rename(columns = { - "road_meters_arr": "road_meters", - "segment_sequence_arr": "segment_sequence", - "road_direction_arr": "primary_direction" - }) - - # Now merge road segments with each destination acting as the road's stop - # and merge on arrays of projected vp against that road - gdf = pd.merge( - road_cutpoints, - vp_projected_wide2, - on = road_id_cols, - how = "inner" - ).query( - 'road_meters >= shape_meters_min & road_meters <= shape_meters_max' - ).drop( - columns = ["shape_meters_min", "shape_meters_max"] + + speed = results3.assign( + meters_elapsed = results3.subseq_stop_meters - results3.stop_meters, + sec_elapsed = results3.subseq_arrival_time_sec - results3.arrival_time_sec, + ).pipe( + segment_calcs.derive_speed, + ("stop_meters", "subseq_stop_meters"), + ("arrival_time_sec", "subseq_arrival_time_sec") ) - nearest_vp_idx = [] - subseq_vp_idx = [] + return speed - for row in gdf.itertuples(): - this_stop_meters = getattr(row, "road_meters") - valid_shape_meters_array = getattr(row, "shape_meters_arr") - valid_vp_idx_array = np.asarray(getattr(row, "vp_idx_arr")) - - if ( - (this_stop_meters >= min(valid_shape_meters_array)) and - (this_stop_meters <= max(valid_shape_meters_array)) - ): - - idx = np.searchsorted( - valid_shape_meters_array, - this_stop_meters, - side="right" - # want our stop_meters value to be < vp_shape_meters, - # side = "left" would be stop_meters <= vp_shape_meters - ) - - # For the next value, if there's nothing to index into, - # just set it to the same position - # if we set subseq_value = getattr(row, )[idx], - # we might not get a consecutive vp - nearest_value = valid_vp_idx_array[idx-1] - subseq_value = nearest_value + 1 - - else: - nearest_value = np.nan - subseq_value = np.nan - - nearest_vp_idx.append(nearest_value) - subseq_vp_idx.append(subseq_value) - - result = gdf[segment_identifier_cols + [ - "primary_direction", "road_meters", - "trip_instance_key"]] - - # Now assign the nearest vp for each trip that's nearest to - # a given stop - # Need to find the one after the stop later - result = result.assign( - nearest_vp_idx = nearest_vp_idx, - subseq_vp_idx = subseq_vp_idx, +if __name__ == "__main__": + + #from segment_speed_utils.project_vars import analysis_date + from shared_utils import rt_dates + analysis_date = rt_dates.DATES["oct2023"] + + start = datetime.datetime.now() + + shape_road_crosswalk = get_shape_road_crosswalk( + analysis_date, + filters = [[("shape_array_key", "in", test_shapes)]] ) - result.to_parquet( - f"{SEGMENT_GCS}projection/nearest_vp_roads_{analysis_date}.parquet" + road_segments_long = make_road_stops_long(shape_road_crosswalk) + + gdf = neighbor.merge_stop_vp_for_nearest_neighbor( + road_segments_long, + analysis_date ) - ''' + + results = neighbor.add_nearest_neighbor_result(gdf, analysis_date) + #results = compute(results)[0] + + #utils.geoparquet_gcs_export( + # results, + # SEGMENT_GCS, + # f"roads_staging/nearest_{analysis_date}" + #) + + results2 = delayed(merge_nn_with_shape)(results) + #results2 = compute(results2)[0] + + #utils.geoparquet_gcs_export( + # results2, + # SEGMENT_GCS, + # f"roads_staging/interp_{analysis_date}" + #) + + speeds = delayed(quick_calculate_speeds)(results2) + + time1 = datetime.datetime.now() + print(f"delayed dfs: {time1 - start}") + + speeds = compute(speeds)[0] + + speeds.to_parquet( + f"{SEGMENT_GCS}roads_staging/" + f"test_speeds_{analysis_date}.parquet") + end = datetime.datetime.now() - print(f"execution time: {end - start}") \ No newline at end of file + print(f"test 25 shapes: {end - start}") \ No newline at end of file diff --git a/rt_segment_speeds/scripts/sjoin_vp_to_roads.py b/rt_segment_speeds/scripts/sjoin_vp_to_roads.py deleted file mode 100644 index cf909faa3..000000000 --- a/rt_segment_speeds/scripts/sjoin_vp_to_roads.py +++ /dev/null @@ -1,183 +0,0 @@ -import dask.dataframe as dd -import dask_geopandas as dg -import datetime -import geopandas as gpd -import pandas as pd - -from dask import delayed -from segment_speed_utils.project_vars import (SEGMENT_GCS, analysis_date, - SHARED_GCS, - PROJECT_CRS) -from segment_speed_utils import helpers, wrangle_shapes - - -def sjoin_vp_to_roads( - vp: dd.DataFrame, - roads: gpd.GeoDataFrame, - road_grouping_cols: list, -) -> dd.DataFrame: - - # Need to do similar thing as sjoin with shapes - # because vp can only join to certain road segments - # not all road segments that shape doesn't travel on - # https://github.com/cal-itp/data-analyses/blob/64afd3b9ed1e239a0ca4da486e2fe1e857b17dde/rt_segment_speeds/scripts/A1_sjoin_vp_segments.py - - vp_gdf = wrangle_shapes.vp_as_gdf(vp) - - results = gpd.sjoin( - vp_gdf, - roads, - how = "inner", - predicate = "within" - ).query( - "shape_array_key_left == shape_array_key_right" - )[ - ["vp_idx", "trip_instance_key"] + - road_grouping_cols - ].drop_duplicates().sort_values("vp_idx").reset_index(drop=True) - - return results - - -def make_wide( - full_results: pd.DataFrame, - road_cols: list -) -> pd.DataFrame: - results_wide = (full_results.groupby(["trip_instance_key"] + road_cols, - observed=True, group_keys=False) - .agg({ - "vp_idx": lambda x: list(set(x)), - "segment_sequence": "nunique", - }) - .reset_index() - .rename(columns = {"vp_idx": "vp_idx_arr"}) - ) - - # No point in keeping results where there are fewer than 2 vp for entire road - results_wide = results_wide.assign( - n_vp = results_wide.apply(lambda x: len(x.vp_idx_arr), axis=1) - ).query( - 'n_vp > 1 & segment_sequence > 1' - ).drop( - columns = ["n_vp", "segment_sequence"] - ).reset_index(drop=True) - - return results_wide - - -def import_vp(analysis_date: str): - vp = dd.read_parquet( - f"{SEGMENT_GCS}vp_usable_{analysis_date}", - columns = ["vp_idx", "trip_instance_key", "x", "y", - "vp_primary_direction"], - ) - - rt_trips = vp.trip_instance_key.unique().compute().tolist() - - trip_to_shape = helpers.import_scheduled_trips( - analysis_date, - columns = ["trip_instance_key", "shape_array_key"], - filters = [[("trip_instance_key", "in", rt_trips)]], - get_pandas = True - ) - - vp2 = dd.merge( - vp, - trip_to_shape, - on = "trip_instance_key", - how = "inner" - ).repartition(npartitions=25) - - return vp2 - - -def import_roads( - analysis_date: str, - road_grouping_cols: list, -) -> dg.GeoDataFrame: - - shape_road_crosswalk = pd.read_parquet( - f"{SEGMENT_GCS}shape_road_crosswalk_{analysis_date}.parquet", - ) - - road_segments = dg.read_parquet( - f"{SHARED_GCS}road_segments/", - columns = road_grouping_cols + ["primary_direction", "geometry"] - ).merge( - shape_road_crosswalk, - on = road_grouping_cols, - how = "inner" - ) - - road_segments = road_segments.assign( - geometry = road_segments.geometry.buffer(15) - ) - - road_segments = road_segments.repartition(npartitions=25) - - return road_segments - - -if __name__ == "__main__": - - start = datetime.datetime.now() - - road_cols = ["linearid", "mtfcc"] - segment_identifier_cols = road_cols + ["segment_sequence"] - - vp = import_vp(analysis_date).persist() - roads = import_roads(analysis_date, segment_identifier_cols).persist() - - vp_dfs = [ - vp[vp.vp_primary_direction == d] - for d in wrangle_shapes.ALL_DIRECTIONS - ] - - # Remove all the Unknowns (vps that aren't moving) - road_dfs = [ - roads[roads.primary_direction != - wrangle_shapes.OPPOSITE_DIRECTIONS[d] - ] for d in wrangle_shapes.ALL_DIRECTIONS - ] - - - vp_cols_dtypes = vp[["vp_idx", "trip_instance_key"]].dtypes.to_dict() - road_cols_dtypes = roads[segment_identifier_cols].dtypes.to_dict() - - results = [ - vp_subset.map_partitions( - sjoin_vp_to_roads, - road_subset, - segment_identifier_cols, - meta = { - **vp_cols_dtypes, - **road_cols_dtypes - }, - align_dataframes = False - ) for vp_subset, road_subset in zip(vp_dfs, road_dfs) - ] - print("map partitions") - - full_results = dd.multi.concat(results, axis=0).reset_index(drop=True) - full_results = full_results.repartition(npartitions=3).persist() - - time1 = datetime.datetime.now() - print(f"map partitions and persist full results: {time1 - start}") - - full_results.to_parquet( - f"{SEGMENT_GCS}vp_sjoin/vp_road_segments_{analysis_date}", - overwrite=True - ) - - full_results = delayed(pd.read_parquet)( - f"{SEGMENT_GCS}vp_sjoin/vp_road_segments_{analysis_date}" - ) - - results_wide = delayed(make_wide)(full_results, road_cols) - - results_wide.compute().to_parquet( - f"{SEGMENT_GCS}vp_sjoin/vp_road_segments_wide_{analysis_date}.parquet", - ) - - end = datetime.datetime.now() - print(f"execution time: {end - start}") \ No newline at end of file diff --git a/rt_segment_speeds/scripts/vp_on_road_segments.ipynb b/rt_segment_speeds/scripts/vp_on_road_segments.ipynb index 2d3e25dda..e31afbbca 100644 --- a/rt_segment_speeds/scripts/vp_on_road_segments.ipynb +++ b/rt_segment_speeds/scripts/vp_on_road_segments.ipynb @@ -7,1108 +7,902 @@ "metadata": {}, "outputs": [], "source": [ - "import dask.dataframe as dd\n", "import geopandas as gpd\n", "import numpy as np\n", "import pandas as pd\n", "\n", - "from segment_speed_utils import helpers, wrangle_shapes\n", - "from segment_speed_utils.project_vars import (SEGMENT_GCS, \n", - " CONFIG_PATH, PROJECT_CRS)\n", + "from shared_utils import rt_dates\n", + "from segment_speed_utils import (helpers, neighbor, \n", + " segment_calcs, wrangle_shapes\n", + " )\n", + "from segment_speed_utils.project_vars import SEGMENT_GCS, SHARED_GCS\n", "\n", - "from segment_speed_utils.project_vars import analysis_date\n", + "analysis_date = rt_dates.DATES[\"oct2023\"]\n", "\n", "road_id_cols = [\"linearid\", \"mtfcc\", \"primary_direction\"]\n", "segment_identifier_cols = road_id_cols + [\"segment_sequence\"]\n", "\n", - "test_trip = \"00139041e36b607c7e10cb7ec023e837\"" + "import nearest_vp_to_road" ] }, { "cell_type": "code", - "execution_count": null, - "id": "d6d09963-2e38-428a-a3d2-881e7725325f", + "execution_count": 2, + "id": "1716354f-0c49-4744-af8f-a49ce538ed71", "metadata": {}, "outputs": [], "source": [ - "df = pd.read_parquet(\n", - " f\"{SEGMENT_GCS}vp_sjoin/vp_road_segments_wide_{analysis_date}.parquet\",\n", - " columns = [\"trip_instance_key\"]\n", - ").drop_duplicates()" + "pd.read_parquet(\n", + " f\"{SEGMENT_GCS}roads_staging/\"\n", + " f\"shape_road_crosswalk_{analysis_date}.parquet\",\n", + ").shape_array_key.unique()[:25]" ] }, { "cell_type": "code", "execution_count": 2, - "id": "51e8ebcb-fb45-40ec-a461-352e46616627", + "id": "40082a7d-be96-490a-913e-4422162c75e6", "metadata": {}, "outputs": [], "source": [ - "test_trips = ['00062c6db9dbef9c80f5ada74b31e257',\n", - " '0009c78b48866a26d664ab00f67d1606',\n", - " '00139041e36b607c7e10cb7ec023e837',\n", - " 'ff780650b98209acf69a71a7dab2502c',\n", - " 'ff86898d6a8ff5df82912699133bf4b6',\n", - " 'ffb44943b394f891d2b2286bb3902305']" + "from dask import delayed, compute\n", + "import dask.dataframe as dd" ] }, { "cell_type": "code", "execution_count": 3, - "id": "251c4aad-b470-4ee6-b52d-f7b151dc46af", + "id": "9383ac86-71fb-4120-b97c-85ee4778ca36", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(17079531, 5)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "import nearest_vp_to_road\n", + "shape_road_crosswalk = nearest_vp_to_road.get_shape_road_crosswalk(\n", + " analysis_date, \n", + ")\n", "\n", - "vp = nearest_vp_to_road.merge_vp_to_crosswalk(\n", - " analysis_date,\n", - " filters = [[(\"trip_instance_key\", \"in\", test_trips)]]\n", - ")" + "shape_road_crosswalk.shape" ] }, { "cell_type": "code", "execution_count": 4, - "id": "b81717ec-0985-4fd4-8c7c-9326127851d3", + "id": "1a1e06b8-6634-4030-8e7a-5ce0e99e7906", "metadata": {}, "outputs": [], "source": [ - "subset_roads = vp.linearid.unique().compute().tolist()" + "road_segments_long = delayed(nearest_vp_to_road.make_road_stops_long)(\n", + " shape_road_crosswalk)" ] }, { "cell_type": "code", - "execution_count": 5, - "id": "e22e5754-9dac-447b-bfb5-dfb33e734552", + "execution_count": null, + "id": "aa06078a-98e7-47c5-b734-09bbd709eb15", "metadata": {}, "outputs": [], "source": [ - "road_segments = nearest_vp_to_road.expand_relevant_road_segments(\n", - " analysis_date,\n", - " segment_identifier_cols = segment_identifier_cols,\n", - " filters = [[(\"linearid\", \"in\", subset_roads)]],\n", - ")" + "road_segments_long = dd.from_delayed(road_segments_long)" ] }, { "cell_type": "code", - "execution_count": 21, - "id": "9c9bf41d-1e39-4581-9873-732a158463c3", + "execution_count": null, + "id": "8981b5ce-f106-4b77-9c61-0844ba78d3df", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
linearidmtfccprimary_directionsegment_sequencedestinationgeometryroad_meters
011010947364633S1100Southbound0POINT (-217551.160 -31852.640)MULTILINESTRING ((-217311.864 -31424.886, -217...500.0
111010947364633S1100Southbound1POINT (-217576.341 -32351.779)MULTILINESTRING ((-217311.864 -31424.886, -217...1000.0
\n", - "
" - ], - "text/plain": [ - " linearid mtfcc primary_direction segment_sequence \\\n", - "0 11010947364633 S1100 Southbound 0 \n", - "1 11010947364633 S1100 Southbound 1 \n", - "\n", - " destination \\\n", - "0 POINT (-217551.160 -31852.640) \n", - "1 POINT (-217576.341 -32351.779) \n", - "\n", - " geometry road_meters \n", - "0 MULTILINESTRING ((-217311.864 -31424.886, -217... 500.0 \n", - "1 MULTILINESTRING ((-217311.864 -31424.886, -217... 1000.0 " - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "road_segments.head(2)" + "road_segments_long" ] }, { "cell_type": "code", - "execution_count": 7, - "id": "5a9f7bb4-c767-4264-b24f-f4bd9689f18c", + "execution_count": null, + "id": "d60d6653-6d12-4f4f-9c52-ee9998bedca2", "metadata": {}, "outputs": [], - "source": [ - "vp = vp.repartition(npartitions=3)" - ] + "source": [] }, { "cell_type": "code", - "execution_count": 8, - "id": "fc2dc807-c97c-4a20-98da-fa97ad3835fc", + "execution_count": null, + "id": "ef152578-f434-4e47-8983-83b664284118", "metadata": {}, "outputs": [], "source": [ - "road_dtypes = vp[road_id_cols].dtypes.to_dict()\n", "\n", - "vp_projected = vp.map_partitions(\n", - " wrangle_shapes.project_vp_onto_segment_geometry,\n", - " road_segments,\n", - " grouping_cols = road_id_cols,\n", - " meta = {\n", - " \"vp_idx\": \"int64\",\n", - " **road_dtypes,\n", - " \"shape_meters\": \"float\"},\n", - " align_dataframes = False\n", - ").persist()\n", "\n", - "# Merge vp with road segment info \n", - "# with projected shape meters against the full road \n", - "df_with_projection = dd.merge(\n", - " vp,\n", - " vp_projected,\n", - " on = [\"vp_idx\"] + road_id_cols,\n", - " how = \"inner\"\n", - ").drop(columns = [\"x\", \"y\"]).compute()" + "\n", + "gdf = delayed(neighbor.merge_stop_vp_for_nearest_neighbor)(\n", + " road_segments_long, \n", + " analysis_date\n", + ")" ] }, { "cell_type": "code", - "execution_count": 23, - "id": "190949c6-8dd2-48a2-b4d9-872ef8c3a015", + "execution_count": null, + "id": "3a602d9b-e440-4f62-931f-2460da0e3f3a", "metadata": {}, "outputs": [], "source": [ - "df_with_projection_wide = (df_with_projection\n", - " .groupby([\"trip_instance_key\"] + road_id_cols)\n", - " .agg({\n", - " \"vp_idx\": lambda x: list(x),\n", - " \"shape_meters\": lambda x: list(x),\n", - " })\n", - " .reset_index()\n", - " .rename(columns = {\n", - " \"vp_idx\": \"vp_idx_arr\",\n", - " \"shape_meters\": \"shape_meters_arr\"\n", - " })\n", - " )" + "road_segments_long.shape" ] }, { "cell_type": "code", - "execution_count": 25, - "id": "c3a7a8c2-7da7-44b5-9129-0c1be76348d3", + "execution_count": null, + "id": "052f3192-2a7a-40f0-bdaf-ef60a69f0e17", "metadata": {}, "outputs": [], "source": [ - "df_with_projection_wide = df_with_projection_wide.assign(\n", - " min_shape_meters = df_with_projection_wide.apply(\n", - " lambda x: x.shape_meters_arr[0], axis=1),\n", - " max_shape_meters = df_with_projection_wide.apply(\n", - " lambda x: x.shape_meters_arr[-1], axis=1), \n", - ")" + "results = delayed(neighbor.add_nearest_neighbor_result)(\n", + " gdf, analysis_date)" ] }, { "cell_type": "code", - "execution_count": 28, - "id": "7b1cb352-a07b-4a00-8546-995801b51ef8", + "execution_count": null, + "id": "c2285565-c417-4b05-a2d3-aa9693bf21d5", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c234ba77-c074-4d54-8ad9-a27b902e901e", "metadata": {}, "outputs": [], "source": [ - "# Now merge road segments with each destination acting as the road's stop\n", - "# and merge on arrays of projected vp against that road\n", - "gdf = pd.merge(\n", - " road_segments,\n", - " df_with_projection_wide,\n", - " on = road_id_cols,\n", - " how = \"inner\"\n", - ")" + "df = pd.read_parquet(\n", + " f\"{SEGMENT_GCS}roads_staging/\"\n", + " f\"test_speeds_{analysis_date}.parquet\"\n", + ") " ] }, { "cell_type": "code", - "execution_count": 29, - "id": "ad4f65d9-b281-4c39-9efc-2c92f1fc719c", + "execution_count": 6, + "id": "31d8d5b1-3e6b-45e1-b271-4e6916a97a86", "metadata": {}, "outputs": [ { "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
linearidmtfccprimary_directionsegment_sequencedestinationgeometryroad_meterstrip_instance_keyvp_idx_arrshape_meters_arrmin_shape_metersmax_shape_meters
011010947364633S1100Southbound0POINT (-217551.160 -31852.640)MULTILINESTRING ((-217311.864 -31424.886, -217...500.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]339.540351339.540351
111010947364633S1100Southbound1POINT (-217576.341 -32351.779)MULTILINESTRING ((-217311.864 -31424.886, -217...1000.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]339.540351339.540351
\n", - "
" - ], "text/plain": [ - " linearid mtfcc primary_direction segment_sequence \\\n", - "0 11010947364633 S1100 Southbound 0 \n", - "1 11010947364633 S1100 Southbound 1 \n", - "\n", - " destination \\\n", - "0 POINT (-217551.160 -31852.640) \n", - "1 POINT (-217576.341 -32351.779) \n", - "\n", - " geometry road_meters \\\n", - "0 MULTILINESTRING ((-217311.864 -31424.886, -217... 500.0 \n", - "1 MULTILINESTRING ((-217311.864 -31424.886, -217... 1000.0 \n", - "\n", - " trip_instance_key vp_idx_arr \\\n", - "0 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - "1 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - "\n", - " shape_meters_arr min_shape_meters \\\n", - "0 [339.54035083029106, 339.54035083029106] 339.540351 \n", - "1 [339.54035083029106, 339.54035083029106] 339.540351 \n", - "\n", - " max_shape_meters \n", - "0 339.540351 \n", - "1 339.540351 " + "" ] }, - "execution_count": 29, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" - } - ], - "source": [ - "gdf.head(2)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "4b91114b-a8a9-4f2a-9c54-04a2b8606531", - "metadata": {}, - "outputs": [ + }, { "data": { + "image/png": "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", "text/plain": [ - "(1000.0000000000001, [339.54035083029106, 339.54035083029106])" + "
" ] }, - "execution_count": 12, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "n = 1\n", - "gdf.road_meters.iloc[n], gdf.shape_meters_arr.iloc[n]" + "df.speed_mph.hist(bins=range(0, 100, 5))" ] }, { "cell_type": "code", - "execution_count": 13, - "id": "adb3d2a4-e384-4657-8622-b6fdec81a7ff", + "execution_count": 7, + "id": "8797146e-6c3a-4dbf-93a7-7e0491d9f371", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "(5500.000000000003, [339.54035083029106, 339.54035083029106])" + "count 16549.000000\n", + "mean 62.695220\n", + "std 15.696857\n", + "min 50.008048\n", + "25% 55.952116\n", + "50% 60.287453\n", + "75% 63.596422\n", + "max 325.685493\n", + "Name: speed_mph, dtype: float64" ] }, - "execution_count": 13, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "n = 10\n", - "gdf.road_meters.iloc[n], gdf.shape_meters_arr.iloc[n]" + "df[(df.speed_mph >= 50) & (df.sec_elapsed >= 20)].speed_mph.describe()" ] }, { "cell_type": "code", - "execution_count": 14, - "id": "acc93032-b8c6-4d5b-930e-0ae422a250d7", + "execution_count": 12, + "id": "c6a1ec62-4d7b-4e1b-93a6-0c9549ec9810", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "(19999.999999999993, [339.54035083029106, 339.54035083029106])" + "" ] }, - "execution_count": 14, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjAAAAGdCAYAAAAMm0nCAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8/fFQqAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAjp0lEQVR4nO3de3CU5f2/8XeOGyJsIjBsoCSQlo4QOYOYVWtBQ1JMrQfakSkiFdSBBmvIjAgtIgdpKBYRNUpVJHaUqnQ8QazJGgqUEk7RKKCiHXHiFDeppbAcN0vy/P74/vIMazgkkM2TO1yvGWbcZ+/cufdDwGt2syTKsixLAAAABol2+gAAAAAtRcAAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOMQMAAAwDgEDAAAME6s0weIlIaGBh04cEBdunRRVFSU08cBAADNYFmWjhw5ol69eik6+uzPs3TYgDlw4IBSU1OdPgYAALgAX3/9tXr37n3W+ztswHTp0kXS/w3A7Xa32r6hUEhlZWXKzs5WXFxcq+2L82P2zmH2zmH2zmH2zggEAkpNTbX/P342HTZgGl82crvdrR4wiYmJcrvdfEG3MWbvHGbvHGbvHGbvrPN9+wffxAsAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOPEOn0ANNV3dklE9/9qSW5E9wcAINJ4BgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGOeiAmbJkiWKiopSfn6+fe3kyZPKy8tTt27d1LlzZ40fP141NTVhH1ddXa3c3FwlJiaqR48eevDBB3Xq1KmwNRs3btTw4cPlcrnUr18/FRcXX8xRAQBAB3LBAbNz50796U9/0uDBg8Ouz5w5U+vWrdPatWu1adMmHThwQLfffrt9f319vXJzc1VXV6etW7fqpZdeUnFxsebNm2ev2b9/v3JzczVmzBhVVVUpPz9f99xzj0pLSy/0uAAAoAO5oIA5evSoJk6cqOeff16XX365ff3w4cNatWqVHn/8cd1www0aMWKEVq9era1bt2rbtm2SpLKyMn3yySd6+eWXNXToUI0bN06LFi1SUVGR6urqJEkrV65Uenq6li1bpgEDBmjGjBn6+c9/ruXLl7fCQwYAAKaLvZAPysvLU25urrKysvToo4/a1ysrKxUKhZSVlWVf69+/v9LS0lRRUaHMzExVVFRo0KBB8ng89pqcnBxNnz5de/fu1bBhw1RRURG2R+Oa01+q+q5gMKhgMGjfDgQCkqRQKKRQKHQhD/OMGvdqzT2/yxVjRWxvKbJnj6S2mD3OjNk7h9k7h9k7o7nzbnHAvPrqq/rggw+0c+fOJvf5/X7Fx8crOTk57LrH45Hf77fXnB4vjfc33neuNYFAQCdOnFCnTp2afO7CwkItWLCgyfWysjIlJiY2/wE2k8/na/U9Gy0dFbGtJUnvvvtuZD9BhEVy9jg3Zu8cZu8cZt+2jh8/3qx1LQqYr7/+Wg888IB8Pp8SEhIu6GCRMmfOHBUUFNi3A4GAUlNTlZ2dLbfb3WqfJxQKyefzaezYsYqLi2u1fU83cH5kv9dnz/yciO4fKW0xe5wZs3cOs3cOs3dG4yso59OigKmsrFRtba2GDx9uX6uvr9fmzZv19NNPq7S0VHV1dTp06FDYszA1NTVKSUmRJKWkpGjHjh1h+za+S+n0Nd9951JNTY3cbvcZn32RJJfLJZfL1eR6XFxcRL7wIrWvJAXroyKybyPT/yBGcvY4N2bvHGbvHGbftpo76xZ9E++NN96o3bt3q6qqyv41cuRITZw40f7vuLg4lZeX2x+zb98+VVdXy+v1SpK8Xq92796t2tpae43P55Pb7VZGRoa95vQ9Gtc07gEAAC5tLXoGpkuXLho4cGDYtcsuu0zdunWzr0+dOlUFBQXq2rWr3G637r//fnm9XmVmZkqSsrOzlZGRoUmTJmnp0qXy+/2aO3eu8vLy7GdQpk2bpqefflqzZs3SlClTtGHDBr3++usqKSlpjccMAAAMd0HvQjqX5cuXKzo6WuPHj1cwGFROTo6eeeYZ+/6YmBitX79e06dPl9fr1WWXXabJkydr4cKF9pr09HSVlJRo5syZWrFihXr37q0XXnhBOTlmfu8GAABoXRcdMBs3bgy7nZCQoKKiIhUVFZ31Y/r06XPed8KMHj1aH3744cUeDwAAdED8LCQAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYJxYpw+Attd3dklE9/9qSW5E9wcAgGdgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYp0UB8+yzz2rw4MFyu91yu93yer3629/+Zt9/8uRJ5eXlqVu3burcubPGjx+vmpqasD2qq6uVm5urxMRE9ejRQw8++KBOnToVtmbjxo0aPny4XC6X+vXrp+Li4gt/hAAAoMNpUcD07t1bS5YsUWVlpXbt2qUbbrhBt9xyi/bu3StJmjlzptatW6e1a9dq06ZNOnDggG6//Xb74+vr65Wbm6u6ujpt3bpVL730koqLizVv3jx7zf79+5Wbm6sxY8aoqqpK+fn5uueee1RaWtpKDxkAAJgutiWLb7755rDbixcv1rPPPqtt27apd+/eWrVqldasWaMbbrhBkrR69WoNGDBA27ZtU2ZmpsrKyvTJJ5/o/fffl8fj0dChQ7Vo0SI99NBDmj9/vuLj47Vy5Uqlp6dr2bJlkqQBAwZoy5YtWr58uXJyclrpYQMAAJO1KGBOV19fr7Vr1+rYsWPyer2qrKxUKBRSVlaWvaZ///5KS0tTRUWFMjMzVVFRoUGDBsnj8dhrcnJyNH36dO3du1fDhg1TRUVF2B6Na/Lz8895nmAwqGAwaN8OBAKSpFAopFAodKEPs4nGvVpzz+9yxVgR27stRGo2bTF7nBmzdw6zdw6zd0Zz593igNm9e7e8Xq9Onjypzp07680331RGRoaqqqoUHx+v5OTksPUej0d+v1+S5Pf7w+Kl8f7G+861JhAI6MSJE+rUqdMZz1VYWKgFCxY0uV5WVqbExMSWPszz8vl8rb5no6WjIrZ1m3j33Xcjun8kZ49zY/bOYfbOYfZt6/jx481a1+KAueKKK1RVVaXDhw/rr3/9qyZPnqxNmza1+ICtbc6cOSooKLBvBwIBpaamKjs7W263u9U+TygUks/n09ixYxUXF9dq+55u4Hyzv99nz/zIvNTXFrPHmTF75zB75zB7ZzS+gnI+LQ6Y+Ph49evXT5I0YsQI7dy5UytWrNAdd9yhuro6HTp0KOxZmJqaGqWkpEiSUlJStGPHjrD9Gt+ldPqa775zqaamRm63+6zPvkiSy+WSy+Vqcj0uLi4iX3iR2leSgvVREdm3rUT6D3okZ49zY/bOYfbOYfZtq7mzvuh/B6ahoUHBYFAjRoxQXFycysvL7fv27dun6upqeb1eSZLX69Xu3btVW1trr/H5fHK73crIyLDXnL5H45rGPQAAAFr0DMycOXM0btw4paWl6ciRI1qzZo02btyo0tJSJSUlaerUqSooKFDXrl3ldrt1//33y+v1KjMzU5KUnZ2tjIwMTZo0SUuXLpXf79fcuXOVl5dnP3sybdo0Pf3005o1a5amTJmiDRs26PXXX1dJSUnrP3oAAGCkFgVMbW2t7rrrLn3zzTdKSkrS4MGDVVpaqrFjx0qSli9frujoaI0fP17BYFA5OTl65pln7I+PiYnR+vXrNX36dHm9Xl122WWaPHmyFi5caK9JT09XSUmJZs6cqRUrVqh379564YUXeAs1AACwtShgVq1adc77ExISVFRUpKKiorOu6dOnz3nfpTJ69Gh9+OGHLTkaAAC4hPCzkAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGCfW6QOg4+k7uyQi+7piLC0dFZGtAQCG4RkYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIcfJQDgkhCpH3EhSV8tyY3Y3gDOjGdgAACAcXgG5gINnF+qYH2U08cAAOCSxDMwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACM06KAKSws1FVXXaUuXbqoR48euvXWW7Vv376wNSdPnlReXp66deumzp07a/z48aqpqQlbU11drdzcXCUmJqpHjx568MEHderUqbA1Gzdu1PDhw+VyudSvXz8VFxdf2CMEAAAdTosCZtOmTcrLy9O2bdvk8/kUCoWUnZ2tY8eO2WtmzpypdevWae3atdq0aZMOHDig22+/3b6/vr5eubm5qqur09atW/XSSy+puLhY8+bNs9fs379fubm5GjNmjKqqqpSfn6977rlHpaWlrfCQAQCA6WJbsvi9994Lu11cXKwePXqosrJS119/vQ4fPqxVq1ZpzZo1uuGGGyRJq1ev1oABA7Rt2zZlZmaqrKxMn3zyid5//315PB4NHTpUixYt0kMPPaT58+crPj5eK1euVHp6upYtWyZJGjBggLZs2aLly5crJyenlR46AAAwVYsC5rsOHz4sSerataskqbKyUqFQSFlZWfaa/v37Ky0tTRUVFcrMzFRFRYUGDRokj8djr8nJydH06dO1d+9eDRs2TBUVFWF7NK7Jz88/61mCwaCCwaB9OxAISJJCoZBCodDFPMwwjXu5oq1W2xPN0zjz1vz9RPM0ztzk2btiIvdnNpJz6QizNxWzd0Zz533BAdPQ0KD8/Hxde+21GjhwoCTJ7/crPj5eycnJYWs9Ho/8fr+95vR4aby/8b5zrQkEAjpx4oQ6derU5DyFhYVasGBBk+tlZWVKTEy8sAd5DotGNrT6nmgen8/n9BEuWSbPfumoyO397rvvRm7z/8/k2ZuO2bet48ePN2vdBQdMXl6e9uzZoy1btlzoFq1qzpw5KigosG8HAgGlpqYqOztbbre71T5PKBSSz+fTw7uiFWyIarV9cX6uaEuLRjZo7NixiouLc/o4l5TGr3uTZz9wfuS+h27P/Mi9tN0RZm8qZu+MxldQzueCAmbGjBlav369Nm/erN69e9vXU1JSVFdXp0OHDoU9C1NTU6OUlBR7zY4dO8L2a3yX0ulrvvvOpZqaGrnd7jM++yJJLpdLLperyfW4uLiIfOEFG6IUrCdgnBCp31Ocn8mzj+Sf17aYicmzNx2zb1vNnXWL3oVkWZZmzJihN998Uxs2bFB6enrY/SNGjFBcXJzKy8vta/v27VN1dbW8Xq8kyev1avfu3aqtrbXX+Hw+ud1uZWRk2GtO36NxTeMeAADg0taiZ2Dy8vK0Zs0avf322+rSpYv9PStJSUnq1KmTkpKSNHXqVBUUFKhr165yu926//775fV6lZmZKUnKzs5WRkaGJk2apKVLl8rv92vu3LnKy8uzn0GZNm2ann76ac2aNUtTpkzRhg0b9Prrr6ukpKSVHz4AADBRi56BefbZZ3X48GGNHj1aPXv2tH+99tpr9prly5frpz/9qcaPH6/rr79eKSkpeuONN+z7Y2JitH79esXExMjr9erOO+/UXXfdpYULF9pr0tPTVVJSIp/PpyFDhmjZsmV64YUXeAs1AACQ1MJnYCzr/G9DTEhIUFFRkYqKis66pk+fPuf9rv3Ro0frww8/bMnxAADAJYKfhQQAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACME+v0AQBAkvrOLnH6CAAMwjMwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4vAsJxhk4v1TB+qiI7P3VktyI7AsAaF08AwMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADAOAQMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjxDp9AAAwXd/ZJRHb2xVjaemoiG0PGItnYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHn4UEoNkGzi9VsD7K6WMAAM/AAAAA8xAwAADAOLyEBHQgfWeXRGRfV4ylpaMisjUAXBCegQEAAMYhYAAAgHF4CQk4TaRegmn01ZLciO4PAJeKFj8Ds3nzZt18883q1auXoqKi9NZbb4Xdb1mW5s2bp549e6pTp07KysrSF198Ebbm4MGDmjhxotxut5KTkzV16lQdPXo0bM3HH3+sH/3oR0pISFBqaqqWLl3a8kcHAAA6pBYHzLFjxzRkyBAVFRWd8f6lS5fqySef1MqVK7V9+3ZddtllysnJ0cmTJ+01EydO1N69e+Xz+bR+/Xpt3rxZ9913n31/IBBQdna2+vTpo8rKSj322GOaP3++nnvuuQt4iAAAoKNp8UtI48aN07hx4854n2VZeuKJJzR37lzdcsstkqQ///nP8ng8euuttzRhwgR9+umneu+997Rz506NHDlSkvTUU0/ppptu0h//+Ef16tVLr7zyiurq6vTiiy8qPj5eV155paqqqvT444+HhQ4AALg0ter3wOzfv19+v19ZWVn2taSkJF199dWqqKjQhAkTVFFRoeTkZDteJCkrK0vR0dHavn27brvtNlVUVOj6669XfHy8vSYnJ0d/+MMf9L///U+XX355k88dDAYVDAbt24FAQJIUCoUUCoVa7TE27uWKtlptTzRP48xNnn1rfi2eiSsmMrPpCLM3VePMI/21g6YaZ87s21Zz592qAeP3+yVJHo8n7LrH47Hv8/v96tGjR/ghYmPVtWvXsDXp6elN9mi870wBU1hYqAULFjS5XlZWpsTExAt8RGe3aGRDq++J5jF59u+++25E94/0v9Vi8uxN5/P5nD7CJYvZt63jx483a12HeRfSnDlzVFBQYN8OBAJKTU1Vdna23G53q32eUCgkn8+nh3dFK9jAz4RpS65oS4tGNhg9+z3zcyK6/8D5pRHZtyPM3lSNsx87dqzi4uKcPs4lpfHve2bfthpfQTmfVg2YlJQUSVJNTY169uxpX6+pqdHQoUPtNbW1tWEfd+rUKR08eND++JSUFNXU1IStabzduOa7XC6XXC5Xk+txcXER+cILNkTxQ+0cYvLsf/hwWYQ/Q2TnYvLsTRepv8twfsy+bTV31q36D9mlp6crJSVF5eXl9rVAIKDt27fL6/VKkrxerw4dOqTKykp7zYYNG9TQ0KCrr77aXrN58+aw18F8Pp+uuOKKM758BAAALi0tDpijR4+qqqpKVVVVkv7vG3erqqpUXV2tqKgo5efn69FHH9U777yj3bt366677lKvXr106623SpIGDBign/zkJ7r33nu1Y8cO/fOf/9SMGTM0YcIE9erVS5L0y1/+UvHx8Zo6dar27t2r1157TStWrAh7iQgAAFy6WvwS0q5duzRmzBj7dmNUTJ48WcXFxZo1a5aOHTum++67T4cOHdJ1112n9957TwkJCfbHvPLKK5oxY4ZuvPFGRUdHa/z48XryySft+5OSklRWVqa8vDyNGDFC3bt317x583gLNQAAkHQBATN69GhZ1tnfShkVFaWFCxdq4cKFZ13TtWtXrVmz5pyfZ/DgwfrHP/7R0uMBAIBLAD/MEQAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgHAIGAAAYh4ABAADGIWAAAIBxCBgAAGAcAgYAABiHgAEAAMYhYAAAgHEIGAAAYBwCBgAAGIeAAQAAxiFgAACAcQgYAABgnFinDwAAOL+B80sVrI9y+hjtzldLcp0+AhzCMzAAAMA4BAwAADAOLyEBAIzVd3ZJxPZ2xVhaOiqyL9/xEtiFa9cBU1RUpMcee0x+v19DhgzRU089pVGjRjl9LAAAWkUkAyzSnI6vdvsS0muvvaaCggI98sgj+uCDDzRkyBDl5OSotrbW6aMBAACHtduAefzxx3Xvvffq7rvvVkZGhlauXKnExES9+OKLTh8NAAA4rF2+hFRXV6fKykrNmTPHvhYdHa2srCxVVFSc8WOCwaCCwaB9+/Dhw5KkgwcPKhQKtdrZQqGQjh8/rthQtOobeEtjW4ptsHT8eAOzdwCzdw6zdw6zP7f//ve/Edn3yJEjkiTLss65rl0GzLfffqv6+np5PJ6w6x6PR5999tkZP6awsFALFixocj09PT0iZ4Qzfun0AS5hzN45zN45zP7sui+L7P5HjhxRUlLSWe9vlwFzIebMmaOCggL7dkNDgw4ePKhu3bopKqr1yjkQCCg1NVVff/213G53q+2L82P2zmH2zmH2zmH2zrAsS0eOHFGvXr3Oua5dBkz37t0VExOjmpqasOs1NTVKSUk548e4XC65XK6wa8nJyZE6otxuN1/QDmH2zmH2zmH2zmH2be9cz7w0apffxBsfH68RI0aovLzcvtbQ0KDy8nJ5vV4HTwYAANqDdvkMjCQVFBRo8uTJGjlypEaNGqUnnnhCx44d09133+300QAAgMPabcDccccd+s9//qN58+bJ7/dr6NCheu+995p8Y29bc7lceuSRR5q8XIXIY/bOYfbOYfbOYfbtW5R1vvcpAQAAtDPt8ntgAAAAzoWAAQAAxiFgAACAcQgYAABgHAKmhYqKitS3b18lJCTo6quv1o4dO5w+UodTWFioq666Sl26dFGPHj106623at++fWFrTp48qby8PHXr1k2dO3fW+PHjm/zDh7g4S5YsUVRUlPLz8+1rzD1y/v3vf+vOO+9Ut27d1KlTJw0aNEi7du2y77csS/PmzVPPnj3VqVMnZWVl6YsvvnDwxB1DfX29Hn74YaWnp6tTp076wQ9+oEWLFoX9HB5m305ZaLZXX33Vio+Pt1588UVr79691r333mslJydbNTU1Th+tQ8nJybFWr15t7dmzx6qqqrJuuukmKy0tzTp69Ki9Ztq0aVZqaqpVXl5u7dq1y8rMzLSuueYaB0/dsezYscPq27evNXjwYOuBBx6wrzP3yDh48KDVp08f61e/+pW1fft268svv7RKS0utf/3rX/aaJUuWWElJSdZbb71lffTRR9bPfvYzKz093Tpx4oSDJzff4sWLrW7dulnr16+39u/fb61du9bq3LmztWLFCnsNs2+fCJgWGDVqlJWXl2ffrq+vt3r16mUVFhY6eKqOr7a21pJkbdq0ybIsyzp06JAVFxdnrV271l7z6aefWpKsiooKp47ZYRw5csT64Q9/aPl8PuvHP/6xHTDMPXIeeugh67rrrjvr/Q0NDVZKSor12GOP2dcOHTpkuVwu6y9/+UtbHLHDys3NtaZMmRJ27fbbb7cmTpxoWRazb894CamZ6urqVFlZqaysLPtadHS0srKyVFFR4eDJOr7Dhw9Lkrp27SpJqqysVCgUCvu96N+/v9LS0vi9aAV5eXnKzc0Nm6/E3CPpnXfe0ciRI/WLX/xCPXr00LBhw/T888/b9+/fv19+vz9s9klJSbr66quZ/UW65pprVF5ers8//1yS9NFHH2nLli0aN26cJGbfnrXbf4m3vfn2229VX1/f5F8C9ng8+uyzzxw6VcfX0NCg/Px8XXvttRo4cKAkye/3Kz4+vskP6/R4PPL7/Q6csuN49dVX9cEHH2jnzp1N7mPukfPll1/q2WefVUFBgX77299q586d+s1vfqP4+HhNnjzZnu+Z/v5h9hdn9uzZCgQC6t+/v2JiYlRfX6/Fixdr4sSJksTs2zECBu1aXl6e9uzZoy1btjh9lA7v66+/1gMPPCCfz6eEhASnj3NJaWho0MiRI/X73/9ekjRs2DDt2bNHK1eu1OTJkx0+Xcf2+uuv65VXXtGaNWt05ZVXqqqqSvn5+erVqxezb+d4CamZunfvrpiYmCbvuKipqVFKSopDp+rYZsyYofXr1+vvf/+7evfubV9PSUlRXV2dDh06FLae34uLU1lZqdraWg0fPlyxsbGKjY3Vpk2b9OSTTyo2NlYej4e5R0jPnj2VkZERdm3AgAGqrq6WJHu+/P3T+h588EHNnj1bEyZM0KBBgzRp0iTNnDlThYWFkph9e0bANFN8fLxGjBih8vJy+1pDQ4PKy8vl9XodPFnHY1mWZsyYoTfffFMbNmxQenp62P0jRoxQXFxc2O/Fvn37VF1dze/FRbjxxhu1e/duVVVV2b9GjhypiRMn2v/N3CPj2muvbfJPBXz++efq06ePJCk9PV0pKSlhsw8EAtq+fTuzv0jHjx9XdHT4/wpjYmLU0NAgidm3a05/F7FJXn31VcvlclnFxcXWJ598Yt13331WcnKy5ff7nT5ahzJ9+nQrKSnJ2rhxo/XNN9/Yv44fP26vmTZtmpWWlmZt2LDB2rVrl+X1ei2v1+vgqTum09+FZFnMPVJ27NhhxcbGWosXL7a++OIL65VXXrESExOtl19+2V6zZMkSKzk52Xr77betjz/+2Lrlllt4K28rmDx5svW9733Pfhv1G2+8YXXv3t2aNWuWvYbZt08ETAs99dRTVlpamhUfH2+NGjXK2rZtm9NH6nAknfHX6tWr7TUnTpywfv3rX1uXX365lZiYaN12223WN99849yhO6jvBgxzj5x169ZZAwcOtFwul9W/f3/rueeeC7u/oaHBevjhhy2Px2O5XC7rxhtvtPbt2+fQaTuOQCBgPfDAA1ZaWpqVkJBgff/737d+97vfWcFg0F7D7NunKMs67Z8bBAAAMADfAwMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADDO/wN1Xh6CvI/BPQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "n = 39\n", - "gdf.road_meters.iloc[n], gdf.shape_meters_arr.iloc[n]" + "df[(df.meters_elapsed >= 1609/2) & \n", + " (df.sec_elapsed >= 40)].speed_mph.hist(bins=range(0, 100, 5))" ] }, { "cell_type": "code", "execution_count": 15, - "id": "6ad917a5-1e01-404c-a975-cac502890721", + "id": "693bbf06-56ff-477e-8f12-0ab962973ddb", "metadata": {}, "outputs": [ { "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
linearidmtfccprimary_directionsegment_sequencedestinationgeometryroad_meterstrip_instance_keyvp_idx_arrshape_meters_arr
011010947364633S1100Southbound0POINT (-217551.160 -31852.640)MULTILINESTRING ((-217311.864 -31424.886, -217...500.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]
111010947364633S1100Southbound1POINT (-217576.341 -32351.779)MULTILINESTRING ((-217311.864 -31424.886, -217...1000.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]
211010947364633S1100Southbound2POINT (-217512.017 -32844.758)MULTILINESTRING ((-217311.864 -31424.886, -217...1500.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]
311010947364633S1100Southbound3POINT (-217506.222 -33343.486)MULTILINESTRING ((-217311.864 -31424.886, -217...2000.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]
411010947364633S1100Southbound4POINT (-217595.334 -33833.011)MULTILINESTRING ((-217311.864 -31424.886, -217...2500.0ffb44943b394f891d2b2286bb3902305[12751727, 12751726][339.54035083029106, 339.54035083029106]
.................................
851108475858996S1200Southbound2POINT (-212979.148 -22668.487)MULTILINESTRING ((-213174.831 -21182.164, -213...1500.000062c6db9dbef9c80f5ada74b31e257[6357586, 6357587, 6357588][189.6522603479828, 293.39996228253057, 308.90...
861108475858996S1200Southbound3POINT (-212909.560 -23163.518)MULTILINESTRING ((-213174.831 -21182.164, -213...2000.000062c6db9dbef9c80f5ada74b31e257[6357586, 6357587, 6357588][189.6522603479828, 293.39996228253057, 308.90...
871108475858996S1200Southbound4POINT (-212845.047 -23658.934)MULTILINESTRING ((-213174.831 -21182.164, -213...2500.000062c6db9dbef9c80f5ada74b31e257[6357586, 6357587, 6357588][189.6522603479828, 293.39996228253057, 308.90...
881108475858996S1200Southbound5POINT (-212774.877 -24153.648)MULTILINESTRING ((-213174.831 -21182.164, -213...3000.000062c6db9dbef9c80f5ada74b31e257[6357586, 6357587, 6357588][189.6522603479828, 293.39996228253057, 308.90...
891108475858996S1200Southbound6POINT (-212657.358 -24637.736)MULTILINESTRING ((-213174.831 -21182.164, -213...3500.000062c6db9dbef9c80f5ada74b31e257[6357586, 6357587, 6357588][189.6522603479828, 293.39996228253057, 308.90...
\n", - "

90 rows × 10 columns

\n", - "
" - ], "text/plain": [ - " linearid mtfcc primary_direction segment_sequence \\\n", - "0 11010947364633 S1100 Southbound 0 \n", - "1 11010947364633 S1100 Southbound 1 \n", - "2 11010947364633 S1100 Southbound 2 \n", - "3 11010947364633 S1100 Southbound 3 \n", - "4 11010947364633 S1100 Southbound 4 \n", - ".. ... ... ... ... \n", - "85 1108475858996 S1200 Southbound 2 \n", - "86 1108475858996 S1200 Southbound 3 \n", - "87 1108475858996 S1200 Southbound 4 \n", - "88 1108475858996 S1200 Southbound 5 \n", - "89 1108475858996 S1200 Southbound 6 \n", - "\n", - " destination \\\n", - "0 POINT (-217551.160 -31852.640) \n", - "1 POINT (-217576.341 -32351.779) \n", - "2 POINT (-217512.017 -32844.758) \n", - "3 POINT (-217506.222 -33343.486) \n", - "4 POINT (-217595.334 -33833.011) \n", - ".. ... \n", - "85 POINT (-212979.148 -22668.487) \n", - "86 POINT (-212909.560 -23163.518) \n", - "87 POINT (-212845.047 -23658.934) \n", - "88 POINT (-212774.877 -24153.648) \n", - "89 POINT (-212657.358 -24637.736) \n", - "\n", - " geometry road_meters \\\n", - "0 MULTILINESTRING ((-217311.864 -31424.886, -217... 500.0 \n", - "1 MULTILINESTRING ((-217311.864 -31424.886, -217... 1000.0 \n", - "2 MULTILINESTRING ((-217311.864 -31424.886, -217... 1500.0 \n", - "3 MULTILINESTRING ((-217311.864 -31424.886, -217... 2000.0 \n", - "4 MULTILINESTRING ((-217311.864 -31424.886, -217... 2500.0 \n", - ".. ... ... \n", - "85 MULTILINESTRING ((-213174.831 -21182.164, -213... 1500.0 \n", - "86 MULTILINESTRING ((-213174.831 -21182.164, -213... 2000.0 \n", - "87 MULTILINESTRING ((-213174.831 -21182.164, -213... 2500.0 \n", - "88 MULTILINESTRING ((-213174.831 -21182.164, -213... 3000.0 \n", - "89 MULTILINESTRING ((-213174.831 -21182.164, -213... 3500.0 \n", - "\n", - " trip_instance_key vp_idx_arr \\\n", - "0 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - "1 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - "2 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - "3 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - "4 ffb44943b394f891d2b2286bb3902305 [12751727, 12751726] \n", - ".. ... ... \n", - "85 00062c6db9dbef9c80f5ada74b31e257 [6357586, 6357587, 6357588] \n", - "86 00062c6db9dbef9c80f5ada74b31e257 [6357586, 6357587, 6357588] \n", - "87 00062c6db9dbef9c80f5ada74b31e257 [6357586, 6357587, 6357588] \n", - "88 00062c6db9dbef9c80f5ada74b31e257 [6357586, 6357587, 6357588] \n", - "89 00062c6db9dbef9c80f5ada74b31e257 [6357586, 6357587, 6357588] \n", - "\n", - " shape_meters_arr \n", - "0 [339.54035083029106, 339.54035083029106] \n", - "1 [339.54035083029106, 339.54035083029106] \n", - "2 [339.54035083029106, 339.54035083029106] \n", - "3 [339.54035083029106, 339.54035083029106] \n", - "4 [339.54035083029106, 339.54035083029106] \n", - ".. ... \n", - "85 [189.6522603479828, 293.39996228253057, 308.90... \n", - "86 [189.6522603479828, 293.39996228253057, 308.90... \n", - "87 [189.6522603479828, 293.39996228253057, 308.90... \n", - "88 [189.6522603479828, 293.39996228253057, 308.90... \n", - "89 [189.6522603479828, 293.39996228253057, 308.90... \n", - "\n", - "[90 rows x 10 columns]" + "count 34836.000000\n", + "mean 35.843176\n", + "std 24.241283\n", + "min 0.025925\n", + "25% 12.368664\n", + "50% 36.909153\n", + "75% 57.950581\n", + "max 294.128701\n", + "Name: speed_mph, dtype: float64" ] }, - "execution_count": 15, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AxesSubplot(0.125,0.11;0.775x0.77)\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "gdf" + "cutoff = 30\n", + "subset = df[(df.meters_elapsed >= 1609/2) & \n", + " (df.sec_elapsed >= cutoff)]\n", + "display(subset.speed_mph.describe())\n", + "subset.speed_mph.hist(bins=range(0, 100, 5))" ] }, { "cell_type": "code", "execution_count": 16, - "id": "0c81a245-6f96-41a0-9243-a63547565705", - "metadata": {}, - "outputs": [], - "source": [ - "nearest_vp_idx = []\n", - "subseq_vp_idx = []\n", - "\n", - "for row in gdf.itertuples():\n", - " \n", - " this_stop_meters = getattr(row, \"road_meters\")\n", - " valid_shape_meters_array = getattr(row, \"shape_meters_arr\")\n", - " valid_vp_idx_array = np.asarray(getattr(row, \"vp_idx_arr\"))\n", - "\n", - " idx = np.searchsorted(\n", - " valid_shape_meters_array,\n", - " this_stop_meters,\n", - " side=\"right\" \n", - " # want our stop_meters value to be < vp_shape_meters,\n", - " # side = \"left\" would be stop_meters <= vp_shape_meters\n", - " )\n", - "\n", - " # For the next value, if there's nothing to index into, \n", - " # just set it to the same position\n", - " # if we set subseq_value = getattr(row, )[idx], \n", - " # we might not get a consecutive vp\n", - " nearest_value = valid_vp_idx_array[idx-1]\n", - " subseq_value = nearest_value + 1\n", - "\n", - " nearest_vp_idx.append(nearest_value)\n", - " subseq_vp_idx.append(subseq_value)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "b4dc8169-bdc3-4a57-aca5-e4266db8a190", + "id": "d3f9212b-2656-4540-a49a-5be333299e4b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Index(['linearid', 'mtfcc', 'primary_direction', 'segment_sequence',\n", - " 'destination', 'geometry', 'road_meters', 'trip_instance_key',\n", - " 'vp_idx_arr', 'shape_meters_arr'],\n", - " dtype='object')" + "count 25461.000000\n", + "mean 26.101453\n", + "std 20.342335\n", + "min 0.025925\n", + "25% 9.173374\n", + "50% 18.607940\n", + "75% 44.616456\n", + "max 215.216123\n", + "Name: speed_mph, dtype: float64" ] }, - "execution_count": 17, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" + }, + { + "data": { + "image/png": "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jh8/rthQtOobeEtjW4ptsHT8eAOzdwCzdw6zdw6zP7f//ve/Edn3yJEjkiTLss65rl0GzLfffqv6+np5PJ6w6x6PR5999tkZP6awsFALFixocj09PT0iZ4Qzfun0AS5hzN45zN45zP7sui+L7P5HjhxRUlLSWe9vlwFzIebMmaOCggL7dkNDgw4ePKhu3bopKqr1yjkQCCg1NVVff/213G53q+2L82P2zmH2zmH2zmH2zrAsS0eOHFGvXr3Oua5dBkz37t0VExOjmpqasOs1NTVKSUk548e4XC65XK6wa8nJyZE6otxuN1/QDmH2zmH2zmH2zmH2be9cz7w0apffxBsfH68RI0aovLzcvtbQ0KDy8nJ5vV4HTwYAANqDdvkMjCQVFBRo8uTJGjlypEaNGqUnnnhCx44d09133+300QAAgMPabcDccccd+s9//qN58+bJ7/dr6NCheu+995p8Y29bc7lceuSRR5q8XIXIY/bOYfbOYfbOYfbtW5R1vvcpAQAAtDPt8ntgAAAAzoWAAQAAxiFgAACAcQgYAABgHAKmhYqKitS3b18lJCTo6quv1o4dO5w+UodTWFioq666Sl26dFGPHj106623at++fWFrTp48qby8PHXr1k2dO3fW+PHjm/zDh7g4S5YsUVRUlPLz8+1rzD1y/v3vf+vOO+9Ut27d1KlTJw0aNEi7du2y77csS/PmzVPPnj3VqVMnZWVl6YsvvnDwxB1DfX29Hn74YaWnp6tTp076wQ9+oEWLFoX9HB5m305ZaLZXX33Vio+Pt1588UVr79691r333mslJydbNTU1Th+tQ8nJybFWr15t7dmzx6qqqrJuuukmKy0tzTp69Ki9Ztq0aVZqaqpVXl5u7dq1y8rMzLSuueYaB0/dsezYscPq27evNXjwYOuBBx6wrzP3yDh48KDVp08f61e/+pW1fft268svv7RKS0utf/3rX/aaJUuWWElJSdZbb71lffTRR9bPfvYzKz093Tpx4oSDJzff4sWLrW7dulnr16+39u/fb61du9bq3LmztWLFCnsNs2+fCJgWGDVqlJWXl2ffrq+vt3r16mUVFhY6eKqOr7a21pJkbdq0ybIsyzp06JAVFxdnrV271l7z6aefWpKsiooKp47ZYRw5csT64Q9/aPl8PuvHP/6xHTDMPXIeeugh67rrrjvr/Q0NDVZKSor12GOP2dcOHTpkuVwu6y9/+UtbHLHDys3NtaZMmRJ27fbbb7cmTpxoWRazb894CamZ6urqVFlZqaysLPtadHS0srKyVFFR4eDJOr7Dhw9Lkrp27SpJqqysVCgUCvu96N+/v9LS0vi9aAV5eXnKzc0Nm6/E3CPpnXfe0ciRI/WLX/xCPXr00LBhw/T888/b9+/fv19+vz9s9klJSbr66quZ/UW65pprVF5ers8//1yS9NFHH2nLli0aN26cJGbfnrXbf4m3vfn2229VX1/f5F8C9ng8+uyzzxw6VcfX0NCg/Px8XXvttRo4cKAkye/3Kz4+vskP6/R4PPL7/Q6csuN49dVX9cEHH2jnzp1N7mPukfPll1/q2WefVUFBgX77299q586d+s1vfqP4+HhNnjzZnu+Z/v5h9hdn9uzZCgQC6t+/v2JiYlRfX6/Fixdr4sSJksTs2zECBu1aXl6e9uzZoy1btjh9lA7v66+/1gMPPCCfz6eEhASnj3NJaWho0MiRI/X73/9ekjRs2DDt2bNHK1eu1OTJkx0+Xcf2+uuv65VXXtGaNWt05ZVXqqqqSvn5+erVqxezb+d4CamZunfvrpiYmCbvuKipqVFKSopDp+rYZsyYofXr1+vvf/+7evfubV9PSUlRXV2dDh06FLae34uLU1lZqdraWg0fPlyxsbGKjY3Vpk2b9OSTTyo2NlYej4e5R0jPnj2VkZERdm3AgAGqrq6WJHu+/P3T+h588EHNnj1bEyZM0KBBgzRp0iTNnDlThYWFkph9e0bANFN8fLxGjBih8vJy+1pDQ4PKy8vl9XodPFnHY1mWZsyYoTfffFMbNmxQenp62P0jRoxQXFxc2O/Fvn37VF1dze/FRbjxxhu1e/duVVVV2b9GjhypiRMn2v/N3CPj2muvbfJPBXz++efq06ePJCk9PV0pKSlhsw8EAtq+fTuzv0jHjx9XdHT4/wpjYmLU0NAgidm3a05/F7FJXn31VcvlclnFxcXWJ598Yt13331WcnKy5ff7nT5ahzJ9+nQrKSnJ2rhxo/XNN9/Yv44fP26vmTZtmpWWlmZt2LDB2rVrl+X1ei2v1+vgqTum09+FZFnMPVJ27NhhxcbGWosXL7a++OIL65VXXrESExOtl19+2V6zZMkSKzk52Xr77betjz/+2Lrlllt4K28rmDx5svW9733Pfhv1G2+8YXXv3t2aNWuWvYbZt08ETAs99dRTVlpamhUfH2+NGjXK2rZtm9NH6nAknfHX6tWr7TUnTpywfv3rX1uXX365lZiYaN12223WN99849yhO6jvBgxzj5x169ZZAwcOtFwul9W/f3/rueeeC7u/oaHBevjhhy2Px2O5XC7rxhtvtPbt2+fQaTuOQCBgPfDAA1ZaWpqVkJBgff/737d+97vfWcFg0F7D7NunKMs67Z8bBAAAMADfAwMAAIxDwAAAAOMQMAAAwDgEDAAAMA4BAwAAjEPAAAAA4xAwAADAOAQMAAAwDgEDAACMQ8AAAADjEDAAAMA4BAwAADDO/wN1Xh6CvI/BPQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "gdf.columns" + "cutoff = 40\n", + "subset = df[(df.meters_elapsed >= 1609/2) & \n", + " (df.sec_elapsed >= cutoff)]\n", + "display(subset.speed_mph.describe())\n", + "subset.speed_mph.hist(bins=range(0, 100, 5))" ] }, { "cell_type": "code", - "execution_count": 20, - "id": "a046c68d-2de8-4190-8480-2763ef7acca9", + "execution_count": 17, + "id": "f7a5b1a6-0120-4c5d-bb7b-9bf0d728c748", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[189.6522603479828, 293.39996228253057, 308.9056048713142]" + "count 21705.000000\n", + "mean 21.146308\n", + "std 17.523701\n", + "min 0.025925\n", + "25% 7.952368\n", + "50% 15.273050\n", + "75% 31.172054\n", + "max 162.082992\n", + "Name: speed_mph, dtype: float64" ] }, - "execution_count": 20, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "gdf[gdf.linearid==\"1108475858996\"].shape_meters_arr.iloc[0]" + "cutoff = 45\n", + "subset = df[(df.meters_elapsed >= 1609/2) & \n", + " (df.sec_elapsed >= cutoff)]\n", + "display(subset.speed_mph.describe())\n", + "subset.speed_mph.hist(bins=range(0, 100, 5))" ] }, { "cell_type": "code", "execution_count": null, - "id": "37ab1ef4-9140-4690-ac97-b07a34fd78ef", + "id": "aa5db652-bb34-43f5-bbfe-9113593b988b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e93b92b-373c-44db-805e-8e98ec56919b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1df84ba-c420-40f9-98df-4791b67ef4a4", + "metadata": {}, + "outputs": [], + "source": [ + "df.speed_mph.hist(bins=range(0, 100, 5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0092cfd5-80e5-4f00-8ffd-89b804ddbb2d", + "metadata": {}, + "outputs": [], + "source": [ + "shape_road_crosswalk" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e9bff1f-fb34-4016-a9e0-d37daf0f3227", "metadata": {}, "outputs": [], "source": [ - "result = gdf[segment_identifier_cols + [\n", - " \"primary_direction\", \"fullname\", \"road_meters\", \n", - " \"trip_instance_key\"]]\n", + "shape_road_crosswalk = pd.read_parquet(\n", + " f\"{SEGMENT_GCS}roads_staging/\"\n", + " f\"shape_road_crosswalk_{analysis_date}.parquet\",\n", + " filters = [[(\"linearid\", \"in\", two_roads)]]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "196c42ef-fbb3-410a-9cf7-ef560380b9b9", + "metadata": {}, + "outputs": [], + "source": [ + "one_shape = \"2419613ed3a3e49420a24f8d7efd3a4e\"\n", + "\n", + "shape_to_trip = helpers.import_scheduled_trips(\n", + " analysis_date,\n", + " columns = [\"trip_instance_key\", \"shape_array_key\"],\n", + " filters = [[(\"shape_array_key\", \"==\", one_shape)]]\n", + ")\n", "\n", - "# Now assign the nearest vp for each trip that's nearest to\n", - "# a given stop\n", - "# Need to find the one after the stop later\n", - "result = result.assign(\n", - " nearest_vp_idx = nearest_vp_idx,\n", - " subseq_vp_idx = subseq_vp_idx,\n", + "one_trip = shape_to_trip.trip_instance_key[5]\n", + "one_trip" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11360899-7b76-4710-871f-818cf1e68082", + "metadata": {}, + "outputs": [], + "source": [ + "shape_road_crosswalk = shape_road_crosswalk.merge(\n", + " shape_to_trip,\n", + " on = \"shape_array_key\",\n", + " how = \"inner\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "f202317d-5ff2-4f97-bcf2-84384bc869c2", + "id": "d6d09963-2e38-428a-a3d2-881e7725325f", "metadata": {}, "outputs": [], "source": [ - "result.head()" + "vp_nn = gpd.read_parquet(\n", + " f\"{SEGMENT_GCS}condensed/vp_nearest_neighbor_{analysis_date}.parquet\",\n", + " filters = [[(\"trip_instance_key\", \"==\", one_trip)]]\n", + ")" ] }, { - "cell_type": "markdown", - "id": "907a3230-7e9f-4557-84fa-89c8c439077d", + "cell_type": "code", + "execution_count": null, + "id": "19ffa1f2-861c-4898-8906-dc3639221486", "metadata": {}, + "outputs": [], "source": [ - "once the sjoin is done and we know which trip_instance_keys are linked\n", - "to what road segments,\n", - "those roads should be expanded from 1 row to many rows.\n", - "go back to long for road segments, but it should be filtered down so that only the relevant cutpoints are included.\n", + "subset_shape = helpers.import_scheduled_trips(\n", + " analysis_date,\n", + " columns = [\"trip_instance_key\", \"shape_array_key\"],\n", + " filters = [[(\"trip_instance_key\", \"==\", one_trip)]],\n", + " get_pandas = True\n", + ")\n", + "\n", + "shapes = helpers.import_scheduled_shapes(\n", + " analysis_date,\n", + " columns = [\"shape_array_key\", \"geometry\"],\n", + " filters = [[(\"shape_array_key\", \"in\", subset_shape.shape_array_key)]],\n", + " crs = \"EPSG:3310\"\n", + ").merge(subset_shape)\n", "\n", - "then, for vp, that entire array that attached onto the linearid, is put in as an input for search. also leave vp primary direction in.\n", - "filter that vp array down for just the direction we want, and find the \"nearest\" vp as that array has been projected against the road." + "shapes = shapes.assign(\n", + " geometry = shapes.geometry.buffer(25)\n", + ").to_crs(\"EPSG:4326\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "197b1c39-4485-4e6b-b6b8-3795c841340f", + "id": "532418d9-0bf8-4ff3-8ae9-6d0f08c2770a", "metadata": {}, "outputs": [], "source": [ - "df = pd.read_parquet(\n", - " f\"{SEGMENT_GCS}nearest_vp_roads_{analysis_date}.parquet\"\n", - ")" + "shapes" ] }, { "cell_type": "code", "execution_count": null, - "id": "4f27fc9c-120c-46da-b9ef-e7222576d4ee", + "id": "a2cb0b70-89cd-445e-ba4e-a7610681ba40", "metadata": {}, "outputs": [], "source": [ - "subset_vp = np.union1d(\n", - " df.nearest_vp_idx.unique(), \n", - " df.subseq_vp_idx.unique()\n", - " )" + "road_segments = gpd.read_parquet(\n", + " f\"{SHARED_GCS}road_segments/\",\n", + " columns = segment_identifier_cols + [\"geometry\"],\n", + " filters = [[(\"linearid\", \"in\", two_roads)]]\n", + ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "7099bef9-a2d6-46f8-b336-7bdfa40dd7ad", + "id": "475f17f6-58e0-4a2d-b91f-a493beefd8bd", "metadata": {}, "outputs": [], "source": [ - "# modify this from interpolate_stop_arrivals\n", - "def attach_vp_shape_meters_with_timestamp(\n", - " analysis_date: str, **kwargs\n", - ") -> pd.DataFrame:\n", - " \"\"\"\n", - " \"\"\"\n", - " # shape_meters is here\n", - " vp_projected = pd.read_parquet(\n", - " f\"{SEGMENT_GCS}projection/vp_projected_roads_{analysis_date}.parquet\",\n", - " **kwargs\n", - " )\n", + "# We can substitute this step when we have already generated crosswalk\n", + "def sjoin_shape_to_road(shapes, roads):\n", + " \n", + " keep_cols = [\"shape_array_key\", \"trip_instance_key\", \n", + " \"linearid\", \n", + " \"mtfcc\", \"segment_sequence\"]\n", " \n", - " # location_timestamp_local is here, and needs to be converted to seconds\n", - " vp_usable = pd.read_parquet(\n", - " f\"{SEGMENT_GCS}vp_usable_{analysis_date}/\",\n", - " columns = [\"vp_idx\", \"location_timestamp_local\"],\n", - " **kwargs,\n", - " )\n", - "\n", - " vp_info = pd.merge(\n", - " vp_projected,\n", - " vp_usable,\n", - " on = \"vp_idx\",\n", - " how = \"inner\"\n", - " )\n", + " roads = roads.to_crs(shapes.crs)\n", + " \n", + " shapes_to_roads = gpd.sjoin(\n", + " shapes,\n", + " roads,\n", + " how = \"inner\",\n", + " predicate = \"intersects\"\n", + " )[keep_cols].drop_duplicates()\n", " \n", - " return vp_info" + " return shapes_to_roads\n", + "\n", + "#road_segments_sjoin = sjoin_shape_to_road(shapes, road_segments)" ] }, { "cell_type": "code", "execution_count": null, - "id": "9d4aa630-762c-4c47-b8c0-b90e81075c2b", + "id": "dbd38536-12dd-4c80-8bb6-9cb2838a6db1", "metadata": {}, "outputs": [], "source": [ - "vp_info = attach_vp_shape_meters_with_timestamp(\n", - " analysis_date, \n", - " filters = [[(\"vp_idx\", \"in\", subset_vp)]]\n", + "road_segments2 = pd.merge(\n", + " road_segments,\n", + " shape_road_crosswalk,\n", + " on = [\"linearid\", \"mtfcc\", \"segment_sequence\"], \n", + " how = \"inner\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "e439b456-d90a-49c3-8ed5-57401b53246e", + "id": "91cee88b-c60e-4c6a-b809-4d99ff31ebcf", "metadata": {}, "outputs": [], "source": [ - "road_trip_cols = road_id_cols + [\n", - " \"primary_direction\", \"trip_instance_key\"]\n", + "road_segments2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79a0e0e2-554f-47bf-9069-ec09a1963526", + "metadata": {}, + "outputs": [], + "source": [ + "import shapely\n", "\n", - "vp_with_nearest_info = pd.merge(\n", - " df,\n", - " vp_info.rename(columns = {\n", - " \"vp_idx\": \"nearest_vp_idx\",\n", - " \"shape_meters\": \"nearest_shape_meters\",\n", - " \"location_timestamp_local\": \"nearest_location_timestamp_local\"\n", - " }),\n", - " on = [\"nearest_vp_idx\"] + road_trip_cols,\n", - " how = \"inner\"\n", + "road_segments0 = road_segments2.assign(\n", + " geometry = road_segments2.apply(\n", + " lambda x: shapely.Point(x.geometry.coords[0]), \n", + " axis=1),\n", + ").assign(stop_type=0)\n", + "\n", + "road_segments1 = road_segments2.assign(\n", + " geometry = road_segments2.apply(\n", + " lambda x: shapely.Point(x.geometry.coords[-1]), \n", + " axis=1),\n", + ").assign(stop_type=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02ba0a84-5d1d-4255-ade1-fb96e1f87b69", + "metadata": {}, + "outputs": [], + "source": [ + "road_segments_long = pd.concat(\n", + " [road_segments0, road_segments1], \n", + " axis=0\n", + ").sort_values(\n", + " [\"linearid\", \"segment_sequence\", \"stop_type\"]\n", + ").rename(\n", + " columns = {\"primary_direction\": \"stop_primary_direction\"}\n", + ").reset_index(drop=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34404271-3665-4c1c-b256-2557b0a3094a", + "metadata": {}, + "outputs": [], + "source": [ + "road_segments_long" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cf6f7ea-015e-42e4-ade4-4ec1d59847e4", + "metadata": {}, + "outputs": [], + "source": [ + "from segment_speed_utils import neighbor\n", + "\n", + "gdf = neighbor.merge_stop_vp_for_nearest_neighbor(\n", + " road_segments_long, \n", + " analysis_date\n", ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "329c055e-7071-4247-a4b9-fe7649acdb53", + "id": "bd51a842-5b37-4aee-a664-d3ce751421be", "metadata": {}, "outputs": [], "source": [ - "df2 = pd.merge(\n", - " vp_with_nearest_info,\n", - " vp_info.rename(columns = {\n", - " \"vp_idx\": \"subseq_vp_idx\",\n", - " \"shape_meters\": \"subseq_shape_meters\",\n", - " \"location_timestamp_local\": \"subseq_location_timestamp_local\"\n", - " }),\n", - " on = [\"subseq_vp_idx\"] + road_trip_cols,\n", - " how = \"inner\"\n", - ")\n" + "results = neighbor.add_nearest_neighbor_result(gdf, analysis_date)" ] }, { "cell_type": "code", "execution_count": null, - "id": "b7de2f78-3793-401e-b836-7dc3085b92e9", + "id": "fae3db53-a575-4e5d-810e-b7a81ff9908a", "metadata": {}, "outputs": [], "source": [ - "sjoin_results = pd.read_parquet(\n", - " f\"{SEGMENT_GCS}vp_sjoin/\"\n", - " f\"vp_road_segments_wide_{analysis_date}.parquet\",\n", - " #filters = [[(\"trip_instance_key\", \"in\", test_trips)]]\n", + "results2 = results.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf01b87b-a45b-4e1e-8fbe-c7b4ad7b5237", + "metadata": {}, + "outputs": [], + "source": [ + "PROJECT_CRS = \"EPSG:3310\"\n", + "\n", + "results2 = results2.assign(\n", + " stop_geometry = results2.stop_geometry.to_crs(PROJECT_CRS),\n", + " vp_coords_trio = results2.vp_coords_trio.to_crs(PROJECT_CRS)\n", ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "8163e08f-3bee-45f1-86e2-68c84d60789a", + "id": "ff9567a4-f988-4739-9df3-3806611976f3", "metadata": {}, "outputs": [], "source": [ - "sjoin_results.trip_instance_key.nunique()" + "shapes = helpers.import_scheduled_shapes(\n", + " analysis_date,\n", + " columns = [\"shape_array_key\", \"geometry\"],\n", + " crs = PROJECT_CRS\n", + ").dropna(subset=\"geometry\")\n", + "\n", + "gdf = pd.merge(\n", + " results2,\n", + " shapes.rename(columns = {\"geometry\": \"shape_geometry\"}),\n", + " on = \"shape_array_key\",\n", + " how = \"inner\"\n", + ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "4f7dfc71-0a3d-4a86-90c8-88fd79a3ee94", + "id": "3832f8fe-cfa0-4b3d-a542-04383f3c2ec3", "metadata": {}, "outputs": [], "source": [ - "one_road_id = \"1104475175563\"\n", - "sjoin_results[sjoin_results.linearid==one_road_id]" + "results2.dtypes" ] }, { "cell_type": "code", "execution_count": null, - "id": "787acf45-8592-47d9-9290-01122be138c4", + "id": "cc950435-0bd7-42bd-88de-a9cc1e18974d", "metadata": {}, "outputs": [], "source": [ - "df2.shape" + "import interpolate_stop_arrival" ] }, { "cell_type": "code", "execution_count": null, - "id": "5f68b8a2-52ac-4676-acf9-27fea9ebb438", + "id": "9c0d9294-563a-435a-a4b8-e502bf1856c6", "metadata": {}, "outputs": [], "source": [ - "df2.columns" + "segment_identifier_cols2 = ['linearid', 'mtfcc', \n", + " 'stop_primary_direction', 'segment_sequence']" ] }, { "cell_type": "code", "execution_count": null, - "id": "00c90d27-c2b0-44c4-a959-064d2ea227bd", + "id": "931aa474-dca9-4e4b-ab6a-b576ad5b052a", "metadata": {}, "outputs": [], "source": [ - "def get_stop_arrivals(df: pd.DataFrame) -> pd.DataFrame:\n", - " \"\"\"\n", - " Apply np.interp to df.\n", - " df must be set up so that a given stop is populated with its\n", - " own stop_meters, as well as columns for nearest and subseq \n", - " shape_meters / location_timestamp_local_sec.\n", - " \"\"\"\n", - " x_col = \"shape_meters\"\n", - " y_col = \"location_timestamp_local\"\n", - " \n", - " stop_arrival_series = []\n", - " for row in df.itertuples():\n", + "stop_meters_series = []\n", + "stop_arrival_series = []\n", + "for row in gdf.itertuples():\n", "\n", - " xp = np.asarray([\n", - " getattr(row, f\"nearest_{x_col}\"), \n", - " getattr(row, f\"subseq_{x_col}\")\n", - " ])\n", + " stop_meters, interpolated_arrival = interpolate_stop_arrival.project_points_onto_shape(\n", + " getattr(row, \"stop_geometry\"),\n", + " getattr(row, \"vp_coords_trio\"),\n", + " getattr(row, \"shape_geometry\"),\n", + " getattr(row, \"location_timestamp_local_trio\")\n", + " )\n", "\n", - " yp = np.asarray([\n", - " getattr(row, f\"nearest_{y_col}\"), \n", - " getattr(row, f\"subseq_{y_col}\")\n", - " ]).astype(\"datetime64[s]\").astype(\"float64\")\n", + " stop_meters_series.append(stop_meters)\n", + " stop_arrival_series.append(interpolated_arrival)\n", "\n", - " stop_position = getattr(row, \"road_meters\")\n", - " interpolated_arrival = np.interp(stop_position, xp, yp)\n", - " stop_arrival_series.append(interpolated_arrival)\n", - " \n", - " df = df.assign(\n", - " arrival_time = stop_arrival_series,\n", - " ).astype(\n", - " {\"arrival_time\": \"datetime64[s]\"}\n", - " ).sort_values(\n", - " [\"trip_instance_key\", \n", - " \"linearid\", \"mtfcc\"]\n", - " ).reset_index(drop=True)\n", - " \n", - " return df" + "results2 = gdf.assign(\n", + " stop_meters = stop_meters_series,\n", + " arrival_time = stop_arrival_series,\n", + ")[segment_identifier_cols2 + [\n", + " \"trip_instance_key\", \"shape_array_key\", \n", + " \"stop_type\",\n", + " \"stop_meters\", \"arrival_time\"]\n", + " ].sort_values(\n", + " segment_identifier_cols2 + [\"trip_instance_key\", \"stop_type\", ]\n", + ").reset_index(drop=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "7c6c8cc7-4c3d-4269-8f40-43a856d7f8b1", + "id": "54bcd8a0-ead6-4ac5-89f2-4cb5291ccda2", "metadata": {}, "outputs": [], "source": [ - "df3 = get_stop_arrivals(df2)" + "grouped_df = results2.groupby(segment_identifier_cols2 + \n", + " [\"trip_instance_key\"])\n", + "\n", + "min_arrival = grouped_df.agg({\"arrival_time\": \"min\"}).reset_index()\n", + "max_arrival = grouped_df.agg({\"arrival_time\": \"max\"}).reset_index()\n", + "\n", + "min_max_arrival = pd.merge(\n", + " min_arrival,\n", + " max_arrival,\n", + " on = segment_identifier_cols2 + [\"trip_instance_key\"]\n", + ").query('arrival_time_x != arrival_time_y')" ] }, { "cell_type": "code", "execution_count": null, - "id": "78c0370c-8c52-4369-bc10-d0713e91f44d", + "id": "9ee89cc4-2a7d-44fd-901a-37ac301898ec", "metadata": {}, "outputs": [], "source": [ - "df3[(df3.subseq_location_timestamp_local > df3.arrival_time) & \n", - " (df3.nearest_location_timestamp_local < df3.arrival_time)].shape" + "results3 = pd.merge(\n", + " results2,\n", + " min_max_arrival[segment_identifier_cols2 + [\"trip_instance_key\"]],\n", + " on = segment_identifier_cols2 + [\"trip_instance_key\"],\n", + " how = \"inner\"\n", + ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "70e79902-c2f6-4dfe-ba6d-69411ea013e7", + "id": "975614bf-ee0b-4a3e-9e5c-02345149d584", "metadata": {}, "outputs": [], "source": [ - "# Something weird is happening here" + "from segment_speed_utils import segment_calcs\n", + "\n", + "results3 = segment_calcs.convert_timestamp_to_seconds(\n", + " results3, [\"arrival_time\"]\n", + ").sort_values(segment_identifier_cols2 + [\"trip_instance_key\"]).reset_index(drop=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a067dea3-67f5-4faf-b5ca-b2eab8ee80b9", + "metadata": {}, + "outputs": [], + "source": [ + "trip_cols = segment_identifier_cols2 + [\"trip_instance_key\"]\n", + "results3 = results3.assign(\n", + " subseq_arrival_time_sec = (results3.groupby(trip_cols, \n", + " observed=True, group_keys=False)\n", + " .arrival_time_sec\n", + " .shift(-1)\n", + " ),\n", + " subseq_stop_meters = (results3.groupby(trip_cols, \n", + " observed=True, group_keys=False)\n", + " .stop_meters\n", + " .shift(-1)\n", + " )\n", + " )" ] }, { "cell_type": "code", "execution_count": null, - "id": "5628d473-5777-4c28-933c-e8736b1a318e", + "id": "05034a5e-cf9d-411d-9293-a1b67387f336", "metadata": {}, "outputs": [], "source": [ - "df3.shape" + "speed = results3.assign(\n", + " meters_elapsed = results3.subseq_stop_meters - results3.stop_meters, \n", + " sec_elapsed = results3.subseq_arrival_time_sec - results3.arrival_time_sec,\n", + ").pipe(\n", + " segment_calcs.derive_speed, \n", + " (\"stop_meters\", \"subseq_stop_meters\"), \n", + " (\"arrival_time_sec\", \"subseq_arrival_time_sec\")\n", + ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "4fb5f201-8abd-40db-9b57-bcbc21d26e27", + "id": "530215e0-bcdc-4b04-a487-1b63ee14034a", "metadata": {}, "outputs": [], "source": [ - "df3[[\"road_meters\", \"nearest_vp_idx\", \"subseq_vp_idx\", \"nearest_shape_meters\",\n", - " \"subseq_shape_meters\",\n", - " \"nearest_location_timestamp_local\",\n", - " \"subseq_location_timestamp_local\", \"arrival_time\"]]" + "speed.dropna().query(\n", + " 'meters_elapsed > 250 & sec_elapsed > 60'\n", + ").speed_mph.describe()" ] }, { "cell_type": "code", "execution_count": null, - "id": "b5f1328b-067a-484b-bbf3-87e16ac5c12d", + "id": "7a3230f5-57d6-41f2-9d3b-8e838ffc470c", "metadata": {}, "outputs": [], "source": [] diff --git a/rt_segment_speeds/segment_speed_utils/project_vars.py b/rt_segment_speeds/segment_speed_utils/project_vars.py index 79a2fec8c..1dd548ae3 100644 --- a/rt_segment_speeds/segment_speed_utils/project_vars.py +++ b/rt_segment_speeds/segment_speed_utils/project_vars.py @@ -18,4 +18,4 @@ PROJECT_CRS = "EPSG:3310" CONFIG_PATH = "./config.yml" ROAD_SEGMENT_METERS = 1_000 -SEGMENT_TYPES = ["stop_segments", "rt_stop_times"] \ No newline at end of file +SEGMENT_TYPES = ["stop_segments", "rt_stop_times"]