{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TimeSeriesList Operations\n",
    "\n",
    "A `TimeSeriesList` is not just a data container — it supports arithmetic, comparisons, and transformations. This notebook demonstrates how to manipulate time series data without dropping down to raw arrays."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "from datetime import datetime, timedelta, timezone\n\nimport timedatamodel as tdm\n\nbase = datetime(2024, 1, 15, tzinfo=timezone.utc)\ntimestamps = [base + timedelta(hours=i) for i in range(24)]\n\ngeneration = 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=\"generation\",\n    unit=\"MW\",\n    data_type=tdm.DataType.OBSERVATION,\n)\n\nconsumption = tdm.TimeSeriesList(\n    tdm.Frequency.PT1H,\n    timestamps=timestamps,\n    values=[\n        90.0, 85.0, 80.0, 78.0, 77.0, 80.0,\n        95.0, 120.0, 145.0, 155.0, 160.0, 158.0,\n        155.0, 150.0, 148.0, 150.0, 160.0, 170.0,\n        165.0, 155.0, 140.0, 120.0, 105.0, 95.0,\n    ],\n    name=\"consumption\",\n    unit=\"MW\",\n    data_type=tdm.DataType.OBSERVATION,\n)",
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scalar arithmetic\n",
    "\n",
    "Scale, offset, negate, or round values with natural Python operators. The result is a new `TimeSeriesList` with metadata preserved."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "generation_kw = generation * 1000\n",
    "generation_kw"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "offset = generation + 10.0\n",
    "negated = -generation\n",
    "rounded = round(generation / 3, 1)\n",
    "\n",
    "print(f\"Original first value:  {generation[0].value}\")\n",
    "print(f\"+ 10:                  {offset[0].value}\")\n",
    "print(f\"Negated:               {negated[0].value}\")\n",
    "print(f\"Divided by 3, rounded: {rounded[0].value}\")"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Element-wise arithmetic between two TimeSeriesList\n",
    "\n",
    "Subtract consumption from generation to get net surplus. Both series must share the same timestamps."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "surplus = generation - consumption\n",
    "surplus"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comparison operators\n",
    "\n",
    "Comparisons return a `TimeSeriesList` of 1.0 / 0.0 (or NaN for missing). Useful for flagging thresholds."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "high_gen = generation > 170.0\n",
    "high_gen"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "gen_exceeds_cons = generation > consumption\n",
    "gen_exceeds_cons"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `abs()` — absolute values\n",
    "\n",
    "Handy when you have signed deviations or residuals."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "deviation = generation - consumption\n",
    "abs_deviation = abs(deviation)\n",
    "abs_deviation.head(6)"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `head()`, `tail()`, and `copy()`\n",
    "\n",
    "Quickly preview or duplicate a series."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "print(\"First 3:\")\n",
    "for dp in generation.head(3):\n",
    "    print(f\"  {dp.timestamp:%H:%M}  {dp.value:.1f} MW\")\n",
    "\n",
    "print(\"\\nLast 3:\")\n",
    "for dp in generation.tail(3):\n",
    "    print(f\"  {dp.timestamp:%H:%M}  {dp.value:.1f} MW\")"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Handling missing values in arithmetic\n",
    "\n",
    "Missing values (`None`) propagate through operations as NaN, just like numpy."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "ts_with_gaps = tdm.TimeSeriesList(\n    tdm.Frequency.PT1H,\n    timestamps=timestamps[:6],\n    values=[100.0, None, 110.0, None, 105.0, 108.0],\n    name=\"sensor\",\n    unit=\"MW\",\n)\n\ndoubled = ts_with_gaps * 2\nfor dp in doubled:\n    print(f\"{dp.timestamp:%H:%M}  {dp.value}\")",
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "You can treat `TimeSeriesList` like a numeric object:\n",
    "\n",
    "- Scalar ops: `+`, `-`, `*`, `/`, `-ts`, `abs()`, `round()`\n",
    "- Element-wise ops between aligned series\n",
    "- Comparisons: `>`, `>=`, `<`, `<=`, `==`, `!=`\n",
    "- Missing values propagate cleanly\n",
    "\n",
    "Next up: **nb_05** introduces multivariate time series with `TimeSeriesTable`."
   ]
  }
 ],
 "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
}