{ "cells": [ { "cell_type": "code", "execution_count": 25, "id": "cae65a79", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loaded 635,995 rows across 57 rides\n", "Time range: 2024-05-15 12:23:34.954315-04:00 → 2026-05-26 18:00:20.956708-04:00\n" ] } ], "source": [ "import os\n", "from pathlib import Path\n", "\n", "import pandas as pd\n", "from dotenv import load_dotenv\n", "from sqlalchemy import create_engine, text\n", "\n", "load_dotenv(Path.cwd() / \".env\")\n", "\n", "engine = create_engine(\n", " \"postgresql+psycopg2://{user}:{pw}@{host}:{port}/{db}\".format(\n", " user=os.environ[\"PG_USER\"],\n", " pw=os.environ[\"PG_PASSWORD\"],\n", " host=os.environ[\"PG_HOST\"],\n", " port=os.environ[\"PG_PORT\"],\n", " db=os.environ[\"PG_DB\"],\n", " )\n", ")\n", "\n", "# LZ_attractions_io carries one row per (timestamp, ride) and uses the\n", "# Item _id that matches both ride_master and LZ_attractions_io_poi.\n", "# (LZ_attractions_io_queuetimes is a same-data duplicate keyed by QueueLine\n", "# id, which doesn't join to anything — don't use it.)\n", "QUERY = text(\n", " \"\"\"\n", " SELECT a.time_stamp,\n", " a._id AS ride_id,\n", " r.ride AS ride_name,\n", " r.capacity,\n", " r.duration,\n", " a.queue_time AS queue_time_sec\n", " FROM knoebels.\"LZ_attractions_io\" a\n", " JOIN knoebels.ride_master r\n", " ON r.attractions_dot_io_id = a._id\n", " WHERE a.queue_time IS NOT NULL\n", " ORDER BY a.time_stamp\n", " \"\"\"\n", ")\n", "\n", "df = pd.read_sql(QUERY, engine)\n", "df[\"queue_time_min\"] = df[\"queue_time_sec\"] / 60.0\n", "\n", "# Scraper writes naive timestamps that represent America/New_York wall clock.\n", "# Localize so downstream time-of-day / day-of-week features are unambiguous.\n", "df[\"time_stamp\"] = (\n", " pd.to_datetime(df[\"time_stamp\"])\n", " .dt.tz_localize(\"America/New_York\", ambiguous=\"NaT\", nonexistent=\"NaT\")\n", ")\n", "df = df.dropna(subset=[\"time_stamp\"]).reset_index(drop=True)\n", "\n", "\n", "def get_ride_time_matrix(df: pd.DataFrame, freq: str = \"5min\") -> pd.DataFrame:\n", " \"\"\"Pivot the long df into a (timestamp x ride_name) matrix of queue times.\n", "\n", " Rows are snapped to `freq` bins so all rides share a common index.\n", " Values are mean queue time in minutes within each bin.\n", " \"\"\"\n", " snapped = df.assign(ts=df[\"time_stamp\"].dt.floor(freq))\n", " return (\n", " snapped.pivot_table(\n", " index=\"ts\",\n", " columns=\"ride_name\",\n", " values=\"queue_time_min\",\n", " aggfunc=\"mean\",\n", " )\n", " .sort_index().ffill().fillna(0) # Impute missing values: forward fill first (assume line stayed the same), then fill remaining NaNs (like morning startup) with 0\n", " )\n", "\n", "\n", "print(f\"Loaded {len(df):,} rows across {df['ride_name'].nunique()} rides\")\n", "print(f\"Time range: {df['time_stamp'].min()} → {df['time_stamp'].max()}\")\n" ] }, { "cell_type": "code", "execution_count": 26, "id": "261c73fd", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| ride_name | \n", "antique_cars | \n", "balloon_race | \n", "black_diamond | \n", "bumper_cars | \n", "cosmotron | \n", "downdraft | \n", "fandango | \n", "flyer | \n", "flying_tigers | \n", "flying_turns | \n", "... | \n", "spanish_bambini | \n", "stratosfear | \n", "super_round-up | \n", "tea_cups | \n", "tilt-a-whirl | \n", "tornado | \n", "tumbling_timbers | \n", "twister | \n", "umbrella_ride | \n", "whipper | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ts | \n", "\n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " |
| 2024-05-15 12:20:00-04:00 | \n", "-0.085045 | \n", "1.45794 | \n", "-0.063402 | \n", "-0.214064 | \n", "0.799599 | \n", "1.160288 | \n", "-2.167269 | \n", "0.171589 | \n", "2.954566 | \n", "-0.970477 | \n", "... | \n", "2.494992 | \n", "0.589849 | \n", "-5.171517 | \n", "1.110184 | \n", "-1.663817 | \n", "1.064078 | \n", "5.035897 | \n", "-0.281509 | \n", "1.255617 | \n", "1.045173 | \n", "
| 2024-05-15 12:55:00-04:00 | \n", "-0.085045 | \n", "1.45794 | \n", "-0.063402 | \n", "-0.214064 | \n", "0.799599 | \n", "1.160288 | \n", "-2.167269 | \n", "0.171589 | \n", "2.954566 | \n", "-0.970477 | \n", "... | \n", "2.494992 | \n", "0.589849 | \n", "-5.171517 | \n", "1.110184 | \n", "-1.663817 | \n", "1.064078 | \n", "5.035897 | \n", "-0.281509 | \n", "1.255617 | \n", "1.045173 | \n", "
| 2024-05-15 13:00:00-04:00 | \n", "-0.085045 | \n", "1.45794 | \n", "-0.063402 | \n", "-0.214064 | \n", "0.799599 | \n", "1.160288 | \n", "-2.167269 | \n", "0.171589 | \n", "2.954566 | \n", "-0.970477 | \n", "... | \n", "2.494992 | \n", "0.589849 | \n", "-5.171517 | \n", "1.110184 | \n", "-1.663817 | \n", "1.064078 | \n", "5.035897 | \n", "-0.281509 | \n", "1.255617 | \n", "1.045173 | \n", "
| 2024-05-15 13:15:00-04:00 | \n", "-0.085045 | \n", "1.45794 | \n", "-0.063402 | \n", "-0.214064 | \n", "0.799599 | \n", "1.160288 | \n", "-2.167269 | \n", "0.171589 | \n", "2.954566 | \n", "-0.970477 | \n", "... | \n", "-0.361261 | \n", "0.589849 | \n", "-5.171517 | \n", "1.110184 | \n", "-1.663817 | \n", "1.064078 | \n", "5.035897 | \n", "-0.281509 | \n", "1.255617 | \n", "1.045173 | \n", "
| 2024-05-15 13:30:00-04:00 | \n", "-0.085045 | \n", "1.45794 | \n", "-0.063402 | \n", "-0.214064 | \n", "0.799599 | \n", "1.160288 | \n", "-2.167269 | \n", "0.171589 | \n", "2.954566 | \n", "-0.970477 | \n", "... | \n", "-0.361261 | \n", "0.589849 | \n", "-5.171517 | \n", "1.110184 | \n", "-1.663817 | \n", "1.064078 | \n", "5.035897 | \n", "-0.281509 | \n", "1.255617 | \n", "1.045173 | \n", "
5 rows × 56 columns
\n", "