{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# NumPy and Pandas Transforms\n",
    "\n",
    "TimeDataModel provides clean patterns for transforming time series data using numpy and pandas. Every `TimeSeriesList` and `TimeSeriesTable` exposes `.arr` (numpy array) and `.df` (pandas DataFrame) properties, plus dedicated methods for writing results back. This keeps your domain model structured while letting you leverage the full scientific Python ecosystem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.735901Z",
     "iopub.status.busy": "2026-03-04T19:11:06.735748Z",
     "iopub.status.idle": "2026-03-04T19:11:06.772487Z",
     "shell.execute_reply": "2026-03-04T19:11:06.771956Z"
    }
   },
   "outputs": [],
   "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)]\n",
    "\n",
    "ts = tdm.TimeSeriesList(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timestamps=timestamps,\n",
    "    values=[\n",
    "        120.0, 115.0, 108.0, 105.0, 102.0, 100.0,\n",
    "        110.0, 135.0, 160.0, 175.0, 180.0, 178.0,\n",
    "        172.0, 170.0, 168.0, 165.0, 175.0, 190.0,\n",
    "        200.0, 195.0, 180.0, 165.0, 145.0, 130.0,\n",
    "    ],\n",
    "    name=\"power\",\n",
    "    unit=\"MW\",\n",
    "    data_type=tdm.DataType.OBSERVATION,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The `.arr` and `.df` properties\n",
    "\n",
    "Every `TimeSeriesList` has two shorthand properties for quick access to the underlying data:\n",
    "- `ts.arr` — returns a numpy `ndarray` (same as `ts.to_numpy()`)\n",
    "- `ts.df` — returns a pandas `DataFrame` (same as `ts.to_pandas_dataframe()`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.773754Z",
     "iopub.status.busy": "2026-03-04T19:11:06.773643Z",
     "iopub.status.idle": "2026-03-04T19:11:06.776594Z",
     "shell.execute_reply": "2026-03-04T19:11:06.776223Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([120., 115., 108., 105., 102., 100., 110., 135., 160., 175., 180.,\n",
       "       178., 172., 170., 168., 165., 175., 190., 200., 195., 180., 165.,\n",
       "       145., 130.])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.777800Z",
     "iopub.status.busy": "2026-03-04T19:11:06.777734Z",
     "iopub.status.idle": "2026-03-04T19:11:06.865796Z",
     "shell.execute_reply": "2026-03-04T19:11:06.865383Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>power</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>timestamp</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2024-01-15 00:00:00+00:00</th>\n",
       "      <td>120.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 01:00:00+00:00</th>\n",
       "      <td>115.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 02:00:00+00:00</th>\n",
       "      <td>108.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 03:00:00+00:00</th>\n",
       "      <td>105.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 04:00:00+00:00</th>\n",
       "      <td>102.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           power\n",
       "timestamp                       \n",
       "2024-01-15 00:00:00+00:00  120.0\n",
       "2024-01-15 01:00:00+00:00  115.0\n",
       "2024-01-15 02:00:00+00:00  108.0\n",
       "2024-01-15 03:00:00+00:00  105.0\n",
       "2024-01-15 04:00:00+00:00  102.0"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pattern 1: `apply_numpy(func)`\n",
    "\n",
    "Pass a function that receives a numpy array and returns a numpy array. Timestamps, frequency, and all metadata are preserved automatically. The output array must have the same length as the input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.866966Z",
     "iopub.status.busy": "2026-03-04T19:11:06.866896Z",
     "iopub.status.idle": "2026-03-04T19:11:06.869286Z",
     "shell.execute_reply": "2026-03-04T19:11:06.868942Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>-0.992844</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>-1.14899</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>-1.3676</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>0.412492</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>-0.212102</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>-0.680547</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00  -0.992844     │\n",
       "│  2024-01-15 01:00   -1.14899     │\n",
       "│  2024-01-15 02:00    -1.3676     │\n",
       "│  ...                     ...     │\n",
       "│  2024-01-15 21:00   0.412492     │\n",
       "│  2024-01-15 22:00  -0.212102     │\n",
       "│  2024-01-15 23:00  -0.680547     │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "normalized = ts.apply_numpy(lambda arr: (arr - arr.mean()) / arr.std())\n",
    "normalized"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.870173Z",
     "iopub.status.busy": "2026-03-04T19:11:06.870120Z",
     "iopub.status.idle": "2026-03-04T19:11:06.871954Z",
     "shell.execute_reply": "2026-03-04T19:11:06.871587Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>6</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>120.0</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>235.0</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>343.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           6             │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00  120.0         │\n",
       "│  2024-01-15 01:00  235.0         │\n",
       "│  2024-01-15 02:00  343.0         │\n",
       "│  2024-01-15 03:00  448.0         │\n",
       "│  2024-01-15 04:00  550.0         │\n",
       "│  2024-01-15 05:00  650.0         │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cumulative = ts.apply_numpy(np.cumsum)\n",
    "cumulative.head(6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.872725Z",
     "iopub.status.busy": "2026-03-04T19:11:06.872676Z",
     "iopub.status.idle": "2026-03-04T19:11:06.874466Z",
     "shell.execute_reply": "2026-03-04T19:11:06.874210Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>120.0</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>115.0</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>110.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>165.0</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>145.0</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>130.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00  120.0         │\n",
       "│  2024-01-15 01:00  115.0         │\n",
       "│  2024-01-15 02:00  110.0         │\n",
       "│  ...                 ...         │\n",
       "│  2024-01-15 21:00  165.0         │\n",
       "│  2024-01-15 22:00  145.0         │\n",
       "│  2024-01-15 23:00  130.0         │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clipped = ts.apply_numpy(lambda arr: np.clip(arr, 110, 180))\n",
    "clipped"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pattern 2: `apply_pandas(func)`\n",
    "\n",
    "Pass a function that receives a pandas DataFrame and returns a pandas DataFrame. This lets you use the full pandas API — rolling windows, resampling, interpolation, and more."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.875406Z",
     "iopub.status.busy": "2026-03-04T19:11:06.875349Z",
     "iopub.status.idle": "2026-03-04T19:11:06.879103Z",
     "shell.execute_reply": "2026-03-04T19:11:06.878742Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>120.0</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>117.5</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>114.333</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>184.167</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>179.167</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>169.167</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00    120.0       │\n",
       "│  2024-01-15 01:00    117.5       │\n",
       "│  2024-01-15 02:00  114.333       │\n",
       "│  ...                   ...       │\n",
       "│  2024-01-15 21:00  184.167       │\n",
       "│  2024-01-15 22:00  179.167       │\n",
       "│  2024-01-15 23:00  169.167       │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rolling_mean = ts.apply_pandas(lambda df: df.rolling(6, min_periods=1).mean())\n",
    "rolling_mean"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.879979Z",
     "iopub.status.busy": "2026-03-04T19:11:06.879920Z",
     "iopub.status.idle": "2026-03-04T19:11:06.882356Z",
     "shell.execute_reply": "2026-03-04T19:11:06.881984Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>NaN</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>-5.0</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>-7.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>-15.0</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>-20.0</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>-15.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00    NaN         │\n",
       "│  2024-01-15 01:00   -5.0         │\n",
       "│  2024-01-15 02:00   -7.0         │\n",
       "│  ...                 ...         │\n",
       "│  2024-01-15 21:00  -15.0         │\n",
       "│  2024-01-15 22:00  -20.0         │\n",
       "│  2024-01-15 23:00  -15.0         │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "diff = ts.apply_pandas(lambda df: df.diff())\n",
    "diff"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.883225Z",
     "iopub.status.busy": "2026-03-04T19:11:06.883176Z",
     "iopub.status.idle": "2026-03-04T19:11:06.885444Z",
     "shell.execute_reply": "2026-03-04T19:11:06.885087Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>6</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>NaN</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>-4.16667</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>-6.08696</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           6             │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00       NaN      │\n",
       "│  2024-01-15 01:00  -4.16667      │\n",
       "│  2024-01-15 02:00  -6.08696      │\n",
       "│  2024-01-15 03:00  -2.77778      │\n",
       "│  2024-01-15 04:00  -2.85714      │\n",
       "│  2024-01-15 05:00  -1.96078      │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pct_change = ts.apply_pandas(lambda df: df.pct_change() * 100)\n",
    "pct_change.head(6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pattern 3: One-liner round-trips with `update_arr()` and `update_df()`\n",
    "\n",
    "Combine `.arr` / `.df` with `update_arr()` / `update_df()` to transform data in a single expression. The result is a new `TimeSeriesList` with all metadata preserved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.886266Z",
     "iopub.status.busy": "2026-03-04T19:11:06.886214Z",
     "iopub.status.idle": "2026-03-04T19:11:06.887900Z",
     "shell.execute_reply": "2026-03-04T19:11:06.887668Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>120.0</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>115.0</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>110.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>165.0</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>145.0</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>130.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00  120.0         │\n",
       "│  2024-01-15 01:00  115.0         │\n",
       "│  2024-01-15 02:00  110.0         │\n",
       "│  ...                 ...         │\n",
       "│  2024-01-15 21:00  165.0         │\n",
       "│  2024-01-15 22:00  145.0         │\n",
       "│  2024-01-15 23:00  130.0         │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.update_arr(ts.arr.clip(110, 180))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.888767Z",
     "iopub.status.busy": "2026-03-04T19:11:06.888697Z",
     "iopub.status.idle": "2026-03-04T19:11:06.891348Z",
     "shell.execute_reply": "2026-03-04T19:11:06.891080Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>8</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>114.333</td></tr>\n",
       "<tr><td>2024-01-15 03:00</td><td>102.333</td></tr>\n",
       "<tr><td>2024-01-15 06:00</td><td>135.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 15:00</td><td>176.667</td></tr>\n",
       "<tr><td>2024-01-15 18:00</td><td>191.667</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>146.667</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           8             │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00  114.333       │\n",
       "│  2024-01-15 03:00  102.333       │\n",
       "│  2024-01-15 06:00    135.0       │\n",
       "│  ...                   ...       │\n",
       "│  2024-01-15 15:00  176.667       │\n",
       "│  2024-01-15 18:00  191.667       │\n",
       "│  2024-01-15 21:00  146.667       │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.update_df(ts.df.resample(\"3h\").mean())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.892290Z",
     "iopub.status.busy": "2026-03-04T19:11:06.892227Z",
     "iopub.status.idle": "2026-03-04T19:11:06.894049Z",
     "shell.execute_reply": "2026-03-04T19:11:06.893792Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>NaN</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>-5.0</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>-7.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>-15.0</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>-20.0</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>-15.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00    NaN         │\n",
       "│  2024-01-15 01:00   -5.0         │\n",
       "│  2024-01-15 02:00   -7.0         │\n",
       "│  ...                 ...         │\n",
       "│  2024-01-15 21:00  -15.0         │\n",
       "│  2024-01-15 22:00  -20.0         │\n",
       "│  2024-01-15 23:00  -15.0         │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.update_df(ts.df.diff())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.894790Z",
     "iopub.status.busy": "2026-03-04T19:11:06.894743Z",
     "iopub.status.idle": "2026-03-04T19:11:06.896595Z",
     "shell.execute_reply": "2026-03-04T19:11:06.896204Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>120.0</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>235.0</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>343.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>3368.0</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>3513.0</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>3643.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00   120.0        │\n",
       "│  2024-01-15 01:00   235.0        │\n",
       "│  2024-01-15 02:00   343.0        │\n",
       "│  ...                  ...        │\n",
       "│  2024-01-15 21:00  3368.0        │\n",
       "│  2024-01-15 22:00  3513.0        │\n",
       "│  2024-01-15 23:00  3643.0        │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.update_arr(np.cumsum(ts.arr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pattern 4: Manual numpy round-trip\n",
    "\n",
    "For transformations where the output shape differs from the input, export to numpy, transform freely, and construct a new `TimeSeriesList`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.897428Z",
     "iopub.status.busy": "2026-03-04T19:11:06.897376Z",
     "iopub.status.idle": "2026-03-04T19:11:06.899013Z",
     "shell.execute_reply": "2026-03-04T19:11:06.898691Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:  <class 'numpy.ndarray'>\n",
      "Shape: (24,)\n",
      "Mean:  151.8 MW\n"
     ]
    }
   ],
   "source": [
    "arr = ts.to_numpy()\n",
    "print(f\"Type:  {type(arr)}\")\n",
    "print(f\"Shape: {arr.shape}\")\n",
    "print(f\"Mean:  {arr.mean():.1f} MW\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.899762Z",
     "iopub.status.busy": "2026-03-04T19:11:06.899709Z",
     "iopub.status.idle": "2026-03-04T19:11:06.901762Z",
     "shell.execute_reply": "2026-03-04T19:11:06.901523Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original length: 24, Smoothed length: 22\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>6</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>114.333</td></tr>\n",
       "<tr><td>2024-01-15 03:00</td><td>109.333</td></tr>\n",
       "<tr><td>2024-01-15 04:00</td><td>105.0</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           6             │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 02:00  114.333       │\n",
       "│  2024-01-15 03:00  109.333       │\n",
       "│  2024-01-15 04:00    105.0       │\n",
       "│  2024-01-15 05:00  102.333       │\n",
       "│  2024-01-15 06:00    104.0       │\n",
       "│  2024-01-15 07:00    115.0       │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "window = 3\n",
    "smoothed_arr = np.convolve(arr, np.ones(window) / window, mode=\"valid\")\n",
    "smoothed_timestamps = timestamps[window - 1 :]\n",
    "\n",
    "ts_smoothed = tdm.TimeSeriesList(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timestamps=smoothed_timestamps,\n",
    "    values=smoothed_arr.tolist(),\n",
    "    name=ts.name,\n",
    "    unit=ts.unit,\n",
    "    data_type=ts.data_type,\n",
    ")\n",
    "print(f\"Original length: {len(ts)}, Smoothed length: {len(ts_smoothed)}\")\n",
    "ts_smoothed.head(6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pattern 5: Manual pandas round-trip\n",
    "\n",
    "For multi-step pandas workflows where a one-liner would be hard to read, break it into separate steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.902690Z",
     "iopub.status.busy": "2026-03-04T19:11:06.902640Z",
     "iopub.status.idle": "2026-03-04T19:11:06.905055Z",
     "shell.execute_reply": "2026-03-04T19:11:06.904748Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>power</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>timestamp</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2024-01-15 00:00:00+00:00</th>\n",
       "      <td>120.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 01:00:00+00:00</th>\n",
       "      <td>115.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 02:00:00+00:00</th>\n",
       "      <td>108.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 03:00:00+00:00</th>\n",
       "      <td>105.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2024-01-15 04:00:00+00:00</th>\n",
       "      <td>102.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           power\n",
       "timestamp                       \n",
       "2024-01-15 00:00:00+00:00  120.0\n",
       "2024-01-15 01:00:00+00:00  115.0\n",
       "2024-01-15 02:00:00+00:00  108.0\n",
       "2024-01-15 03:00:00+00:00  105.0\n",
       "2024-01-15 04:00:00+00:00  102.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = ts.to_pandas_dataframe()\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.905883Z",
     "iopub.status.busy": "2026-03-04T19:11:06.905835Z",
     "iopub.status.idle": "2026-03-04T19:11:06.908302Z",
     "shell.execute_reply": "2026-03-04T19:11:06.907972Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original:  24 points\n",
      "Resampled: 8 points\n",
      "Unit preserved: MW\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>8</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>114.333</td></tr>\n",
       "<tr><td>2024-01-15 03:00</td><td>102.333</td></tr>\n",
       "<tr><td>2024-01-15 06:00</td><td>135.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 15:00</td><td>176.667</td></tr>\n",
       "<tr><td>2024-01-15 18:00</td><td>191.667</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>146.667</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           8             │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00  114.333       │\n",
       "│  2024-01-15 03:00  102.333       │\n",
       "│  2024-01-15 06:00    135.0       │\n",
       "│  ...                   ...       │\n",
       "│  2024-01-15 15:00  176.667       │\n",
       "│  2024-01-15 18:00  191.667       │\n",
       "│  2024-01-15 21:00  146.667       │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_resampled = df.resample(\"3h\").mean()\n",
    "\n",
    "ts_resampled = ts.update_from_pandas(df_resampled)\n",
    "print(f\"Original:  {len(ts)} points\")\n",
    "print(f\"Resampled: {len(ts_resampled)} points\")\n",
    "print(f\"Unit preserved: {ts_resampled.unit}\")\n",
    "ts_resampled"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.909032Z",
     "iopub.status.busy": "2026-03-04T19:11:06.908989Z",
     "iopub.status.idle": "2026-03-04T19:11:06.910875Z",
     "shell.execute_reply": "2026-03-04T19:11:06.910556Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesList</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>power</td></tr>\n",
       "<tr><td>Length</td><td>24</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW</td></tr>\n",
       "<tr><td>Data type</td><td>OBSERVATION</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>power</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>120.0</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>117.083</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>113.0</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>178.601</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>168.997</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>157.851</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesList\n",
       "┌──────────────────────────────────┐\n",
       "│  Name:             power         │\n",
       "│  Length:           24            │\n",
       "│  Frequency:        PT1H          │\n",
       "│  Timezone:         UTC (+00:00)  │\n",
       "│  Unit:             MW            │\n",
       "│  Data type:        OBSERVATION   │\n",
       "├──────────────────────────────────┤\n",
       "│  2024-01-15 00:00    120.0       │\n",
       "│  2024-01-15 01:00  117.083       │\n",
       "│  2024-01-15 02:00    113.0       │\n",
       "│  ...                   ...       │\n",
       "│  2024-01-15 21:00  178.601       │\n",
       "│  2024-01-15 22:00  168.997       │\n",
       "│  2024-01-15 23:00  157.851       │\n",
       "└──────────────────────────────────┘"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_ewm = df.ewm(span=6).mean()\n",
    "\n",
    "ts_ewm = ts.update_from_pandas(df_ewm)\n",
    "ts_ewm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transforms on TimeSeriesTable\n",
    "\n",
    "All patterns — `apply_*`, `update_arr()`, `update_df()`, `.arr`, `.df` — also work on `TimeSeriesTable`, applying across all columns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.911651Z",
     "iopub.status.busy": "2026-03-04T19:11:06.911592Z",
     "iopub.status.idle": "2026-03-04T19:11:06.913905Z",
     "shell.execute_reply": "2026-03-04T19:11:06.913578Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesTable</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>unnamed</td></tr>\n",
       "<tr><td>Columns</td><td>wind, solar, hydro</td></tr>\n",
       "<tr><td>Length</td><td>24 × 3</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW, MW, MW</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>wind</th><th>solar</th><th>hydro</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>81.5236</td><td>0.0</td><td>48.715</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>85.592</td><td>0.0</td><td>48.9436</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>104.536</td><td>0.0</td><td>51.5969</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>55.812</td><td>0.0</td><td>50.6561</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>75.3208</td><td>0.0</td><td>52.6143</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>79.2274</td><td>0.0</td><td>50.6708</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesTable\n",
       "┌─────────────────────────────────────────────┐\n",
       "│  Name:             unnamed                  │\n",
       "│  Columns:          wind, solar, hydro       │\n",
       "│  Length:           24 × 3                   │\n",
       "│  Frequency:        PT1H                     │\n",
       "│  Timezone:         UTC (+00:00)             │\n",
       "│  Unit:             MW, MW, MW               │\n",
       "├─────────────────────────────────────────────┤\n",
       "│                       wind  solar    hydro  │\n",
       "│  2024-01-15 00:00  81.5236    0.0   48.715  │\n",
       "│  2024-01-15 01:00   85.592    0.0  48.9436  │\n",
       "│  2024-01-15 02:00  104.536    0.0  51.5969  │\n",
       "│  ...                   ...    ...      ...  │\n",
       "│  2024-01-15 21:00   55.812    0.0  50.6561  │\n",
       "│  2024-01-15 22:00  75.3208    0.0  52.6143  │\n",
       "│  2024-01-15 23:00  79.2274    0.0  50.6708  │\n",
       "└─────────────────────────────────────────────┘"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rng = np.random.default_rng(42)\n",
    "\n",
    "table = tdm.TimeSeriesTable(\n",
    "    tdm.Frequency.PT1H,\n",
    "    timestamps=timestamps,\n",
    "    values=np.column_stack([\n",
    "        80 + 40 * np.sin(np.linspace(0, 2 * np.pi, 24)) + rng.normal(0, 5, 24),\n",
    "        np.clip(60 * np.sin(np.linspace(-0.5, np.pi + 0.5, 24)), 0, None),\n",
    "        50 + rng.normal(0, 3, 24),\n",
    "    ]),\n",
    "    names=[\"wind\", \"solar\", \"hydro\"],\n",
    "    units=[\"MW\", \"MW\", \"MW\"],\n",
    ")\n",
    "table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.914727Z",
     "iopub.status.busy": "2026-03-04T19:11:06.914663Z",
     "iopub.status.idle": "2026-03-04T19:11:06.916807Z",
     "shell.execute_reply": "2026-03-04T19:11:06.916480Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesTable</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>unnamed</td></tr>\n",
       "<tr><td>Columns</td><td>wind, solar, hydro</td></tr>\n",
       "<tr><td>Length</td><td>24 × 3</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW, MW, MW</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>wind</th><th>solar</th><th>hydro</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>81.5236</td><td>0.0</td><td>48.715</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>83.5578</td><td>0.0</td><td>48.8293</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>90.5504</td><td>0.0</td><td>49.7518</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>48.7795</td><td>9.72651</td><td>49.9265</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>56.3025</td><td>3.88045</td><td>51.0792</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>65.0506</td><td>0.602954</td><td>51.0728</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesTable\n",
       "┌────────────────────────────────────────────────┐\n",
       "│  Name:             unnamed                     │\n",
       "│  Columns:          wind, solar, hydro          │\n",
       "│  Length:           24 × 3                      │\n",
       "│  Frequency:        PT1H                        │\n",
       "│  Timezone:         UTC (+00:00)                │\n",
       "│  Unit:             MW, MW, MW                  │\n",
       "├────────────────────────────────────────────────┤\n",
       "│                       wind     solar    hydro  │\n",
       "│  2024-01-15 00:00  81.5236       0.0   48.715  │\n",
       "│  2024-01-15 01:00  83.5578       0.0  48.8293  │\n",
       "│  2024-01-15 02:00  90.5504       0.0  49.7518  │\n",
       "│  ...                   ...       ...      ...  │\n",
       "│  2024-01-15 21:00  48.7795   9.72651  49.9265  │\n",
       "│  2024-01-15 22:00  56.3025   3.88045  51.0792  │\n",
       "│  2024-01-15 23:00  65.0506  0.602954  51.0728  │\n",
       "└────────────────────────────────────────────────┘"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "table_rolling = table.apply_pandas(lambda df: df.rolling(4, min_periods=1).mean())\n",
    "table_rolling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.917554Z",
     "iopub.status.busy": "2026-03-04T19:11:06.917507Z",
     "iopub.status.idle": "2026-03-04T19:11:06.919421Z",
     "shell.execute_reply": "2026-03-04T19:11:06.919109Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesTable</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>unnamed</td></tr>\n",
       "<tr><td>Columns</td><td>wind, solar, hydro</td></tr>\n",
       "<tr><td>Length</td><td>6 × 3</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW, MW, MW</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>wind</th><th>solar</th><th>hydro</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>0.0594697</td><td>-1.19819</td><td>-0.868761</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>0.208941</td><td>-1.19819</td><td>-0.759577</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>0.904926</td><td>-1.19819</td><td>0.507806</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesTable\n",
       "┌─────────────────────────────────────────────────────┐\n",
       "│  Name:             unnamed                          │\n",
       "│  Columns:          wind, solar, hydro               │\n",
       "│  Length:           6 × 3                            │\n",
       "│  Frequency:        PT1H                             │\n",
       "│  Timezone:         UTC (+00:00)                     │\n",
       "│  Unit:             MW, MW, MW                       │\n",
       "├─────────────────────────────────────────────────────┤\n",
       "│                         wind      solar      hydro  │\n",
       "│  2024-01-15 00:00  0.0594697   -1.19819  -0.868761  │\n",
       "│  2024-01-15 01:00   0.208941   -1.19819  -0.759577  │\n",
       "│  2024-01-15 02:00   0.904926   -1.19819   0.507806  │\n",
       "│  2024-01-15 03:00     1.2503   -1.09413   0.268693  │\n",
       "│  2024-01-15 04:00   0.949919  -0.632539   0.336456  │\n",
       "│  2024-01-15 05:00    1.20314  -0.189238   0.362377  │\n",
       "└─────────────────────────────────────────────────────┘"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "table_norm = table.apply_numpy(\n",
    "    lambda arr: (arr - arr.mean(axis=0)) / arr.std(axis=0)\n",
    ")\n",
    "table_norm.head(6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.920157Z",
     "iopub.status.busy": "2026-03-04T19:11:06.920114Z",
     "iopub.status.idle": "2026-03-04T19:11:06.922198Z",
     "shell.execute_reply": "2026-03-04T19:11:06.921909Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesTable</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>unnamed</td></tr>\n",
       "<tr><td>Columns</td><td>wind, solar, hydro</td></tr>\n",
       "<tr><td>Length</td><td>24 × 3</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW, MW, MW</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>wind</th><th>solar</th><th>hydro</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>81.5236</td><td>0.0</td><td>48.715</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>83.5578</td><td>0.0</td><td>48.8293</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>90.5504</td><td>0.0</td><td>49.7518</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>48.7795</td><td>9.72651</td><td>49.9265</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>56.3025</td><td>3.88045</td><td>51.0792</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>65.0506</td><td>0.602954</td><td>51.0728</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesTable\n",
       "┌────────────────────────────────────────────────┐\n",
       "│  Name:             unnamed                     │\n",
       "│  Columns:          wind, solar, hydro          │\n",
       "│  Length:           24 × 3                      │\n",
       "│  Frequency:        PT1H                        │\n",
       "│  Timezone:         UTC (+00:00)                │\n",
       "│  Unit:             MW, MW, MW                  │\n",
       "├────────────────────────────────────────────────┤\n",
       "│                       wind     solar    hydro  │\n",
       "│  2024-01-15 00:00  81.5236       0.0   48.715  │\n",
       "│  2024-01-15 01:00  83.5578       0.0  48.8293  │\n",
       "│  2024-01-15 02:00  90.5504       0.0  49.7518  │\n",
       "│  ...                   ...       ...      ...  │\n",
       "│  2024-01-15 21:00  48.7795   9.72651  49.9265  │\n",
       "│  2024-01-15 22:00  56.3025   3.88045  51.0792  │\n",
       "│  2024-01-15 23:00  65.0506  0.602954  51.0728  │\n",
       "└────────────────────────────────────────────────┘"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "table.update_df(table.df.rolling(4, min_periods=1).mean())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-03-04T19:11:06.922950Z",
     "iopub.status.busy": "2026-03-04T19:11:06.922906Z",
     "iopub.status.idle": "2026-03-04T19:11:06.924722Z",
     "shell.execute_reply": "2026-03-04T19:11:06.924394Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       ".ts-repr { font-family: monospace; font-size: 13px; max-width: 640px; display: inline-grid; }\n",
       ".ts-repr .ts-header {\n",
       "  font-weight: bold; font-size: 14px;\n",
       "  padding: 6px 10px; border-bottom: 2px solid #4a4a4a;\n",
       "  background: #f0f0f0; color: #1a1a1a;\n",
       "}\n",
       ".ts-repr .ts-meta { padding: 6px 10px; background: #fafafa; overflow: hidden; min-width: 0; }\n",
       ".ts-repr .ts-meta table { border-collapse: collapse; width: 100%; table-layout: fixed; }\n",
       ".ts-repr .ts-meta td { padding: 1px 8px 1px 0; white-space: nowrap; }\n",
       ".ts-repr .ts-meta td:first-child { color: #475569; font-weight: 600; width: 90px; }\n",
       ".ts-repr .ts-meta td:last-child { color: #1a1a1a; overflow: hidden; text-overflow: ellipsis; }\n",
       ".ts-repr .ts-data { padding: 6px 10px; }\n",
       ".ts-repr .ts-data table {\n",
       "  border-collapse: collapse; text-align: right;\n",
       "}\n",
       ".ts-repr .ts-data th {\n",
       "  text-align: right; padding: 3px 10px; border-bottom: 1px solid #ccc;\n",
       "  color: #555; font-weight: 600;\n",
       "}\n",
       ".ts-repr .ts-data th.ts-idx { text-align: left; }\n",
       ".ts-repr .ts-data td { padding: 2px 10px; }\n",
       ".ts-repr .ts-data tr:hover { background: #f5f5f5; }\n",
       ".ts-repr .ts-data td:first-child { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-data td.ts-idx { text-align: left; color: #1e293b; }\n",
       ".ts-repr .ts-ellipsis { text-align: center !important; color: #999; }\n",
       "@media (prefers-color-scheme: dark) {\n",
       "  .ts-repr .ts-header { background: #1e293b; color: #e2e8f0; border-color: #475569; }\n",
       "  .ts-repr .ts-meta { background: #0f172a; }\n",
       "  .ts-repr .ts-meta td:first-child { color: #94a3b8; }\n",
       "  .ts-repr .ts-meta td:last-child { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data th { color: #94a3b8; border-color: #334155; }\n",
       "  .ts-repr .ts-data td { color: #e2e8f0; }\n",
       "  .ts-repr .ts-data td:first-child { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data td.ts-idx { color: #cbd5e1; }\n",
       "  .ts-repr .ts-data tr:hover { background: #1e293b; }\n",
       "  .ts-repr .ts-ellipsis { color: #64748b; }\n",
       "}\n",
       "</style>\n",
       "<div class=\"ts-repr\">\n",
       "<div class=\"ts-header\">TimeSeriesTable</div>\n",
       "<div class=\"ts-meta\"><table>\n",
       "<tr><td>Name</td><td>unnamed</td></tr>\n",
       "<tr><td>Columns</td><td>wind, solar, hydro</td></tr>\n",
       "<tr><td>Length</td><td>24 × 3</td></tr>\n",
       "<tr><td>Frequency</td><td>PT1H</td></tr>\n",
       "<tr><td>Timezone</td><td>UTC (+00:00)</td></tr>\n",
       "<tr><td>Unit</td><td>MW, MW, MW</td></tr>\n",
       "</table></div>\n",
       "<div class=\"ts-data\"><table>\n",
       "<tr><th class=\"ts-idx\">timestamp</th><th>wind</th><th>solar</th><th>hydro</th></tr>\n",
       "<tr><td>2024-01-15 00:00</td><td>81.5236</td><td>40.0</td><td>48.715</td></tr>\n",
       "<tr><td>2024-01-15 01:00</td><td>85.592</td><td>40.0</td><td>48.9436</td></tr>\n",
       "<tr><td>2024-01-15 02:00</td><td>104.536</td><td>40.0</td><td>51.5969</td></tr>\n",
       "<tr><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td><td class=\"ts-ellipsis\">&hellip;</td></tr>\n",
       "<tr><td>2024-01-15 21:00</td><td>55.812</td><td>40.0</td><td>50.6561</td></tr>\n",
       "<tr><td>2024-01-15 22:00</td><td>75.3208</td><td>40.0</td><td>52.6143</td></tr>\n",
       "<tr><td>2024-01-15 23:00</td><td>79.2274</td><td>40.0</td><td>50.6708</td></tr>\n",
       "</table></div>\n",
       "</div>"
      ],
      "text/plain": [
       "TimeSeriesTable\n",
       "┌─────────────────────────────────────────────┐\n",
       "│  Name:             unnamed                  │\n",
       "│  Columns:          wind, solar, hydro       │\n",
       "│  Length:           24 × 3                   │\n",
       "│  Frequency:        PT1H                     │\n",
       "│  Timezone:         UTC (+00:00)             │\n",
       "│  Unit:             MW, MW, MW               │\n",
       "├─────────────────────────────────────────────┤\n",
       "│                       wind  solar    hydro  │\n",
       "│  2024-01-15 00:00  81.5236   40.0   48.715  │\n",
       "│  2024-01-15 01:00   85.592   40.0  48.9436  │\n",
       "│  2024-01-15 02:00  104.536   40.0  51.5969  │\n",
       "│  ...                   ...    ...      ...  │\n",
       "│  2024-01-15 21:00   55.812   40.0  50.6561  │\n",
       "│  2024-01-15 22:00  75.3208   40.0  52.6143  │\n",
       "│  2024-01-15 23:00  79.2274   40.0  50.6708  │\n",
       "└─────────────────────────────────────────────┘"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "table.update_arr(np.clip(table.arr, 40, 120))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "Five patterns for transforming time series data:\n",
    "\n",
    "| Pattern | Method | Best for |\n",
    "|---------|--------|----------|\n",
    "| `ts.apply_numpy(func)` | Functional | Same-length vectorized ops (normalize, cumsum) |\n",
    "| `ts.apply_pandas(func)` | Functional | Rolling windows, diff, pct_change |\n",
    "| `ts.update_arr(ts.arr.clip(...))` | One-liner | Quick numpy transforms via `.arr` |\n",
    "| `ts.update_df(ts.df.resample(...).mean())` | One-liner | Quick pandas transforms via `.df` |\n",
    "| Manual `to_numpy()` / `to_pandas_dataframe()` | Multi-step | Shape-changing ops, complex workflows |\n",
    "\n",
    "All patterns preserve metadata. Use `.arr` / `.df` for read access and `update_arr()` / `update_df()` to write results back.\n",
    "\n",
    "Next up: **nb_03** covers unit handling, validation, and rich metadata."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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
}
