{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multivariate Time Series\n",
    "\n",
    "When you have multiple signals that share the same timestamps — like wind, solar, and hydro power — use `TimeSeriesTable` (also aliased as `MultivariateTimeSeries`). It stores per-column metadata and a 2D numpy array of values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:41.910911Z",
     "iopub.status.busy": "2026-03-01T13:36:41.910806Z",
     "iopub.status.idle": "2026-03-01T13:36:41.992138Z",
     "shell.execute_reply": "2026-03-01T13:36:41.991760Z"
    }
   },
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'timedatamodel'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mModuleNotFoundError\u001b[39m                       Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mdatetime\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m datetime, timedelta, timezone\n\u001b[32m      3\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mnumpy\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mnp\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mtimedatamodel\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mtdm\u001b[39;00m\n\u001b[32m      7\u001b[39m base = datetime(\u001b[32m2024\u001b[39m, \u001b[32m1\u001b[39m, \u001b[32m15\u001b[39m, tzinfo=timezone.utc)\n\u001b[32m      8\u001b[39m timestamps = [base + timedelta(hours=i) \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[32m24\u001b[39m)]\n",
      "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'timedatamodel'"
     ]
    }
   ],
   "source": [
    "from datetime import datetime, timedelta, timezone\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "import timedatamodel as tdm\n",
    "\n",
    "base = datetime(2024, 1, 15, tzinfo=timezone.utc)\n",
    "timestamps = [base + timedelta(hours=i) for i in range(24)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a TimeSeriesTable\n",
    "\n",
    "Pass a 2D array (rows = timestamps, columns = variables) along with per-column metadata."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:41.993553Z",
     "iopub.status.busy": "2026-03-01T13:36:41.993471Z",
     "iopub.status.idle": "2026-03-01T13:36:42.009044Z",
     "shell.execute_reply": "2026-03-01T13:36:42.008610Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'tdm' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m      6\u001b[39m hydro = \u001b[32m50\u001b[39m + rng.normal(\u001b[32m0\u001b[39m, \u001b[32m3\u001b[39m, \u001b[32m24\u001b[39m)\n\u001b[32m      8\u001b[39m values = np.column_stack([wind, solar, hydro])\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m table = \u001b[43mtdm\u001b[49m.TimeSeriesTable(\n\u001b[32m     11\u001b[39m     tdm.Frequency.PT1H,\n\u001b[32m     12\u001b[39m     timezone=\u001b[33m\"\u001b[39m\u001b[33mUTC\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m     13\u001b[39m     timestamps=timestamps,\n\u001b[32m     14\u001b[39m     values=values,\n\u001b[32m     15\u001b[39m     names=[\u001b[33m\"\u001b[39m\u001b[33mwind\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33msolar\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mhydro\u001b[39m\u001b[33m\"\u001b[39m],\n\u001b[32m     16\u001b[39m     units=[\u001b[33m\"\u001b[39m\u001b[33mMW\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mMW\u001b[39m\u001b[33m\"\u001b[39m, \u001b[33m\"\u001b[39m\u001b[33mMW\u001b[39m\u001b[33m\"\u001b[39m],\n\u001b[32m     17\u001b[39m     data_types=[tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT],\n\u001b[32m     18\u001b[39m )\n\u001b[32m     19\u001b[39m table\n",
      "\u001b[31mNameError\u001b[39m: name 'tdm' is not defined"
     ]
    }
   ],
   "source": [
    "rng = np.random.default_rng(42)\n",
    "\n",
    "wind = 80 + 40 * np.sin(np.linspace(0, 2 * np.pi, 24)) + rng.normal(0, 5, 24)\n",
    "solar = np.clip(60 * np.sin(np.linspace(-0.5, np.pi + 0.5, 24)), 0, None) + rng.normal(0, 2, 24)\n",
    "solar = np.clip(solar, 0, None)\n",
    "hydro = 50 + rng.normal(0, 3, 24)\n",
    "\n",
    "values = np.column_stack([wind, solar, hydro])\n",
    "\n",
    "table = tdm.TimeSeriesTable(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timezone=\"UTC\",\n",
    "    timestamps=timestamps,\n",
    "    values=values,\n",
    "    names=[\"wind\", \"solar\", \"hydro\"],\n",
    "    units=[\"MW\", \"MW\", \"MW\"],\n",
    "    data_types=[tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT],\n",
    ")\n",
    "table"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inspecting table properties"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.010117Z",
     "iopub.status.busy": "2026-03-01T13:36:42.010044Z",
     "iopub.status.idle": "2026-03-01T13:36:42.017321Z",
     "shell.execute_reply": "2026-03-01T13:36:42.016948Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mColumns:     \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mtable\u001b[49m.column_names\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m      2\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mShape:       (\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(table)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtable.n_columns\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m)\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      3\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mBegin:       \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtable.begin\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "print(f\"Columns:     {table.column_names}\")\n",
    "print(f\"Shape:       ({len(table)}, {table.n_columns})\")\n",
    "print(f\"Begin:       {table.begin}\")\n",
    "print(f\"End:         {table.end}\")\n",
    "print(f\"Has missing: {table.has_missing}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Selecting a single column\n",
    "\n",
    "`select_column()` extracts one column as a univariate `TimeSeries`, carrying over its metadata."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.018321Z",
     "iopub.status.busy": "2026-03-01T13:36:42.018249Z",
     "iopub.status.idle": "2026-03-01T13:36:42.025011Z",
     "shell.execute_reply": "2026-03-01T13:36:42.024616Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m ts_wind = \u001b[43mtable\u001b[49m.select_column(\u001b[33m\"\u001b[39m\u001b[33mwind\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      2\u001b[39m ts_wind\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "ts_wind = table.select_column(\"wind\")\n",
    "ts_wind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.025941Z",
     "iopub.status.busy": "2026-03-01T13:36:42.025878Z",
     "iopub.status.idle": "2026-03-01T13:36:42.032719Z",
     "shell.execute_reply": "2026-03-01T13:36:42.032380Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m ts_solar = \u001b[43mtable\u001b[49m.select_column(\u001b[33m\"\u001b[39m\u001b[33msolar\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      2\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mName: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts_solar.name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, Unit: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts_solar.unit\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, Data type: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts_solar.data_type\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "ts_solar = table.select_column(\"solar\")\n",
    "print(f\"Name: {ts_solar.name}, Unit: {ts_solar.unit}, Data type: {ts_solar.data_type}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Merging univariate series into a table\n",
    "\n",
    "`TimeSeries.merge()` is the reverse operation — combine several univariate series that share the same timestamps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.033740Z",
     "iopub.status.busy": "2026-03-01T13:36:42.033686Z",
     "iopub.status.idle": "2026-03-01T13:36:42.047143Z",
     "shell.execute_reply": "2026-03-01T13:36:42.046814Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'tdm' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m ts_a = \u001b[43mtdm\u001b[49m.TimeSeries(\n\u001b[32m      2\u001b[39m     tdm.Frequency.PT1H,\n\u001b[32m      3\u001b[39m     timestamps=timestamps,\n\u001b[32m      4\u001b[39m     values=wind.tolist(),\n\u001b[32m      5\u001b[39m     name=\u001b[33m\"\u001b[39m\u001b[33mwind\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m      6\u001b[39m     unit=\u001b[33m\"\u001b[39m\u001b[33mMW\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m      7\u001b[39m )\n\u001b[32m      8\u001b[39m ts_b = tdm.TimeSeries(\n\u001b[32m      9\u001b[39m     tdm.Frequency.PT1H,\n\u001b[32m     10\u001b[39m     timestamps=timestamps,\n\u001b[32m   (...)\u001b[39m\u001b[32m     13\u001b[39m     unit=\u001b[33m\"\u001b[39m\u001b[33mMW\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m     14\u001b[39m )\n\u001b[32m     15\u001b[39m ts_c = tdm.TimeSeries(\n\u001b[32m     16\u001b[39m     tdm.Frequency.PT1H,\n\u001b[32m     17\u001b[39m     timestamps=timestamps,\n\u001b[32m   (...)\u001b[39m\u001b[32m     20\u001b[39m     unit=\u001b[33m\"\u001b[39m\u001b[33mMW\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m     21\u001b[39m )\n",
      "\u001b[31mNameError\u001b[39m: name 'tdm' is not defined"
     ]
    }
   ],
   "source": [
    "ts_a = tdm.TimeSeries(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timestamps=timestamps,\n",
    "    values=wind.tolist(),\n",
    "    name=\"wind\",\n",
    "    unit=\"MW\",\n",
    ")\n",
    "ts_b = tdm.TimeSeries(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timestamps=timestamps,\n",
    "    values=solar.tolist(),\n",
    "    name=\"solar\",\n",
    "    unit=\"MW\",\n",
    ")\n",
    "ts_c = tdm.TimeSeries(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timestamps=timestamps,\n",
    "    values=hydro.tolist(),\n",
    "    name=\"hydro\",\n",
    "    unit=\"MW\",\n",
    ")\n",
    "\n",
    "merged = tdm.TimeSeries.merge([ts_a, ts_b, ts_c])\n",
    "merged"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Decomposing a table back to univariate series"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.048248Z",
     "iopub.status.busy": "2026-03-01T13:36:42.048187Z",
     "iopub.status.idle": "2026-03-01T13:36:42.054902Z",
     "shell.execute_reply": "2026-03-01T13:36:42.054543Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'merged' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m univariate_list = \u001b[43mmerged\u001b[49m.to_univariate_list()\n\u001b[32m      2\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m ts \u001b[38;5;129;01min\u001b[39;00m univariate_list:\n\u001b[32m      3\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts.name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(ts)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m points, unit=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts.unit\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'merged' is not defined"
     ]
    }
   ],
   "source": [
    "univariate_list = merged.to_univariate_list()\n",
    "for ts in univariate_list:\n",
    "    print(f\"{ts.name}: {len(ts)} points, unit={ts.unit}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Table arithmetic\n",
    "\n",
    "Scalar arithmetic works on all columns simultaneously."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.055815Z",
     "iopub.status.busy": "2026-03-01T13:36:42.055761Z",
     "iopub.status.idle": "2026-03-01T13:36:42.062136Z",
     "shell.execute_reply": "2026-03-01T13:36:42.061708Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m table_gw = \u001b[43mtable\u001b[49m * \u001b[32m0.001\u001b[39m\n\u001b[32m      2\u001b[39m table_gw.head(\u001b[32m5\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "table_gw = table * 0.001\n",
    "table_gw.head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Iteration and indexing\n",
    "\n",
    "Each row is a `(timestamp, [values...])` tuple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.063052Z",
     "iopub.status.busy": "2026-03-01T13:36:42.062998Z",
     "iopub.status.idle": "2026-03-01T13:36:42.069416Z",
     "shell.execute_reply": "2026-03-01T13:36:42.069034Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m ts_val, row_vals = \u001b[43mtable\u001b[49m[\u001b[32m0\u001b[39m]\n\u001b[32m      2\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mTimestamp: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts_val\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m      3\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mValues:    \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mrow_vals\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "ts_val, row_vals = table[0]\n",
    "print(f\"Timestamp: {ts_val}\")\n",
    "print(f\"Values:    {row_vals}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.070305Z",
     "iopub.status.busy": "2026-03-01T13:36:42.070256Z",
     "iopub.status.idle": "2026-03-01T13:36:42.076933Z",
     "shell.execute_reply": "2026-03-01T13:36:42.076523Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[10]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m ts_val, row_vals \u001b[38;5;129;01min\u001b[39;00m \u001b[43mtable\u001b[49m.head(\u001b[32m4\u001b[39m):\n\u001b[32m      2\u001b[39m     wind_mw, solar_mw, hydro_mw = row_vals\n\u001b[32m      3\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mts_val\u001b[38;5;132;01m:\u001b[39;00m\u001b[33m%Y-%m-%d %H:%M\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m  wind=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mwind_mw\u001b[38;5;132;01m:\u001b[39;00m\u001b[33m6.1f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m  solar=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00msolar_mw\u001b[38;5;132;01m:\u001b[39;00m\u001b[33m5.1f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m  hydro=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mhydro_mw\u001b[38;5;132;01m:\u001b[39;00m\u001b[33m5.1f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "for ts_val, row_vals in table.head(4):\n",
    "    wind_mw, solar_mw, hydro_mw = row_vals\n",
    "    print(f\"{ts_val:%Y-%m-%d %H:%M}  wind={wind_mw:6.1f}  solar={solar_mw:5.1f}  hydro={hydro_mw:5.1f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Conversion to pandas DataFrame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-01T13:36:42.077826Z",
     "iopub.status.busy": "2026-03-01T13:36:42.077766Z",
     "iopub.status.idle": "2026-03-01T13:36:42.084110Z",
     "shell.execute_reply": "2026-03-01T13:36:42.083726Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'table' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m df = \u001b[43mtable\u001b[49m.to_pandas_dataframe()\n\u001b[32m      2\u001b[39m df.head()\n",
      "\u001b[31mNameError\u001b[39m: name 'table' is not defined"
     ]
    }
   ],
   "source": [
    "df = table.to_pandas_dataframe()\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "- `TimeSeriesTable` stores multiple aligned columns with per-column metadata\n",
    "- `select_column()` extracts a single column as a `TimeSeries`\n",
    "- `TimeSeries.merge()` combines univariate series into a table\n",
    "- `to_univariate_list()` decomposes a table back to individual series\n",
    "- Scalar arithmetic applies across all columns\n",
    "\n",
    "Next up: **nb_06** covers `TimeSeriesCube` and `TimeSeriesCollection` for higher-dimensional data."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.14.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
