diff --git a/_shared_utils/requirements.txt b/_shared_utils/requirements.txt
index 8b78c73c8..4d32bf763 100644
--- a/_shared_utils/requirements.txt
+++ b/_shared_utils/requirements.txt
@@ -1,8 +1,10 @@
-e .
+altair==5.3.0
+altair-transform==0.2.0
gtfs-segments==0.1.0
pyairtable==2.2.2
-great-tables==0.4.0
-polars==0.20.16
+great_tables==0.6.1
omegaconf==2.3.0 # better yaml configuration
-altair==5.3.0
-altair-transform==0.2.0
+polars==0.20.29
+quarto-cli==1.4.554
+quarto==0.1.0
diff --git a/quarterly_performance_objective/Makefile b/quarterly_performance_objective/Makefile
index 422cd4a99..a6232d36a 100644
--- a/quarterly_performance_objective/Makefile
+++ b/quarterly_performance_objective/Makefile
@@ -1,4 +1,16 @@
quarterly_performance_report:
#cd ../rt_segment_speeds/ && make && pip install -r requirements.txt && cd ..
python clean_data.py
- #cd ../ && make build_quarterly_performance_metrics -f Makefile
\ No newline at end of file
+ python compile_time_series.py
+ #cd ../ && make build_quarterly_performance_metrics -f Makefile
+
+quarto_report:
+ # this renders as html
+ #quarto render report.ipynb --execute
+ # to convert ipynb to qmd
+ quarto convert report.ipynb
+ # to convert qmd to ipynb
+ quarto convert report.qmd
+ #https://quarto.org/docs/computations/parameters.html#jupyter couldn't get this to work
+ #quarto render report.qmd --execute-params params.yml
+ quarto publish report.qmd
\ No newline at end of file
diff --git a/quarterly_performance_objective/README.md b/quarterly_performance_objective/README.md
index 74577a7c2..f577ebbe6 100644
--- a/quarterly_performance_objective/README.md
+++ b/quarterly_performance_objective/README.md
@@ -1,11 +1,11 @@
# Mass Transit Performance Objectives
-## Performance Objective 01: Increase total amount of service on the SHN and reliability of that service by 2024
+## Performance Objective 01: Increase total amount of service on the SHN and reliability (speed) of that service
Transit routes along the SHN can be categorized into 3 groups:
1. **On SHN** - where at least 20% of the transit route runs the SHN (within 50 ft)
-1. **Intersects SHN** - where at least 35% of the transit route runs within 0.5 mile of the SHN.
+1. **Intersects SHN** - where at least 20% of the transit route runs within 0.5 mile of the SHN.
1. **Other** - all other transit routes.
@@ -14,18 +14,12 @@ Initially presented for the Planning and Modal Advisory Committee (PMAC).
## Workflow
### Data Generation
-1. [Aggregate from shape level to route level](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/A1_scheduled_route_level_df)
-1. [Generate processed data for categories and service hours](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/A2_generate_routes_on_shn_data.py) with GTFS schedule data
-1. [Categorize routes into 3 groups](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/A3_categorize_routes.py)
-1. [Merge service hours and speeds and estimate delay](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/A4_add_route_speeds.py)
+1. [Clean and process data each month](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/clean_data.py)
+1. [Compile monthly data into time-series](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/compile_time_series.py)
-### Helper Scripts for Reports
-1. [data prep functions](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/report_metrics.py)
-1. [chart functions](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/report_charts.py)
+### Helper Functions
+[Categorize routes](https://github.com/cal-itp/data-analyses/blob/main/rt_segment_speeds/segment_speed_utils/parallel_corridors.py) into on SHN, parallel / intersects SHN, or other.
### Reports
-Create a report of current quarter's snapshot as well as a historical comparison of quarterly metrics report.
-
-1. [current quarter's snapshot](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/current_quarter_report.ipynb)
-1. [historical comparison](https://github.com/cal-itp/data-analyses/blob/main/quarterly_performance_objective/historical_service_hours_v2.ipynb)
\ No newline at end of file
+Create a report of current quarter's snapshot with a historical comparison.
\ No newline at end of file
diff --git a/quarterly_performance_objective/check-route-categories.ipynb b/quarterly_performance_objective/check-route-categories.ipynb
deleted file mode 100644
index cd8d1f84a..000000000
--- a/quarterly_performance_objective/check-route-categories.ipynb
+++ /dev/null
@@ -1,345 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "0fe6a2ee-2b13-4541-b88d-767967d336cf",
- "metadata": {},
- "source": [
- "# Categorize `on_shn`, `parallel` (affected by SHN), and `other`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "49a3bed2-9a44-4f33-a882-f4bb46ff5853",
- "metadata": {},
- "outputs": [],
- "source": [
- "import geopandas as gpd\n",
- "import pandas as pd\n",
- "\n",
- "from update_vars import (ANALYSIS_DATE, \n",
- " BUS_SERVICE_GCS, COMPILED_CACHED_GCS)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a4d90a10-fffb-4da3-a491-46d348979d8a",
- "metadata": {},
- "source": [
- "## `on_shn`\n",
- "* Since `on_shn` is the primary category, and it's drawn with a 50 ft buffer around hwy centerline, no longer need to use `pct_highway` (set `pct_highway > 0`)\n",
- "* Is 25% too high of a threshold? \n",
- "* `pct_route` threshold of 20% and 25% both fall within the top 70%-75% of routes\n",
- "* Settle for at least 20% of route length runs within 50 ft of hwy (on hwy)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "f6351dd0-74e5-4e70-9bd7-534aa391630e",
- "metadata": {},
- "outputs": [],
- "source": [
- "df = gpd.read_parquet(f\"{BUS_SERVICE_GCS}routes_on_shn_{ANALYSIS_DATE}.parquet\")\n",
- "\n",
- "print(f\"# rows (route_id-Route pairs): {len(df)}\")\n",
- "print(f\"# route_id: {len(df[['itp_id', 'route_id']].drop_duplicates())}\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "da4f50a0-d90d-4f9e-bd0e-43f4627e3875",
- "metadata": {},
- "outputs": [],
- "source": [
- "unique_routes = (df.sort_values([\"itp_id\", \"route_id\", \"pct_route\"], \n",
- " ascending=[True, True, False])\n",
- " .drop_duplicates(subset=[\"itp_id\", \"route_id\"])\n",
- " .reset_index(drop=True)\n",
- " )\n",
- "\n",
- "ptile = []\n",
- "\n",
- "for i in range(5, 100, 5):\n",
- " ptile.append(i/100)\n",
- "\n",
- "unique_routes.pct_route.describe(percentiles=ptile)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "4f73424c-12ec-4704-839b-99b993f1422b",
- "metadata": {},
- "outputs": [],
- "source": [
- "for r in range(20, 35, 5):\n",
- " subset = unique_routes[unique_routes.pct_route >= r/100]\n",
- " \n",
- " print(f\"route threshold: {r/100} - {len(subset)}\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7923082e-901a-4b79-807d-fdfd4d6f2324",
- "metadata": {},
- "outputs": [],
- "source": [
- "twenty = unique_routes[unique_routes.pct_route >= 0.20]\n",
- "twentyfive = unique_routes[unique_routes.pct_route >= 0.25]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b3d9928a-b126-42ef-b31a-8db35d361693",
- "metadata": {},
- "outputs": [],
- "source": [
- "def make_map(gdf: gpd.GeoDataFrame): \n",
- " \n",
- " cols = [\"itp_id\", \"route_id\", \"geometry\"]\n",
- "\n",
- " m = (gdf[cols].drop_duplicates()\n",
- " .explore(\"itp_id\", categorical=True, tiles = \"CartoDB Positron\")\n",
- " )\n",
- " \n",
- " print(f\"route threshold: {gdf.pct_route.min()}\")\n",
- " display(m)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c2219597-792a-4d68-b63a-04dc898ecdcc",
- "metadata": {},
- "outputs": [],
- "source": [
- "#make_map(twenty)\n",
- "#make_map(twentyfive)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7bad865c-4c0f-433d-81f2-f695564fa6ca",
- "metadata": {},
- "outputs": [],
- "source": [
- "itp_id = 182\n",
- "\n",
- "operator_twenty = twenty[twenty.itp_id==itp_id]\n",
- "operator_twentyfive = twentyfive[twentyfive.itp_id==itp_id]\n",
- "\n",
- "difference_routes = list(set(operator_twentyfive.route_id)\n",
- " .symmetric_difference(set(operator_twenty.route_id)))\n",
- "\n",
- "make_map(operator_twenty)\n",
- "make_map(operator_twentyfive)\n",
- "\n",
- "print(\"Routes Included if Threshold is 20%\")\n",
- "make_map(operator_twenty[operator_twenty.route_id.isin(difference_routes)])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "af1e8a15-13ec-4b1e-b24a-543e3b28c4cc",
- "metadata": {},
- "outputs": [],
- "source": [
- "itp_id = 4\n",
- "\n",
- "operator_twenty = twenty[twenty.itp_id==itp_id]\n",
- "operator_twentyfive = twentyfive[twentyfive.itp_id==itp_id]\n",
- "\n",
- "difference_routes = list(set(operator_twentyfive.route_id)\n",
- " .symmetric_difference(set(operator_twenty.route_id)))\n",
- "\n",
- "make_map(operator_twenty)\n",
- "make_map(operator_twentyfive)\n",
- "\n",
- "print(\"Routes Included if Threshold is 20%\")\n",
- "make_map(operator_twenty[operator_twenty.route_id.isin(difference_routes)])"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "915efd7b-dfd8-4285-96b4-6257f3392885",
- "metadata": {},
- "source": [
- "## `parallel`\n",
- "\n",
- "* These are routes that are affected by SHN, where bottlenecks might occur because bus routes have to pass through where there are on-ramps. \n",
- "* Use a 0.5 mile buffer from SHN, and see whether threshold should be 30%? 20%? lower? higher?\n",
- "* It's much more marginal to add a couple more routes in this category, go with 20%, which will grab about 60% of the 260 routes that was tagged as being `parallel` and is not `on_shn`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "4251cc97-0a2d-4acb-b4cf-3493a528f323",
- "metadata": {},
- "outputs": [],
- "source": [
- "df2 = gpd.read_parquet(f\"{BUS_SERVICE_GCS}parallel_or_intersecting_{ANALYSIS_DATE}.parquet\")\n",
- "\n",
- "print(f\"# rows (route_id-Route pairs): {len(df2)}\")\n",
- "print(f\"# route_id: {len(df2[['itp_id', 'route_id']].drop_duplicates())}\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7e0278d5-1a80-4a50-8a98-9643f639751c",
- "metadata": {},
- "outputs": [],
- "source": [
- "unique_routes2 = (df2.sort_values([\"itp_id\", \"route_id\", \"pct_route\"], \n",
- " ascending=[True, True, False])\n",
- " .drop_duplicates(subset=[\"itp_id\", \"route_id\"])\n",
- " .reset_index(drop=True)\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "568aaa67-1468-449b-b58c-77a8217b7937",
- "metadata": {},
- "outputs": [],
- "source": [
- "route_cols = [\"itp_id\", \"route_id\"]\n",
- "\n",
- "on_shn = (unique_routes[unique_routes.pct_route >= 0.2]\n",
- " .assign(category=\"on_shn\")\n",
- " .rename(columns = {\"pct_route\": \"pct_route_on_hwy\"})\n",
- " [route_cols + [\"pct_route_on_hwy\"]]\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d09c3c4b-8c44-4a18-9164-c7b134060a63",
- "metadata": {},
- "outputs": [],
- "source": [
- "unique_routes3 = pd.merge(\n",
- " on_shn,\n",
- " unique_routes2[route_cols + [\"pct_route\"]],\n",
- " on = route_cols,\n",
- " how = \"outer\",\n",
- " validate = \"1:1\",\n",
- " indicator=True\n",
- ")\n",
- "\n",
- "unique_routes3._merge.value_counts()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "187e5c99-2a62-4076-9b18-f0929b0035a3",
- "metadata": {},
- "outputs": [],
- "source": [
- "ptile = []\n",
- "\n",
- "for i in range(5, 100, 5):\n",
- " ptile.append(i/100)\n",
- "\n",
- "unique_routes3[unique_routes3._merge==\"right_only\"].pct_route.describe(percentiles=ptile)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b3344e39-0cec-4578-a966-d99d11d49069",
- "metadata": {},
- "outputs": [],
- "source": [
- "# There's quite a lot more routes we can add\n",
- "# in this intersects_shn group\n",
- "for r in range(20, 35, 5):\n",
- " subset = unique_routes3[(unique_routes3._merge==\"right_only\") &\n",
- " (unique_routes3.pct_route >= r/100)]\n",
- " \n",
- " print(f\"route threshold: {r/100} - {len(subset)}\")\n",
- " \n",
- " make_map(df2[df2.route_id.isin(subset.route_id)])"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "441f9dc1-ec2d-4cb8-8317-5658dc82c836",
- "metadata": {},
- "source": [
- "Depends whether we want another 50% of the routes of the 1,900 routes (`pct_route >= 0.35`)\n",
- "\n",
- "Looking at LA Metro, we do want to grab all the routes that span big boulevards, and don't want to be too restrictive. Stick with `pct_route >= 0.35`, since that's close to 1/3 of the route, and gives more options for improvements. Grabbing another 50% of the 1,900 routes is ok."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a143425e-0067-431d-bf3c-ef41062589f6",
- "metadata": {},
- "outputs": [],
- "source": [
- "unique_routes3[unique_routes3._merge==\"right_only\"].itp_id.value_counts()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "67e8defe-2aef-4d85-8fdb-fd23d1c2e850",
- "metadata": {},
- "outputs": [],
- "source": [
- "itp_id = 182\n",
- "\n",
- "for r in range(25, 50, 5):\n",
- " subset = unique_routes3[(unique_routes3._merge==\"right_only\") &\n",
- " (unique_routes3.pct_route >= r/100)]\n",
- " \n",
- " print(f\"route threshold: {r/100} - {len(subset)}\")\n",
- " \n",
- " make_map(df2[(df2.itp_id==itp_id) & \n",
- " (df2.route_id.isin(subset.route_id))])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "ffbfe1a4-b37c-416e-a87b-4ce83a9d549d",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/quarterly_performance_objective/check-stable-route-categories.ipynb b/quarterly_performance_objective/check-stable-route-categories.ipynb
deleted file mode 100644
index 19beede4a..000000000
--- a/quarterly_performance_objective/check-stable-route-categories.ipynb
+++ /dev/null
@@ -1,319 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "037b6307-b03d-4630-9e99-00c94fcae40d",
- "metadata": {},
- "source": [
- "# Are route categories stable quarter to quarter?\n",
- "\n",
- "If a `route_id` is `parallel` in one quarter, would it change to `on_shn` in another? It should be pretty stable, since how often would a bus route drastically deviate from its original route? \n",
- "\n",
- "Freeways don't change quarter to quarter.\n",
- "\n",
- "Check if there are large shifts in categories from current quarter to prior quarter."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "519e516a-f08e-44c4-9315-2f1848991ba8",
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/opt/conda/lib/python3.9/site-packages/geopandas/_compat.py:123: UserWarning: The Shapely GEOS version (3.11.1-CAPI-1.17.1) is incompatible with the GEOS version PyGEOS was compiled with (3.10.1-CAPI-1.16.0). Conversions between both will be slow.\n",
- " warnings.warn(\n",
- "/tmp/ipykernel_1812/2943624353.py:1: UserWarning: Shapely 2.0 is installed, but because PyGEOS is also installed, GeoPandas will still use PyGEOS by default for now. To force to use and test Shapely 2.0, you have to set the environment variable USE_PYGEOS=0. You can do this before starting the Python process, or in your code before importing geopandas:\n",
- "\n",
- "import os\n",
- "os.environ['USE_PYGEOS'] = '0'\n",
- "import geopandas\n",
- "\n",
- "In a future release, GeoPandas will switch to using Shapely by default. If you are using PyGEOS directly (calling PyGEOS functions on geometries from GeoPandas), this will then stop working and you are encouraged to migrate from PyGEOS to Shapely 2.0 (https://shapely.readthedocs.io/en/latest/migration_pygeos.html).\n",
- " import geopandas as gpd\n"
- ]
- }
- ],
- "source": [
- "import geopandas as gpd\n",
- "import pandas as pd\n",
- "\n",
- "from shared_utils import rt_dates\n",
- "from update_vars import BUS_SERVICE_GCS"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "4f723d37-9ee1-4184-9b8e-e641debb5de4",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Only look at v2 warehouse\n",
- "dfs = {}\n",
- "for key, date in rt_dates.PMAC.items():\n",
- " if \"2023\" in date:\n",
- " df = gpd.read_parquet(f\"{BUS_SERVICE_GCS}routes_categorized_{date}.parquet\")\n",
- " dfs[key] = df"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "212768d2-44d6-4751-9d6b-3308e95b8f9d",
- "metadata": {},
- "outputs": [],
- "source": [
- "keep_cols = [\n",
- " \"feed_key\", \"name\",\n",
- " \"category\", \"route_id\", \n",
- " \"district\"\n",
- "]\n",
- "\n",
- "df1 = dfs[\"Q1_2023\"][keep_cols]\n",
- "df2 = dfs[\"Q2_2023\"][keep_cols]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "bbbea287-3737-4eae-8c78-ebb388e5c382",
- "metadata": {},
- "outputs": [],
- "source": [
- "def compare_col(df1, df2, col):\n",
- " print(df1[col].value_counts())\n",
- " print(df2[col].value_counts())\n",
- " print(df1[col].value_counts(normalize=True))\n",
- " print(df2[col].value_counts(normalize=True))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "6b0c0c1b-f1f4-4bdd-aa09-a4afd03525ca",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "intersects_shn 1229\n",
- "on_shn 548\n",
- "other 492\n",
- "Name: category, dtype: int64\n",
- "intersects_shn 1248\n",
- "on_shn 566\n",
- "other 519\n",
- "Name: category, dtype: int64\n",
- "intersects_shn 0.541648\n",
- "on_shn 0.241516\n",
- "other 0.216836\n",
- "Name: category, dtype: float64\n",
- "intersects_shn 0.534934\n",
- "on_shn 0.242606\n",
- "other 0.222460\n",
- "Name: category, dtype: float64\n"
- ]
- }
- ],
- "source": [
- "compare_col(df1, df2, \"category\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "4a36a890-7cf0-4333-836c-f8660118cd46",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "4.0 664\n",
- "7.0 518\n",
- "3.0 216\n",
- "8.0 184\n",
- "11.0 151\n",
- "5.0 142\n",
- "10.0 93\n",
- "6.0 86\n",
- "12.0 71\n",
- "2.0 41\n",
- "1.0 39\n",
- "9.0 16\n",
- "Name: district, dtype: int64\n",
- "4.0 647\n",
- "7.0 529\n",
- "3.0 214\n",
- "8.0 181\n",
- "11.0 163\n",
- "5.0 144\n",
- "10.0 129\n",
- "6.0 116\n",
- "12.0 71\n",
- "1.0 40\n",
- "2.0 35\n",
- "9.0 16\n",
- "Name: district, dtype: int64\n",
- "4.0 0.298964\n",
- "7.0 0.233228\n",
- "3.0 0.097253\n",
- "8.0 0.082846\n",
- "11.0 0.067987\n",
- "5.0 0.063935\n",
- "10.0 0.041873\n",
- "6.0 0.038721\n",
- "12.0 0.031968\n",
- "2.0 0.018460\n",
- "1.0 0.017560\n",
- "9.0 0.007204\n",
- "Name: district, dtype: float64\n",
- "4.0 0.283151\n",
- "7.0 0.231510\n",
- "3.0 0.093654\n",
- "8.0 0.079212\n",
- "11.0 0.071335\n",
- "5.0 0.063020\n",
- "10.0 0.056455\n",
- "6.0 0.050766\n",
- "12.0 0.031072\n",
- "1.0 0.017505\n",
- "2.0 0.015317\n",
- "9.0 0.007002\n",
- "Name: district, dtype: float64\n"
- ]
- }
- ],
- "source": [
- "compare_col(df1, df2, \"district\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "12a837d8-6849-44c7-9f05-733d78809349",
- "metadata": {},
- "outputs": [],
- "source": [
- "m1 = pd.merge(\n",
- " df1, \n",
- " df2,\n",
- " on = [\"feed_key\", \"name\", \"route_id\"],\n",
- " how = \"outer\",\n",
- " validate = \"1:1\",\n",
- " indicator=\"compare_categories\"\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "5482a309-8825-4e14-928c-29bb594d9d60",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "right_only 2061\n",
- "left_only 1997\n",
- "both 272\n",
- "Name: compare_categories, dtype: int64"
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "m1.compare_categories.value_counts()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "id": "2ec0413a-3ec0-49bb-aaf6-96866554480e",
- "metadata": {},
- "outputs": [],
- "source": [
- "in_both = m1[(m1.compare_categories==\"both\")]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "38359bf6-37d3-4aa1-90f4-ae43720061a2",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(272, 8)"
- ]
- },
- "execution_count": 10,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "in_both.shape"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "80cc287d-89dc-4563-80f9-a2fc029036ed",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(0, 8)"
- ]
- },
- "execution_count": 11,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "in_both[(in_both.category_x != in_both.category_y)].shape"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7f6628ea-5f21-46c2-9a57-81d3d7a4041e",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/quarterly_performance_objective/clean_data.py b/quarterly_performance_objective/clean_data.py
index 21493d6a1..d74b88d3d 100644
--- a/quarterly_performance_objective/clean_data.py
+++ b/quarterly_performance_objective/clean_data.py
@@ -10,11 +10,9 @@
from segment_speed_utils import (helpers, gtfs_schedule_wrangling,
parallel_corridors)
from calitp_data_analysis import geography_utils, utils
-from update_vars import (
- GTFS_DATA_DICT, SEGMENT_GCS,
- BUS_SERVICE_GCS, ANALYSIS_DATE
-)
-
+from shared_utils import rt_dates
+from update_vars import (GTFS_DATA_DICT, SEGMENT_GCS,
+ BUS_SERVICE_GCS)
def aggregate_to_route_service_hours(analysis_date: str) -> pd.DataFrame:
"""
@@ -69,6 +67,9 @@ def get_route_summary_speed(analysis_date: str) -> pd.DataFrame:
def process_df(analysis_date: str) -> gpd.GeoDataFrame:
"""
+ Tag routes by on_shn / parallel / other,
+ and attach average route_speeds, caltrans_district.
+ Get route-level df for an analysis date to use in report.
"""
# Get gdf of unique routes tagging them by on_shn/parallel/other
df = parallel_corridors.routes_by_on_shn_parallel_categories(analysis_date)
@@ -76,8 +77,8 @@ def process_df(analysis_date: str) -> gpd.GeoDataFrame:
# Get df of route service hours
route_service_hours = aggregate_to_route_service_hours(analysis_date)
- # Get crosswalk linking schedule_gtfs_dataset_key to the organization_name (which we use for portfolio)
- # and caltrans_district
+ # Get crosswalk linking schedule_gtfs_dataset_key to the organization_name
+ # (which we use for portfolio) and caltrans_district
crosswalk = helpers.import_schedule_gtfs_key_organization_crosswalk(
analysis_date,
columns = ["schedule_gtfs_dataset_key", "organization_name",
@@ -87,6 +88,7 @@ def process_df(analysis_date: str) -> gpd.GeoDataFrame:
# Get route summary speeds
route_speeds = get_route_summary_speed(analysis_date)
+ # Merge route categories with route service hours
df2 = pd.merge(
df,
route_service_hours,
@@ -94,6 +96,7 @@ def process_df(analysis_date: str) -> gpd.GeoDataFrame:
how = "inner"
)
+ # Merge in crosswalk for caltrans_district
df3 = pd.merge(
df2,
crosswalk,
@@ -108,6 +111,7 @@ def process_df(analysis_date: str) -> gpd.GeoDataFrame:
geography_utils.FEET_PER_MI)
).drop(columns = "route_length_feet")
+ # Merge in route average speeds (left merge because not every operator has RT)
df4 = pd.merge(
df3,
route_speeds,
@@ -124,15 +128,20 @@ def process_df(analysis_date: str) -> gpd.GeoDataFrame:
if __name__ == "__main__":
- start = datetime.datetime.now()
+ analysis_date_list = [rt_dates.DATES["jun2024"]]
+
+ for d in analysis_date_list:
+ start = datetime.datetime.now()
- gdf = process_df(ANALYSIS_DATE)
+ gdf = process_df(d)
- utils.geoparquet_gcs_export(
- gdf,
- BUS_SERVICE_GCS,
- f"routes_categorized_with_speed_{ANALYSIS_DATE}"
- )
+ utils.geoparquet_gcs_export(
+ gdf,
+ BUS_SERVICE_GCS,
+ f"routes_categorized_with_speed_{d}"
+ )
+
+ del gdf
- end = datetime.datetime.now()
- print(f"quarterly route df for {ANALYSIS_DATE}: {end - start}")
\ No newline at end of file
+ end = datetime.datetime.now()
+ print(f"quarterly route df for {d}: {end - start}")
\ No newline at end of file
diff --git a/quarterly_performance_objective/compile_time_series.py b/quarterly_performance_objective/compile_time_series.py
new file mode 100644
index 000000000..80c9f5af5
--- /dev/null
+++ b/quarterly_performance_objective/compile_time_series.py
@@ -0,0 +1,282 @@
+"""
+Get quarterly metrics as a time-series df
+to use in report.
+"""
+import datetime
+import geopandas as gpd
+import pandas as pd
+
+#from calitp_data_analysis import utils
+from segment_speed_utils import time_series_utils
+from shared_utils import rt_dates
+from update_vars import BUS_SERVICE_GCS
+
+operator_cols = [
+ "name",
+ "organization_name", "caltrans_district"
+]
+
+district_cols = ["caltrans_district"]
+
+category_cols = ["category", "year_quarter"]
+
+
+def assemble_time_series(
+ date_list: list
+) -> gpd.GeoDataFrame:
+ """
+ Assemble time-series data and add column showing what
+ year-quarter the service_date belongs to.
+ We'll aggregate all the available data for each
+ quarter (which could be 1-3 dates).
+ """
+ df = time_series_utils.concatenate_datasets_across_dates(
+ BUS_SERVICE_GCS,
+ f"routes_categorized_with_speed",
+ date_list,
+ data_type = "gdf",
+ )
+
+ df = df.assign(
+ year_quarter = (df.service_date.dt.year.astype(str) +
+ "-Q" +
+ df.service_date.dt.quarter.astype(str)
+ )
+ )
+
+ return df
+
+
+def service_hours_aggregation(
+ df: pd.DataFrame,
+ group_cols: list
+) -> pd.DataFrame:
+ """
+ Aggregate service hours by some grouping of columns
+ and also add service hours per route.
+ """
+ df2 = (
+ df
+ .groupby(group_cols,
+ observed=True, group_keys=False)
+ .agg({"service_hours": "sum",
+ "route_key": "count",
+ "service_date": "nunique"
+ })
+ .reset_index()
+ .rename(columns = {
+ "route_key": "n_routes",
+ "service_date": "n_dates"
+ })
+ )
+
+ df2 = df2.assign(
+ # if we have multiple days, the n_routes counted will reflect that
+ service_hours_per_route = df2.service_hours.divide(df2.n_routes).round(2),
+ daily_service_hours = df2.service_hours.divide(df2.n_dates),
+ daily_routes = df2.n_routes.divide(df2.n_dates).round(0).astype(int)
+ )
+
+ return df2
+
+
+def speed_aggregation(
+ df: pd.DataFrame,
+ group_cols: list
+) -> pd.DataFrame:
+ """
+ Aggregate speeds (wherever route averages are available).
+ """
+ df2 = (
+ df[df.speed_mph.notna()]
+ .groupby(group_cols,
+ observed=True, group_keys=False)
+ .agg({"speed_mph": "mean",
+ "route_key": "count",
+ "service_date": "nunique"
+ })
+ .reset_index()
+ .rename(columns = {
+ "route_key": "n_vp_routes",
+ "service_date": "n_dates"
+ })
+ )
+
+ df2 = df2.assign(
+ speed_mph = df2.speed_mph.round(2),
+ daily_vp_routes = df2.n_vp_routes.divide(df2.n_dates).round(0).astype(int)
+ ).drop(columns = "n_dates")
+
+ return df2
+
+
+def aggregated_metrics(
+ df: gpd.GeoDataFrame,
+ group_cols: list
+) -> pd.DataFrame:
+ """
+ Aggregate metrics by grouping of columns (either
+ by operator or statewide).
+ """
+ service_hours_agg = service_hours_aggregation(df, group_cols)
+ speed_agg = speed_aggregation(df, group_cols)
+
+ df2 = pd.merge(
+ service_hours_agg,
+ speed_agg,
+ on = group_cols,
+ how = "left",
+ ).fillna(
+ {"n_vp_routes": 0}
+ ).astype(
+ {"n_vp_routes": "int"}
+ )
+
+ return df2
+
+
+def get_dissolved_geometry(
+ df: pd.DataFrame,
+ group_cols: list
+) -> gpd.GeoDataFrame:
+
+ unique_combos = df[
+ group_cols + ["route_id", "geometry"]
+ ].drop_duplicates(
+ subset=group_cols + ["route_id"]
+ )
+
+ # Simplify geometry for quicker dissolve (25ft)
+ unique_combos = unique_combos.assign(
+ geometry = unique_combos.geometry.simplify(tolerance=25)
+ )
+
+ route_geom = unique_combos[
+ group_cols + ["geometry"]
+ ].dissolve(by=group_cols).reset_index()
+
+ return route_geom
+
+
+def assemble_aggregated_df_with_subtotals(
+ df: gpd.GeoDataFrame,
+ group_cols: list
+) -> gpd.GeoDataFrame:
+ """
+ Statewide (aggregate across operators) df with service hours and
+ speed metrics.
+ Also add subtotals for on_shn + parallel
+ and statewide totals.
+ """
+ by_category = aggregated_metrics(
+ df, group_cols
+ )
+
+ shn_categories = ["on_shn", "parallel"]
+
+ shn_subtotal_df = aggregated_metrics(
+ df[df.category.isin(shn_categories)].assign(
+ category = "shn_subtotal"),
+ group_cols
+ )
+
+ total_df = aggregated_metrics(
+ df.assign(
+ category = "total"
+ ),
+ group_cols
+ )
+
+ final_df = pd.concat([
+ by_category,
+ shn_subtotal_df,
+ total_df],
+ axis=0, ignore_index=True
+ )
+
+ return final_df
+
+
+def add_time_series_list_columns(
+ df: pd.DataFrame,
+ group_cols: list,
+ time_series_cols: list,
+) -> pd.DataFrame:
+ """
+ """
+ group_cols2 = [c for c in group_cols if c != "year_quarter"]
+
+ list_aggregation = (df.sort_values("year_quarter")
+ .groupby(group_cols2)
+ .agg({
+ **{c: lambda x: list(x)
+ for c in time_series_cols}
+ }).reset_index()
+ .rename(columns = {
+ **{c: f"{c}_ts" for c in time_series_cols}
+ })
+ )
+
+ df2 = pd.merge(
+ df,
+ list_aggregation,
+ on = group_cols2,
+ how = "inner"
+ )
+
+ return df2
+
+if __name__ == "__main__":
+
+ start = datetime.datetime.now()
+
+ all_dates = rt_dates.y2023_dates + rt_dates.y2024_dates
+
+ df = assemble_time_series(all_dates)
+
+ time_series_cols = ["service_hours_per_route", "speed_mph"]
+
+ operator_df = assemble_aggregated_df_with_subtotals(
+ df, operator_cols + category_cols)
+
+ operator_df2 = add_time_series_list_columns(
+ operator_df,
+ operator_cols + category_cols,
+ time_series_cols
+ )
+
+ operator_df2.to_parquet(
+ f"{BUS_SERVICE_GCS}"
+ "quarterly_metrics/operator_time_series.parquet"
+ )
+
+ district_df = assemble_aggregated_df_with_subtotals(
+ df, district_cols + category_cols)
+
+ district_df2 = add_time_series_list_columns(
+ district_df,
+ district_cols + category_cols,
+ time_series_cols
+ )
+
+ district_df2.to_parquet(
+ f"{BUS_SERVICE_GCS}"
+ "quarterly_metrics/district_time_series.parquet"
+ )
+
+ statewide_df = assemble_aggregated_df_with_subtotals(
+ df, category_cols)
+
+ statewide_df2 = add_time_series_list_columns(
+ statewide_df,
+ category_cols,
+ time_series_cols
+ )
+
+ statewide_df2.to_parquet(
+ f"{BUS_SERVICE_GCS}"
+ "quarterly_metrics/statewide_time_series.parquet"
+ )
+
+ end = datetime.datetime.now()
+ print(f"quarterly metrics time-series: {end - start}")
\ No newline at end of file
diff --git a/quarterly_performance_objective/current_quarter_report.ipynb b/quarterly_performance_objective/current_quarter_report.ipynb
deleted file mode 100644
index 57eefcee6..000000000
--- a/quarterly_performance_objective/current_quarter_report.ipynb
+++ /dev/null
@@ -1,511 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "aba3b0aa-73bf-4a8d-b4ee-64ecc98196fc",
- "metadata": {},
- "source": [
- "# Current Quarter\n",
- "\n",
- "01 - Increase total amount of service on the SHN and reliability of that service by 2024\n",
- "\n",
- "## Routes on the State Highway Network (SHN)\n",
- "\n",
- "Transit routes along the SHN can be categorized into 3 groups:\n",
- "1. **On SHN** - where at least 20% of the transit route runs the SHN (within 50 ft) \n",
- "2. **Intersects SHN** - where at least 35% of the transit route runs within 0.5 mile of the SHN.\n",
- "3. **Other** - all other transit routes.\n",
- "\n",
- "### Metrics\n",
- "* service hours, service hours per route\n",
- "* delay hours, delay hours per route\n",
- "* map of route by category (and by mode)\n",
- "\n",
- "The metrics are shown for for transit routes **on the SHN** and **intersects SHN**."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "792ee3df-ebcb-4bf7-a7f2-f7f903efe126",
- "metadata": {},
- "outputs": [],
- "source": [
- "%%capture\n",
- "import warnings\n",
- "warnings.filterwarnings('ignore')\n",
- "\n",
- "import altair as alt\n",
- "import calitp_data_analysis.magics\n",
- "import geopandas as gpd\n",
- "import intake\n",
- "import pandas as pd\n",
- "\n",
- "from IPython.display import HTML\n",
- "\n",
- "import report_metrics\n",
- "import report_charts\n",
- "from update_vars import BUS_SERVICE_GCS, CURRENT_QUARTER, ANALYSIS_DATE\n",
- "from shared_utils import portfolio_utils, rt_dates\n",
- "from calitp_data_analysis import calitp_color_palette as cp\n",
- "from calitp_data_analysis import styleguide\n",
- "\n",
- "hq_catalog = intake.open_catalog(\"../high_quality_transit_areas/*.yml\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a15fcf6a-b0d4-47f0-be78-118ddf43ab91",
- "metadata": {
- "tags": [
- "parameters"
- ]
- },
- "outputs": [],
- "source": [
- "# parameters cell"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e00a8689-f56e-47d4-bd4a-6795122a1e86",
- "metadata": {},
- "outputs": [],
- "source": [
- "%%capture_parameters\n",
- "QUARTER_CLEANED = CURRENT_QUARTER.replace('_', ' ')\n",
- "CURRENT_QUARTER, ANALYSIS_DATE, QUARTER_CLEANED"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "add13293-3d9d-40e0-b1fb-c50b89fe84de",
- "metadata": {},
- "outputs": [],
- "source": [
- "df = report_metrics.prep_data_for_report(ANALYSIS_DATE)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c22bad3d-2f2c-4e1c-8aae-edc59377410a",
- "metadata": {},
- "source": [
- "## Statewide Stats for {QUARTER_CLEANED} ({ANALYSIS_DATE})"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "79217ca7-e3d6-46eb-b5f9-4bf79c230f24",
- "metadata": {},
- "outputs": [],
- "source": [
- "summary = report_metrics.get_service_hours_summary_table(df) "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "92e49d50-009f-4b2e-9661-688b81c38694",
- "metadata": {},
- "outputs": [],
- "source": [
- "all_hours = portfolio_utils.aggregate_by_geography(\n",
- " summary.assign(category=\"All\"),\n",
- " group_cols = [\"category\"],\n",
- " sum_cols = [\"unique_route\", \"service_hours\"]\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5ef150ae-3dda-42ec-af43-13b36df5e231",
- "metadata": {},
- "outputs": [],
- "source": [
- "STATEWIDE_HOURS = all_hours.service_hours.iloc[0]\n",
- "FORMATTED_HOURS = f'{STATEWIDE_HOURS:,}' \n",
- "AVG_SERVICE = round(STATEWIDE_HOURS / all_hours.unique_route.iloc[0], 1)\n",
- "\n",
- "display(\n",
- " HTML(\n",
- " f\"
{FORMATTED_HOURS} total service hours statewide
\"\n",
- " f\"{AVG_SERVICE} service hours per route statewide
\"\n",
- "\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "78da2714-2999-4763-ab71-b041a68c5d68",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Chart utils\n",
- "HEIGHT = 200\n",
- "WIDTH = 150\n",
- "\n",
- "color_dict = {\n",
- " \"On SHN\": cp.CALITP_CATEGORY_BRIGHT_COLORS[3],\n",
- " \"Intersects SHN\": cp.CALITP_CATEGORY_BRIGHT_COLORS[4],\n",
- " \"Other\": cp.CALITP_CATEGORY_BRIGHT_COLORS[1]\n",
- "}\n",
- "\n",
- "def make_bar_by_category(df: pd.DataFrame, x_col: str,\n",
- " y_col: str) -> alt.Chart:\n",
- " \n",
- " base_bar = report_charts.base_bar(df, x_col = x_col)\n",
- " legend_sort_order = [\"On SHN\", \"Intersects SHN\", \"Other\"]\n",
- " \n",
- " y_title = y_col.replace('_', ' ').title()\n",
- " \n",
- " chart = base_bar.encode(\n",
- " x = alt.X(x_col, sort = legend_sort_order),\n",
- " y = alt.Y(f\"{y_col}:Q\", title = f\"{y_title}\"),\n",
- " color = alt.Color(f\"{x_col}:N\", title = f\"{x_col.title()}\",\n",
- " scale = alt.Scale(domain = legend_sort_order,\n",
- " range = list(color_dict.values())))\n",
- " )\n",
- " \n",
- " chart = (chart\n",
- " .properties(width = WIDTH, height = HEIGHT, \n",
- " title= f\"{y_title} by Category\")\n",
- " )\n",
- " \n",
- " return chart\n",
- "\n",
- "s1 = make_bar_by_category(summary, x_col = \"category\", \n",
- " y_col = \"service_hours_per_route\")\n",
- "s2 = make_bar_by_category(summary, x_col = \"category\", \n",
- " y_col = \"service_hours\")\n",
- "\n",
- "combined = alt.hconcat(s1, s2)\n",
- "combined = styleguide.apply_chart_config(combined)\n",
- "\n",
- "combined"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "81c94bf7-f55b-43e2-9204-ad70b45f61a2",
- "metadata": {},
- "outputs": [],
- "source": [
- "service_cols_dict = {\n",
- " \"category\": \"Category\",\n",
- " \"service_hours\": \"Service Hours\",\n",
- " \"pct_service_hours\": \"% Service Hours\",\n",
- " \"unique_route\": \"# Routes\",\n",
- " \"pct_unique_route\": \"% Routes\",\n",
- " \"service_hours_per_route\": \"Service Hours per Route\",\n",
- "}\n",
- "\n",
- "summary_styled = portfolio_utils.style_table(\n",
- " summary, \n",
- " rename_cols = service_cols_dict, \n",
- " integer_cols = [\"Service Hours\", \"# Routes\"],\n",
- " one_decimal_cols = [\"Service Hours per Route\"],\n",
- " left_align_cols = \"first\",\n",
- " center_align_cols = \"all\",\n",
- " custom_format_cols = {'{:.1%}': [\"% Service Hours\", \"% Routes\"]},\n",
- " display_table = True\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4466056e-6c6e-437f-acec-f9e9323d5153",
- "metadata": {},
- "source": [
- "## Reliability (Delay)\n",
- "\n",
- "Note: Not every route has GTFS Real-Time information, which supplies delay data."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "0867cb50-8fdc-43a7-a286-e3c782792cb3",
- "metadata": {},
- "outputs": [],
- "source": [
- "delay_summary = report_metrics.get_delay_summary_table(df)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3e12806f-bf4a-453d-a753-ffe42f2a7b90",
- "metadata": {},
- "outputs": [],
- "source": [
- "delay_summary"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "fdf3b32c-6842-4c16-b076-805c7f1c9f68",
- "metadata": {},
- "outputs": [],
- "source": [
- "STATEWIDE_DELAY = delay_summary.delay_hours.sum()\n",
- "FORMATTED_HOURS = f'{STATEWIDE_DELAY:,g}' \n",
- "AVG_DELAY = round(STATEWIDE_DELAY / delay_summary.unique_route.sum(), 2)\n",
- "\n",
- "\n",
- "display(\n",
- " HTML(\n",
- " f\"{FORMATTED_HOURS} total delay hours statewide
\"\n",
- " f\"{AVG_DELAY} delay hours per route statewide
\"\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a677fc4c-3156-4ecc-abbb-334e3849cec4",
- "metadata": {},
- "outputs": [],
- "source": [
- "d1 = make_bar_by_category(delay_summary, x_col = \"category\", \n",
- " y_col = \"delay_hours_per_route\")\n",
- "d2 = make_bar_by_category(delay_summary, x_col = \"category\", \n",
- " y_col = \"delay_hours\")\n",
- "\n",
- "combined = alt.hconcat(d1, d2)\n",
- "combined = styleguide.apply_chart_config(combined)\n",
- "\n",
- "combined"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6c3fdecd-9b56-4121-9d97-f1b22fe67fab",
- "metadata": {},
- "outputs": [],
- "source": [
- "delay_cols_dict = {\n",
- " \"category\": \"Category\",\n",
- " \"delay_hours\": \"Total Delay Hours\",\n",
- " \"pct_delay_hours\": \"% Delay Hours\",\n",
- " \"unique_route\": \"# Routes\",\n",
- " \"pct_unique_route\": \"% Routes\",\n",
- " \"delay_hours_per_route\": \"Delay Hours per Route\",\n",
- "}\n",
- "\n",
- "delay_summary_styled = portfolio_utils.style_table(\n",
- " delay_summary, \n",
- " rename_cols = delay_cols_dict, \n",
- " integer_cols = [\"Total Delay Hours\", \"# Routes\"],\n",
- " two_decimal_cols = [\"Delay Hours per Route\"],\n",
- " left_align_cols = \"first\",\n",
- " center_align_cols = \"all\",\n",
- " custom_format_cols = {'{:.1%}': [\"% Delay Hours\", \"% Routes\"]},\n",
- " display_table = True\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "94f69cf9-4e05-4c49-b19b-81661d394169",
- "metadata": {},
- "source": [
- "## By District"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d4a74098-6dce-42ec-8393-545e97bd7e04",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Have some rows where district is missing, \n",
- "# but only for intersects_shn and other categories\n",
- "# focus on just the on_shn category and do district breakdown\n",
- "df[(df.district.isna())].category.value_counts()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5f948e21-1555-4892-a4c2-1da54a84c268",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Chart utils\n",
- "WIDTH = 300\n",
- "HEIGHT = 200\n",
- "\n",
- "by_district_service = report_metrics.by_district_on_shn_breakdown(\n",
- " df, [\"service_hours\", \"unique_route\"])\n",
- "\n",
- "bar_total = (report_charts.make_district_bar(\n",
- " by_district_service, \"service_hours\")\n",
- " .properties(width=WIDTH, height=HEIGHT) \n",
- " )\n",
- "bar_avg = (report_charts.make_district_bar(\n",
- " by_district_service, \"avg_service_hours\")\n",
- " .properties(width=WIDTH, height=HEIGHT)\n",
- " )\n",
- "\n",
- "service_hours_chart = report_charts.configure_hconcat_charts(\n",
- " [bar_avg, bar_total], \n",
- " x_scale=\"independent\", \n",
- " y_scale=\"independent\", \n",
- " chart_title=\"Service Hours by District\")\n",
- "\n",
- "service_hours_chart"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "24d959ea-e8fe-46fc-a2d2-bb85ada4f627",
- "metadata": {},
- "outputs": [],
- "source": [
- "by_district_delay = report_metrics.by_district_on_shn_breakdown(\n",
- " df, [\"delay_hours\", \"unique_route\"]\n",
- ")\n",
- "\n",
- "bar_total = (report_charts.make_district_bar(\n",
- " by_district_delay, \"delay_hours\")\n",
- " .properties(width=WIDTH, height=HEIGHT) \n",
- " )\n",
- "bar_avg = (report_charts.make_district_bar(\n",
- " by_district_delay, \"avg_delay_hours\")\n",
- " .properties(width=WIDTH, height=HEIGHT)\n",
- " )\n",
- "\n",
- "delay_hours_chart = report_charts.configure_hconcat_charts(\n",
- " [bar_avg, bar_total], \n",
- " x_scale=\"independent\", \n",
- " y_scale=\"independent\", \n",
- " chart_title=\"Delay Hours by District\")\n",
- "\n",
- "delay_hours_chart"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "aee65310-bd12-4dfe-9fb4-237d43d0a163",
- "metadata": {},
- "source": [
- "## Map of Routes by Category"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "058dd099-90e2-490e-8a26-5858e00022fb",
- "metadata": {},
- "outputs": [],
- "source": [
- "def data_for_viz(df: gpd.GeoDataFrame):\n",
- " gdf = report_metrics.clean_up_category_values(df)\n",
- " \n",
- " # line must fall within CA\n",
- " ca = hq_catalog.ca_boundary.read().to_crs(f\"EPSG: {gdf.crs.to_epsg()}\")\n",
- "\n",
- " gdf = gpd.sjoin(\n",
- " gdf,\n",
- " ca,\n",
- " how = \"inner\",\n",
- " predicate = \"within\",\n",
- " ).drop(columns= [\"index_right\"])\n",
- " \n",
- " # Drop columns that shouldn't get displayed in tooltip\n",
- " drop_cols = [\n",
- " \"rt_sched_category\", \n",
- " \"State\", \"unique_route\"]\n",
- " \n",
- " gdf2 = gdf.drop(columns = drop_cols)\n",
- " \n",
- " return gdf2\n",
- "\n",
- "\n",
- "gdf = data_for_viz(df)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "559fbcde-f01d-400f-bb15-b7c770997abb",
- "metadata": {},
- "source": [
- "### All Routes"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "735ccd97-9c90-47c6-b768-bc09f02b887b",
- "metadata": {},
- "outputs": [],
- "source": [
- "def make_map(gdf: gpd.GeoDataFrame): \n",
- " # category is going to be sorted alphabetically,\n",
- " # so sort our color_dict\n",
- " sorted_dict = dict(sorted(color_dict.items()))\n",
- " \n",
- " m = gdf.explore(\n",
- " \"category\", categorical=True,\n",
- " cmap = list(sorted_dict.values()),\n",
- " tiles = \"Carto DB Positron\"\n",
- " )\n",
- "\n",
- " return m\n",
- " \n",
- "m = make_map(gdf)\n",
- "m"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "948cb692-79b3-428a-a91d-e38ed3763d06",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- },
- "widgets": {
- "application/vnd.jupyter.widget-state+json": {
- "state": {},
- "version_major": 2,
- "version_minor": 0
- }
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/quarterly_performance_objective/download_trips_v2_backfill.py b/quarterly_performance_objective/download_trips_v2_backfill.py
deleted file mode 100644
index 098dea1cd..000000000
--- a/quarterly_performance_objective/download_trips_v2_backfill.py
+++ /dev/null
@@ -1,173 +0,0 @@
-"""
-Download all trips and shapes for a day for v2.
-
-Use this to figure out why v1 and v2 aggregate service hours are so different.s
-"""
-import os
-os.environ["CALITP_BQ_MAX_BYTES"] = str(300_000_000_000)
-
-import datetime as dt
-import pandas as pd
-import sys
-
-from calitp.tables import tbls
-from siuba import *
-from loguru import logger
-
-from shared_utils import (gtfs_utils_v2, gtfs_utils,
- rt_dates)
-from calitp_data_analysis import utils, geography_utils
-from update_vars import COMPILED_CACHED_GCS
-
-def scheduled_operators(analysis_date: str):
- """
- This is how HQTA data is downloaded...so do the same here for backfill.
- """
- all_operators = gtfs_utils_v2.schedule_daily_feed_to_organization(
- selected_date = analysis_date,
- keep_cols = None,
- get_df = True,
- feed_option = "use_subfeeds"
- )
-
- keep_cols = ["feed_key", "name"]
-
- operators_to_include = all_operators[keep_cols]
-
- # There shouldn't be any duplicates by name, since we got rid
- # of precursor feeds. But, just in case, don't allow dup names.
- operators_to_include = (operators_to_include
- .drop_duplicates(subset="name")
- .reset_index(drop=True)
- )
-
- return operators_to_include
-
-
-if __name__=="__main__":
- logger.add("./logs/download_trips_v2_backfill.log")
- logger.add(sys.stderr,
- format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}",
- level="INFO")
-
- analysis_date = rt_dates.PMAC["Q2_2022"]
- VERSION = "v2"
- logger.info(f"Analysis date: {analysis_date} warehouse {VERSION}")
-
- start = dt.datetime.now()
-
- if VERSION == "v1":
- ITP_IDS = (tbls.gtfs_schedule.agency()
- >> distinct(_.calitp_itp_id)
- >> filter(_.calitp_itp_id != 200)
- >> collect()
- ).calitp_itp_id.tolist()
-
- IDS_TO_RUN = sorted(ITP_IDS)
-
- logger.info(f"# operators to run: {len(IDS_TO_RUN)}")
-
- dataset = "trips"
- logger.info(f"*********** Download {dataset} data ***********")
-
-
- keep_trip_cols = [
- "calitp_itp_id", "calitp_url_number",
- "service_date", "trip_key", "trip_id",
- "route_id", "direction_id", "shape_id",
- "calitp_extracted_at", "calitp_deleted_at",
- "trip_first_departure_ts", "trip_last_arrival_ts",
- "service_hours"
- ]
-
- trips = gtfs_utils.get_trips(
- selected_date = analysis_date,
- itp_id_list = IDS_TO_RUN,
- trip_cols = keep_trip_cols,
- get_df = True,
- )
-
- trips.to_parquet(
- f"{COMPILED_CACHED_GCS}{dataset}_{analysis_date}_{VERSION}.parquet")
-
- trips = pd.read_parquet(
- f"{COMPILED_CACHED_GCS}trips_{analysis_date}_{VERSION}.parquet")
-
- dataset = "routelines"
- logger.info(f"*********** Download {dataset} data ***********")
-
- routelines = gtfs_utils.get_route_shapes(
- selected_date = analysis_date,
- itp_id_list = IDS_TO_RUN,
- get_df = True,
- crs = geography_utils.CA_NAD83Albers,
- trip_df = trips
- )[["calitp_itp_id", "calitp_url_number", "shape_id", "geometry"]]
-
- utils.geoparquet_gcs_export(
- routelines,
- COMPILED_CACHED_GCS,
- f"{dataset}_{analysis_date}_{VERSION}.parquet"
- )
-
-
-
- elif VERSION == "v2":
- operators_df = scheduled_operators(analysis_date)
-
- FEEDS_TO_RUN = sorted(operators_df.feed_key.unique().tolist())
-
- logger.info(f"# operators to run: {len(FEEDS_TO_RUN)}")
-
- dataset = "trips"
- logger.info(f"*********** Download {dataset} data ***********")
-
- keep_trip_cols = [
- "feed_key", "name", "regional_feed_type",
- "service_date", "trip_key", "trip_id",
- "route_key", "route_id", "route_type",
- "route_short_name", "route_long_name", "route_desc",
- "direction_id",
- "shape_array_key", "shape_id",
- "trip_first_departure_sec", "trip_last_arrival_sec",
- "service_hours"
- ]
-
- trips = gtfs_utils_v2.get_trips(
- selected_date = analysis_date,
- operator_feeds = FEEDS_TO_RUN,
- trip_cols = keep_trip_cols,
- get_df = True,
- )
-
- trips.to_parquet(
- f"{COMPILED_CACHED_GCS}{dataset}_{analysis_date}_{VERSION}.parquet")
-
- dataset = "routelines"
- logger.info(f"*********** Download {dataset} data ***********")
-
- keep_shape_cols = [
- "feed_key",
- "shape_id", "shape_array_key",
- "n_trips",
- # n_trips is new column...can help if we want
- # to choose between shape_ids
- # geometry already returned when get_df is True
- ]
-
- routelines = gtfs_utils_v2.get_shapes(
- selected_date = analysis_date,
- operator_feeds = FEEDS_TO_RUN,
- shape_cols = keep_shape_cols,
- get_df = True,
- crs = geography_utils.CA_NAD83Albers,
- )
-
- utils.geoparquet_gcs_export(
- routelines,
- COMPILED_CACHED_GCS,
- f"{dataset}_{analysis_date}_{VERSION}.parquet"
- )
-
- end = dt.datetime.now()
- logger.info(f"execution time: {end - start}")
diff --git a/quarterly_performance_objective/historical_report.ipynb b/quarterly_performance_objective/historical_report.ipynb
deleted file mode 100644
index 5c8809546..000000000
--- a/quarterly_performance_objective/historical_report.ipynb
+++ /dev/null
@@ -1,504 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "0801efda-e075-4826-a167-a8e54a8167fb",
- "metadata": {},
- "source": [
- "# Historical Trends\n",
- "\n",
- "01 - Increase total amount of service on the SHN and reliability of that service by 2024\n",
- "\n",
- "## Routes on the State Highway Network (SHN)\n",
- "Transit routes along the SHN can be categorized into 3 groups:\n",
- "1. **On SHN** - where at least 20% of the transit route runs the SHN (within 50 ft) \n",
- "2. **Intersects SHN** - where at least 35% of the transit route runs within 0.5 mile of the SHN.\n",
- "3. **Other** - all other transit routes.\n",
- "\n",
- "## Metrics\n",
- "* service hours, service hours per route\n",
- "* delay hours, delay hours per route\n",
- "\n",
- "The metrics are shown for for transit routes **on the SHN** and **intersects SHN**."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "34d1396e-e0c7-4804-8647-4aae76cc300c",
- "metadata": {},
- "outputs": [],
- "source": [
- "%%capture\n",
- "import warnings\n",
- "warnings.filterwarnings('ignore')\n",
- "\n",
- "import altair as alt\n",
- "import calitp_data_analysis.magics\n",
- "import pandas as pd\n",
- "\n",
- "import report_metrics\n",
- "from shared_utils import rt_dates, portfolio_utils\n",
- "from calitp_data_analysis import calitp_color_palette as cp\n",
- "from calitp_data_analysis import styleguide\n",
- "from bus_service_utils import chart_utils"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c5fb6bec-7aa3-4ee2-97a2-21f5713df370",
- "metadata": {},
- "outputs": [],
- "source": [
- "quarterly_metrics_dict"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "cc1eeb8f-d2ae-4152-8624-a7a10012d6bf",
- "metadata": {},
- "outputs": [],
- "source": [
- "quarterly_metrics_dict = {k: v for k, v in rt_dates.PMAC.items() \n",
- " if \"2022\" not in k and k != \"Q1_2023\"}\n",
- "\n",
- "summary_df = report_metrics.concatenate_summary_across_dates(\n",
- " quarterly_metrics_dict, summary_dataset = \"summary\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "ba2aff18-9a0a-40cc-8bfd-5ef655e64071",
- "metadata": {},
- "outputs": [],
- "source": [
- "def get_statewide_averages(df: pd.DataFrame) -> pd.DataFrame:\n",
- "\n",
- " var_list = [\"service_hours\", \"delay_hours\", \n",
- " \"unique_route\", \"delay_unique_route\"]\n",
- "\n",
- " group_cols = [\"year_quarter\", \"service_date\", \"year\", \"quarter\"]\n",
- "\n",
- " all_routes = portfolio_utils.aggregate_by_geography(\n",
- " df[df.variable.isin(var_list)],\n",
- " group_cols + [\"variable\"],\n",
- " sum_cols = [\"value\"]\n",
- " )\n",
- " \n",
- " # Make wide, to calculate average again\n",
- " all_routes2 = pd.pivot(all_routes, \n",
- " index = group_cols, \n",
- " columns = \"variable\", values = \"value\"\n",
- " ).reset_index()\n",
- " \n",
- " all_routes2 = all_routes2.assign(\n",
- " service_hours_per_route = (all_routes2.service_hours.divide(\n",
- " all_routes2.unique_route)).round(2), \n",
- " delay_hours_per_route = (all_routes2.delay_hours.divide(\n",
- " all_routes2.delay_unique_route)).round(2),\n",
- " category = \"All\"\n",
- " )\n",
- " \n",
- " #https://stackoverflow.com/questions/55027108/pandas-rename-index\n",
- " # Get rid of column name\n",
- " all_routes2.columns.name = \"\"\n",
- " \n",
- " # Wrangle back to long!\n",
- " value_vars = [c for c in all_routes2.columns if c != \"category\" and \n",
- " c not in group_cols\n",
- " ]\n",
- "\n",
- " all_routes3 = pd.melt(\n",
- " all_routes2, \n",
- " id_vars = group_cols + [\"category\"],\n",
- " var_name = \"variable\",\n",
- " value_vars = value_vars \n",
- " )\n",
- " \n",
- " return all_routes3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a65763be-f6e8-4ea4-bcf0-62e0ce3dd811",
- "metadata": {},
- "outputs": [],
- "source": [
- "statewide_avg = get_statewide_averages(summary_df)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b9b7e294-74b9-451d-b169-6eca6a649d72",
- "metadata": {},
- "outputs": [],
- "source": [
- "# chart utils\n",
- "HEIGHT = 250\n",
- "WIDTH = 200\n",
- "\n",
- "def base_quarterly_bar(df: pd.DataFrame, variable: str,\n",
- " x_col: str, y_col: str) -> alt.Chart:\n",
- " \n",
- " bar = (alt.Chart(df)\n",
- " .mark_bar()\n",
- " .encode(\n",
- " x=alt.X(f\"{x_col}:O\", \n",
- " #axis=alt.Axis(format=f'%Y Q%q'), \n",
- " title = None),\n",
- " y=alt.Y(f\"{y_col}:Q\", title = chart_utils.labeling(variable)),\n",
- " )\n",
- " )\n",
- " \n",
- " return bar"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "bcf6de1e-b4e2-488b-8310-e9fe7e82184a",
- "metadata": {},
- "outputs": [],
- "source": [
- "def quarterly_bar_for_category(\n",
- " df: pd.DataFrame, \n",
- " variable_list: list = [\"service_hours\", \"delay_hours\"], \n",
- " category: str = \"On SHN\",\n",
- " x_col: str = \"year_quarter\", \n",
- " y_col: str = \"value\", \n",
- " chart_height: int = 200, chart_width: int = 500,\n",
- ") -> alt.Chart: \n",
- " \"\"\"\n",
- " Plot quarterly metrics within the same category.\n",
- " Ex: for all routes on SHN, show service hours, delay hours, \n",
- " avg service hours, etc\n",
- " \"\"\"\n",
- " subset = df[(df.variable.isin(variable_list)) & (df.category==category)]\n",
- " \n",
- " var1 = variable_list[0]\n",
- " df1 = subset[subset.variable==var1]\n",
- " var2 = variable_list[1]\n",
- " df2 = subset[subset.variable==var2]\n",
- "\n",
- " category = df1.category.iloc[0]\n",
- " \n",
- " color_dict = {\n",
- " \"On SHN\": cp.CALITP_CATEGORY_BRIGHT_COLORS[4],\n",
- " \"Intersects SHN\": cp.CALITP_CATEGORY_BRIGHT_COLORS[3],\n",
- " \"All\": cp.CALITP_CATEGORY_BRIGHT_COLORS[0],\n",
- " \"service_hours\": cp.CALITP_CATEGORY_BRIGHT_COLORS[4], # light blue\n",
- " \"delay_hours\": cp.CALITP_CATEGORY_BRIGHT_COLORS[1], # light orange\n",
- " \"service_hours_per_route\": cp.CALITP_CATEGORY_BRIGHT_COLORS[0], # med blue\n",
- " \"delay_hours_per_route\": cp.CALITP_CATEGORY_BOLD_COLORS[1], # dark orange\n",
- " }\n",
- " \n",
- " tooltip = ['year', 'quarter', 'year_quarter', \n",
- " 'variable', 'category', 'value']\n",
- " \n",
- " bar1 = (base_quarterly_bar(df1, var1, x_col, y_col)\n",
- " .encode(color = alt.value(color_dict[var1]), \n",
- " tooltip = tooltip)\n",
- " .properties(title={\n",
- " \"text\": f\"{chart_utils.labeling(var1)}\",\n",
- " \"subtitle\": f\"{category}\"\n",
- " }, width = chart_width, height = chart_height)\n",
- " .interactive()\n",
- " )\n",
- " \n",
- " bar2 = (base_quarterly_bar(df2, var2, x_col, y_col)\n",
- " .encode(color=alt.value(color_dict[var2]), \n",
- " tooltip = tooltip)\n",
- " .properties(title={\n",
- " \"text\": f\"{chart_utils.labeling(var2)}\",\n",
- " \"subtitle\": f\"{category}\"\n",
- " }, width = chart_width, height = chart_height)\n",
- " .interactive()\n",
- " )\n",
- " \n",
- " if var1 == \"service_hours\":\n",
- " space = 0\n",
- " else:\n",
- " space = 25\n",
- " combined = (styleguide.apply_chart_config(alt.hconcat(bar1, bar2, \n",
- " spacing=space))\n",
- " .resolve_scale(y=\"independent\")\n",
- " )\n",
- "\n",
- " return combined"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "38b33eff-e35d-45f6-8338-efc20fe77e12",
- "metadata": {},
- "source": [
- "## All Routes"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a71a4b63-f133-4800-b754-3c5390425960",
- "metadata": {},
- "outputs": [],
- "source": [
- "category = \"All\"\n",
- "var_list = [\"service_hours_per_route\", \"delay_hours_per_route\"]\n",
- "\n",
- "s1 = quarterly_bar_for_category(\n",
- " statewide_avg,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n",
- "\n",
- "\n",
- "var_list = [\"service_hours\", \"delay_hours\"]\n",
- "\n",
- "s2 = quarterly_bar_for_category(\n",
- " statewide_avg,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c73c9542-f54d-44ee-92cc-80a751864de8",
- "metadata": {},
- "outputs": [],
- "source": [
- "s1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3928dcc1-58e7-4a1b-b91f-8e8c5044eb7c",
- "metadata": {},
- "outputs": [],
- "source": [
- "s2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "aa5564b6-6056-4dbc-8f36-193d7cf9043c",
- "metadata": {},
- "source": [
- "## Routes on SHN"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b3b929dd-f394-457f-b57d-2a292f7f3fd8",
- "metadata": {},
- "outputs": [],
- "source": [
- "category = \"On SHN\"\n",
- "var_list = [\"service_hours_per_route\", \"delay_hours_per_route\"]\n",
- "\n",
- "o1 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n",
- "\n",
- "var_list = [\"service_hours\", \"delay_hours\"]\n",
- "\n",
- "o2 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e8990963-9167-439b-a929-e6ef9107256e",
- "metadata": {},
- "outputs": [],
- "source": [
- "o1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6f154b08-d722-4959-be45-32bd93560576",
- "metadata": {},
- "outputs": [],
- "source": [
- "o2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "e69f82d3-0dd4-454c-b89b-70f290c6f590",
- "metadata": {},
- "source": [
- "## Routes Intersecting SHN"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "9243c9f7-d096-4c2f-bc24-c3947ef2e1aa",
- "metadata": {},
- "outputs": [],
- "source": [
- "category = \"Intersects SHN\"\n",
- "var_list = [\"service_hours_per_route\", \"delay_hours_per_route\"]\n",
- "\n",
- "i1 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n",
- "\n",
- "var_list = [\"service_hours\", \"delay_hours\"]\n",
- "\n",
- "i2 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e7b40537-948b-4871-95b7-f73d3cd20410",
- "metadata": {},
- "outputs": [],
- "source": [
- "i1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "91447b75-d247-4bf5-b522-7b9f22a94a17",
- "metadata": {},
- "outputs": [],
- "source": [
- "i2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "60fc11da-799b-46d6-b682-5158c4705fe9",
- "metadata": {},
- "source": [
- "## Routes on SHN by District"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "ca1363fb-8fd9-4578-a466-7105c21c4170",
- "metadata": {},
- "outputs": [],
- "source": [
- "district_df = report_metrics.concatenate_summary_across_dates(\n",
- " quarterly_metrics_dict, summary_dataset = \"district\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "497496fb-63d6-4fa8-a4d1-d3e8fd3673dc",
- "metadata": {},
- "outputs": [],
- "source": [
- "def facet_by_district(df: pd.DataFrame, variable: str) -> alt.Chart:\n",
- " subset = df[df.variable == variable]\n",
- " \n",
- " bar = (alt.Chart(subset)\n",
- " .mark_bar()\n",
- " .encode(\n",
- " x=alt.X(f\"year_quarter:O\"),\n",
- " y = alt.Y(\"value:Q\", title=f\"{variable.replace('_', ' '.title())}\"),\n",
- " color = alt.Color(\"district:N\", title=None, \n",
- " scale = alt.Scale(\n",
- " range = cp.CALITP_CATEGORY_BRIGHT_COLORS + \n",
- " cp.CALITP_CATEGORY_BOLD_COLORS), legend=None),\n",
- " tooltip = [\"district\", \"year_quarter\", \"value\", \"variable\"]\n",
- " ).facet(facet=\"district:N\", columns = 1, spacing=10, \n",
- " title = f\"{variable.replace('_', ' ').title()}\")\n",
- " .interactive()\n",
- " )\n",
- " \n",
- " return bar"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "8fa9eef8-ee56-42cf-9110-e445f223025d",
- "metadata": {},
- "outputs": [],
- "source": [
- "bar1 = facet_by_district(district_df, \"avg_service_hours\")\n",
- "bar2 = facet_by_district(district_df, \"avg_delay_hours\")\n",
- "\n",
- "district_chart = styleguide.apply_chart_config(alt.hconcat(bar1, bar2))\n",
- "district_chart"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a0f0c816-03dc-41ca-81b7-8a6c11930c52",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/quarterly_performance_objective/historical_service_hours_v2.ipynb b/quarterly_performance_objective/historical_service_hours_v2.ipynb
deleted file mode 100644
index d4440265d..000000000
--- a/quarterly_performance_objective/historical_service_hours_v2.ipynb
+++ /dev/null
@@ -1,659 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "0801efda-e075-4826-a167-a8e54a8167fb",
- "metadata": {},
- "source": [
- "# Historical Trends\n",
- "\n",
- "01 - Increase total amount of service on the SHN and reliability of that service by 2024\n",
- "\n",
- "## Routes on the State Highway Network (SHN)\n",
- "Transit routes along the SHN can be categorized into 3 groups:\n",
- "1. **On SHN** - where at least 20% of the transit route runs the SHN (within 50 ft) \n",
- "2. **Intersects SHN** - where at least 35% of the transit route runs within 0.5 mile of the SHN.\n",
- "3. **Other** - all other transit routes.\n",
- "\n",
- "## Metrics\n",
- "* service hours, service hours per route\n",
- "* delay hours, delay hours per route\n",
- "\n",
- "The metrics are shown for for transit routes **on the SHN** and **intersects SHN**."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "34d1396e-e0c7-4804-8647-4aae76cc300c",
- "metadata": {},
- "outputs": [],
- "source": [
- "%%capture\n",
- "import warnings\n",
- "warnings.filterwarnings('ignore')\n",
- "\n",
- "import altair as alt\n",
- "import calitp_data_analysis.magics\n",
- "import geopandas as gpd\n",
- "import pandas as pd\n",
- "\n",
- "from calitp_data_analysis import calitp_color_palette as cp\n",
- "from calitp_data_analysis import styleguide\n",
- "import report_metrics\n",
- "from shared_utils import rt_dates, portfolio_utils\n",
- "from bus_service_utils import chart_utils\n",
- "from update_vars import BUS_SERVICE_GCS"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "0390d418-6861-4e5c-8775-101bf80e97dd",
- "metadata": {},
- "outputs": [],
- "source": [
- "def quarterly_summary_long(analysis_date: str) -> pd.DataFrame: \n",
- " \"\"\"\n",
- " For historical report, get a long df of service hours and delay hours \n",
- " summary tables.\n",
- " \"\"\"\n",
- " #df = report_metrics.prep_data_for_report(analysis_date)\n",
- " df = gpd.read_parquet(\n",
- " f\"{BUS_SERVICE_GCS}routes_categorized_{analysis_date}.parquet\"\n",
- " )\n",
- " \n",
- " \n",
- " service_summary = report_metrics.get_service_hours_summary_table(df) \n",
- " '''\n",
- " delay_summary = (get_delay_summary_table(df)\n",
- " .rename(columns = {\"unique_route\": \"delay_unique_route\"})\n",
- " )\n",
- " ''' \n",
- " # Make long\n",
- " service_value_vars = [c for c in service_summary.columns if c != \"category\"]\n",
- " #delay_value_vars = [c for c in delay_summary.columns if c != \"category\"]\n",
- "\n",
- " service_long = pd.melt(\n",
- " service_summary,\n",
- " id_vars = \"category\",\n",
- " value_vars = service_value_vars,\n",
- " )\n",
- " '''\n",
- " delay_long = pd.melt(\n",
- " delay_summary, \n",
- " id_vars = \"category\", \n",
- " value_vars = delay_value_vars\n",
- " )\n",
- " '''\n",
- " # Concatenante\n",
- " summary = pd.concat([service_long, \n",
- " #delay_long\n",
- " ], axis=0)\n",
- " summary = summary.assign(\n",
- " service_date = analysis_date\n",
- " )\n",
- " \n",
- " return summary"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "35403601-0b56-4c2b-a787-844db8fd5176",
- "metadata": {},
- "outputs": [],
- "source": [
- "def concatenate_summary_across_dates(\n",
- " rt_dates_dict: dict, \n",
- " summary_dataset: str) -> pd.DataFrame: \n",
- " df = pd.DataFrame()\n",
- "\n",
- " rt_dates_reversed = {value: key for key, value in rt_dates_dict.items()}\n",
- "\n",
- " for date, quarter in rt_dates_reversed.items():\n",
- " if summary_dataset == \"summary\":\n",
- " one_quarter = quarterly_summary_long(date)\n",
- " \n",
- " elif summary_dataset == \"district\":\n",
- " one_quarter = district_breakdown_long(date)\n",
- " df = pd.concat([df, one_quarter], axis=0)\n",
- "\n",
- " df = df.assign(\n",
- " year_quarter = df.service_date.map(rt_dates_reversed)\n",
- " )\n",
- "\n",
- " df = df.assign(\n",
- " quarter = df.year_quarter.str.split('_', expand=True)[0],\n",
- " year = df.year_quarter.str.split('_', expand=True)[1].astype(int),\n",
- " )\n",
- " \n",
- " # Get it to be year first\n",
- " df = df.assign(\n",
- " year_quarter = df.year.astype(str) + ' ' + df.quarter\n",
- " )\n",
- " \n",
- " return df"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "cc1eeb8f-d2ae-4152-8624-a7a10012d6bf",
- "metadata": {},
- "outputs": [],
- "source": [
- "quarterly_metrics_dict = {k: v for k, v in rt_dates.PMAC.items() \n",
- " if k != \"Q1_2022\"}\n",
- "\n",
- "summary_df = concatenate_summary_across_dates(\n",
- " quarterly_metrics_dict, summary_dataset = \"summary\")\n",
- "\n",
- "#summary_df = report_metrics.concatenate_summary_across_dates(\n",
- "# quarterly_metrics_dict, summary_dataset = \"summary\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "f83b6045-730d-4082-ab6d-5716e879ce09",
- "metadata": {},
- "outputs": [],
- "source": [
- "def get_statewide_averages(df: pd.DataFrame) -> pd.DataFrame:\n",
- "\n",
- " var_list = [\"service_hours\", \n",
- " #\"delay_hours\", \n",
- " \"unique_route\", \n",
- " #\"delay_unique_route\"\n",
- " ]\n",
- "\n",
- " group_cols = [\"year_quarter\", \"service_date\", \"year\", \"quarter\"]\n",
- "\n",
- " all_routes = portfolio_utils.aggregate_by_geography(\n",
- " df[df.variable.isin(var_list)],\n",
- " group_cols + [\"variable\"],\n",
- " sum_cols = [\"value\"]\n",
- " )\n",
- " \n",
- " # Make wide, to calculate average again\n",
- " all_routes2 = pd.pivot(all_routes, \n",
- " index = group_cols, \n",
- " columns = \"variable\", values = \"value\"\n",
- " ).reset_index()\n",
- " \n",
- " all_routes2 = all_routes2.assign(\n",
- " service_hours_per_route = (all_routes2.service_hours.divide(\n",
- " all_routes2.unique_route)).round(2), \n",
- " #delay_hours_per_route = (all_routes2.delay_hours.divide(\n",
- " # all_routes2.delay_unique_route)).round(2),\n",
- " category = \"All\"\n",
- " )\n",
- " \n",
- " #https://stackoverflow.com/questions/55027108/pandas-rename-index\n",
- " # Get rid of column name\n",
- " all_routes2.columns.name = \"\"\n",
- " \n",
- " # Wrangle back to long!\n",
- " value_vars = [c for c in all_routes2.columns if c != \"category\" and \n",
- " c not in group_cols\n",
- " ]\n",
- "\n",
- " all_routes3 = pd.melt(\n",
- " all_routes2, \n",
- " id_vars = group_cols + [\"category\"],\n",
- " var_name = \"variable\",\n",
- " value_vars = value_vars \n",
- " )\n",
- " \n",
- " return all_routes3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a65763be-f6e8-4ea4-bcf0-62e0ce3dd811",
- "metadata": {},
- "outputs": [],
- "source": [
- "statewide_avg = get_statewide_averages(summary_df)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b9b7e294-74b9-451d-b169-6eca6a649d72",
- "metadata": {},
- "outputs": [],
- "source": [
- "# chart utils\n",
- "HEIGHT = 250\n",
- "WIDTH = 200\n",
- "\n",
- "def base_quarterly_bar(df: pd.DataFrame, variable: str,\n",
- " x_col: str, y_col: str) -> alt.Chart:\n",
- " \n",
- " bar = (alt.Chart(df)\n",
- " .mark_bar()\n",
- " .encode(\n",
- " x=alt.X(f\"{x_col}:O\", \n",
- " # formatting for quarters is weird, construct our own string\n",
- " #axis=alt.Axis(format='Q%q-%Y'), \n",
- " title = None),\n",
- " y=alt.Y(f\"{y_col}:Q\", title = chart_utils.labeling(variable)),\n",
- " )\n",
- " )\n",
- " \n",
- " return bar"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "bcf6de1e-b4e2-488b-8310-e9fe7e82184a",
- "metadata": {},
- "outputs": [],
- "source": [
- "def quarterly_bar_for_category(\n",
- " df: pd.DataFrame, \n",
- " variable_list: list = [\"service_hours\", \"delay_hours\"], \n",
- " category: str = \"On SHN\",\n",
- " x_col: str = \"year_quarter\", \n",
- " y_col: str = \"value\", \n",
- " chart_height: int = 200, chart_width: int = 500,\n",
- ") -> alt.Chart: \n",
- " \"\"\"\n",
- " Plot quarterly metrics within the same category.\n",
- " Ex: for all routes on SHN, show service hours, delay hours, \n",
- " avg service hours, etc\n",
- " \"\"\"\n",
- " subset = df[(df.variable.isin(variable_list)) & \n",
- " (df.category==category)]\n",
- " \n",
- " var1 = variable_list[0]\n",
- " df1 = subset[subset.variable==var1]\n",
- " #var2 = variable_list[1]\n",
- " #df2 = subset[subset.variable==var2]\n",
- "\n",
- " category = df1.category.iloc[0]\n",
- " \n",
- " color_dict = {\n",
- " \"On SHN\": cp.CALITP_CATEGORY_BRIGHT_COLORS[4],\n",
- " \"Intersects SHN\": cp.CALITP_CATEGORY_BRIGHT_COLORS[3],\n",
- " \"All\": cp.CALITP_CATEGORY_BRIGHT_COLORS[0],\n",
- " \"service_hours\": cp.CALITP_CATEGORY_BRIGHT_COLORS[4], # light blue\n",
- " \"delay_hours\": cp.CALITP_CATEGORY_BRIGHT_COLORS[1], # light orange\n",
- " \"service_hours_per_route\": cp.CALITP_CATEGORY_BRIGHT_COLORS[0], # med blue\n",
- " \"delay_hours_per_route\": cp.CALITP_CATEGORY_BOLD_COLORS[1], # dark orange\n",
- " }\n",
- " \n",
- " tooltip = ['year', 'quarter', 'year_quarter', \n",
- " 'variable', 'category', 'value']\n",
- " \n",
- " bar1 = (base_quarterly_bar(df1, var1, x_col, y_col)\n",
- " .encode(color = alt.value(color_dict[var1]), \n",
- " tooltip = tooltip)\n",
- " .properties(title={\n",
- " \"text\": f\"{chart_utils.labeling(var1)}\",\n",
- " \"subtitle\": f\"{category}\"\n",
- " }, width = chart_width, height = chart_height)\n",
- " )\n",
- " \n",
- " '''\n",
- " bar2 = (base_quarterly_bar(df2, var2, x_col, y_col)\n",
- " .encode(color=alt.value(color_dict[var2]), \n",
- " tooltip = tooltip)\n",
- " .properties(title={\n",
- " \"text\": f\"{chart_utils.labeling(var2)}\",\n",
- " \"subtitle\": f\"{category}\"\n",
- " }, width = chart_width, height = chart_height)\n",
- " )\n",
- " '''\n",
- " if var1 == \"service_hours\":\n",
- " space = 0\n",
- " else:\n",
- " space = 25\n",
- " combined = (styleguide.apply_chart_config(alt.hconcat(bar1, #bar2, \n",
- " spacing=space))\n",
- " .resolve_scale(y=\"independent\")\n",
- " )\n",
- "\n",
- " return combined"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "38b33eff-e35d-45f6-8338-efc20fe77e12",
- "metadata": {},
- "source": [
- "## All Routes"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "a71a4b63-f133-4800-b754-3c5390425960",
- "metadata": {},
- "outputs": [],
- "source": [
- "category = \"All\"\n",
- "var_list = [\"service_hours_per_route\", \n",
- " #\"delay_hours_per_route\"\n",
- " ]\n",
- "\n",
- "s1 = quarterly_bar_for_category(\n",
- " statewide_avg,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n",
- "\n",
- "\n",
- "var_list = [\"service_hours\", \n",
- " #\"delay_hours\"\n",
- " ]\n",
- "\n",
- "s2 = quarterly_bar_for_category(\n",
- " statewide_avg,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "c73c9542-f54d-44ee-92cc-80a751864de8",
- "metadata": {},
- "outputs": [],
- "source": [
- "s1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3928dcc1-58e7-4a1b-b91f-8e8c5044eb7c",
- "metadata": {},
- "outputs": [],
- "source": [
- "s2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "aa5564b6-6056-4dbc-8f36-193d7cf9043c",
- "metadata": {},
- "source": [
- "## Routes on SHN"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "b3b929dd-f394-457f-b57d-2a292f7f3fd8",
- "metadata": {},
- "outputs": [],
- "source": [
- "category = \"On SHN\"\n",
- "var_list = [\"service_hours_per_route\", \n",
- " #\"delay_hours_per_route\"\n",
- " ]\n",
- "\n",
- "o1 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n",
- "\n",
- "var_list = [\"service_hours\", \n",
- " #\"delay_hours\"\n",
- " ]\n",
- "\n",
- "o2 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e8990963-9167-439b-a929-e6ef9107256e",
- "metadata": {},
- "outputs": [],
- "source": [
- "o1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6f154b08-d722-4959-be45-32bd93560576",
- "metadata": {},
- "outputs": [],
- "source": [
- "o2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "e69f82d3-0dd4-454c-b89b-70f290c6f590",
- "metadata": {},
- "source": [
- "## Routes Intersecting SHN"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "9243c9f7-d096-4c2f-bc24-c3947ef2e1aa",
- "metadata": {},
- "outputs": [],
- "source": [
- "category = \"Intersects SHN\"\n",
- "var_list = [\"service_hours_per_route\", \n",
- " #\"delay_hours_per_route\"\n",
- " ]\n",
- "\n",
- "i1 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n",
- "\n",
- "var_list = [\"service_hours\", \n",
- " #\"delay_hours\"\n",
- " ]\n",
- "\n",
- "i2 = quarterly_bar_for_category(\n",
- " summary_df,\n",
- " variable_list = var_list, \n",
- " category = category,\n",
- " x_col = \"year_quarter\",\n",
- " y_col = \"value\",\n",
- " chart_height = HEIGHT, chart_width = WIDTH\n",
- ")\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "e7b40537-948b-4871-95b7-f73d3cd20410",
- "metadata": {},
- "outputs": [],
- "source": [
- "i1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "91447b75-d247-4bf5-b522-7b9f22a94a17",
- "metadata": {},
- "outputs": [],
- "source": [
- "i2"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "60fc11da-799b-46d6-b682-5158c4705fe9",
- "metadata": {},
- "source": [
- "## Routes on SHN by District"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "2ebf727b-dccc-4840-babc-fdd55bbba21a",
- "metadata": {},
- "outputs": [],
- "source": [
- "def district_breakdown_long(analysis_date: str) -> pd.DataFrame: \n",
- " \"\"\"\n",
- " For historical report, get a long df of service hours and delay hours \n",
- " summary tables.\n",
- " \"\"\"\n",
- " df = gpd.read_parquet(\n",
- " f\"{BUS_SERVICE_GCS}routes_categorized_{analysis_date}.parquet\")\n",
- " \n",
- " by_district_summary = report_metrics.by_district_on_shn_breakdown(\n",
- " df, sum_cols = [\"service_hours\", \"unique_route\"])\n",
- " '''\n",
- " by_district_delay = by_district_on_shn_breakdown(\n",
- " df, sum_cols = [\"delay_hours\", \"unique_route\"]\n",
- " ).rename(columns = {\"unique_route\": \"delay_unique_route\"})\n",
- " ''' \n",
- " # Make long\n",
- " service_value_vars = [c for c in by_district_summary.columns if c != 'district']\n",
- " #delay_value_vars = [c for c in by_district_delay.columns if c != 'district']\n",
- "\n",
- " service_long = pd.melt(\n",
- " by_district_summary,\n",
- " id_vars = \"district\",\n",
- " value_vars = service_value_vars,\n",
- " )\n",
- " '''\n",
- " delay_long = pd.melt(\n",
- " by_district_delay, \n",
- " id_vars = \"district\", \n",
- " value_vars = delay_value_vars\n",
- " )\n",
- " '''\n",
- " # Concatenante\n",
- " summary = pd.concat([service_long, \n",
- " #delay_long\n",
- " ], axis=0)\n",
- " summary = summary.assign(\n",
- " service_date = analysis_date\n",
- " )\n",
- " \n",
- " return summary"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "42d89815-5960-4b82-872f-3f2a04ba1c2a",
- "metadata": {},
- "outputs": [],
- "source": [
- "district_df = concatenate_summary_across_dates(\n",
- " quarterly_metrics_dict, summary_dataset=\"district\")\n",
- " \n",
- "#district_df = report_metrics.concatenate_summary_across_dates(\n",
- "# quarterly_metrics_dict, summary_dataset = \"district\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "497496fb-63d6-4fa8-a4d1-d3e8fd3673dc",
- "metadata": {},
- "outputs": [],
- "source": [
- "def facet_by_district(df: pd.DataFrame, variable: str) -> alt.Chart:\n",
- " subset = df[df.variable == variable]\n",
- " \n",
- " bar = (alt.Chart(subset)\n",
- " .mark_bar()\n",
- " .encode(\n",
- " x=alt.X(f\"year_quarter:O\"),\n",
- " y = alt.Y(\"value:Q\", title=f\"{variable.replace('_', ' '.title())}\"),\n",
- " color = alt.Color(\"district:N\", title=None, \n",
- " scale = alt.Scale(\n",
- " range = cp.CALITP_CATEGORY_BRIGHT_COLORS + \n",
- " cp.CALITP_CATEGORY_BOLD_COLORS), legend=None),\n",
- " tooltip = [\"district\", \"year_quarter\", \"value\", \"variable\"]\n",
- " ).facet(facet=\"district:N\", columns = 1, spacing=10, \n",
- " title = f\"{variable.replace('_', ' ').title()}\")\n",
- " .interactive()\n",
- " )\n",
- " \n",
- " return bar"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "8fa9eef8-ee56-42cf-9110-e445f223025d",
- "metadata": {},
- "outputs": [],
- "source": [
- "bar1 = facet_by_district(district_df, \"avg_service_hours\")\n",
- "#bar2 = facet_by_district(district_df, \"avg_delay_hours\")\n",
- "\n",
- "district_chart = styleguide.apply_chart_config(alt.hconcat(bar1, \n",
- " #bar2\n",
- " ))\n",
- "district_chart"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "3444fae3-7ca5-410c-adc3-4def6c830901",
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.13"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/quarterly_performance_objective/logs/quarterly_performance_pipeline.log b/quarterly_performance_objective/logs/quarterly_performance_pipeline.log
deleted file mode 100644
index a69a646b1..000000000
--- a/quarterly_performance_objective/logs/quarterly_performance_pipeline.log
+++ /dev/null
@@ -1,20 +0,0 @@
-2023-11-08 12:10:16.976 | INFO | __main__::179 - route level service: 2023-10-11 0:00:21.452201
-2023-11-08 12:11:10.785 | INFO | __main__::52 - create intermediate dfs: 2023-10-11 0:00:37.861739
-2023-11-08 12:11:37.530 | INFO | __main__::159 - categorize routes: 2023-10-11 0:00:09.897875
-2023-11-08 17:18:04.683 | INFO | __main__::163 - attach speed: 2023-10-11 0:00:11.320978
-2023-11-08 17:28:35.081 | INFO | __main__::179 - route level service: 2023-07-12 0:00:21.622882
-2023-11-08 17:29:29.484 | INFO | __main__::52 - create intermediate dfs: 2023-07-12 0:00:37.504010
-2023-11-08 17:29:55.583 | INFO | __main__::159 - categorize routes: 2023-07-12 0:00:09.474284
-2023-11-08 17:30:24.241 | INFO | __main__::163 - attach speed: 2023-07-12 0:00:10.915350
-2023-11-08 17:31:11.749 | INFO | __main__::179 - route level service: 2023-04-12 0:00:21.043589
-2023-11-08 17:32:03.341 | INFO | __main__::52 - create intermediate dfs: 2023-04-12 0:00:34.668850
-2023-11-08 17:32:28.409 | INFO | __main__::159 - categorize routes: 2023-04-12 0:00:08.669938
-2023-11-08 17:32:57.670 | INFO | __main__::163 - attach speed: 2023-04-12 0:00:12.054249
-2024-05-08 12:46:39.216 | INFO | __main__::151 - route level service: 2024-01-17 0:00:22.935689
-2024-05-08 12:48:37.229 | INFO | __main__::52 - create intermediate dfs: 2024-01-17 0:00:53.393774
-2024-05-08 12:54:21.710 | INFO | __main__::159 - categorize routes: 2024-01-17 0:00:10.477356
-2024-05-08 13:02:58.552 | INFO | __main__::130 - attach speed: 2024-01-17 0:00:05.890457
-2024-05-08 13:16:55.619 | INFO | __main__::151 - route level service: 2024-04-17 0:00:22.346017
-2024-05-08 13:18:08.149 | INFO | __main__::52 - create intermediate dfs: 2024-04-17 0:00:52.865328
-2024-05-08 13:18:38.545 | INFO | __main__::159 - categorize routes: 2024-04-17 0:00:10.733925
-2024-05-08 13:19:03.130 | INFO | __main__::130 - attach speed: 2024-04-17 0:00:05.055891
diff --git a/quarterly_performance_objective/new_report.html b/quarterly_performance_objective/new_report.html
new file mode 100644
index 000000000..0e549b89b
--- /dev/null
+++ b/quarterly_performance_objective/new_report.html
@@ -0,0 +1,6743 @@
+
+
+
+
+
+
+
+
+
+Quarterly Performance Metrics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Statewide Metrics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+10,999.5 |
+22.6 |
+486 |
+
+
+ |
+22.6 |
+280 |
+
+
+ |
+
+
+Intersects SHN |
+41,127.3 |
+36.5 |
+1,128 |
+
+
+ |
+14.0 |
+817 |
+
+
+ |
+
+
+Other |
+20,060.2 |
+46.5 |
+431 |
+
+
+ |
+14.7 |
+308 |
+
+
+ |
+
+
+On or Intersects SHN |
+52,126.8 |
+32.3 |
+1,614 |
+
+
+ |
+16.2 |
+1,096 |
+
+
+ |
+
+
+Total |
+72,187.0 |
+35.3 |
+2,045 |
+
+
+ |
+15.9 |
+1,404 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+District Breakdown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+123.1 |
+10.3 |
+12 |
+
+
+ |
+31.2 |
+12 |
+
+
+ |
+
+
+Intersects SHN |
+127.5 |
+9.8 |
+13 |
+
+
+ |
+15.9 |
+12 |
+
+
+ |
+
+
+Other |
+56.4 |
+18.8 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+On or Intersects SHN |
+250.6 |
+10.0 |
+25 |
+
+
+ |
+23.4 |
+24 |
+
+
+ |
+
+
+Total |
+269.4 |
+10.4 |
+26 |
+
+
+ |
+23.4 |
+24 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+240.2 |
+8.0 |
+30 |
+
+
+ |
+22.4 |
+8 |
+
+
+ |
+
+
+Intersects SHN |
+109.3 |
+9.7 |
+11 |
+
+
+ |
+17.7 |
+7 |
+
+
+ |
+
+
+On or Intersects SHN |
+349.5 |
+8.5 |
+41 |
+
+
+ |
+20.2 |
+15 |
+
+
+ |
+
+
+Total |
+349.5 |
+8.5 |
+41 |
+
+
+ |
+20.2 |
+15 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+469.6 |
+7.2 |
+65 |
+
+
+ |
+23.7 |
+27 |
+
+
+ |
+
+
+Intersects SHN |
+1,025.2 |
+15.2 |
+68 |
+
+
+ |
+13.9 |
+39 |
+
+
+ |
+
+
+Other |
+440.6 |
+17.4 |
+25 |
+
+
+ |
+17.5 |
+17 |
+
+
+ |
+
+
+On or Intersects SHN |
+1,494.8 |
+11.2 |
+133 |
+
+
+ |
+17.9 |
+66 |
+
+
+ |
+
+
+Total |
+1,935.4 |
+12.2 |
+158 |
+
+
+ |
+17.8 |
+83 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+2,580.6 |
+34.6 |
+75 |
+
+
+ |
+17.1 |
+58 |
+
+
+ |
+
+
+Intersects SHN |
+10,239.1 |
+31.1 |
+329 |
+
+
+ |
+13.7 |
+268 |
+
+
+ |
+
+
+Other |
+2,723.1 |
+38.0 |
+72 |
+
+
+ |
+13.1 |
+61 |
+
+
+ |
+
+
+On or Intersects SHN |
+12,819.7 |
+31.8 |
+404 |
+
+
+ |
+14.3 |
+326 |
+
+
+ |
+
+
+Total |
+15,542.8 |
+32.7 |
+475 |
+
+
+ |
+14.1 |
+388 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+865.1 |
+17.7 |
+49 |
+
+
+ |
+24.5 |
+32 |
+
+
+ |
+
+
+Intersects SHN |
+958.2 |
+13.9 |
+69 |
+
+
+ |
+14.3 |
+46 |
+
+
+ |
+
+
+Other |
+150.5 |
+14.1 |
+11 |
+
+
+ |
+17.8 |
+6 |
+
+
+ |
+
+
+On or Intersects SHN |
+1,823.3 |
+15.4 |
+118 |
+
+
+ |
+18.5 |
+77 |
+
+
+ |
+
+
+Total |
+1,973.8 |
+15.3 |
+129 |
+
+
+ |
+18.4 |
+83 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+493.1 |
+11.8 |
+42 |
+
+
+ |
+30.6 |
+12 |
+
+
+ |
+
+
+Intersects SHN |
+1,308.0 |
+22.8 |
+57 |
+
+
+ |
+16.3 |
+31 |
+
+
+ |
+
+
+Other |
+860.2 |
+34.0 |
+25 |
+
+
+ |
+15.4 |
+15 |
+
+
+ |
+
+
+On or Intersects SHN |
+1,801.1 |
+18.2 |
+99 |
+
+
+ |
+20.3 |
+43 |
+
+
+ |
+
+
+Total |
+2,661.3 |
+21.4 |
+124 |
+
+
+ |
+19.0 |
+58 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+3,241.6 |
+40.9 |
+79 |
+
+
+ |
+19.4 |
+64 |
+
+
+ |
+
+
+Intersects SHN |
+17,208.9 |
+58.5 |
+294 |
+
+
+ |
+13.1 |
+206 |
+
+
+ |
+
+
+Other |
+10,475.4 |
+78.0 |
+134 |
+
+
+ |
+13.1 |
+110 |
+
+
+ |
+
+
+On or Intersects SHN |
+20,450.5 |
+54.8 |
+373 |
+
+
+ |
+14.6 |
+270 |
+
+
+ |
+
+
+Total |
+30,925.9 |
+60.9 |
+508 |
+
+
+ |
+14.1 |
+380 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+1,193.4 |
+21.4 |
+56 |
+
+
+ |
+25.6 |
+27 |
+
+
+ |
+
+
+Intersects SHN |
+1,995.1 |
+28.1 |
+71 |
+
+
+ |
+17.0 |
+48 |
+
+
+ |
+
+
+Other |
+1,596.4 |
+23.8 |
+67 |
+
+
+ |
+19.8 |
+31 |
+
+
+ |
+
+
+On or Intersects SHN |
+3,188.5 |
+25.2 |
+127 |
+
+
+ |
+20.1 |
+75 |
+
+
+ |
+
+
+Total |
+4,784.9 |
+24.7 |
+194 |
+
+
+ |
+20.0 |
+106 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+71.6 |
+9.3 |
+8 |
+
+
+ |
+42.4 |
+5 |
+
+
+ |
+
+
+Intersects SHN |
+33.2 |
+10.0 |
+3 |
+
+
+ |
+11.8 |
+2 |
+
+
+ |
+
+
+Other |
+7.5 |
+7.5 |
+1 |
+
+
+ |
+18.3 |
+1 |
+
+
+ |
+
+
+On or Intersects SHN |
+104.8 |
+9.5 |
+11 |
+
+
+ |
+34.1 |
+7 |
+
+
+ |
+
+
+Total |
+107.3 |
+9.5 |
+11 |
+
+
+ |
+33.4 |
+8 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+570.9 |
+15.9 |
+36 |
+
+
+ |
+27.9 |
+16 |
+
+
+ |
+
+
+Intersects SHN |
+1,010.6 |
+15.6 |
+65 |
+
+
+ |
+14.1 |
+32 |
+
+
+ |
+
+
+Other |
+715.5 |
+20.6 |
+35 |
+
+
+ |
+16.5 |
+19 |
+
+
+ |
+
+
+On or Intersects SHN |
+1,581.6 |
+15.7 |
+101 |
+
+
+ |
+18.6 |
+48 |
+
+
+ |
+
+
+Total |
+2,297.1 |
+17.0 |
+135 |
+
+
+ |
+18.1 |
+66 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+709.6 |
+28.4 |
+25 |
+
+
+ |
+22.8 |
+15 |
+
+
+ |
+
+
+Intersects SHN |
+4,888.3 |
+42.9 |
+114 |
+
+
+ |
+14.1 |
+103 |
+
+
+ |
+
+
+Other |
+900.2 |
+33.3 |
+27 |
+
+
+ |
+15.8 |
+20 |
+
+
+ |
+
+
+On or Intersects SHN |
+5,597.9 |
+40.3 |
+139 |
+
+
+ |
+15.2 |
+118 |
+
+
+ |
+
+
+Total |
+6,498.1 |
+39.1 |
+166 |
+
+
+ |
+15.3 |
+138 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+On SHN |
+440.6 |
+47.2 |
+9 |
+
+
+ |
+16.1 |
+5 |
+
+
+ |
+
+
+Intersects SHN |
+2,206.0 |
+66.8 |
+33 |
+
+
+ |
+14.3 |
+21 |
+
+
+ |
+
+
+Other |
+2,177.0 |
+64.7 |
+34 |
+
+
+ |
+14.1 |
+29 |
+
+
+ |
+
+
+On or Intersects SHN |
+2,646.6 |
+62.5 |
+42 |
+
+
+ |
+14.6 |
+26 |
+
+
+ |
+
+
+Total |
+4,823.5 |
+63.5 |
+76 |
+
+
+ |
+14.3 |
+55 |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+Operator Breakdown
+Only the SHN subtotal (on SHN and parallel) is shown for each operator.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+name |
+organization_name |
+Category |
+Service |
+Speed (mph) |
+
+
+Daily Service Hours |
+Service Hours per Route |
+# Routes |
+Service Hours per Route (time-series) |
+Average Speed |
+# Routes |
+Speed (time-series) |
+
+
+
+
+Mountain Transit GMV Schedule |
+Mountain Area Regional Transit Authority |
+On or Intersects SHN |
+112.6 |
+16.1 |
+7 |
+ |
+19.0 |
+7 |
+ |
+
+
+OCTA Schedule |
+Orange County Transportation Authority |
+On or Intersects SHN |
+1,395.8 |
+53.7 |
+26 |
+
+
+ |
+14.6 |
+26 |
+
+
+ |
+
+
+Needles Schedule |
+City of Needles |
+On or Intersects SHN |
+11.0 |
+11.0 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Nevada County Schedule |
+Nevada County |
+On or Intersects SHN |
+50.8 |
+8.5 |
+6 |
+
+
+ |
+22.1 |
+6 |
+
+
+ |
+
+
+North County Schedule |
+North County Transit District |
+On or Intersects SHN |
+881.0 |
+24.0 |
+37 |
+
+
+ |
+17.1 |
+34 |
+
+
+ |
+
+
+Norwalk Avail Schedule |
+City of Norwalk |
+On or Intersects SHN |
+235.9 |
+39.3 |
+6 |
+
+
+ |
+11.8 |
+6 |
+
+
+ |
+
+
+Mountain Transit Schedule |
+Mountain Area Regional Transit Authority |
+On or Intersects SHN |
+101.5 |
+14.5 |
+7 |
+
+
+ |
+ |
+ |
+ |
+
+
+OmniTrans Schedule |
+OmniTrans |
+On or Intersects SHN |
+954.1 |
+56.1 |
+17 |
+
+
+ |
+16.1 |
+13 |
+
+
+ |
+
+
+Redding Schedule |
+Shasta County |
+On or Intersects SHN |
+140.1 |
+8.8 |
+16 |
+
+
+ |
+20.2 |
+15 |
+
+
+ |
+
+
+Pasadena Schedule |
+City of Pasadena |
+On or Intersects SHN |
+203.0 |
+22.6 |
+9 |
+
+
+ |
+13.4 |
+9 |
+
+
+ |
+
+
+Placer Schedule |
+Placer County |
+On or Intersects SHN |
+104.9 |
+13.1 |
+8 |
+
+
+ |
+ |
+ |
+ |
+
+
+Plumas Schedule |
+Plumas Transit Systems |
+On or Intersects SHN |
+31.7 |
+10.6 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+PresidiGo Schedule |
+Presidio Trust |
+On or Intersects SHN |
+39.5 |
+19.7 |
+2 |
+
+
+ |
+11.4 |
+2 |
+
+
+ |
+
+
+Riverside Schedule |
+Riverside Transit Agency |
+On or Intersects SHN |
+978.8 |
+36.7 |
+27 |
+
+
+ |
+17.4 |
+27 |
+
+
+ |
+
+
+Redwood Coast Schedulel |
+Redwood Coast Transit Authority |
+On or Intersects SHN |
+21.5 |
+3.6 |
+6 |
+ |
+21.2 |
+6 |
+ |
+
+
+Morro Bay Cal-ITP Schedule |
+City of Morro Bay |
+On or Intersects SHN |
+12.3 |
+12.3 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Palos Verdes PTA Schedule |
+Palos Verdes Peninsula Transit Authority |
+On or Intersects SHN |
+1.6 |
+1.6 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Morongo Basin Schedule |
+Basin Transit |
+On or Intersects SHN |
+69.9 |
+11.7 |
+6 |
+
+
+ |
+ |
+ |
+ |
+
+
+La Campana Schedule |
+City of Bell |
+On or Intersects SHN |
+9.6 |
+9.6 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Montebello Schedule |
+City of Montebello |
+On or Intersects SHN |
+244.1 |
+48.8 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+Rosemead Passio Schedule |
+City of Rosemead |
+On or Intersects SHN |
+27.5 |
+13.8 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+LADPW Schedule |
+Los Angeles County |
+On or Intersects SHN |
+240.0 |
+15.0 |
+16 |
+
+
+ |
+ |
+ |
+ |
+
+
+LAX FlyAway Schedule |
+Los Angeles World Airports |
+On or Intersects SHN |
+271.8 |
+68.0 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Laguna Beach Schedule |
+City of Laguna Beach |
+On or Intersects SHN |
+119.9 |
+40.0 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+Lake Schedule |
+Lake Transit Authority |
+On or Intersects SHN |
+117.6 |
+13.1 |
+9 |
+
+
+ |
+26.1 |
+9 |
+
+
+ |
+
+
+Lassen Schedule |
+Lassen Transit Service Agency |
+On or Intersects SHN |
+30.0 |
+7.5 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Lawndale Beat GMV Schedule |
+City of Lawndale |
+On or Intersects SHN |
+20.8 |
+10.4 |
+2 |
+
+
+ |
+10.6 |
+2 |
+
+
+ |
+
+
+Lawndale Schedule |
+City of Lawndale |
+On or Intersects SHN |
+20.8 |
+10.4 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Monterey Salinas Schedule |
+Monterey-Salinas Transit |
+On or Intersects SHN |
+458.7 |
+16.4 |
+28 |
+
+
+ |
+17.4 |
+28 |
+
+
+ |
+
+
+Lompoc Schedule |
+City of Lompoc |
+On or Intersects SHN |
+43.5 |
+8.7 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+Lynwood Schedule |
+City of Lynwood |
+On or Intersects SHN |
+39.8 |
+9.9 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Madera County Connection Schedule |
+Madera County |
+On or Intersects SHN |
+36.8 |
+9.2 |
+4 |
+
+
+ |
+32.2 |
+4 |
+
+
+ |
+
+
+Madera Metro Schedule |
+City of Madera |
+On or Intersects SHN |
+56.4 |
+14.1 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Manteca Schedule |
+City of Manteca |
+On or Intersects SHN |
+20.2 |
+10.1 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Maywood Schedule |
+City of Maywood |
+On or Intersects SHN |
+10.0 |
+10.0 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Mendocino Schedule |
+Mendocino Transit Authority |
+On or Intersects SHN |
+88.8 |
+12.7 |
+7 |
+
+
+ |
+22.4 |
+7 |
+
+
+ |
+
+
+Merced GMV Schedule |
+Transit Joint Powers Authority for Merced County |
+On or Intersects SHN |
+211.0 |
+17.6 |
+12 |
+
+
+ |
+ |
+ |
+ |
+
+
+Merced Schedule |
+Transit Joint Powers Authority for Merced County |
+On or Intersects SHN |
+207.8 |
+18.9 |
+11 |
+
+
+ |
+ |
+ |
+ |
+
+
+Long Beach Schedule |
+Long Beach Transit |
+On or Intersects SHN |
+914.3 |
+48.1 |
+19 |
+
+
+ |
+11.6 |
+19 |
+
+
+ |
+
+
+Roseville Schedule |
+City of Roseville |
+On or Intersects SHN |
+98.4 |
+4.9 |
+20 |
+
+
+ |
+ |
+ |
+ |
+
+
+Visalia Schedule |
+City of Visalia |
+On or Intersects SHN |
+314.6 |
+26.2 |
+12 |
+
+
+ |
+19.6 |
+12 |
+
+
+ |
+
+
+SBMTD Schedule |
+Santa Barbara Metropolitan Transit District |
+On or Intersects SHN |
+425.2 |
+12.9 |
+33 |
+
+
+ |
+14.7 |
+20 |
+
+
+ |
+
+
+Torrance Schedule |
+City of Torrance |
+On or Intersects SHN |
+375.1 |
+34.1 |
+11 |
+
+
+ |
+14.8 |
+11 |
+
+
+ |
+
+
+Tracy Schedule |
+City of Tracy |
+On or Intersects SHN |
+49.1 |
+5.5 |
+9 |
+
+
+ |
+ |
+ |
+ |
+
+
+Trinity Schedule |
+Trinity County |
+On or Intersects SHN |
+17.7 |
+4.4 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Tuolumne Remix Schedule |
+Tuolumne County Transit Agency |
+On or Intersects SHN |
+25.3 |
+8.4 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+Turlock Schedule |
+City of Turlock |
+On or Intersects SHN |
+34.0 |
+8.5 |
+4 |
+
+
+ |
+16.2 |
+4 |
+
+
+ |
+
+
+UCSC Schedule |
+University of California, Santa Cruz |
+On or Intersects SHN |
+0.7 |
+0.7 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Unitrans Schedule |
+University of California, Davis |
+On or Intersects SHN |
+213.2 |
+13.6 |
+16 |
+
+
+ |
+10.8 |
+13 |
+
+
+ |
+
+
+VCTC GMV Schedule |
+City of Ojai |
+On or Intersects SHN |
+559.9 |
+16.5 |
+34 |
+
+
+ |
+19.3 |
+32 |
+
+
+ |
+
+
+VCTC GMV Schedule |
+Ventura County Transportation Commission |
+On or Intersects SHN |
+565.7 |
+16.4 |
+34 |
+
+
+ |
+18.3 |
+32 |
+
+
+ |
+
+
+Victor Valley GMV Schedule |
+Victor Valley Transit Authority |
+On or Intersects SHN |
+299.6 |
+15.0 |
+20 |
+
+
+ |
+23.6 |
+20 |
+
+
+ |
+
+
+Victor Valley Schedule |
+Victor Valley Transit Authority |
+On or Intersects SHN |
+289.6 |
+15.2 |
+19 |
+
+
+ |
+ |
+ |
+ |
+
+
+LA Metro Rail Schedule |
+Los Angeles County Metropolitan Transportation Authority |
+On or Intersects SHN |
+970.5 |
+194.1 |
+5 |
+
+
+ |
+25.0 |
+5 |
+
+
+ |
+
+
+YARTS Schedule |
+Yosemite Area Regional Transportation System |
+On or Intersects SHN |
+56.3 |
+24.1 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Yolobus Schedule |
+Yolo County Transportation District |
+On or Intersects SHN |
+196.2 |
+10.9 |
+18 |
+
+
+ |
+22.5 |
+18 |
+
+
+ |
+
+
+Yuba-Sutter Schedule |
+Yuba-Sutter Transit Authority |
+On or Intersects SHN |
+159.7 |
+11.4 |
+14 |
+
+
+ |
+ |
+ |
+ |
+
+
+Yuma Schedule |
+Yuma County Intergovernmental Public Transportation Authority |
+On or Intersects SHN |
+14.8 |
+7.4 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+eTrans Schedule |
+City of Escalon |
+On or Intersects SHN |
+3.4 |
+3.4 |
+1 |
+
+
+ |
+18.4 |
+1 |
+
+
+ |
+
+
+Tehama Schedule |
+Tehama County |
+On or Intersects SHN |
+84.6 |
+9.4 |
+9 |
+
+
+ |
+ |
+ |
+ |
+
+
+Tehama Schedule |
+Susanville Indian Rancheria |
+On or Intersects SHN |
+74.8 |
+9.3 |
+8 |
+
+
+ |
+ |
+ |
+ |
+
+
+Tahoe Transportation District Schedule |
+Tahoe Transportation District |
+On or Intersects SHN |
+31.2 |
+15.6 |
+2 |
+
+
+ |
+ |
+ |
+
+
+ |
+
+
+Tahoe Transportation District GMV Schedule |
+Tahoe Transportation District |
+On or Intersects SHN |
+35.1 |
+17.6 |
+2 |
+
+
+ |
+15.4 |
+2 |
+
+
+ |
+
+
+SLO Schedule |
+City of San Luis Obispo |
+On or Intersects SHN |
+88.0 |
+13.2 |
+7 |
+
+
+ |
+ |
+ |
+ |
+
+
+SLORTA Schedule |
+San Luis Obispo Regional Transit Authority |
+On or Intersects SHN |
+167.8 |
+11.2 |
+15 |
+
+
+ |
+24.0 |
+14 |
+
+
+ |
+
+
+Sacramento Schedule |
+City of Rancho Cordova |
+On or Intersects SHN |
+974.7 |
+21.2 |
+46 |
+
+
+ |
+15.1 |
+42 |
+
+
+ |
+
+
+Sage Stage Schedule |
+Modoc Transportation Agency |
+On or Intersects SHN |
+7.8 |
+7.8 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+San Diego Schedule |
+Flagship Cruises and Events Inc. |
+On or Intersects SHN |
+4,570.2 |
+51.4 |
+89 |
+
+
+ |
+14.6 |
+86 |
+
+
+ |
+
+
+San Diego Schedule |
+San Diego Metropolitan Transit System |
+On or Intersects SHN |
+4,577.3 |
+54.5 |
+84 |
+
+
+ |
+14.2 |
+83 |
+
+
+ |
+
+
+San Joaquin Schedule |
+San Joaquin Regional Transit District |
+On or Intersects SHN |
+406.4 |
+15.1 |
+27 |
+
+
+ |
+17.6 |
+25 |
+
+
+ |
+
+
+Santa Cruz Schedule |
+Santa Cruz Metropolitan Transit District |
+On or Intersects SHN |
+506.2 |
+26.6 |
+19 |
+
+
+ |
+18.6 |
+19 |
+
+
+ |
+
+
+Roseville Transit GMV Schedule |
+City of Roseville |
+On or Intersects SHN |
+107.1 |
+5.1 |
+21 |
+ |
+ |
+ |
+ |
+
+
+Santa Maria Schedule |
+City of Santa Maria |
+On or Intersects SHN |
+157.0 |
+12.1 |
+13 |
+
+
+ |
+16.3 |
+1 |
+
+
+ |
+
+
+Siskiyou Schedule |
+Siskiyou County |
+On or Intersects SHN |
+44.1 |
+8.8 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+South County Transit Link Schedule |
+Sacramento County |
+On or Intersects SHN |
+42.2 |
+14.1 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+Spirit Bus Passio Schedule |
+City of Monterey Park |
+On or Intersects SHN |
+50.5 |
+8.4 |
+6 |
+
+
+ |
+ |
+ |
+ |
+
+
+StanRTA Schedule |
+Stanislaus Regional Transit Authority |
+On or Intersects SHN |
+700.1 |
+26.9 |
+26 |
+
+
+ |
+20.8 |
+26 |
+
+
+ |
+
+
+Stanford Schedule |
+Stanford University |
+On or Intersects SHN |
+179.7 |
+10.6 |
+17 |
+
+
+ |
+ |
+ |
+ |
+
+
+SunLine Avail Schedule |
+SunLine Transit Agency |
+On or Intersects SHN |
+40.2 |
+10.1 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+TART, North Lake Tahoe Schedule |
+North Lake Tahoe Express |
+On or Intersects SHN |
+91.4 |
+15.2 |
+6 |
+
+
+ |
+ |
+ |
+ |
+
+
+TCRTA TripShot Schedule |
+Tulare County Regional Transit Agency |
+On or Intersects SHN |
+258.3 |
+10.3 |
+25 |
+
+
+ |
+ |
+ |
+ |
+
+
+Santa Ynez Mecatran Schedule |
+City of Solvang |
+On or Intersects SHN |
+32.6 |
+16.3 |
+2 |
+
+
+ |
+21.1 |
+2 |
+
+
+ |
+
+
+LA Metro Bus Schedule |
+Los Angeles County Metropolitan Transportation Authority |
+On or Intersects SHN |
+9,498.6 |
+143.9 |
+66 |
+
+
+ |
+13.8 |
+63 |
+
+
+ |
+
+
+Santa Clarita Schedule |
+City of Santa Clarita |
+On or Intersects SHN |
+215.3 |
+10.2 |
+21 |
+
+
+ |
+20.7 |
+21 |
+
+
+ |
+
+
+Kings Schedule |
+Kings County Area Public Transit Agency |
+On or Intersects SHN |
+146.2 |
+12.2 |
+12 |
+
+
+ |
+22.7 |
+11 |
+
+
+ |
+
+
+Bay Area 511 Rio Vista Delta Breeze Schedule |
+City of Rio Vista |
+On or Intersects SHN |
+7.3 |
+7.3 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 SFO AirTrain Schedule |
+San Francisco International Airport |
+On or Intersects SHN |
+342.3 |
+85.6 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 SamTrans Schedule |
+San Mateo County Transit District |
+On or Intersects SHN |
+1,475.0 |
+23.1 |
+64 |
+
+
+ |
+13.3 |
+62 |
+
+
+ |
+
+
+Bay Area 511 Santa Clara Transit Schedule |
+Santa Clara Valley Transportation Authority |
+On or Intersects SHN |
+2,867.7 |
+52.1 |
+55 |
+
+
+ |
+15.8 |
+46 |
+
+
+ |
+
+
+Bay Area 511 Santa Rosa CityBus Schedule |
+City of Santa Rosa |
+On or Intersects SHN |
+177.7 |
+11.8 |
+15 |
+
+
+ |
+13.9 |
+14 |
+
+
+ |
+
+
+Bay Area 511 SolTrans Schedule |
+Solano County Transit |
+On or Intersects SHN |
+110.3 |
+11.0 |
+10 |
+
+
+ |
+15.0 |
+10 |
+
+
+ |
+
+
+Bay Area 511 SolTrans Schedule |
+Solano Transportation Authority |
+On or Intersects SHN |
+112.6 |
+10.2 |
+11 |
+
+
+ |
+14.7 |
+11 |
+
+
+ |
+
+
+Bay Area 511 Sonoma County Transit Schedule |
+Cloverdale Transit |
+On or Intersects SHN |
+258.2 |
+11.2 |
+23 |
+
+
+ |
+18.3 |
+22 |
+
+
+ |
+
+
+Bay Area 511 Sonoma-Marin Area Rail Transit Schedule |
+Sonoma-Marin Area Rail Transit District |
+On or Intersects SHN |
+48.8 |
+48.8 |
+1 |
+
+
+ |
+36.2 |
+1 |
+
+
+ |
+
+
+Bay Area 511 South San Francisco Shuttle Schedule |
+City of South San Francisco |
+On or Intersects SHN |
+31.4 |
+10.5 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 Tri Delta Schedule |
+Eastern Contra Costa Transit Authority |
+On or Intersects SHN |
+427.5 |
+29.8 |
+14 |
+
+
+ |
+16.4 |
+12 |
+
+
+ |
+
+
+Bay Area 511 Tri-Valley Wheels Schedule |
+Livermore-Amador Valley Transit Authority |
+On or Intersects SHN |
+192.4 |
+8.4 |
+23 |
+
+
+ |
+14.9 |
+23 |
+
+
+ |
+
+
+Bay Area 511 Union City Transit Schedule |
+City of Union City |
+On or Intersects SHN |
+131.5 |
+26.3 |
+5 |
+
+
+ |
+14.2 |
+5 |
+
+
+ |
+
+
+Bay Area 511 Vacaville City Coach Schedule |
+City of Vacaville |
+On or Intersects SHN |
+64.9 |
+13.0 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 Vine Transit Schedule |
+Napa Valley Transportation Authority |
+On or Intersects SHN |
+219.1 |
+21.9 |
+10 |
+
+
+ |
+18.2 |
+6 |
+
+
+ |
+
+
+Bay Area 511 WestCAT Schedule |
+Western Contra Costa Transit Authority |
+On or Intersects SHN |
+172.0 |
+17.2 |
+10 |
+
+
+ |
+20.0 |
+10 |
+
+
+ |
+
+
+Beach Cities GMV Schedule |
+City of Redondo Beach |
+On or Intersects SHN |
+91.2 |
+45.6 |
+2 |
+
+
+ |
+11.6 |
+2 |
+
+
+ |
+
+
+Bay Area 511 Petaluma Schedule |
+City of Petaluma |
+On or Intersects SHN |
+45.5 |
+6.5 |
+7 |
+
+
+ |
+14.9 |
+6 |
+
+
+ |
+
+
+Bay Area 511 Mission Bay Schedule |
+Mission Bay Transportation Management Agency |
+On or Intersects SHN |
+60.5 |
+15.1 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 Marin Schedule |
+Marin County Transit District |
+On or Intersects SHN |
+293.7 |
+22.6 |
+13 |
+
+
+ |
+14.8 |
+13 |
+
+
+ |
+
+
+Bay Area 511 MVGO Schedule |
+Mountain View Transportation Management Association |
+On or Intersects SHN |
+43.9 |
+5.5 |
+8 |
+
+
+ |
+12.0 |
+8 |
+
+
+ |
+
+
+LA DOT Schedule |
+City of Los Angeles |
+On or Intersects SHN |
+2,003.3 |
+40.1 |
+50 |
+
+
+ |
+12.2 |
+50 |
+
+
+ |
+
+
+Alhambra Schedule |
+City of Alhambra |
+On or Intersects SHN |
+50.4 |
+25.2 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Amador Schedule |
+Amador Regional Transit System |
+On or Intersects SHN |
+39.0 |
+6.5 |
+6 |
+
+
+ |
+ |
+ |
+ |
+
+
+Anaheim Resort Schedule |
+Anaheim Transportation Network |
+On or Intersects SHN |
+1,130.9 |
+84.8 |
+13 |
+
+
+ |
+ |
+ |
+ |
+
+
+Antelope Valley Transit Authority Schedule |
+Antelope Valley Transit Authority |
+On or Intersects SHN |
+408.0 |
+34.0 |
+12 |
+
+
+ |
+18.3 |
+8 |
+
+
+ |
+
+
+Arcadia Schedule |
+City of Arcadia |
+On or Intersects SHN |
+10.8 |
+10.8 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Arvin Schedule |
+City of Arvin |
+On or Intersects SHN |
+14.1 |
+4.7 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+Auburn Schedule |
+City of Auburn |
+On or Intersects SHN |
+6.7 |
+6.7 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bear Schedule |
+University of California, Berkeley |
+On or Intersects SHN |
+4.2 |
+4.2 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+B-Line Schedule |
+Butte County Association of Governments |
+On or Intersects SHN |
+177.0 |
+9.3 |
+19 |
+
+
+ |
+19.4 |
+19 |
+
+
+ |
+
+
+Bay Area 511 AC Transit Schedule |
+Alameda-Contra Costa Transit District |
+On or Intersects SHN |
+2,719.2 |
+37.6 |
+72 |
+
+
+ |
+13.2 |
+72 |
+
+
+ |
+
+
+Bay Area 511 BART Schedule |
+San Francisco Bay Area Rapid Transit District |
+On or Intersects SHN |
+219.9 |
+73.3 |
+3 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 Caltrain Schedule |
+Peninsula Corridor Joint Powers Board |
+On or Intersects SHN |
+158.5 |
+31.7 |
+5 |
+
+
+ |
+36.2 |
+5 |
+
+
+ |
+
+
+Bay Area 511 Capitol Corridor Schedule |
+Capitol Corridor Joint Powers Authority |
+On or Intersects SHN |
+11.8 |
+11.8 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 Commute.org Schedule |
+City of Menlo Park |
+On or Intersects SHN |
+130.9 |
+6.9 |
+19 |
+ |
+ |
+ |
+ |
+
+
+Bay Area 511 County Connection Schedule |
+Central Contra Costa Transit Authority |
+On or Intersects SHN |
+352.8 |
+11.8 |
+30 |
+
+
+ |
+15.1 |
+30 |
+
+
+ |
+
+
+Bay Area 511 Emery Go-Round Schedule |
+Emeryville Transportation Management Agency |
+On or Intersects SHN |
+38.4 |
+38.4 |
+1 |
+
+
+ |
+7.9 |
+1 |
+
+
+ |
+
+
+Bay Area 511 Fairfield and Suisun Transit Schedule |
+City of Fairfield |
+On or Intersects SHN |
+68.7 |
+18.8 |
+4 |
+
+
+ |
+16.8 |
+4 |
+
+
+ |
+
+
+Baldwin Park Schedule |
+City of Baldwin Park |
+On or Intersects SHN |
+64.8 |
+12.9 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+Beaumont Pass Schedule |
+City of Beaumont |
+On or Intersects SHN |
+62.8 |
+7.8 |
+8 |
+
+
+ |
+26.0 |
+8 |
+
+
+ |
+
+
+Bay Area 511 Muni Schedule |
+City and County of San Francisco |
+On or Intersects SHN |
+5,028.2 |
+102.6 |
+49 |
+
+
+ |
+9.2 |
+49 |
+
+
+ |
+
+
+Bellflower Bus Schedule |
+City of Bellflower |
+On or Intersects SHN |
+10.0 |
+10.0 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+El Segundo Schedule |
+City of El Segundo |
+On or Intersects SHN |
+2.9 |
+2.9 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Elk Grove Schedule |
+City of Elk Grove |
+On or Intersects SHN |
+88.6 |
+7.4 |
+12 |
+
+
+ |
+21.8 |
+12 |
+
+
+ |
+
+
+Flixbus Schedule |
+Greyhound |
+On or Intersects SHN |
+297.6 |
+31.9 |
+9 |
+
+
+ |
+ |
+ |
+ |
+
+
+Foothill Schedule |
+City of Duarte |
+On or Intersects SHN |
+1,910.2 |
+63.7 |
+30 |
+
+
+ |
+17.1 |
+30 |
+
+
+ |
+
+
+Fresno County Schedule |
+Fresno County Rural Transit Agency |
+On or Intersects SHN |
+20.8 |
+5.2 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Fresno Schedule |
+City of Fresno |
+On or Intersects SHN |
+540.0 |
+67.5 |
+8 |
+
+
+ |
+14.3 |
+8 |
+
+
+ |
+
+
+GET Schedule |
+Golden Empire Transit District |
+On or Intersects SHN |
+378.1 |
+34.4 |
+11 |
+
+
+ |
+17.0 |
+11 |
+
+
+ |
+
+
+Get Around Town Express Schedule |
+City of South Gate |
+On or Intersects SHN |
+13.3 |
+13.3 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+El Monte Schedule |
+City of El Monte |
+On or Intersects SHN |
+63.7 |
+9.1 |
+7 |
+
+
+ |
+ |
+ |
+ |
+
+
+Glendale Schedule |
+City of Glendale |
+On or Intersects SHN |
+234.9 |
+24.7 |
+10 |
+
+
+ |
+ |
+ |
+ |
+
+
+Glenn Schedule |
+Glenn County |
+On or Intersects SHN |
+19.5 |
+19.5 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Go West Schedule |
+City of West Covina |
+On or Intersects SHN |
+21.9 |
+10.9 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Grapeline Schedule |
+City of Lodi |
+On or Intersects SHN |
+56.9 |
+6.3 |
+9 |
+
+
+ |
+ |
+ |
+ |
+
+
+Guadalupe Flyer Schedule |
+City of Guadalupe |
+On or Intersects SHN |
+26.0 |
+13.0 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Humboldt Schedule |
+City of Eureka |
+On or Intersects SHN |
+89.0 |
+9.2 |
+10 |
+
+
+ |
+22.0 |
+9 |
+
+
+ |
+
+
+Imperial Valley Transit Schedule |
+Imperial County Transportation Commission |
+On or Intersects SHN |
+129.5 |
+10.0 |
+13 |
+
+
+ |
+ |
+ |
+ |
+
+
+Bell Gardens Schedule |
+City of Bell Gardens |
+On or Intersects SHN |
+29.8 |
+29.8 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Kern Schedule |
+Kern County |
+On or Intersects SHN |
+154.8 |
+11.9 |
+13 |
+
+
+ |
+ |
+ |
+ |
+
+
+Glendora Schedule |
+City of Glendora |
+On or Intersects SHN |
+9.2 |
+4.6 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+El Dorado Schedule |
+El Dorado County Transit Authority |
+On or Intersects SHN |
+99.8 |
+14.2 |
+7 |
+
+
+ |
+ |
+ |
+ |
+
+
+G Trans Schedule |
+City of Gardena |
+On or Intersects SHN |
+210.1 |
+70.0 |
+3 |
+
+
+ |
+15.5 |
+3 |
+
+
+ |
+
+
+Cerritos on Wheels Website Schedule |
+City of Cerritos |
+On or Intersects SHN |
+21.6 |
+3.6 |
+6 |
+
+
+ |
+ |
+ |
+ |
+
+
+BruinBus Schedule |
+University of California, Los Angeles |
+On or Intersects SHN |
+37.0 |
+12.3 |
+3 |
+
+
+ |
+16.4 |
+1 |
+
+
+ |
+
+
+Eastern Sierra Schedule |
+Eastern Sierra Transit Authority |
+On or Intersects SHN |
+104.8 |
+9.5 |
+11 |
+
+
+ |
+34.1 |
+7 |
+
+
+ |
+
+
+Burbank Schedule |
+City of Burbank |
+On or Intersects SHN |
+42.4 |
+42.4 |
+1 |
+
+
+ |
+11.6 |
+1 |
+
+
+ |
+
+
+Calabasas Schedule |
+City of Calabasas |
+On or Intersects SHN |
+19.3 |
+4.8 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Calaveras Schedule |
+Calaveras Transit Agency |
+On or Intersects SHN |
+38.7 |
+19.4 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Cerritos on Wheels Schedule |
+City of Cerritos |
+On or Intersects SHN |
+21.6 |
+10.8 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Clean Air Express Schedule |
+Santa Barbara County Association of Governments |
+On or Intersects SHN |
+28.9 |
+7.2 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Clovis Schedule |
+City of Clovis |
+On or Intersects SHN |
+34.1 |
+20.4 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Big Blue Bus Schedule |
+City of Santa Monica |
+On or Intersects SHN |
+1,129.7 |
+62.8 |
+18 |
+
+
+ |
+11.2 |
+18 |
+
+
+ |
+
+
+Commerce Schedule |
+City of Commerce |
+On or Intersects SHN |
+110.1 |
+18.3 |
+6 |
+
+
+ |
+13.5 |
+6 |
+
+
+ |
+
+
+County Express Schedule |
+San Benito County Local Transportation Authority |
+On or Intersects SHN |
+36.9 |
+22.1 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+Cudahy Schedule |
+City of Cudahy |
+On or Intersects SHN |
+9.2 |
+9.2 |
+1 |
+
+
+ |
+ |
+ |
+ |
+
+
+Culver City Schedule |
+City of Culver City |
+On or Intersects SHN |
+424.7 |
+42.5 |
+10 |
+
+
+ |
+11.4 |
+10 |
+
+
+ |
+
+
+Delano Schedule |
+City of Delano |
+On or Intersects SHN |
+26.9 |
+6.7 |
+4 |
+
+
+ |
+ |
+ |
+ |
+
+
+Desert Roadrunner GMV Schedule |
+Palo Verde Valley Transit Agency |
+On or Intersects SHN |
+35.2 |
+7.0 |
+5 |
+
+
+ |
+23.7 |
+3 |
+
+
+ |
+
+
+Desert Roadrunner Schedule |
+Palo Verde Valley Transit Agency |
+On or Intersects SHN |
+36.2 |
+7.2 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+DowneyLINK GMV Schedule |
+City of Downey |
+On or Intersects SHN |
+53.7 |
+10.7 |
+5 |
+
+
+ |
+ |
+ |
+ |
+
+
+Corona Schedule |
+City of Corona |
+On or Intersects SHN |
+45.4 |
+22.7 |
+2 |
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/quarterly_performance_objective/new_report.qmd b/quarterly_performance_objective/new_report.qmd
new file mode 100644
index 000000000..aba74537f
--- /dev/null
+++ b/quarterly_performance_objective/new_report.qmd
@@ -0,0 +1,264 @@
+---
+title: Quarterly Performance Metrics
+execute:
+ echo: false
+format:
+ html:
+ mainfont: sans-serif
+ monofont: sans-serif
+ anchor-sections: true
+ toc: true
+ toc-title: Contents
+ toc-depth: 3
+ code-links:
+ - text: Analysis Products
+ icon: bar-chart-fill
+ href: 'https://analysis.calitp.org'
+ - text: Reach Out!
+ icon: envelope
+ href: 'mailto:hello@calitp.org'
+jupyter: python3
+---
+
+```{python}
+import altair as alt
+import geopandas as gpd
+import pandas as pd
+import polars as pl
+
+from great_tables import GT, _data_color, loc, md, nanoplot_options, style
+
+from update_vars import BUS_SERVICE_GCS
+```
+
+```{python}
+def category_wrangling(
+ df: pd.DataFrame,
+ col: str = "category",
+ sort_key: list = ["on_shn", "parallel", "other", "shn_subtotal", "total"]
+) -> pd.DataFrame:
+ """
+ Custom sort order for categorical variable
+ https://stackoverflow.com/questions/23482668/sorting-by-a-custom-list-in-pandas
+ """
+ category_values = {
+ "on_shn": "On SHN",
+ "parallel": "Intersects SHN",
+ "other": "Other",
+ "shn_subtotal": "On or Intersects SHN",
+ "total": "Total"
+ }
+
+ df = df.sort_values(
+ col, key=lambda c: c.map(lambda e: sort_key.index(e))
+ )
+
+ df = df.assign(
+ category = df.category.map(category_values)
+ )
+
+ return df
+
+def get_hex(color_name: str) -> str:
+ """
+ Since some of the color names don't pull the hex code,
+ we'll grab it here.
+ https://github.com/posit-dev/great-tables/blob/main/great_tables/_data_color/constants.py
+ """
+ return _data_color.constants.COLOR_NAME_TO_HEX[color_name]
+```
+
+```{python}
+current_quarter = "2024-Q2"
+
+operator_df = pd.read_parquet(
+ f"{BUS_SERVICE_GCS}"
+ "quarterly_metrics/operator_time_series.parquet",
+ filters = [[("year_quarter", "==", current_quarter)]]
+).pipe(category_wrangling)
+
+district_df = pd.read_parquet(
+ f"{BUS_SERVICE_GCS}"
+ "quarterly_metrics/district_time_series.parquet",
+ filters = [[("year_quarter", "==", current_quarter)]]
+).pipe(category_wrangling)
+
+statewide_df = pd.read_parquet(
+ f"{BUS_SERVICE_GCS}"
+ "quarterly_metrics/statewide_time_series.parquet",
+ filters = [[("year_quarter", "==", current_quarter)]]
+).pipe(category_wrangling)
+```
+
+```{python}
+def shared_nano_options(
+ point_stroke_color: str,
+ line_stroke_color: str,
+ point_fill_color: str,
+ area_fill_color: str
+):
+ nano_options = nanoplot_options(
+ data_point_radius=6,
+ data_point_stroke_color=get_hex(point_stroke_color),
+ data_point_fill_color=get_hex(point_fill_color),
+ data_point_stroke_width=3,
+ data_line_type="curved",
+ data_line_stroke_color=get_hex(line_stroke_color),
+ data_line_stroke_width=8,
+ data_area_fill_color=get_hex(area_fill_color),
+ #vertical_guide_stroke_color=None,
+ show_y_axis_guide=True,
+ #show_vertical_guides=False,
+ interactive_data_values = True,
+ #reference_line_color=get_hex("salmon1"),
+ show_reference_line=False
+ )
+
+ return nano_options
+```
+
+```{python}
+def plot_table(df: pd.DataFrame):
+
+ MIN_SPEED, MAX_SPEED = df.speed_mph.min(), df.speed_mph.max()
+ MIN_SERVICE, MAX_SERVICE = df.service_hours_per_route.min(), df.service_hours_per_route.max()
+
+ table = (
+ GT(pl.from_pandas(df))
+ .fmt_nanoplot(
+ columns="speed_mph_ts",
+ plot_type="line",
+ expand_y=[round(MIN_SPEED, 0), round(MAX_SPEED, 0)],
+ options=shared_nano_options(
+ point_stroke_color = "black",
+ line_stroke_color = "green",
+ point_fill_color = "white",
+ area_fill_color = "seagreen2")
+ ).fmt_nanoplot(
+ columns="service_hours_per_route_ts",
+ plot_type="line",
+ expand_y=[round(MIN_SERVICE, 0), round(MAX_SERVICE, 0)],
+ options=shared_nano_options(
+ point_stroke_color = "black",
+ line_stroke_color = "steelblue1",
+ point_fill_color = "white",
+ area_fill_color = "lightskyblue2",
+ )
+ ).fmt_number(
+ columns = [
+ "daily_service_hours",
+ "service_hours_per_route",
+ "speed_mph"], decimals=1
+ ).fmt_integer(
+ columns = ["daily_routes", "daily_vp_routes"]
+ ).cols_label(
+ category = "Category",
+ daily_service_hours = "Daily Service Hours",
+ service_hours_per_route = "Service Hours per Route",
+ speed_mph = "Average Speed",
+ daily_routes = "# Routes",
+ daily_vp_routes = "# Routes",
+ service_hours_per_route_ts = "Service Hours per Route (time-series)",
+ speed_mph_ts = "Speed (time-series)",
+ ).tab_header(
+ title = "Service Hours and Speed",
+ subtitle = f"{current_quarter}"
+ ).tab_spanner(
+ label="Service",
+ columns=["daily_service_hours",
+ "service_hours_per_route", "daily_routes",
+ "service_hours_per_route_ts"]
+ ).tab_spanner(
+ label="Speed (mph)",
+ columns = ["speed_mph", "daily_vp_routes", "speed_mph_ts"]
+ ).tab_options(
+ container_width = "100%",
+ table_background_color="white",
+ table_body_hlines_style="none",
+ table_body_vlines_style="none",
+ heading_background_color="white",
+ column_labels_background_color="white",
+ row_group_background_color="white",
+ stub_background_color="white",
+ source_notes_background_color="white",
+ table_font_size="14px",
+ heading_align="center"
+ ).cols_hide(
+ ["year_quarter", "service_hours", "n_routes",
+ "n_dates", "n_vp_routes"]
+ ).sub_missing(
+ columns = ["speed_mph", "speed_mph_ts", "daily_vp_routes"],
+ missing_text = ""
+ ).tab_style(
+ style=style.text(weight="bold"),
+ locations=loc.body(rows=pl.col("category") == "Total")
+ ).tab_style(
+ style=style.text(
+ weight="normal", style="italic", color=get_hex("gray20")),
+ locations=loc.body(
+ rows=pl.col("category") == "On or Intersects SHN"),
+ ).cols_align(align="center")
+ .cols_align(align="left", columns="category")
+
+ )
+
+ return table
+```
+
+## Statewide Metrics
+
+```{python}
+plot_table(statewide_df)
+```
+
+## District Breakdown
+
+```{python}
+def district_table_specs(table, one_district, one_quarter):
+ table2 = (table
+ .cols_hide("caltrans_district")
+ .tab_header(
+ title = f"District {one_district}",
+ subtitle = f"Service Hours and Speed {one_quarter}")
+ )
+
+ return table2
+
+
+for i in sorted(district_df.caltrans_district.unique()):
+ table = plot_table(
+ district_df[district_df.caltrans_district==i])
+
+ table = district_table_specs(table, i, current_quarter)
+
+ display(table)
+```
+
+## Operator Breakdown
+
+Only the SHN subtotal (on SHN and parallel) is shown for each operator.
+
+```{python}
+
+def operator_table_specs(table):
+ table2 = (table
+ .cols_hide("caltrans_district")
+ .tab_style(
+ style=style.text(
+ weight="normal", style="normal", color=get_hex("black")),
+ locations=loc.body(
+ rows=pl.col("category") == "On or Intersects SHN"),
+ )
+ )
+
+ return table2
+
+table = plot_table(
+ operator_df[operator_df.category=="On or Intersects SHN"]
+)
+
+table = operator_table_specs(table)
+display(table)
+```
+
+
diff --git a/quarterly_performance_objective/report_charts.py b/quarterly_performance_objective/report_charts.py
deleted file mode 100644
index 0e35dfaf6..000000000
--- a/quarterly_performance_objective/report_charts.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import altair as alt
-import pandas as pd
-
-from typing import List, Literal
-
-from calitp_data_analysis import styleguide
-from calitp_data_analysis import calitp_color_palette as cp
-
-def base_bar(df: pd.DataFrame, x_col: str) -> alt.Chart:
- chart = (alt.Chart(df)
- .mark_bar()
- .encode(
- x=alt.X(f"{x_col}:N", title=f"{x_col.title()}")
- )
- )
- return chart
-
-
-def make_district_bar(df: pd.DataFrame, y_col: str) -> alt.Chart:
- """
- Make bar chart that's total service hours or
- average service hours by district.
- """
- y_title = f"{y_col.replace('_', ' ').title()}"
-
- if y_col == "service_hours":
- value_format = ",.0f"
- elif y_col == "avg_delay_hours":
- value_format = ",.1f"
- else:
- value_format = ",.1f"
-
- Y_MAX = df[y_col].max() * 1.1
-
- bar = base_bar(df, x_col="district")
-
- bar = (bar.encode(
- y=alt.Y(f"{y_col}:Q", title=f"{y_title}",
- scale=alt.Scale(domain=[0, Y_MAX]),
- axis=None
- ),
- color=alt.Color("district:N",
- scale=alt.Scale(
- range=cp.CALITP_CATEGORY_BRIGHT_COLORS
- ), legend=None
- )
- )
- )
- #https://stackoverflow.com/questions/54015250/altair-setting-constant-label-color-for-bar-chart
- text = (bar
- .mark_text(align="center", baseline="bottom",
- color="black", dy=-5
- )
- .encode(text=alt.Text(y_col, format=value_format),
- # Set color here, because encoding for mark_text gets
- # superseded by alt.Color
- color=alt.value("black"),
- tooltip=["district:N",
- alt.Tooltip(f"{y_col}:Q", format=",",
- title=f"{y_col.replace('_', ' '.title())}"
- )]
-
- )
- )
-
- chart = ((bar + text)
- .properties(title = {
- "text": f"{y_title} by District",
- "subtitle": "Routes on SHN"
- }).interactive()
- )
-
- return chart
-
-
-def configure_hconcat_charts(
- my_chart_list: List[alt.Chart],
- x_scale: Literal["independent", "shared"] = "independent",
- y_scale: Literal["independent", "shared"] = "independent",
- chart_title: str = "Title"
-) -> alt.Chart:
- """
- Horizontally concatenate altair charts,
- and also add top-level configurations after hconcat is done
- """
- combined = (alt.hconcat(*my_chart_list)
- .resolve_scale(x = x_scale, y = y_scale)
- )
-
-
- combined = (styleguide.apply_chart_config(combined)
- .properties(title = chart_title)
- .configure_axis(grid=False)
- .configure_view(strokeWidth=0)
- )
-
- return combined
\ No newline at end of file
diff --git a/quarterly_performance_objective/report_metrics.py b/quarterly_performance_objective/report_metrics.py
deleted file mode 100644
index 47632b233..000000000
--- a/quarterly_performance_objective/report_metrics.py
+++ /dev/null
@@ -1,249 +0,0 @@
-"""
-Functions to calculate summary stats for report.
-
-Two reports: current quarter, historical report.
-Since we need to the same dataset across notebooks,
-generate the various pieces needed in the report too.
-"""
-import geopandas as gpd
-import intake
-import pandas as pd
-
-from typing import Literal
-
-from shared_utils import portfolio_utils
-
-catalog = intake.open_catalog("*.yml")
-
-def aggregate_calculate_percent_and_average(
- df: pd.DataFrame,
- group_cols: list,
- sum_cols: list) -> pd.DataFrame:
- """
- Create columns with pct values.
- """
- agg_df = portfolio_utils.aggregate_by_geography(
- df,
- group_cols = group_cols,
- sum_cols = sum_cols,
- )
-
- for c in sum_cols:
- new_col = f"pct_{c}"
- agg_df[new_col] = (agg_df[c] / agg_df[c].sum()).round(3)
- agg_df[c] = agg_df[c].round(0)
-
- return agg_df
-
-#https://stackoverflow.com/questions/23482668/sorting-by-a-custom-list-in-pandas
-def sort_by_column(df: pd.DataFrame,
- col: str = "category",
- sort_key: list = ["on_shn", "intersects_shn", "other"]
- ) -> pd.DataFrame:
- # Custom sort order for categorical variable
- df = df.sort_values(
- col, key=lambda c: c.map(lambda e: sort_key.index(e)))
- return df
-
-
-def clean_up_category_values(df: pd.DataFrame) -> pd.DataFrame:
- category_values = {
- "on_shn": "On SHN",
- "intersects_shn": "Intersects SHN",
- "other": "Other"
- }
-
- df = df.assign(
- category = df.category.map(category_values)
- )
-
- return df
-
-
-def prep_data_for_report(analysis_date: str) -> gpd.GeoDataFrame:
- # https://stackoverflow.com/questions/69781678/intake-catalogue-level-parameters
-
- return catalog.routes_categorized_with_speed(
- analysis_date = analysis_date).read()
-
-def get_service_hours_summary_table(df: pd.DataFrame)-> pd.DataFrame:
- """
- Aggregate by parallel/on_shn/other category.
- Calculate number and pct of service hours, routes.
- """
-
- summary = aggregate_calculate_percent_and_average(
- df,
- group_cols = ["category"],
- sum_cols = ["service_hours", "unique_route"]
- ).astype({"unique_route": int})
-
- summary = sort_by_column(summary).pipe(clean_up_category_values)
-
- summary = summary.assign(
- service_hours_per_route = round(summary.service_hours /
- summary.unique_route, 2)
- )
-
- return summary
-
-
-def get_delay_summary_table(df: pd.DataFrame) -> pd.DataFrame:
- # Note: merge_delay both narrows down the dataset quite a bit
- delay_df = df[df.rt_sched_category=="schedule_and_vp"]
-
- delay_summary = aggregate_calculate_percent_and_average(
- delay_df,
- group_cols = ["category"],
- sum_cols = ["delay_hours", "unique_route"]
- ).astype({"unique_route": int})
-
- delay_summary = (sort_by_column(delay_summary)
- .pipe(clean_up_category_values)
- )
-
- delay_summary = delay_summary.assign(
- delay_hours_per_route = round(delay_summary.delay_hours /
- delay_summary.unique_route, 2)
- )
-
- return delay_summary
-
-
-def by_district_on_shn_breakdown(df: pd.DataFrame,
- sum_cols: list) -> pd.DataFrame:
- """
- Get service hours or delay hours by district, and
- add in percent and average metrics.
- """
- by_district = aggregate_calculate_percent_and_average(
- df[df.category=="on_shn"],
- group_cols = ["district"],
- sum_cols = sum_cols
- ).astype(int).sort_values("district").reset_index(drop=True)
-
- # Calculate average
- if "service_hours" in by_district.columns:
- numerator_col = "service_hours"
- elif "delay_hours" in by_district.columns:
- numerator_col = "delay_hours"
-
- by_district = by_district.assign(
- avg = by_district[numerator_col].divide(
- by_district.unique_route).round(1)
- ).rename(columns = {"avg": f"avg_{numerator_col}"})
-
- return by_district
-
-
-def quarterly_summary_long(analysis_date: str) -> pd.DataFrame:
- """
- For historical report, get a long df of service hours and delay hours
- summary tables.
- """
- df = prep_data_for_report(analysis_date)
-
- service_summary = get_service_hours_summary_table(df)
- delay_summary = (get_delay_summary_table(df)
- .rename(columns = {"unique_route": "delay_unique_route"})
- )
-
- # Make long
- service_value_vars = [c for c in service_summary.columns if c != "category"]
- delay_value_vars = [c for c in delay_summary.columns if c != "category"]
-
- service_long = pd.melt(
- service_summary,
- id_vars = "category",
- value_vars = service_value_vars,
- )
-
- delay_long = pd.melt(
- delay_summary,
- id_vars = "category",
- value_vars = delay_value_vars
- )
-
- # Concatenante
- summary = pd.concat([service_long, delay_long], axis=0)
- summary = summary.assign(
- service_date = analysis_date
- )
-
- return summary
-
-
-def district_breakdown_long(analysis_date: str) -> pd.DataFrame:
- """
- For historical report, get a long df of service hours and delay hours
- summary tables.
- """
- df = prep_data_for_report(analysis_date)
-
- by_district_summary = by_district_on_shn_breakdown(
- df, sum_cols = ["service_hours", "unique_route"])
-
- by_district_delay = by_district_on_shn_breakdown(
- df, sum_cols = ["delay_hours", "unique_route"]
- ).rename(columns = {"unique_route": "delay_unique_route"})
-
- # Make long
- service_value_vars = [c for c in by_district_summary.columns if c != 'district']
- delay_value_vars = [c for c in by_district_delay.columns if c != 'district']
-
- service_long = pd.melt(
- by_district_summary,
- id_vars = "district",
- value_vars = service_value_vars,
- )
-
- delay_long = pd.melt(
- by_district_delay,
- id_vars = "district",
- value_vars = delay_value_vars
- )
-
- # Concatenante
- summary = pd.concat([service_long, delay_long], axis=0)
- summary = summary.assign(
- service_date = analysis_date
- )
-
- return summary
-
-
-def concatenate_summary_across_dates(
- rt_dates_dict: dict,
- summary_dataset: Literal["summary", "district"],
-) -> pd.DataFrame:
- """
- Loop across dates available for quarterly performance metrics,
- and concatenate into 1 long df.
- """
- df = pd.DataFrame()
-
- rt_dates_reversed = {value: key for key, value in rt_dates_dict.items()}
-
- for date, quarter in rt_dates_reversed.items():
- if summary_dataset == "summary":
- one_quarter = quarterly_summary_long(date)
-
- elif summary_dataset == "district":
- one_quarter = district_breakdown_long(date)
- df = pd.concat([df, one_quarter], axis=0)
-
- df = df.assign(
- year_quarter = df.service_date.map(rt_dates_reversed)
- )
-
- df = df.assign(
- quarter = df.year_quarter.str.split('_', expand=True)[0],
- year = df.year_quarter.str.split('_', expand=True)[1].astype(int),
- )
-
- # Get it to be year first
- df = df.assign(
- year_quarter = df.year.astype(str) + ' ' + df.quarter
- )
-
- return df
\ No newline at end of file
diff --git a/quarterly_performance_objective/update_vars.py b/quarterly_performance_objective/update_vars.py
index 3a341821d..b6bf21fa3 100644
--- a/quarterly_performance_objective/update_vars.py
+++ b/quarterly_performance_objective/update_vars.py
@@ -7,5 +7,6 @@
COMPILED_CACHED_GCS = GTFS_DATA_DICT.gcs_paths.COMPILED_CACHED_VIEWS
SEGMENT_GCS = GTFS_DATA_DICT.gcs_paths.SEGMENT_GCS
-CURRENT_QUARTER = "Q2_2024"
-ANALYSIS_DATE = rt_dates.PMAC[CURRENT_QUARTER]
\ No newline at end of file
+analysis_date = rt_dates.DATES["jun2024"]
+#CURRENT_QUARTER = "Q2_2024"
+#ANALYSIS_DATE = rt_dates.PMAC[CURRENT_QUARTER]
\ No newline at end of file
diff --git a/rt_segment_speeds/segment_speed_utils/parallel_corridors.py b/rt_segment_speeds/segment_speed_utils/parallel_corridors.py
index f739ceb8f..6a4cad464 100644
--- a/rt_segment_speeds/segment_speed_utils/parallel_corridors.py
+++ b/rt_segment_speeds/segment_speed_utils/parallel_corridors.py
@@ -10,19 +10,26 @@
Usually, this can be achieved by merging `trips` and `shapes`.
"""
import geopandas as gpd
+import gcsfs
import pandas as pd
from calitp_data_analysis import geography_utils, utils
from segment_speed_utils import gtfs_schedule_wrangling, helpers
from shared_utils import catalog_utils
+BUS_SERVICE_GCS = "gs://calitp-analytics-data/data-analyses/bus_service_increase/"
+fs = gcsfs.GCSFileSystem()
+hwy_group_cols = ["Route", "County", "District", "RouteType"]
+
def process_transit_routes(analysis_date: str) -> gpd.GeoDataFrame:
"""
For each route, select the longest shape for each route
to overlay with SHN.
Also count how many routes there are for each operator.
"""
- longest_shape = gtfs_schedule_wrangling.longest_shape_by_route_direction(analysis_date)
+ longest_shape = gtfs_schedule_wrangling.longest_shape_by_route_direction(
+ analysis_date
+ ).pipe(helpers.remove_shapes_outside_ca)
gdf = longest_shape.assign(
total_routes = longest_shape.groupby("feed_key").route_key.transform("nunique")
@@ -58,7 +65,8 @@ def process_highways(
direction_cols = ["NB", "SB", "EB", "WB"]
df = (gpd.read_parquet(SHN_FILE)
- .to_crs(geography_utils.CA_StatePlane))
+ .to_crs(geography_utils.CA_StatePlane)
+ )
# Get dummies for direction
# Can make data wide instead of long
@@ -97,14 +105,17 @@ def overlay_transit_to_highways(
Returns: geopandas.GeoDataFrame, with geometry column reflecting
the areas of intersection.
- """
- hwy_group_cols = ["Route", "County", "District", "RouteType"]
-
+ """
# Can pass a different buffer zone to determine parallel corridors
- highways = process_highways(
- group_cols = hwy_group_cols,
- buffer_feet = hwy_buffer_feet
- )
+ HWY_FILE = f"{BUS_SERVICE_GCS}highways_buffer{hwy_buffer_feet}.parquet"
+
+ if fs.exists(HWY_FILE):
+ highways = gpd.read_parquet(HWY_FILE)
+ else:
+ highways = process_highways(
+ group_cols = hwy_group_cols,
+ buffer_feet = hwy_buffer_feet
+ )
transit_routes = process_transit_routes(analysis_date)
# Overlay
@@ -197,4 +208,37 @@ def routes_by_on_shn_parallel_categories(
category = df[category_cols].idxmax(axis=1)
)[keep_cols]
- return df2
\ No newline at end of file
+ return df2
+
+
+if __name__ == "__main__":
+
+ SHN_HWY_BUFFER_FEET = 50
+ PARALLEL_HWY_BUFFER_FEET = int(geography_utils.FEET_PER_MI * 0.5)
+
+ highways_shn_buffer = process_highways(
+ group_cols = hwy_group_cols,
+ buffer_feet = SHN_HWY_BUFFER_FEET
+ )
+
+ utils.geoparquet_gcs_export(
+ highways_shn_buffer,
+ BUS_SERVICE_GCS,
+ f"highways_buffer{SHN_HWY_BUFFER_FEET}"
+ )
+
+ print(f"exported highways_buffer{SHN_HWY_BUFFER_FEET}")
+ del highways_shn_buffer
+
+ highways_parallel_buffer = process_highways(
+ group_cols = hwy_group_cols,
+ buffer_feet = PARALLEL_HWY_BUFFER_FEET
+ )
+
+ utils.geoparquet_gcs_export(
+ highways_parallel_buffer,
+ BUS_SERVICE_GCS,
+ f"highways_buffer{PARALLEL_HWY_BUFFER_FEET}"
+ )
+
+ print(f"exported highways_buffer{PARALLEL_HWY_BUFFER_FEET}")
\ No newline at end of file