This document is a comprehensive, deep-dive explanation of exactly what every major component and logic block does across the entire TaxiIQ project.
This file is the entry point for the API server.
- Lines 5-9: Initializes
app = FastAPI(...). This creates the web server. - Lines 13-19: Adds
CORSMiddleware. This is crucial. It allows the React frontend (running on port5173) to make HTTP requests to the backend (port8000) without being blocked by the browser's security policies. - Line 20:
GZipMiddlewarecompresses the API responses so large data payloads (like mapping data) load faster over the network. - Lines 22-28:
app.include_router(...). Instead of putting hundreds of API routes in one file, FastAPI allows "mounting" separate files. For example, all prediction routes are mapped to/api.
This file builds the "brain" of the app. It downloads raw data, cleans it, engineers features, and trains the models.
- Lines 18-33: Defines URLs for Parquet files hosted by NYC TLC (Taxi & Limousine Commission) for both Yellow and Green taxis across 2025-2026.
- Lines 35-44: Iterates through the list, downloading the files if they don't already exist locally in the
data/folder.
- Lines 49-51: Loads a lookup CSV. Flags zones in Manhattan (
is_manhattan) or Airports (is_airportvia string matching on "JFK" or "LaGuardia"). - Lines 55-64: Merges Yellow and Green datasets. It standardizes column names (Green taxis use
lpep_, Yellow usetpep_). - Lines 73-91 (Robust Cleaning):
- Filters out anomalies:
trip_durationmust be > 1 min and < 120 mins. trip_distancemust be > 0.1 miles.- Drops rows with missing Location IDs.
- Filters out anomalies:
- Lines 94-101 (Feature Engineering):
- Extracts the hour and day of the week from the timestamp.
- Creates
is_rush_hourif the hour is 7,8,9 AM or 4,5,6,7 PM. - Calculates
speed = distance / duration. Filters out speeds > 80 mph.
- Lines 109-118 (Volatility & Spikes):
- Groups trips by
route(Pickup → Dropoff). - Calculates
corridor_volatility= Standard Deviation of duration / Mean duration. High volatility means the route is unpredictable. - Marks
is_price_spikeif the fare was 1.5x higher than the route's average.
- Groups trips by
- Lines 120-124: Calculates
delay_ratioby comparing a specific trip's duration against the overall historical average for that starting zone. - Lines 127-149: Saves the aggregated data as
zone_metrics.parquetandcorridor_metrics.parquetfor instant loading in the API.
- Lines 154-157: Defines
FEATS(the 12 inputs to the model). Splits data 80% Train / 20% Test. - Lines 160-172: Loops through three Scikit-Learn models (
LinearRegression,RandomForestRegressor,GradientBoostingRegressor). Fits them to the training data. - Line 172: Uses
joblib.dump()to save the trained mathematical models into.pklfiles inmodels_saved/.
- Groups similar zones and corridors using KMeans clustering so the UI can display human-readable labels.
- Lines 184-186: Zones are split into 4 clusters. Metadata maps these to names like
"High Demand Hub"or"Slow / High Congestion". - Lines 190-192: Corridors are split into 3 clusters (e.g.,
"Highly Volatile"vs"Reliable / Fast").
This file serves the live predictions to the user.
- Lines 12-35: At startup, it loads the 3
.pklmodels and the KMeans scalers from disk into memory. - Lines 52-70 (
build_features): Takes the incoming JSON request from the user and turns it into an exact array of 12 numbers that the ML model expects (matching the training phase). - Lines 73-121 (
predict_eta):- Predicts the ETA using all three models individually.
- Ensemble Formula (Line 84):
eta = (rf_pred * 0.5 + gbm_pred * 0.4 + lr_pred * 0.1). Random Forest gets 50% trust because it's best at outliers, GBM gets 40% for precision, and Linear Regression gets 10% to keep things grounded. - Line 89: Calculates
p90(worst-case ETA) by scaling the base ETA upward based on thecorridor_volatilityfactor. - Lines 100-109: Passes the ETA and Volatility into the KMeans model to return a text insight, like "Congested Corridor".
- Lines 124-163 (
estimate_price):- Lines 126-128: Sets base pricing variables ($3 base, $1.75/mi, $0.35/min). Uses a
1.25xcongestion multiplier ifis_rushis true. - Lines 135-139: Calculates
expected_priceand a range (band_mintoband_max). Theband_maxincreases if the route has high volatility. - Lines 142-154: Creates a
price_driversarray. If the logic detects an airport trip or a rush hour trip, it pushes explanations into this array so the UI can tell the user why the price is what it is.
- Lines 126-128: Sets base pricing variables ($3 base, $1.75/mi, $0.35/min). Uses a
- Lines 24-48 (
/zone-stats): Groups the pre-computed parquet data by zone. Maps the KMeans cluster IDs back to string names (e.g., ID 0 -> "High Demand Hub"). - Lines 235-309 (
/zone-map-data): This is critical for the Leaflet Map. It contains a hardcoded dictionary (NYC_ZONE_COORDS) of Lat/Lng coordinates. It merges the analytical data (trips per hour, delay ratio) with these coordinates so the frontend map can plot markers.
- Lines 28-62 (
/nearby-price): Implements "Zone Discovery". It filters the zone database looking foravg_price <= budget. It also uses a Haversine formula (viacalculate_distance) to tell the user the physical walking distance to a cheaper alternate zone.
Handles persistent storage for user-submitted trip feedback.
- Lines 13-19: Initializes a PostgreSQL engine with connection pooling (
pool_size=10). This prevents the server from crashing if 100 users submit feedback at once. - Lines 25-58 (
FeedbackRecord): An SQLAlchemy ORM model representing thetrip_feedbacktable. It stores exactly what the API predicted vs what the user actually experienced, allowing for future model retraining. - Lines 102-121 (
get_db): A dependency function. When an API route needs the database, it calls this. It includes awhileloop that retries connection 3 times in case the database temporarily goes offline.
- Lines 22-51 (
useEffect): When the page loads, it fetches the list of NYC zones from the API and populates the<datalist>elements so the user gets an autocomplete dropdown. It also auto-fills the current date and time. - Lines 53-83 (
handleSubmit): Triggered when the user clicks submit. It validates thatactualPriceisn't empty, sets a loading state, builds a JSON payload, and posts it to the API. If successful, it flips a boolean to show the Success screen. - Lines 108-244 (UI Rendering): Uses inline styles heavily. It reads
darkModefrom the global React Context to switch between dark borders (#374151) and light backgrounds (#FFFFFF). It maps through[1, 2, 3, 4, 5]to render interactive star emojis for therating.
- Lines 24-30 (
useEffect): Implements "Live" polling. It callssetInterval(() => fetchData(), 30000), which automatically pings the backend for fresh corridor stats every 30 seconds without the user refreshing. - Lines 43-55 (
useMemoHook): Handles the search and sorting efficiently. It filters the corridors array bysearchTerm. IfsortByis set toavg_speed, it runs a javascript array.sort()to re-order the cards. - Lines 172-282 (The Card Engine): Maps over the sorted data to draw cards.
- Calculates
popularity = (trip_count / maxTrips) * 100and renders it as a colored CSS gradient progress bar. - Line 191: Calls
getReliabilityColor(). If the backend flagged the cluster as "Reliable", it applies a green#10B981theme to the card's badge. If "Unstable", it applies red#EF4444. - Lines 264-280: Renders a "View Live Tracking" button. Clicking this triggers
handleViewLive, which saves the pickup/dropoff into browserlocalStorageand navigates to the map page.
- Calculates