{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rU_PV6xN6ijY"
   },
   "source": "# ATM Quantum Cash Optimization — POC v1\n\n**Amaç:** Ham ATM journal loglarından canonical event'ler, transaction aggregates, denomination inventory snapshot'ları ve ATM cash-state üretmek; ardından forecasting ve replenishment optimization için hazır bir veri sözleşmesi oluşturmak.\n\n> Bu POC'nin önemli sınırı: sağlanan iki örnek journal'da **cash-in / deposit** sinyalleri güçlüdür; **cash withdrawal / dispense** sinyali yeterli değildir. Bu nedenle gerçek cash-demand forecast modeli için withdrawal/dispense feed'i ayrıca bağlanmalıdır. Buradaki forecasting bölümü, gerçek withdrawal veri kontratını hazırlar ve örnek amaçlı sentetik withdrawal üretimi içerir.\n",
   "id": "rU_PV6xN6ijY"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DYtfn6vW6ijf"
   },
   "source": "## 0 — Notebook architecture\n\n```text\nRaw EJ/AJ\n   ↓\nJournal Parser\n   ↓\nCanonical Events\n   ↓\nTransaction Reconstruction\n   ↓\nDenomination Snapshot Extraction\n   ↓\nATM Cash-State Reconstruction\n   ↓\nFeature Dataset\n   ↓\nForecast Contract\n   ↓\nClassical MILP / QUBO\n   ↓\nQuantum Backend Adapter Layer\n```\n",
   "id": "DYtfn6vW6ijf"
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "GhNCSR6q6ijg",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557240312,
     "user_tz": -180,
     "elapsed": 11,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "647328b6-8da9-4ab6-8aef-6f177d56b999"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Files:\n",
      " - /mnt/data/EJ Data 2098.txt True 26807\n",
      " - /mnt/data/AHB deposit AJ.txt True 18412\n"
     ]
    }
   ],
   "source": "# 1 — Environment\nimport re\nimport json\nimport math\nimport hashlib\nfrom pathlib import Path\nfrom datetime import datetime, timedelta\n\nimport pandas as pd\nimport numpy as np\n\ntry:\n    from IPython.display import display\nexcept Exception:\n    pass\n\nBASE = Path(\"/mnt/data\")\nFILES = [\n    BASE / \"EJ Data 2098.txt\",\n    BASE / \"AHB deposit AJ.txt\",\n]\n\nprint(\"Files:\")\nfor f in FILES:\n    print(\" -\", f, f.exists(), f.stat().st_size if f.exists() else \"\")\n",
   "id": "GhNCSR6q6ijg"
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "a63NRFiG6ijj",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557247243,
     "user_tz": -180,
     "elapsed": 27,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "df4b00cb-186d-403a-e1d4-b62f3254ab92"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "EJ Data 2098.txt: 25,809 chars / 999 lines\n",
      "AHB deposit AJ.txt: 17,717 chars / 696 lines\n"
     ]
    }
   ],
   "source": "# 2 — Load raw journals\ndef load_text(path: Path) -> str:\n    return path.read_text(encoding=\"utf-8\", errors=\"ignore\")\n\nraw = {f.name: load_text(f) for f in FILES}\n\nfor name, txt in raw.items():\n    print(f\"{name}: {len(txt):,} chars / {len(txt.splitlines()):,} lines\")\n",
   "id": "a63NRFiG6ijj"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bTMW8sQb6ijk"
   },
   "source": "## 3 — Canonical event taxonomy\n\nThe parser intentionally separates raw device messages from business-semantic events.\n\nCore event types:\n\n- `TRANSACTION_STARTED`\n- `TRANSACTION_COMPLETED`\n- `TRANSACTION_FAILED`\n- `CASH_IN_STARTED`\n- `CASH_IN_COMPLETED`\n- `CASH_OUT_STARTED`\n- `CASH_OUT_COMPLETED`\n- `CASH_REJECTED`\n- `CASSETTE_SNAPSHOT`\n- `CASSETTE_REMOVED`\n- `CASSETTE_INSERTED`\n- `SUPERVISOR_ENTRY`\n- `SUPERVISOR_EXIT`\n- `ATM_OUT_OF_SERVICE`\n- `ATM_IN_SERVICE`\n- `HARDWARE_ERROR`\n- `RECOVERY_FAILED`\n- `RECOVERY_COMPLETED`\n- `SERIAL_READ_FAILURE`\n",
   "id": "bTMW8sQb6ijk"
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "pl649N1O6ijk",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557252709,
     "user_tz": -180,
     "elapsed": 6,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "# 4 — Journal parser helpers\nHEADER_RE = re.compile(\n    r'^\\*(?P<seq>\\d+)\\*(?P<date>\\d{2}/\\d{2}/\\d{4})\\*(?P<time>\\d{2}:\\d{2})\\*'\n)\nTIME_RE = re.compile(r'(?P<h>\\d{2}):(?P<m>\\d{2}):(?P<s>\\d{2})')\nMACHINE_RE = re.compile(r'MACHINE ID:\\s*(?P<machine>\\S+)')\nTSN_RE = re.compile(r'\\bTSN:\\s*(?P<tsn>\\d+)')\nHOST_TSN_RE = re.compile(r'HOST TSN:\\s*(?P<host_tsn>\\d+)')\nAMOUNT_RE = re.compile(r'AMOUNT:\\s*(?P<amount>[\\d,]+(?:\\.\\d+)?)\\s*(?P<ccy>[A-Z]{3})?')\nDATE_RE = re.compile(r'DATE\\s+(?P<date>\\d{2}/\\d{2}/\\d{2,4})\\s+(?P<time>\\d{2}:\\d{2}:\\d{2})')\nDENOM_LINE_RE = re.compile(\n    r'^\\s*(?:AED\\s*)?(?P<denom>10|20|50|100|200|500|1000)\\s+X\\s+(?P<count>\\d+)\\s*=',\n    re.I\n)\nCASSETTE_RE = re.compile(r'^(?:\\*[^*]+\\*)?\\s*DENOM\\s+CASS', re.I)\n\ndef parse_dt(date_str, time_str):\n    for fmt in (\"%d/%m/%Y %H:%M:%S\", \"%d/%m/%y %H:%M:%S\"):\n        try:\n            return datetime.strptime(f\"{date_str} {time_str}\", fmt)\n        except ValueError:\n            pass\n    return pd.NaT\n\ndef redact_sensitive(text: str) -> str:\n    # Keep the original raw text out of analytics data.\n    text = re.sub(r'(CARD NO[: ]+)(\\d+)', r'\\1[REDACTED]', text, flags=re.I)\n    text = re.sub(r'(CARD:\\s*)(\\d+)', r'\\1[REDACTED]', text, flags=re.I)\n    text = re.sub(r'(MOBILE NUMBER:\\s*)([^\\n]+)', r'\\1[REDACTED]', text, flags=re.I)\n    text = re.sub(r'(EIDA DATA:\\s*)([^\\n]+)', r'\\1[REDACTED]', text, flags=re.I)\n    text = re.sub(r'(ACCT NO\\.?\\s*)(\\S+)', r'\\1[REDACTED]', text, flags=re.I)\n    text = re.sub(r'(FROM A/C#:\\s*)(\\S+)', r'\\1[REDACTED]', text, flags=re.I)\n    return text\n",
   "id": "pl649N1O6ijk"
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 674
    },
    "id": "jYUask-h6ijm",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557257690,
     "user_tz": -180,
     "elapsed": 224,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "f9c4d637-3c6f-4ca3-ffaa-266f0f8dcded"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "(268, 11)\n"
     ]
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "         source_file  seq          event_time             event_type  \\\n",
       "0   EJ Data 2098.txt  342 2025-02-21 12:35:00     TRANSACTION_FAILED   \n",
       "1   EJ Data 2098.txt  352 2025-02-21 12:37:00     TRANSACTION_FAILED   \n",
       "2   EJ Data 2098.txt  355 2025-02-21 12:38:00        CASH_IN_STARTED   \n",
       "3   EJ Data 2098.txt  356 2025-02-21 12:40:00      CASH_IN_COMPLETED   \n",
       "4   EJ Data 2098.txt  416 2025-02-21 13:14:00        CASH_IN_STARTED   \n",
       "5   EJ Data 2098.txt  417 2025-02-21 13:15:00      CASH_IN_COMPLETED   \n",
       "6   EJ Data 2098.txt  423 2025-02-21 13:16:00     TRANSACTION_FAILED   \n",
       "7   EJ Data 2098.txt  428 2025-02-21 13:17:00     TRANSACTION_FAILED   \n",
       "8   EJ Data 2098.txt  433 2025-02-21 13:19:00                  OTHER   \n",
       "9   EJ Data 2098.txt  439 2025-02-21 13:23:00                  OTHER   \n",
       "10  EJ Data 2098.txt  445 2025-02-21 13:27:00  TRANSACTION_COMPLETED   \n",
       "11  EJ Data 2098.txt  450 2025-02-21 13:31:00     TRANSACTION_FAILED   \n",
       "12  EJ Data 2098.txt  455 2025-02-21 13:33:00     TRANSACTION_FAILED   \n",
       "13  EJ Data 2098.txt  460 2025-02-21 13:34:00     TRANSACTION_FAILED   \n",
       "14  EJ Data 2098.txt  465 2025-02-21 13:35:00     TRANSACTION_FAILED   \n",
       "15  EJ Data 2098.txt  470 2025-02-21 13:38:00        CASH_IN_STARTED   \n",
       "16  EJ Data 2098.txt  471 2025-02-21 13:39:00      CASH_IN_COMPLETED   \n",
       "17  EJ Data 2098.txt  477 2025-02-21 13:40:00  TRANSACTION_COMPLETED   \n",
       "18  EJ Data 2098.txt  482 2025-02-21 13:41:00  TRANSACTION_COMPLETED   \n",
       "19  EJ Data 2098.txt  502 2025-02-21 13:55:00  TRANSACTION_COMPLETED   \n",
       "\n",
       "      atm_id   tsn host_tsn  amount currency denomination_counts  \\\n",
       "0   ADB02098  3011     3011     NaN      AED                  {}   \n",
       "1   ADB02098  3013     3013     NaN      AED                  {}   \n",
       "2   ADB02098  3015     None     NaN      AED                  {}   \n",
       "3   ADB02098  3014     None    20.0      AED                  {}   \n",
       "4   ADB02098  3019     3019     NaN      AED                  {}   \n",
       "5   ADB02098  3020     3019    10.0      AED                  {}   \n",
       "6   ADB02098  3022     3022     NaN      AED                  {}   \n",
       "7   ADB02098  3024     3024     NaN      AED                  {}   \n",
       "8   ADB02098  3025     3025     NaN      AED                  {}   \n",
       "9   ADB02098  3027     3027     NaN      AED                  {}   \n",
       "10  ADB02098  3029     3029   500.0      AED                  {}   \n",
       "11  ADB02098  3032     3032     NaN      AED                  {}   \n",
       "12  ADB02098  3034     3034     NaN      AED                  {}   \n",
       "13  ADB02098  3036     3036     NaN      AED                  {}   \n",
       "14  ADB02098  3038     3038     NaN      AED                  {}   \n",
       "15  ADB02098  3040     3040     NaN      AED                  {}   \n",
       "16  ADB02098  3041     3040    10.0      AED                  {}   \n",
       "17  ADB02098  3043     3043     NaN      AED                  {}   \n",
       "18  ADB02098  3045     3045   550.0      AED                  {}   \n",
       "19  ADB02098  3048     3048     NaN      AED                  {}   \n",
       "\n",
       "                                         raw_redacted  \n",
       "0   *342*02/21/2025*12:35*\\n     *PRIMARY CARD REA...  \n",
       "1   *352*02/21/2025*12:37*\\n     *PRIMARY CARD REA...  \n",
       "2   *355*02/21/2025*12:38*\\n     *CARDLESS TRANSAC...  \n",
       "3   *356*02/21/2025*12:40*\\n*3015*1*w*0, M-00, R-1...  \n",
       "4   *416*02/21/2025*13:14*\\n     *PRIMARY CARD REA...  \n",
       "5   *417*02/21/2025*13:15*\\n*3019*1*w*0, M-00, R-1...  \n",
       "6   *423*02/21/2025*13:16*\\n     *PRIMARY CARD REA...  \n",
       "7   *428*02/21/2025*13:17*\\n     *PRIMARY CARD REA...  \n",
       "8   *433*02/21/2025*13:19*\\n     *PRIMARY CARD REA...  \n",
       "9   *439*02/21/2025*13:23*\\n     *PRIMARY CARD REA...  \n",
       "10  *445*02/21/2025*13:27*\\n     *PRIMARY CARD REA...  \n",
       "11  *450*02/21/2025*13:31*\\n     *PRIMARY CARD REA...  \n",
       "12  *455*02/21/2025*13:33*\\n     *PRIMARY CARD REA...  \n",
       "13  *460*02/21/2025*13:34*\\n     *PRIMARY CARD REA...  \n",
       "14  *465*02/21/2025*13:35*\\n     *PRIMARY CARD REA...  \n",
       "15  *470*02/21/2025*13:38*\\n     *PRIMARY CARD REA...  \n",
       "16  *471*02/21/2025*13:39*\\n*3040*1*w*0, M-00, R-1...  \n",
       "17  *477*02/21/2025*13:40*\\n     *PRIMARY CARD REA...  \n",
       "18  *482*02/21/2025*13:41*\\n     *PRIMARY CARD REA...  \n",
       "19  *502*02/21/2025*13:55*\\n     *PRIMARY CARD REA...  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-ef0de80e-e90c-4786-ba32-d353676196ac\" class=\"colab-df-container\">\n",
       "    <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>source_file</th>\n",
       "      <th>seq</th>\n",
       "      <th>event_time</th>\n",
       "      <th>event_type</th>\n",
       "      <th>atm_id</th>\n",
       "      <th>tsn</th>\n",
       "      <th>host_tsn</th>\n",
       "      <th>amount</th>\n",
       "      <th>currency</th>\n",
       "      <th>denomination_counts</th>\n",
       "      <th>raw_redacted</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>342</td>\n",
       "      <td>2025-02-21 12:35:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3011</td>\n",
       "      <td>3011</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*342*02/21/2025*12:35*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>352</td>\n",
       "      <td>2025-02-21 12:37:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3013</td>\n",
       "      <td>3013</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*352*02/21/2025*12:37*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>355</td>\n",
       "      <td>2025-02-21 12:38:00</td>\n",
       "      <td>CASH_IN_STARTED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3015</td>\n",
       "      <td>None</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*355*02/21/2025*12:38*\\n     *CARDLESS TRANSAC...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>356</td>\n",
       "      <td>2025-02-21 12:40:00</td>\n",
       "      <td>CASH_IN_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3014</td>\n",
       "      <td>None</td>\n",
       "      <td>20.0</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*356*02/21/2025*12:40*\\n*3015*1*w*0, M-00, R-1...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>416</td>\n",
       "      <td>2025-02-21 13:14:00</td>\n",
       "      <td>CASH_IN_STARTED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3019</td>\n",
       "      <td>3019</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*416*02/21/2025*13:14*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>417</td>\n",
       "      <td>2025-02-21 13:15:00</td>\n",
       "      <td>CASH_IN_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3020</td>\n",
       "      <td>3019</td>\n",
       "      <td>10.0</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*417*02/21/2025*13:15*\\n*3019*1*w*0, M-00, R-1...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>423</td>\n",
       "      <td>2025-02-21 13:16:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3022</td>\n",
       "      <td>3022</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*423*02/21/2025*13:16*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>428</td>\n",
       "      <td>2025-02-21 13:17:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3024</td>\n",
       "      <td>3024</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*428*02/21/2025*13:17*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>433</td>\n",
       "      <td>2025-02-21 13:19:00</td>\n",
       "      <td>OTHER</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3025</td>\n",
       "      <td>3025</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*433*02/21/2025*13:19*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>439</td>\n",
       "      <td>2025-02-21 13:23:00</td>\n",
       "      <td>OTHER</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3027</td>\n",
       "      <td>3027</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*439*02/21/2025*13:23*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>445</td>\n",
       "      <td>2025-02-21 13:27:00</td>\n",
       "      <td>TRANSACTION_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3029</td>\n",
       "      <td>3029</td>\n",
       "      <td>500.0</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*445*02/21/2025*13:27*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>450</td>\n",
       "      <td>2025-02-21 13:31:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3032</td>\n",
       "      <td>3032</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*450*02/21/2025*13:31*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>455</td>\n",
       "      <td>2025-02-21 13:33:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3034</td>\n",
       "      <td>3034</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*455*02/21/2025*13:33*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>460</td>\n",
       "      <td>2025-02-21 13:34:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3036</td>\n",
       "      <td>3036</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*460*02/21/2025*13:34*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>465</td>\n",
       "      <td>2025-02-21 13:35:00</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3038</td>\n",
       "      <td>3038</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*465*02/21/2025*13:35*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>470</td>\n",
       "      <td>2025-02-21 13:38:00</td>\n",
       "      <td>CASH_IN_STARTED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3040</td>\n",
       "      <td>3040</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*470*02/21/2025*13:38*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>471</td>\n",
       "      <td>2025-02-21 13:39:00</td>\n",
       "      <td>CASH_IN_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3041</td>\n",
       "      <td>3040</td>\n",
       "      <td>10.0</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*471*02/21/2025*13:39*\\n*3040*1*w*0, M-00, R-1...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>477</td>\n",
       "      <td>2025-02-21 13:40:00</td>\n",
       "      <td>TRANSACTION_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3043</td>\n",
       "      <td>3043</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*477*02/21/2025*13:40*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>482</td>\n",
       "      <td>2025-02-21 13:41:00</td>\n",
       "      <td>TRANSACTION_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3045</td>\n",
       "      <td>3045</td>\n",
       "      <td>550.0</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*482*02/21/2025*13:41*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>502</td>\n",
       "      <td>2025-02-21 13:55:00</td>\n",
       "      <td>TRANSACTION_COMPLETED</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3048</td>\n",
       "      <td>3048</td>\n",
       "      <td>NaN</td>\n",
       "      <td>AED</td>\n",
       "      <td>{}</td>\n",
       "      <td>*502*02/21/2025*13:55*\\n     *PRIMARY CARD REA...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-ef0de80e-e90c-4786-ba32-d353676196ac')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-ef0de80e-e90c-4786-ba32-d353676196ac button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-ef0de80e-e90c-4786-ba32-d353676196ac');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(events\",\n  \"rows\": 20,\n  \"fields\": [\n    {\n      \"column\": \"source_file\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"EJ Data 2098.txt\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"seq\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 47,\n        \"min\": 342,\n        \"max\": 502,\n        \"num_unique_values\": 20,\n        \"samples\": [\n          342\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"event_time\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2025-02-21 12:35:00\",\n        \"max\": \"2025-02-21 13:55:00\",\n        \"num_unique_values\": 20,\n        \"samples\": [\n          \"2025-02-21 12:35:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"event_type\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 5,\n        \"samples\": [\n          \"CASH_IN_STARTED\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"ADB02098\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"tsn\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 20,\n        \"samples\": [\n          \"3011\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"host_tsn\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 16,\n        \"samples\": [\n          \"3011\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"amount\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 280.8380316125293,\n        \"min\": 10.0,\n        \"max\": 550.0,\n        \"num_unique_values\": 4,\n        \"samples\": [\n          10.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"currency\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"AED\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"denomination_counts\",\n      \"properties\": {\n        \"dtype\": \"object\",\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"raw_redacted\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 20,\n        \"samples\": [\n          \"*342*02/21/2025*12:35*\\n     *PRIMARY CARD READER ACTIVATED*\\n CARD INSERTED\\n 12:35:43 CARD TAKEN\\n 12:35:43 OPCODE = C CCC  A\\nRESPONSE: MESSAGE FORMAT ERROR\\nMACHINE ID: ADB02098\\nTSN: 3011\\nHOST TSN: 3011\\nCARD NO: [REDACTED]\\nFROM A/C#: \\n [REDACTED] TRANSACTION END\\n\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 5 — Parse journal into event rows\ndef infer_event_type(block: str) -> str:\n    u = block.upper()\n    if \"SUPERVISOR MODE ENTRY\" in u:\n        return \"SUPERVISOR_ENTRY\"\n    if \"SUPERVISOR MODE EXIT\" in u:\n        return \"SUPERVISOR_EXIT\"\n    if \"SST OUT OF SERVICE\" in u:\n        return \"ATM_OUT_OF_SERVICE\"\n    if \"SST IN SERVICE\" in u:\n        return \"ATM_IN_SERVICE\"\n    if \"CASHIN RECOVERY FAILED\" in u or \"HARDWARE ERROR\" in u:\n        return \"RECOVERY_FAILED\"\n    if \"CASHIN RECOVERY OK\" in u:\n        return \"RECOVERY_COMPLETED\"\n    if \"FAILED SERIAL NUMBER READS\" in u:\n        return \"SERIAL_READ_FAILURE\"\n    if \"CIM-DEPOSIT COMPLETED\" in u:\n        return \"CASH_IN_COMPLETED\"\n    if \"CASHIN DEPOSIT SELECTED\" in u:\n        return \"CASH_IN_SELECTED\"\n    if \"CIM-DEPOSIT ACTIVATED\" in u:\n        return \"CASH_IN_STARTED\"\n    if \"CIM-INPUT REFUSED\" in u:\n        return \"CASH_REJECTED\"\n    if \"TRANSACTION FAILED\" in u or \"MESSAGE FORMAT ERROR\" in u or \"INVALID CARD NUMBER\" in u:\n        return \"TRANSACTION_FAILED\"\n    if \"TRANSACTION END\" in u:\n        return \"TRANSACTION_COMPLETED\"\n    if \"TRANSACTION STARTED\" in u or \"CARDLESS TRANSACTION STARTED\" in u:\n        return \"TRANSACTION_STARTED\"\n    if \"BNA CASSETTE\" in u and \"REMOVED\" in u:\n        return \"CASSETTE_REMOVED\"\n    if \"BNA CASSETTE\" in u and \"INSERTED\" in u:\n        return \"CASSETTE_INSERTED\"\n    if \"DENOM   CASS\" in u:\n        return \"CASSETTE_SNAPSHOT\"\n    if \"CASH OUT\" in u or \"DISPENS\" in u or \"WITHDRAWAL\" in u:\n        return \"CASH_OUT_CANDIDATE\"\n    return \"OTHER\"\n\ndef split_blocks(text: str):\n    # Journal blocks begin with *sequence*date*time*\n    starts = [m.start() for m in re.finditer(r'(?m)^\\*\\d+\\*\\d{2}/\\d{2}/\\d{4}\\*\\d{2}:\\d{2}\\*', text)]\n    if not starts:\n        return [text]\n    starts.append(len(text))\n    return [text[starts[i]:starts[i+1]] for i in range(len(starts)-1)]\n\ndef parse_file(name, text):\n    events = []\n    for block in split_blocks(text):\n        first = HEADER_RE.search(block.splitlines()[0] if block.splitlines() else \"\")\n        seq = first.group(\"seq\") if first else None\n        event_date = first.group(\"date\") if first else None\n        event_time_hm = first.group(\"time\") if first else None\n\n        machine = MACHINE_RE.search(block)\n        tsn = TSN_RE.search(block)\n        host = HOST_TSN_RE.search(block)\n        amount = AMOUNT_RE.search(block)\n\n        tx_date = tx_time = None\n        dt_match = DATE_RE.search(block)\n        if dt_match:\n            tx_date, tx_time = dt_match.group(\"date\"), dt_match.group(\"time\")\n\n        if tx_date and tx_time:\n            dt = parse_dt(tx_date, tx_time)\n        elif event_date and event_time_hm:\n            dt = datetime.strptime(f\"{event_date} {event_time_hm}\", \"%m/%d/%Y %H:%M\")\n        else:\n            dt = pd.NaT\n\n        event_type = infer_event_type(block)\n\n        # Denomination lines inside the block\n        denoms = {}\n        for m in DENOM_LINE_RE.finditer(block):\n            denoms[f\"AED{m.group('denom')}\"] = int(m.group(\"count\"))\n\n        row = {\n            \"source_file\": name,\n            \"seq\": int(seq) if seq else None,\n            \"event_time\": dt,\n            \"event_type\": event_type,\n            \"atm_id\": machine.group(\"machine\") if machine else None,\n            \"tsn\": tsn.group(\"tsn\") if tsn else None,\n            \"host_tsn\": host.group(\"host_tsn\") if host else None,\n            \"amount\": float(amount.group(\"amount\").replace(\",\", \"\")) if amount else None,\n            \"currency\": amount.group(\"ccy\") if amount and amount.group(\"ccy\") else \"AED\",\n            \"denomination_counts\": denoms,\n            \"raw_redacted\": redact_sensitive(block[:1200]),\n        }\n        events.append(row)\n\n    return pd.DataFrame(events)\n\nevents = pd.concat(\n    [parse_file(name, text) for name, text in raw.items()],\n    ignore_index=True\n)\n\nevents[\"event_time\"] = pd.to_datetime(events[\"event_time\"], errors=\"coerce\")\nevents = events.sort_values([\"atm_id\", \"event_time\", \"seq\"], na_position=\"last\").reset_index(drop=True)\n\nprint(events.shape)\ndisplay(events.head(20))\n",
   "id": "jYUask-h6ijm"
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 968
    },
    "id": "ruZLJwyx6ijo",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557265106,
     "user_tz": -180,
     "elapsed": 60,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "5438bb07-96a3-49ad-8e9a-8a7de53d4f12"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "           source_file             event_type  count\n",
       "2   AHB deposit AJ.txt                  OTHER     30\n",
       "4   AHB deposit AJ.txt  TRANSACTION_COMPLETED      9\n",
       "5   AHB deposit AJ.txt    TRANSACTION_STARTED      9\n",
       "1   AHB deposit AJ.txt        CASH_IN_STARTED      5\n",
       "3   AHB deposit AJ.txt    SERIAL_READ_FAILURE      4\n",
       "0   AHB deposit AJ.txt      CASH_IN_COMPLETED      2\n",
       "12    EJ Data 2098.txt                  OTHER    118\n",
       "17    EJ Data 2098.txt  TRANSACTION_COMPLETED     28\n",
       "19    EJ Data 2098.txt    TRANSACTION_STARTED     22\n",
       "18    EJ Data 2098.txt     TRANSACTION_FAILED      9\n",
       "6     EJ Data 2098.txt         ATM_IN_SERVICE      7\n",
       "7     EJ Data 2098.txt     ATM_OUT_OF_SERVICE      7\n",
       "8     EJ Data 2098.txt      CASH_IN_COMPLETED      3\n",
       "9     EJ Data 2098.txt        CASH_IN_STARTED      3\n",
       "15    EJ Data 2098.txt       SUPERVISOR_ENTRY      3\n",
       "16    EJ Data 2098.txt        SUPERVISOR_EXIT      3\n",
       "11    EJ Data 2098.txt       CASSETTE_REMOVED      2\n",
       "14    EJ Data 2098.txt        RECOVERY_FAILED      2\n",
       "10    EJ Data 2098.txt      CASSETTE_INSERTED      1\n",
       "13    EJ Data 2098.txt     RECOVERY_COMPLETED      1"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-82cc46af-f0e8-4bdb-89fd-f3dd357eba35\" class=\"colab-df-container\">\n",
       "    <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>source_file</th>\n",
       "      <th>event_type</th>\n",
       "      <th>count</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>OTHER</td>\n",
       "      <td>30</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>TRANSACTION_COMPLETED</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>TRANSACTION_STARTED</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>CASH_IN_STARTED</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>SERIAL_READ_FAILURE</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>CASH_IN_COMPLETED</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>OTHER</td>\n",
       "      <td>118</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>TRANSACTION_COMPLETED</td>\n",
       "      <td>28</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>TRANSACTION_STARTED</td>\n",
       "      <td>22</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>TRANSACTION_FAILED</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ATM_IN_SERVICE</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ATM_OUT_OF_SERVICE</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>CASH_IN_COMPLETED</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>CASH_IN_STARTED</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>SUPERVISOR_ENTRY</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>SUPERVISOR_EXIT</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>CASSETTE_REMOVED</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>RECOVERY_FAILED</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>CASSETTE_INSERTED</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>RECOVERY_COMPLETED</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-82cc46af-f0e8-4bdb-89fd-f3dd357eba35')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-82cc46af-f0e8-4bdb-89fd-f3dd357eba35 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-82cc46af-f0e8-4bdb-89fd-f3dd357eba35');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "  <div id=\"id_03b527b9-386b-4e70-ae37-8da77e297d9e\">\n",
       "    <style>\n",
       "      .colab-df-generate {\n",
       "        background-color: #E8F0FE;\n",
       "        border: none;\n",
       "        border-radius: 50%;\n",
       "        cursor: pointer;\n",
       "        display: none;\n",
       "        fill: #1967D2;\n",
       "        height: 32px;\n",
       "        padding: 0 0 0 0;\n",
       "        width: 32px;\n",
       "      }\n",
       "\n",
       "      .colab-df-generate:hover {\n",
       "        background-color: #E2EBFA;\n",
       "        box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "        fill: #174EA6;\n",
       "      }\n",
       "\n",
       "      [theme=dark] .colab-df-generate {\n",
       "        background-color: #3B4455;\n",
       "        fill: #D2E3FC;\n",
       "      }\n",
       "\n",
       "      [theme=dark] .colab-df-generate:hover {\n",
       "        background-color: #434B5C;\n",
       "        box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "        filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "        fill: #FFFFFF;\n",
       "      }\n",
       "    </style>\n",
       "    <button class=\"colab-df-generate\" onclick=\"generateWithVariable('coverage')\"\n",
       "            title=\"Generate code using this dataframe.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
       "       width=\"24px\">\n",
       "    <path d=\"M7,19H8.4L18.45,9,17,7.55,7,17.6ZM5,21V16.75L18.45,3.32a2,2,0,0,1,2.83,0l1.4,1.43a1.91,1.91,0,0,1,.58,1.4,1.91,1.91,0,0,1-.58,1.4L9.25,21ZM18.45,9,17,7.55Zm-12,3A5.31,5.31,0,0,0,4.9,8.1,5.31,5.31,0,0,0,1,6.5,5.31,5.31,0,0,0,4.9,4.9,5.31,5.31,0,0,0,6.5,1,5.31,5.31,0,0,0,8.1,4.9,5.31,5.31,0,0,0,12,6.5,5.46,5.46,0,0,0,6.5,12Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "    <script>\n",
       "      (() => {\n",
       "      const buttonEl =\n",
       "        document.querySelector('#id_03b527b9-386b-4e70-ae37-8da77e297d9e button.colab-df-generate');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      buttonEl.onclick = () => {\n",
       "        google.colab.notebook.generateWithVariable('coverage');\n",
       "      }\n",
       "      })();\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "variable_name": "coverage",
       "summary": "{\n  \"name\": \"coverage\",\n  \"rows\": 20,\n  \"fields\": [\n    {\n      \"column\": \"source_file\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 2,\n        \"samples\": [\n          \"EJ Data 2098.txt\",\n          \"AHB deposit AJ.txt\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"event_type\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 15,\n        \"samples\": [\n          \"SUPERVISOR_ENTRY\",\n          \"CASSETTE_REMOVED\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"count\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 26,\n        \"min\": 1,\n        \"max\": 118,\n        \"num_unique_values\": 11,\n        \"samples\": [\n          118,\n          30\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "\n",
      "ATM population:\n"
     ]
    },
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "     atm_id  events\n",
       "0  ADB02098      24\n",
       "1  ADB05181       8\n",
       "2  ADB05445       2\n",
       "3  ADB05296       1\n",
       "4  ADB05446       1\n",
       "5  ADB05632       1\n",
       "6  ADB05660       1"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-aa8802b2-433a-468c-8f01-9bb1055c87f6\" class=\"colab-df-container\">\n",
       "    <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>atm_id</th>\n",
       "      <th>events</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>24</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ADB05446</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>ADB05632</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ADB05660</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-aa8802b2-433a-468c-8f01-9bb1055c87f6')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-aa8802b2-433a-468c-8f01-9bb1055c87f6 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-aa8802b2-433a-468c-8f01-9bb1055c87f6');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"      int((events[\\\"event_type\\\"] == \\\"CASH_OUT_CANDIDATE\\\")\",\n  \"rows\": 7,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 7,\n        \"samples\": [\n          \"ADB02098\",\n          \"ADB05181\",\n          \"ADB05632\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"events\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 8,\n        \"min\": 1,\n        \"max\": 24,\n        \"num_unique_values\": 4,\n        \"samples\": [\n          8,\n          1,\n          24\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "\n",
      "Cash-out candidate events found: 0\n"
     ]
    }
   ],
   "source": "# 6 — Data quality / parser coverage report\ncoverage = (\n    events.groupby([\"source_file\", \"event_type\"], dropna=False)\n    .size()\n    .reset_index(name=\"count\")\n    .sort_values([\"source_file\", \"count\"], ascending=[True, False])\n)\n\ndisplay(coverage)\n\nprint(\"\\nATM population:\")\ndisplay(events[\"atm_id\"].dropna().value_counts().rename_axis(\"atm_id\").reset_index(name=\"events\"))\n\nprint(\"\\nCash-out candidate events found:\",\n      int((events[\"event_type\"] == \"CASH_OUT_CANDIDATE\").sum()))\n",
   "id": "ruZLJwyx6ijo"
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 931
    },
    "id": "6otFLnpO6ijp",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557272074,
     "user_tz": -180,
     "elapsed": 116,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "5a1fa832-cd73-4081-a70f-c5835152cbc9"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "           source_file    atm_id   tsn         first_event  \\\n",
       "0   AHB deposit AJ.txt  ADB05181  9576 2025-02-25 18:44:00   \n",
       "1   AHB deposit AJ.txt  ADB05181  9683 2025-02-25 20:24:00   \n",
       "2   AHB deposit AJ.txt  ADB05296  6096 2025-02-17 20:02:00   \n",
       "3   AHB deposit AJ.txt  ADB05446  3373 2025-02-26 09:15:00   \n",
       "4   AHB deposit AJ.txt  ADB05632  6818 2025-01-27 11:37:00   \n",
       "5   AHB deposit AJ.txt  ADB05660  7406 2025-02-25 21:14:00   \n",
       "6   AHB deposit AJ.txt       NaN   NaN 2025-02-25 18:45:00   \n",
       "7     EJ Data 2098.txt  ADB02098  3011 2025-02-21 12:35:00   \n",
       "8     EJ Data 2098.txt  ADB02098  3013 2025-02-21 12:37:00   \n",
       "9     EJ Data 2098.txt  ADB02098  3014 2025-02-21 12:40:00   \n",
       "10    EJ Data 2098.txt  ADB02098  3020 2025-02-21 13:15:00   \n",
       "11    EJ Data 2098.txt  ADB02098  3022 2025-02-21 13:16:00   \n",
       "12    EJ Data 2098.txt  ADB02098  3024 2025-02-21 13:17:00   \n",
       "13    EJ Data 2098.txt  ADB02098  3029 2025-02-21 13:27:00   \n",
       "14    EJ Data 2098.txt  ADB02098  3032 2025-02-21 13:31:00   \n",
       "15    EJ Data 2098.txt  ADB02098  3034 2025-02-21 13:33:00   \n",
       "16    EJ Data 2098.txt  ADB02098  3036 2025-02-21 13:34:00   \n",
       "17    EJ Data 2098.txt  ADB02098  3038 2025-02-21 13:35:00   \n",
       "18    EJ Data 2098.txt  ADB02098  3041 2025-02-21 13:39:00   \n",
       "19    EJ Data 2098.txt  ADB02098  3043 2025-02-21 13:40:00   \n",
       "20    EJ Data 2098.txt  ADB02098  3045 2025-02-21 13:41:00   \n",
       "21    EJ Data 2098.txt  ADB02098  3048 2025-02-21 13:55:00   \n",
       "22    EJ Data 2098.txt  ADB02098  3050 2025-02-21 13:56:00   \n",
       "23    EJ Data 2098.txt  ADB02098  3052 2025-02-21 13:57:00   \n",
       "24    EJ Data 2098.txt  ADB02098  3054 2025-02-21 13:58:00   \n",
       "25    EJ Data 2098.txt  ADB02098  3099 2025-02-21 15:34:00   \n",
       "26    EJ Data 2098.txt       NaN  3026 2025-02-21 13:22:00   \n",
       "27    EJ Data 2098.txt       NaN  3028 2025-02-21 13:25:00   \n",
       "28    EJ Data 2098.txt       NaN   NaN 2025-02-21 12:32:00   \n",
       "\n",
       "            last_event  event_count   amount  cash_in  cash_out  failed  \\\n",
       "0  2025-02-25 18:44:00            1   4500.0        1         0       0   \n",
       "1  2025-02-25 20:24:00            1    500.0        1         0       0   \n",
       "2  2025-02-17 20:02:00            1   6000.0        0         0       0   \n",
       "3  2025-02-26 09:15:00            1  20000.0        0         0       0   \n",
       "4  2025-01-27 11:37:00            1   4398.0        0         0       0   \n",
       "5  2025-02-25 21:14:00            1  23000.0        0         0       0   \n",
       "6  2025-02-26 00:18:00            5      NaN        0         0       0   \n",
       "7  2025-02-21 12:35:00            1      NaN        0         0       1   \n",
       "8  2025-02-21 12:37:00            1      NaN        0         0       1   \n",
       "9  2025-02-21 12:40:00            1     20.0        1         0       0   \n",
       "10 2025-02-21 13:15:00            1     10.0        1         0       0   \n",
       "11 2025-02-21 13:16:00            1      NaN        0         0       1   \n",
       "12 2025-02-21 13:17:00            1      NaN        0         0       1   \n",
       "13 2025-02-21 13:27:00            1    500.0        0         0       0   \n",
       "14 2025-02-21 13:31:00            1      NaN        0         0       1   \n",
       "15 2025-02-21 13:33:00            1      NaN        0         0       1   \n",
       "16 2025-02-21 13:34:00            1      NaN        0         0       1   \n",
       "17 2025-02-21 13:35:00            1      NaN        0         0       1   \n",
       "18 2025-02-21 13:39:00            1     10.0        1         0       0   \n",
       "19 2025-02-21 13:40:00            1      NaN        0         0       0   \n",
       "20 2025-02-21 13:41:00            1    550.0        0         0       0   \n",
       "21 2025-02-21 13:55:00            1      NaN        0         0       0   \n",
       "22 2025-02-21 13:56:00            1      NaN        0         0       0   \n",
       "23 2025-02-21 13:57:00            1      NaN        0         0       0   \n",
       "24 2025-02-21 13:58:00            1      NaN        0         0       0   \n",
       "25 2025-02-21 15:34:00            1      NaN        0         0       1   \n",
       "26 2025-02-21 13:22:00            1      NaN        0         0       0   \n",
       "27 2025-02-21 13:25:00            1      NaN        0         0       0   \n",
       "28 2025-02-21 13:54:00           19      NaN        0         0       0   \n",
       "\n",
       "    duration_sec  \n",
       "0            0.0  \n",
       "1            0.0  \n",
       "2            0.0  \n",
       "3            0.0  \n",
       "4            0.0  \n",
       "5            0.0  \n",
       "6        19980.0  \n",
       "7            0.0  \n",
       "8            0.0  \n",
       "9            0.0  \n",
       "10           0.0  \n",
       "11           0.0  \n",
       "12           0.0  \n",
       "13           0.0  \n",
       "14           0.0  \n",
       "15           0.0  \n",
       "16           0.0  \n",
       "17           0.0  \n",
       "18           0.0  \n",
       "19           0.0  \n",
       "20           0.0  \n",
       "21           0.0  \n",
       "22           0.0  \n",
       "23           0.0  \n",
       "24           0.0  \n",
       "25           0.0  \n",
       "26           0.0  \n",
       "27           0.0  \n",
       "28        4920.0  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-fbd78705-ecaa-4255-ae12-1e9342ef4a42\" class=\"colab-df-container\">\n",
       "    <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>source_file</th>\n",
       "      <th>atm_id</th>\n",
       "      <th>tsn</th>\n",
       "      <th>first_event</th>\n",
       "      <th>last_event</th>\n",
       "      <th>event_count</th>\n",
       "      <th>amount</th>\n",
       "      <th>cash_in</th>\n",
       "      <th>cash_out</th>\n",
       "      <th>failed</th>\n",
       "      <th>duration_sec</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>ADB05181</td>\n",
       "      <td>9576</td>\n",
       "      <td>2025-02-25 18:44:00</td>\n",
       "      <td>2025-02-25 18:44:00</td>\n",
       "      <td>1</td>\n",
       "      <td>4500.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>ADB05181</td>\n",
       "      <td>9683</td>\n",
       "      <td>2025-02-25 20:24:00</td>\n",
       "      <td>2025-02-25 20:24:00</td>\n",
       "      <td>1</td>\n",
       "      <td>500.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>ADB05296</td>\n",
       "      <td>6096</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>1</td>\n",
       "      <td>6000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>ADB05446</td>\n",
       "      <td>3373</td>\n",
       "      <td>2025-02-26 09:15:00</td>\n",
       "      <td>2025-02-26 09:15:00</td>\n",
       "      <td>1</td>\n",
       "      <td>20000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>ADB05632</td>\n",
       "      <td>6818</td>\n",
       "      <td>2025-01-27 11:37:00</td>\n",
       "      <td>2025-01-27 11:37:00</td>\n",
       "      <td>1</td>\n",
       "      <td>4398.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>ADB05660</td>\n",
       "      <td>7406</td>\n",
       "      <td>2025-02-25 21:14:00</td>\n",
       "      <td>2025-02-25 21:14:00</td>\n",
       "      <td>1</td>\n",
       "      <td>23000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>AHB deposit AJ.txt</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>2025-02-25 18:45:00</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>5</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>19980.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3011</td>\n",
       "      <td>2025-02-21 12:35:00</td>\n",
       "      <td>2025-02-21 12:35:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3013</td>\n",
       "      <td>2025-02-21 12:37:00</td>\n",
       "      <td>2025-02-21 12:37:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3014</td>\n",
       "      <td>2025-02-21 12:40:00</td>\n",
       "      <td>2025-02-21 12:40:00</td>\n",
       "      <td>1</td>\n",
       "      <td>20.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3020</td>\n",
       "      <td>2025-02-21 13:15:00</td>\n",
       "      <td>2025-02-21 13:15:00</td>\n",
       "      <td>1</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3022</td>\n",
       "      <td>2025-02-21 13:16:00</td>\n",
       "      <td>2025-02-21 13:16:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3024</td>\n",
       "      <td>2025-02-21 13:17:00</td>\n",
       "      <td>2025-02-21 13:17:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3029</td>\n",
       "      <td>2025-02-21 13:27:00</td>\n",
       "      <td>2025-02-21 13:27:00</td>\n",
       "      <td>1</td>\n",
       "      <td>500.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3032</td>\n",
       "      <td>2025-02-21 13:31:00</td>\n",
       "      <td>2025-02-21 13:31:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3034</td>\n",
       "      <td>2025-02-21 13:33:00</td>\n",
       "      <td>2025-02-21 13:33:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3036</td>\n",
       "      <td>2025-02-21 13:34:00</td>\n",
       "      <td>2025-02-21 13:34:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3038</td>\n",
       "      <td>2025-02-21 13:35:00</td>\n",
       "      <td>2025-02-21 13:35:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3041</td>\n",
       "      <td>2025-02-21 13:39:00</td>\n",
       "      <td>2025-02-21 13:39:00</td>\n",
       "      <td>1</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3043</td>\n",
       "      <td>2025-02-21 13:40:00</td>\n",
       "      <td>2025-02-21 13:40:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3045</td>\n",
       "      <td>2025-02-21 13:41:00</td>\n",
       "      <td>2025-02-21 13:41:00</td>\n",
       "      <td>1</td>\n",
       "      <td>550.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3048</td>\n",
       "      <td>2025-02-21 13:55:00</td>\n",
       "      <td>2025-02-21 13:55:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3050</td>\n",
       "      <td>2025-02-21 13:56:00</td>\n",
       "      <td>2025-02-21 13:56:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3052</td>\n",
       "      <td>2025-02-21 13:57:00</td>\n",
       "      <td>2025-02-21 13:57:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3054</td>\n",
       "      <td>2025-02-21 13:58:00</td>\n",
       "      <td>2025-02-21 13:58:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>ADB02098</td>\n",
       "      <td>3099</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3026</td>\n",
       "      <td>2025-02-21 13:22:00</td>\n",
       "      <td>2025-02-21 13:22:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3028</td>\n",
       "      <td>2025-02-21 13:25:00</td>\n",
       "      <td>2025-02-21 13:25:00</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>EJ Data 2098.txt</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>2025-02-21 12:32:00</td>\n",
       "      <td>2025-02-21 13:54:00</td>\n",
       "      <td>19</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>4920.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-fbd78705-ecaa-4255-ae12-1e9342ef4a42')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-fbd78705-ecaa-4255-ae12-1e9342ef4a42 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-fbd78705-ecaa-4255-ae12-1e9342ef4a42');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(tx_agg\",\n  \"rows\": 29,\n  \"fields\": [\n    {\n      \"column\": \"source_file\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 2,\n        \"samples\": [\n          \"EJ Data 2098.txt\",\n          \"AHB deposit AJ.txt\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 6,\n        \"samples\": [\n          \"ADB05181\",\n          \"ADB05296\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"tsn\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 27,\n        \"samples\": [\n          \"3014\",\n          \"3032\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"first_event\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2025-01-27 11:37:00\",\n        \"max\": \"2025-02-26 09:15:00\",\n        \"num_unique_values\": 29,\n        \"samples\": [\n          \"2025-02-21 13:25:00\",\n          \"2025-02-21 13:34:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"last_event\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2025-01-27 11:37:00\",\n        \"max\": \"2025-02-26 09:15:00\",\n        \"num_unique_values\": 29,\n        \"samples\": [\n          \"2025-02-21 13:25:00\",\n          \"2025-02-21 13:34:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"event_count\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3,\n        \"min\": 1,\n        \"max\": 19,\n        \"num_unique_values\": 3,\n        \"samples\": [\n          1,\n          5\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"amount\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 8268.521028575788,\n        \"min\": 10.0,\n        \"max\": 23000.0,\n        \"num_unique_values\": 9,\n        \"samples\": [\n          10.0,\n          500.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cash_in\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 1,\n        \"num_unique_values\": 2,\n        \"samples\": [\n          0,\n          1\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cash_out\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"failed\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 1,\n        \"num_unique_values\": 2,\n        \"samples\": [\n          1\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"duration_sec\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3789.2099856396085,\n        \"min\": 0.0,\n        \"max\": 19980.0,\n        \"num_unique_values\": 3,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 7 — Extract business transaction aggregates\nTX_TYPES = {\n    \"CASH_IN_COMPLETED\",\n    \"CASH_OUT_COMPLETED\",\n    \"TRANSACTION_FAILED\",\n    \"TRANSACTION_COMPLETED\",\n}\n\ntx_events = events[events[\"event_type\"].isin(TX_TYPES)].copy()\n\ntx_agg = (\n    tx_events.groupby([\"source_file\", \"atm_id\", \"tsn\"], dropna=False)\n    .agg(\n        first_event=(\"event_time\", \"min\"),\n        last_event=(\"event_time\", \"max\"),\n        event_count=(\"event_type\", \"size\"),\n        amount=(\"amount\", \"max\"),\n        cash_in=(\"event_type\", lambda s: int((s == \"CASH_IN_COMPLETED\").any())),\n        cash_out=(\"event_type\", lambda s: int((s == \"CASH_OUT_COMPLETED\").any())),\n        failed=(\"event_type\", lambda s: int((s == \"TRANSACTION_FAILED\").any())),\n    )\n    .reset_index()\n)\n\ntx_agg[\"duration_sec\"] = (\n    tx_agg[\"last_event\"] - tx_agg[\"first_event\"]\n).dt.total_seconds()\n\ndisplay(tx_agg.head(30))\n",
   "id": "6otFLnpO6ijp"
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 52
    },
    "id": "oZoE5j_86ijq",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557282792,
     "user_tz": -180,
     "elapsed": 42,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "e784b2e7-4eeb-454b-af09-a2fc7b325312"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "Empty DataFrame\n",
       "Columns: []\n",
       "Index: []"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-a1b8ca4c-57fe-4154-a0d2-ae3e6bd4b438\" class=\"colab-df-container\">\n",
       "    <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",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-a1b8ca4c-57fe-4154-a0d2-ae3e6bd4b438')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-a1b8ca4c-57fe-4154-a0d2-ae3e6bd4b438 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-a1b8ca4c-57fe-4154-a0d2-ae3e6bd4b438');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"    )\",\n  \"rows\": 0,\n  \"fields\": []\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 8 — Cash-in denomination extraction\ndenom_rows = []\nfor _, r in events[events[\"event_type\"] == \"CASH_IN_COMPLETED\"].iterrows():\n    for denom, count in (r[\"denomination_counts\"] or {}).items():\n        denom_rows.append({\n            \"source_file\": r[\"source_file\"],\n            \"atm_id\": r[\"atm_id\"],\n            \"event_time\": r[\"event_time\"],\n            \"tsn\": r[\"tsn\"],\n            \"denomination\": denom,\n            \"notes\": count,\n            \"cash_value\": int(denom.replace(\"AED\",\"\")) * count\n        })\n\ncashin_denoms = pd.DataFrame(denom_rows)\ndisplay(cashin_denoms.head(30))\n\nif not cashin_denoms.empty:\n    print(\"Cash-in value by denomination:\")\n    display(\n        cashin_denoms.groupby(\"denomination\")[\"cash_value\"]\n        .sum().sort_index().to_frame()\n    )\n",
   "id": "oZoE5j_86ijq"
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 52
    },
    "id": "Ygyu-zXT6ijr",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557286663,
     "user_tz": -180,
     "elapsed": 17,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "bf06e4c7-9798-4391-fcbc-5328fab638dc"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "Empty DataFrame\n",
       "Columns: []\n",
       "Index: []"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-245d0307-ebd7-4458-9983-cdf5a2104f83\" class=\"colab-df-container\">\n",
       "    <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",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-245d0307-ebd7-4458-9983-cdf5a2104f83')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-245d0307-ebd7-4458-9983-cdf5a2104f83 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-245d0307-ebd7-4458-9983-cdf5a2104f83');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(snapshots\",\n  \"rows\": 0,\n  \"fields\": []\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 9 — Parse cassette snapshots\nsnapshot_rows = []\nfor _, r in events.iterrows():\n    if r[\"event_type\"] == \"CASSETTE_SNAPSHOT\":\n        # The generic parser stores denomination rows; for snapshot lines\n        # we keep the surrounding raw block for a richer parser in v2.\n        snapshot_rows.append({\n            \"source_file\": r[\"source_file\"],\n            \"atm_id\": r[\"atm_id\"],\n            \"event_time\": r[\"event_time\"],\n            \"tsn\": r[\"tsn\"],\n            \"denomination_counts\": r[\"denomination_counts\"]\n        })\n\nsnapshots = pd.DataFrame(snapshot_rows)\ndisplay(snapshots.head(10))\n",
   "id": "Ygyu-zXT6ijr"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "QurHu7fR6ijt"
   },
   "source": "## 10 — Inventory state reconstruction\n\nFor the POC we use the latest valid denomination snapshot as the observed ATM inventory state.\n\n**Production v2 requirement:** inventory transitions must be reconciled against cassette movement, cash-in/cash-out transactions and CIT replenishment confirmations. A snapshot alone is not sufficient to guarantee accounting-grade inventory.\n",
   "id": "QurHu7fR6ijt"
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "id": "6aJcbEfQ6iju",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557290519,
     "user_tz": -180,
     "elapsed": 5,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    }
   },
   "outputs": [],
   "source": "# 11 — Build a latest-observed inventory state per ATM\ndef explode_snapshot_counts(df):\n    rows = []\n    for _, r in df.iterrows():\n        counts = r[\"denomination_counts\"] or {}\n        for denom, count in counts.items():\n            rows.append({\n                \"atm_id\": r[\"atm_id\"],\n                \"event_time\": r[\"event_time\"],\n                \"denomination\": denom,\n                \"notes\": count,\n                \"cash_value\": int(denom.replace(\"AED\",\"\")) * int(count),\n            })\n    return pd.DataFrame(rows)\n\nsnapshot_notes = explode_snapshot_counts(snapshots)\n\nif not snapshot_notes.empty:\n    latest_state = (\n        snapshot_notes.sort_values(\"event_time\")\n        .groupby([\"atm_id\", \"denomination\"], as_index=False)\n        .tail(1)\n        .sort_values([\"atm_id\",\"denomination\"])\n    )\n    display(latest_state)\n\n    atm_cash = (\n        latest_state.groupby(\"atm_id\")\n        .agg(\n            cash_on_hand=(\"cash_value\", \"sum\"),\n            observed_denoms=(\"denomination\", \"nunique\"),\n            last_snapshot=(\"event_time\", \"max\")\n        )\n        .reset_index()\n    )\n    display(atm_cash)\nelse:\n    latest_state = pd.DataFrame()\n    atm_cash = pd.DataFrame(columns=[\"atm_id\",\"cash_on_hand\",\"observed_denoms\",\"last_snapshot\"])\n",
   "id": "6aJcbEfQ6iju"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Y-qR4uFi6ijv"
   },
   "source": "## 12 — Operational risk features\n\nJournal events can enrich the forecasting/optimization problem with operational state.\n\nExamples from the supplied journals include supervisor interventions, cassette manipulation, recovery failures, hardware errors, invalid transactions and retry-related outcomes. These are not cash demand themselves; they become **risk/context variables**.\n",
   "id": "Y-qR4uFi6ijv"
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 160
    },
    "id": "UEbA5gex6ijw",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557295622,
     "user_tz": -180,
     "elapsed": 103,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "f2b7fb6b-e8b5-4891-ec64-d9142762e3d9"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "risk_metric    atm_id  transaction_failures  serial_read_failures  \\\n",
       "0            ADB02098                     9                     0   \n",
       "1            ADB05181                     0                     2   \n",
       "2            ADB05445                     0                     1   \n",
       "\n",
       "risk_metric  supervisor_entries  recovery_failures  recovery_successes  \\\n",
       "0                             0                  0                   0   \n",
       "1                             0                  0                   0   \n",
       "2                             0                  0                   0   \n",
       "\n",
       "risk_metric  oos_events  in_service_events  cassette_removed  \\\n",
       "0                     0                  0                 0   \n",
       "1                     0                  0                 0   \n",
       "2                     0                  0                 0   \n",
       "\n",
       "risk_metric  cassette_inserted  \n",
       "0                            0  \n",
       "1                            0  \n",
       "2                            0  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-549c058c-4abe-4e21-80ee-9161efd748ff\" class=\"colab-df-container\">\n",
       "    <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>risk_metric</th>\n",
       "      <th>atm_id</th>\n",
       "      <th>transaction_failures</th>\n",
       "      <th>serial_read_failures</th>\n",
       "      <th>supervisor_entries</th>\n",
       "      <th>recovery_failures</th>\n",
       "      <th>recovery_successes</th>\n",
       "      <th>oos_events</th>\n",
       "      <th>in_service_events</th>\n",
       "      <th>cassette_removed</th>\n",
       "      <th>cassette_inserted</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>9</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-549c058c-4abe-4e21-80ee-9161efd748ff')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-549c058c-4abe-4e21-80ee-9161efd748ff button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-549c058c-4abe-4e21-80ee-9161efd748ff');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "  <div id=\"id_bead6723-b8c9-4315-95da-d17acbc1d8a9\">\n",
       "    <style>\n",
       "      .colab-df-generate {\n",
       "        background-color: #E8F0FE;\n",
       "        border: none;\n",
       "        border-radius: 50%;\n",
       "        cursor: pointer;\n",
       "        display: none;\n",
       "        fill: #1967D2;\n",
       "        height: 32px;\n",
       "        padding: 0 0 0 0;\n",
       "        width: 32px;\n",
       "      }\n",
       "\n",
       "      .colab-df-generate:hover {\n",
       "        background-color: #E2EBFA;\n",
       "        box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "        fill: #174EA6;\n",
       "      }\n",
       "\n",
       "      [theme=dark] .colab-df-generate {\n",
       "        background-color: #3B4455;\n",
       "        fill: #D2E3FC;\n",
       "      }\n",
       "\n",
       "      [theme=dark] .colab-df-generate:hover {\n",
       "        background-color: #434B5C;\n",
       "        box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "        filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "        fill: #FFFFFF;\n",
       "      }\n",
       "    </style>\n",
       "    <button class=\"colab-df-generate\" onclick=\"generateWithVariable('risk_features')\"\n",
       "            title=\"Generate code using this dataframe.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
       "       width=\"24px\">\n",
       "    <path d=\"M7,19H8.4L18.45,9,17,7.55,7,17.6ZM5,21V16.75L18.45,3.32a2,2,0,0,1,2.83,0l1.4,1.43a1.91,1.91,0,0,1,.58,1.4,1.91,1.91,0,0,1-.58,1.4L9.25,21ZM18.45,9,17,7.55Zm-12,3A5.31,5.31,0,0,0,4.9,8.1,5.31,5.31,0,0,0,1,6.5,5.31,5.31,0,0,0,4.9,4.9,5.31,5.31,0,0,0,6.5,1,5.31,5.31,0,0,0,8.1,4.9,5.31,5.31,0,0,0,12,6.5,5.46,5.46,0,0,0,6.5,12Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "    <script>\n",
       "      (() => {\n",
       "      const buttonEl =\n",
       "        document.querySelector('#id_bead6723-b8c9-4315-95da-d17acbc1d8a9 button.colab-df-generate');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      buttonEl.onclick = () => {\n",
       "        google.colab.notebook.generateWithVariable('risk_features');\n",
       "      }\n",
       "      })();\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "variable_name": "risk_features",
       "summary": "{\n  \"name\": \"risk_features\",\n  \"rows\": 3,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"ADB02098\",\n          \"ADB05181\",\n          \"ADB05445\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"transaction_failures\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 5,\n        \"min\": 0,\n        \"max\": 9,\n        \"num_unique_values\": 2,\n        \"samples\": [\n          0,\n          9\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"serial_read_failures\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 1,\n        \"min\": 0,\n        \"max\": 2,\n        \"num_unique_values\": 3,\n        \"samples\": [\n          0,\n          2\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"supervisor_entries\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"recovery_failures\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"recovery_successes\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"oos_events\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"in_service_events\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cassette_removed\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cassette_inserted\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 13 — Operational risk features\nrisk_event_map = {\n    \"SUPERVISOR_ENTRY\": \"supervisor_entries\",\n    \"RECOVERY_FAILED\": \"recovery_failures\",\n    \"RECOVERY_COMPLETED\": \"recovery_successes\",\n    \"TRANSACTION_FAILED\": \"transaction_failures\",\n    \"ATM_OUT_OF_SERVICE\": \"oos_events\",\n    \"ATM_IN_SERVICE\": \"in_service_events\",\n    \"SERIAL_READ_FAILURE\": \"serial_read_failures\",\n    \"CASSETTE_REMOVED\": \"cassette_removed\",\n    \"CASSETTE_INSERTED\": \"cassette_inserted\",\n}\n\nrisk = events.copy()\nrisk[\"risk_metric\"] = risk[\"event_type\"].map(risk_event_map)\n\nrisk_features = (\n    risk.dropna(subset=[\"risk_metric\"])\n    .groupby([\"atm_id\", \"risk_metric\"])\n    .size()\n    .unstack(fill_value=0)\n    .reset_index()\n)\n\nfor col in risk_event_map.values():\n    if col not in risk_features.columns:\n        risk_features[col] = 0\n\ndisplay(risk_features)\n",
   "id": "UEbA5gex6ijw"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qV9E9xx76ijx"
   },
   "source": "## 14 — Optimization-ready feature contract\n\nThe downstream optimizer should consume a clean table like:\n\n```text\natm_id\nas_of_time\ndenomination\ncurrent_notes\nforecast_p50\nforecast_p80\nforecast_p95\nmin_stock\nmax_stock\ncassette_capacity\nservice_cost\ncash_holding_cost\nstockout_cost\nroute_cost\nservice_window\nrisk_score\n```\n\nThis is the interface between AI forecasting and Quantum/Hybrid optimization.\n",
   "id": "qV9E9xx76ijx"
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000
    },
    "id": "nKiahMPQ6ijy",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557302372,
     "user_tz": -180,
     "elapsed": 81,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "d68dfc54-ebb8-4571-927a-b48e0ea5360a"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "      atm_id          as_of_time denomination  current_notes  forecast_p50  \\\n",
       "0   ADB02098 2025-02-21 15:34:00        AED10              0           0.0   \n",
       "1   ADB02098 2025-02-21 15:34:00        AED20              0           0.0   \n",
       "2   ADB02098 2025-02-21 15:34:00        AED50              0           0.0   \n",
       "3   ADB02098 2025-02-21 15:34:00       AED100              0           0.0   \n",
       "4   ADB02098 2025-02-21 15:34:00       AED200              0           0.0   \n",
       "5   ADB02098 2025-02-21 15:34:00       AED500              0           0.0   \n",
       "6   ADB02098 2025-02-21 15:34:00      AED1000              0           0.0   \n",
       "7   ADB05181 2025-02-25 22:37:00        AED10              0           0.0   \n",
       "8   ADB05181 2025-02-25 22:37:00        AED20              0           0.0   \n",
       "9   ADB05181 2025-02-25 22:37:00        AED50              0           0.0   \n",
       "10  ADB05181 2025-02-25 22:37:00       AED100              0           0.0   \n",
       "11  ADB05181 2025-02-25 22:37:00       AED200              0           0.0   \n",
       "12  ADB05181 2025-02-25 22:37:00       AED500              0           0.0   \n",
       "13  ADB05181 2025-02-25 22:37:00      AED1000              0           0.0   \n",
       "14  ADB05296 2025-02-17 20:02:00        AED10              0           0.0   \n",
       "15  ADB05296 2025-02-17 20:02:00        AED20              0           0.0   \n",
       "16  ADB05296 2025-02-17 20:02:00        AED50              0           0.0   \n",
       "17  ADB05296 2025-02-17 20:02:00       AED100              0           0.0   \n",
       "18  ADB05296 2025-02-17 20:02:00       AED200              0           0.0   \n",
       "19  ADB05296 2025-02-17 20:02:00       AED500              0           0.0   \n",
       "20  ADB05296 2025-02-17 20:02:00      AED1000              0           0.0   \n",
       "21  ADB05445 2025-02-26 00:18:00        AED10              0           0.0   \n",
       "22  ADB05445 2025-02-26 00:18:00        AED20              0           0.0   \n",
       "23  ADB05445 2025-02-26 00:18:00        AED50              0           0.0   \n",
       "24  ADB05445 2025-02-26 00:18:00       AED100              0           0.0   \n",
       "25  ADB05445 2025-02-26 00:18:00       AED200              0           0.0   \n",
       "26  ADB05445 2025-02-26 00:18:00       AED500              0           0.0   \n",
       "27  ADB05445 2025-02-26 00:18:00      AED1000              0           0.0   \n",
       "28  ADB05446 2025-02-26 09:15:00        AED10              0           0.0   \n",
       "29  ADB05446 2025-02-26 09:15:00        AED20              0           0.0   \n",
       "\n",
       "    forecast_p80  forecast_p95  min_stock  max_stock  cassette_capacity  \\\n",
       "0            0.0           0.0        0.0        0.0                0.0   \n",
       "1            0.0           0.0        0.0        0.0                0.0   \n",
       "2            0.0           0.0        0.0        0.0                0.0   \n",
       "3            0.0           0.0        0.0        0.0                0.0   \n",
       "4            0.0           0.0        0.0        0.0                0.0   \n",
       "5            0.0           0.0        0.0        0.0                0.0   \n",
       "6            0.0           0.0        0.0        0.0                0.0   \n",
       "7            0.0           0.0        0.0        0.0                0.0   \n",
       "8            0.0           0.0        0.0        0.0                0.0   \n",
       "9            0.0           0.0        0.0        0.0                0.0   \n",
       "10           0.0           0.0        0.0        0.0                0.0   \n",
       "11           0.0           0.0        0.0        0.0                0.0   \n",
       "12           0.0           0.0        0.0        0.0                0.0   \n",
       "13           0.0           0.0        0.0        0.0                0.0   \n",
       "14           0.0           0.0        0.0        0.0                0.0   \n",
       "15           0.0           0.0        0.0        0.0                0.0   \n",
       "16           0.0           0.0        0.0        0.0                0.0   \n",
       "17           0.0           0.0        0.0        0.0                0.0   \n",
       "18           0.0           0.0        0.0        0.0                0.0   \n",
       "19           0.0           0.0        0.0        0.0                0.0   \n",
       "20           0.0           0.0        0.0        0.0                0.0   \n",
       "21           0.0           0.0        0.0        0.0                0.0   \n",
       "22           0.0           0.0        0.0        0.0                0.0   \n",
       "23           0.0           0.0        0.0        0.0                0.0   \n",
       "24           0.0           0.0        0.0        0.0                0.0   \n",
       "25           0.0           0.0        0.0        0.0                0.0   \n",
       "26           0.0           0.0        0.0        0.0                0.0   \n",
       "27           0.0           0.0        0.0        0.0                0.0   \n",
       "28           0.0           0.0        0.0        0.0                0.0   \n",
       "29           0.0           0.0        0.0        0.0                0.0   \n",
       "\n",
       "    service_cost  cash_holding_cost  stockout_cost  route_cost  risk_score  \n",
       "0            1.0              0.001           10.0         1.0         0.0  \n",
       "1            1.0              0.001           10.0         1.0         0.0  \n",
       "2            1.0              0.001           10.0         1.0         0.0  \n",
       "3            1.0              0.001           10.0         1.0         0.0  \n",
       "4            1.0              0.001           10.0         1.0         0.0  \n",
       "5            1.0              0.001           10.0         1.0         0.0  \n",
       "6            1.0              0.001           10.0         1.0         0.0  \n",
       "7            1.0              0.001           10.0         1.0         0.0  \n",
       "8            1.0              0.001           10.0         1.0         0.0  \n",
       "9            1.0              0.001           10.0         1.0         0.0  \n",
       "10           1.0              0.001           10.0         1.0         0.0  \n",
       "11           1.0              0.001           10.0         1.0         0.0  \n",
       "12           1.0              0.001           10.0         1.0         0.0  \n",
       "13           1.0              0.001           10.0         1.0         0.0  \n",
       "14           1.0              0.001           10.0         1.0         0.0  \n",
       "15           1.0              0.001           10.0         1.0         0.0  \n",
       "16           1.0              0.001           10.0         1.0         0.0  \n",
       "17           1.0              0.001           10.0         1.0         0.0  \n",
       "18           1.0              0.001           10.0         1.0         0.0  \n",
       "19           1.0              0.001           10.0         1.0         0.0  \n",
       "20           1.0              0.001           10.0         1.0         0.0  \n",
       "21           1.0              0.001           10.0         1.0         0.0  \n",
       "22           1.0              0.001           10.0         1.0         0.0  \n",
       "23           1.0              0.001           10.0         1.0         0.0  \n",
       "24           1.0              0.001           10.0         1.0         0.0  \n",
       "25           1.0              0.001           10.0         1.0         0.0  \n",
       "26           1.0              0.001           10.0         1.0         0.0  \n",
       "27           1.0              0.001           10.0         1.0         0.0  \n",
       "28           1.0              0.001           10.0         1.0         0.0  \n",
       "29           1.0              0.001           10.0         1.0         0.0  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-d2d28bf6-9775-4de7-86c5-75fdf369dc87\" class=\"colab-df-container\">\n",
       "    <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>atm_id</th>\n",
       "      <th>as_of_time</th>\n",
       "      <th>denomination</th>\n",
       "      <th>current_notes</th>\n",
       "      <th>forecast_p50</th>\n",
       "      <th>forecast_p80</th>\n",
       "      <th>forecast_p95</th>\n",
       "      <th>min_stock</th>\n",
       "      <th>max_stock</th>\n",
       "      <th>cassette_capacity</th>\n",
       "      <th>service_cost</th>\n",
       "      <th>cash_holding_cost</th>\n",
       "      <th>stockout_cost</th>\n",
       "      <th>route_cost</th>\n",
       "      <th>risk_score</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-21 15:34:00</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>2025-02-25 22:37:00</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>2025-02-17 20:02:00</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>2025-02-26 00:18:00</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>ADB05446</td>\n",
       "      <td>2025-02-26 09:15:00</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>ADB05446</td>\n",
       "      <td>2025-02-26 09:15:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-d2d28bf6-9775-4de7-86c5-75fdf369dc87')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-d2d28bf6-9775-4de7-86c5-75fdf369dc87 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-d2d28bf6-9775-4de7-86c5-75fdf369dc87');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(opt\",\n  \"rows\": 30,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 5,\n        \"samples\": [\n          \"ADB05181\",\n          \"ADB05446\",\n          \"ADB05296\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"as_of_time\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2025-02-17 20:02:00\",\n        \"max\": \"2025-02-26 09:15:00\",\n        \"num_unique_values\": 5,\n        \"samples\": [\n          \"2025-02-25 22:37:00\",\n          \"2025-02-26 09:15:00\",\n          \"2025-02-17 20:02:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"denomination\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 7,\n        \"samples\": [\n          \"AED10\",\n          \"AED20\",\n          \"AED500\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"current_notes\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"forecast_p50\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"forecast_p80\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"forecast_p95\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"min_stock\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"max_stock\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cassette_capacity\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"service_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 1.0,\n        \"max\": 1.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          1.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cash_holding_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 6.616421313379338e-19,\n        \"min\": 0.001,\n        \"max\": 0.001,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.001\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"stockout_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 10.0,\n        \"max\": 10.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          10.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"route_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 1.0,\n        \"max\": 1.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          1.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"risk_score\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 0.0,\n        \"max\": 0.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 15 — Build optimization contract skeleton\ndenoms = [\"AED10\",\"AED20\",\"AED50\",\"AED100\",\"AED200\",\"AED500\",\"AED1000\"]\n\nif not latest_state.empty:\n    opt = latest_state[[\"atm_id\",\"event_time\",\"denomination\",\"notes\"]].copy()\n    opt = opt.rename(columns={\"event_time\":\"as_of_time\", \"notes\":\"current_notes\"})\nelse:\n    # Fallback skeleton so the notebook remains runnable even if no snapshots parse.\n    unique_atms = events[\"atm_id\"].dropna().unique().tolist()\n    if unique_atms:\n        rows = []\n        for atm in unique_atms:\n            for d in denoms:\n                rows.append({\n                    \"atm_id\": atm,\n                    \"as_of_time\": events.loc[events[\"atm_id\"] == atm, \"event_time\"].max(),\n                    \"denomination\": d,\n                    \"current_notes\": 0\n                })\n        opt = pd.DataFrame(rows)\n    else:\n        opt = pd.DataFrame(columns=[\"atm_id\",\"as_of_time\",\"denomination\",\"current_notes\"])\n\nopt[\"forecast_p50\"] = 0.0\nopt[\"forecast_p80\"] = 0.0\nopt[\"forecast_p95\"] = 0.0\nopt[\"min_stock\"] = 0.0\nopt[\"max_stock\"] = 0.0\nopt[\"cassette_capacity\"] = 0.0\nopt[\"service_cost\"] = 1.0\nopt[\"cash_holding_cost\"] = 0.001\nopt[\"stockout_cost\"] = 10.0\nopt[\"route_cost\"] = 1.0\nopt[\"risk_score\"] = 0.0\n\ndisplay(opt.head(30))\n",
   "id": "nKiahMPQ6ijy"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "STyGTT5i6ijz"
   },
   "source": "## 16 — Withdrawal data contract\n\nThe actual forecasting model needs a withdrawal table. Expected production schema:\n\n```text\natm_id\ntimestamp\ntsn\ncurrency\nwithdrawal_amount\ndenomination\ndispensed_notes\nretracted_notes\ndispense_status\nchannel\n```\n\nUntil this feed is connected, the notebook can generate synthetic withdrawal data **only for algorithm plumbing tests**, not for business KPI claims.\n",
   "id": "STyGTT5i6ijz"
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 217
    },
    "id": "dFDWChuH6ij0",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557309516,
     "user_tz": -180,
     "elapsed": 1793,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "66ec2a04-6a36-4021-a672-e5120d82042b"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "     atm_id           timestamp denomination  dispensed_notes  \\\n",
       "0  ADB02098 2025-02-01 00:30:00       AED100                1   \n",
       "1  ADB02098 2025-02-01 00:22:00       AED500                1   \n",
       "2  ADB02098 2025-02-01 00:24:00       AED100                1   \n",
       "3  ADB02098 2025-02-01 00:49:00       AED100                1   \n",
       "4  ADB02098 2025-02-01 00:05:00        AED50                1   \n",
       "\n",
       "   withdrawal_amount dispense_status  \n",
       "0              100.0         SUCCESS  \n",
       "1              500.0         SUCCESS  \n",
       "2              100.0         SUCCESS  \n",
       "3              100.0         SUCCESS  \n",
       "4               50.0         SUCCESS  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-1de9b1cc-d783-462b-a546-9c6a44369ed8\" class=\"colab-df-container\">\n",
       "    <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>atm_id</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>denomination</th>\n",
       "      <th>dispensed_notes</th>\n",
       "      <th>withdrawal_amount</th>\n",
       "      <th>dispense_status</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:30:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>100.0</td>\n",
       "      <td>SUCCESS</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:22:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>1</td>\n",
       "      <td>500.0</td>\n",
       "      <td>SUCCESS</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:24:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>100.0</td>\n",
       "      <td>SUCCESS</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:49:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>100.0</td>\n",
       "      <td>SUCCESS</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:05:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>SUCCESS</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-1de9b1cc-d783-462b-a546-9c6a44369ed8')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-1de9b1cc-d783-462b-a546-9c6a44369ed8 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-1de9b1cc-d783-462b-a546-9c6a44369ed8');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"print(\\\"Synthetic rows:\\\", len(synthetic_withdrawals))\",\n  \"rows\": 5,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"ADB02098\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"timestamp\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2025-02-01 00:05:00\",\n        \"max\": \"2025-02-01 00:49:00\",\n        \"num_unique_values\": 5,\n        \"samples\": [\n          \"2025-02-01 00:22:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"denomination\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 3,\n        \"samples\": [\n          \"AED100\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"dispensed_notes\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 1,\n        \"max\": 1,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          1\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"withdrawal_amount\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 185.7417562100671,\n        \"min\": 50.0,\n        \"max\": 500.0,\n        \"num_unique_values\": 3,\n        \"samples\": [\n          100.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"dispense_status\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"SUCCESS\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Synthetic rows: 18096\n"
     ]
    }
   ],
   "source": "# 17 — Synthetic withdrawal generator for algorithm plumbing\ndef make_synthetic_withdrawals(atm_ids, start=\"2025-02-01\", days=28, seed=42):\n    rng = np.random.default_rng(seed)\n    rows = []\n    start_dt = pd.Timestamp(start)\n    for atm in atm_ids:\n        base = rng.uniform(10, 60)\n        for h in range(days * 24):\n            ts = start_dt + pd.Timedelta(hours=h)\n            day_factor = 1.25 if ts.dayofweek in [4,5] else 1.0\n            hour_factor = 1.6 if 17 <= ts.hour <= 21 else (0.7 if 0 <= ts.hour <= 6 else 1.0)\n            lam = max(0.5, base * day_factor * hour_factor)\n            count = rng.poisson(lam / 10)\n            for _ in range(count):\n                denom = rng.choice([10,20,50,100,200,500,1000], p=[.08,.06,.18,.36,.08,.18,.06])\n                rows.append({\n                    \"atm_id\": atm,\n                    \"timestamp\": ts + pd.Timedelta(minutes=int(rng.integers(0,60))),\n                    \"denomination\": f\"AED{int(denom)}\",\n                    \"dispensed_notes\": 1,\n                    \"withdrawal_amount\": float(denom),\n                    \"dispense_status\": \"SUCCESS\"\n                })\n    return pd.DataFrame(rows)\n\natm_ids = events[\"atm_id\"].dropna().unique().tolist()\nsynthetic_withdrawals = make_synthetic_withdrawals(atm_ids)\ndisplay(synthetic_withdrawals.head())\nprint(\"Synthetic rows:\", len(synthetic_withdrawals))\n",
   "id": "dFDWChuH6ij0"
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 962
    },
    "id": "uHLfVcK-6ij0",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557314849,
     "user_tz": -180,
     "elapsed": 93,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "8fb7fe9b-b946-4d78-ee73-a6b40dee6bf2"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "      atm_id           timestamp denomination  notes  amount  transactions  \\\n",
       "0   ADB02098 2025-02-01 00:00:00        AED10      1    10.0             1   \n",
       "1   ADB02098 2025-02-01 00:00:00       AED100      3   300.0             3   \n",
       "2   ADB02098 2025-02-01 00:00:00        AED50      1    50.0             1   \n",
       "3   ADB02098 2025-02-01 00:00:00       AED500      2  1000.0             2   \n",
       "4   ADB02098 2025-02-01 01:00:00       AED100      3   300.0             3   \n",
       "5   ADB02098 2025-02-01 01:00:00      AED1000      1  1000.0             1   \n",
       "6   ADB02098 2025-02-01 01:00:00       AED200      1   200.0             1   \n",
       "7   ADB02098 2025-02-01 01:00:00        AED50      2   100.0             2   \n",
       "8   ADB02098 2025-02-01 02:00:00       AED100      1   100.0             1   \n",
       "9   ADB02098 2025-02-01 02:00:00        AED20      1    20.0             1   \n",
       "10  ADB02098 2025-02-01 02:00:00        AED50      2   100.0             2   \n",
       "11  ADB02098 2025-02-01 02:00:00       AED500      1   500.0             1   \n",
       "12  ADB02098 2025-02-01 03:00:00       AED100      4   400.0             4   \n",
       "13  ADB02098 2025-02-01 03:00:00        AED20      1    20.0             1   \n",
       "14  ADB02098 2025-02-01 03:00:00       AED500      1   500.0             1   \n",
       "15  ADB02098 2025-02-01 04:00:00        AED50      1    50.0             1   \n",
       "16  ADB02098 2025-02-01 05:00:00       AED100      2   200.0             2   \n",
       "17  ADB02098 2025-02-01 05:00:00        AED50      1    50.0             1   \n",
       "18  ADB02098 2025-02-01 06:00:00       AED100      1   100.0             1   \n",
       "19  ADB02098 2025-02-01 06:00:00        AED20      1    20.0             1   \n",
       "20  ADB02098 2025-02-01 06:00:00       AED200      1   200.0             1   \n",
       "21  ADB02098 2025-02-01 06:00:00        AED50      1    50.0             1   \n",
       "22  ADB02098 2025-02-01 07:00:00      AED1000      1  1000.0             1   \n",
       "23  ADB02098 2025-02-01 07:00:00        AED20      1    20.0             1   \n",
       "24  ADB02098 2025-02-01 07:00:00       AED200      2   400.0             2   \n",
       "25  ADB02098 2025-02-01 07:00:00        AED50      1    50.0             1   \n",
       "26  ADB02098 2025-02-01 07:00:00       AED500      1   500.0             1   \n",
       "27  ADB02098 2025-02-01 08:00:00       AED100      1   100.0             1   \n",
       "28  ADB02098 2025-02-01 08:00:00       AED200      1   200.0             1   \n",
       "29  ADB02098 2025-02-01 08:00:00        AED50      1    50.0             1   \n",
       "\n",
       "    dow  hour  is_weekend  \n",
       "0     5     0           1  \n",
       "1     5     0           1  \n",
       "2     5     0           1  \n",
       "3     5     0           1  \n",
       "4     5     1           1  \n",
       "5     5     1           1  \n",
       "6     5     1           1  \n",
       "7     5     1           1  \n",
       "8     5     2           1  \n",
       "9     5     2           1  \n",
       "10    5     2           1  \n",
       "11    5     2           1  \n",
       "12    5     3           1  \n",
       "13    5     3           1  \n",
       "14    5     3           1  \n",
       "15    5     4           1  \n",
       "16    5     5           1  \n",
       "17    5     5           1  \n",
       "18    5     6           1  \n",
       "19    5     6           1  \n",
       "20    5     6           1  \n",
       "21    5     6           1  \n",
       "22    5     7           1  \n",
       "23    5     7           1  \n",
       "24    5     7           1  \n",
       "25    5     7           1  \n",
       "26    5     7           1  \n",
       "27    5     8           1  \n",
       "28    5     8           1  \n",
       "29    5     8           1  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-58b8140a-1f6d-4409-aaba-dceddeb9db88\" class=\"colab-df-container\">\n",
       "    <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>atm_id</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>denomination</th>\n",
       "      <th>notes</th>\n",
       "      <th>amount</th>\n",
       "      <th>transactions</th>\n",
       "      <th>dow</th>\n",
       "      <th>hour</th>\n",
       "      <th>is_weekend</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:00:00</td>\n",
       "      <td>AED10</td>\n",
       "      <td>1</td>\n",
       "      <td>10.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>3</td>\n",
       "      <td>300.0</td>\n",
       "      <td>3</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 00:00:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>2</td>\n",
       "      <td>1000.0</td>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 01:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>3</td>\n",
       "      <td>300.0</td>\n",
       "      <td>3</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 01:00:00</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>1</td>\n",
       "      <td>1000.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 01:00:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>1</td>\n",
       "      <td>200.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 01:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>2</td>\n",
       "      <td>100.0</td>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 02:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>100.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 02:00:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>1</td>\n",
       "      <td>20.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 02:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>2</td>\n",
       "      <td>100.0</td>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 02:00:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>1</td>\n",
       "      <td>500.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 03:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>4</td>\n",
       "      <td>400.0</td>\n",
       "      <td>4</td>\n",
       "      <td>5</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 03:00:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>1</td>\n",
       "      <td>20.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 03:00:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>1</td>\n",
       "      <td>500.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 04:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 05:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>2</td>\n",
       "      <td>200.0</td>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 05:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 06:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>100.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 06:00:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>1</td>\n",
       "      <td>20.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 06:00:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>1</td>\n",
       "      <td>200.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 06:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 07:00:00</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>1</td>\n",
       "      <td>1000.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>7</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 07:00:00</td>\n",
       "      <td>AED20</td>\n",
       "      <td>1</td>\n",
       "      <td>20.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>7</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 07:00:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>2</td>\n",
       "      <td>400.0</td>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "      <td>7</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 07:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>7</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 07:00:00</td>\n",
       "      <td>AED500</td>\n",
       "      <td>1</td>\n",
       "      <td>500.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>7</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 08:00:00</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>100.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>8</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 08:00:00</td>\n",
       "      <td>AED200</td>\n",
       "      <td>1</td>\n",
       "      <td>200.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>8</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>2025-02-01 08:00:00</td>\n",
       "      <td>AED50</td>\n",
       "      <td>1</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>8</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-58b8140a-1f6d-4409-aaba-dceddeb9db88')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-58b8140a-1f6d-4409-aaba-dceddeb9db88 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-58b8140a-1f6d-4409-aaba-dceddeb9db88');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(hourly\",\n  \"rows\": 30,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"ADB02098\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"timestamp\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2025-02-01 00:00:00\",\n        \"max\": \"2025-02-01 08:00:00\",\n        \"num_unique_values\": 9,\n        \"samples\": [\n          \"2025-02-01 07:00:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"denomination\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 7,\n        \"samples\": [\n          \"AED10\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"notes\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 1,\n        \"max\": 4,\n        \"num_unique_values\": 4,\n        \"samples\": [\n          3\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"amount\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 296.9053024462339,\n        \"min\": 10.0,\n        \"max\": 1000.0,\n        \"num_unique_values\": 9,\n        \"samples\": [\n          500.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"transactions\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 1,\n        \"max\": 4,\n        \"num_unique_values\": 4,\n        \"samples\": [\n          3\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"dow\",\n      \"properties\": {\n        \"dtype\": \"int32\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          5\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"hour\",\n      \"properties\": {\n        \"dtype\": \"int32\",\n        \"num_unique_values\": 9,\n        \"samples\": [\n          7\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"is_weekend\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 1,\n        \"max\": 1,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          1\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 18 — Hourly demand dataset\nhourly = (\n    synthetic_withdrawals\n    .assign(timestamp=lambda x: pd.to_datetime(x[\"timestamp\"]).dt.floor(\"h\"))\n    .groupby([\"atm_id\",\"timestamp\",\"denomination\"], as_index=False)\n    .agg(\n        notes=(\"dispensed_notes\",\"sum\"),\n        amount=(\"withdrawal_amount\",\"sum\"),\n        transactions=(\"dispensed_notes\",\"count\")\n    )\n)\n\nhourly[\"dow\"] = hourly[\"timestamp\"].dt.dayofweek\nhourly[\"hour\"] = hourly[\"timestamp\"].dt.hour\nhourly[\"is_weekend\"] = hourly[\"dow\"].isin([5,6]).astype(int)\n\ndisplay(hourly.head(30))\n",
   "id": "uHLfVcK-6ij0"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DcHcbzsp6ij1"
   },
   "source": "## 19 — Baseline forecast\n\nFor the first POC we use a transparent statistical baseline. When the real withdrawal feed is connected, this cell can be replaced by LightGBM / XGBoost / TFT / probabilistic forecasting.\n\nThe important design principle is that the optimizer receives **quantiles**, not only point predictions.\n",
   "id": "DcHcbzsp6ij1"
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 962
    },
    "id": "cVGQ_oIZ6ij1",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557320557,
     "user_tz": -180,
     "elapsed": 1221,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "786013ac-445b-4398-a761-11500da9df27"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "      atm_id denomination  hour  p50  p80   p95\n",
       "0   ADB02098        AED10     0  1.0  2.0  2.75\n",
       "1   ADB02098        AED10     1  1.0  2.0  2.00\n",
       "2   ADB02098        AED10     2  1.0  1.0  1.00\n",
       "3   ADB02098        AED10     3  1.0  1.6  2.00\n",
       "4   ADB02098        AED10     4  1.0  1.0  1.00\n",
       "5   ADB02098        AED10     5  1.0  1.0  1.00\n",
       "6   ADB02098        AED10     6  1.0  1.8  2.00\n",
       "7   ADB02098        AED10     7  1.0  1.0  2.50\n",
       "8   ADB02098        AED10     8  1.0  1.4  2.00\n",
       "9   ADB02098        AED10     9  1.0  2.0  2.50\n",
       "10  ADB02098        AED10    10  1.0  1.0  1.65\n",
       "11  ADB02098        AED10    11  1.0  1.0  1.00\n",
       "12  ADB02098        AED10    12  1.0  1.2  2.55\n",
       "13  ADB02098        AED10    13  1.0  2.0  2.00\n",
       "14  ADB02098        AED10    14  1.0  1.0  1.50\n",
       "15  ADB02098        AED10    15  1.0  1.0  2.00\n",
       "16  ADB02098        AED10    16  1.0  2.0  2.00\n",
       "17  ADB02098        AED10    17  1.0  2.0  2.30\n",
       "18  ADB02098        AED10    18  1.0  1.2  2.00\n",
       "19  ADB02098        AED10    19  1.0  2.0  2.50\n",
       "20  ADB02098        AED10    20  1.0  1.0  2.35\n",
       "21  ADB02098        AED10    21  1.0  1.0  2.50\n",
       "22  ADB02098        AED10    22  1.0  1.0  1.00\n",
       "23  ADB02098        AED10    23  1.0  2.0  2.00\n",
       "24  ADB02098       AED100     0  2.0  2.0  3.00\n",
       "25  ADB02098       AED100     1  2.0  3.0  4.00\n",
       "26  ADB02098       AED100     2  1.0  2.0  3.90\n",
       "27  ADB02098       AED100     3  1.0  3.0  4.00\n",
       "28  ADB02098       AED100     4  2.0  2.8  3.20\n",
       "29  ADB02098       AED100     5  1.0  2.0  2.90"
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-943bbd92-413c-40e5-b038-6f460b2336b1\" class=\"colab-df-container\">\n",
       "    <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>atm_id</th>\n",
       "      <th>denomination</th>\n",
       "      <th>hour</th>\n",
       "      <th>p50</th>\n",
       "      <th>p80</th>\n",
       "      <th>p95</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.75</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>1</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>2</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>3</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.6</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>4</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>5</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>6</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.8</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>7</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>8</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.4</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>9</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>10</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.65</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>11</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>12</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.2</td>\n",
       "      <td>2.55</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>13</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>14</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>15</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>16</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>17</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.30</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>18</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.2</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>19</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>20</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.35</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>21</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>22</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>23</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>1</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>4.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>2</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>3.90</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>3</td>\n",
       "      <td>1.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>4.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>4</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.8</td>\n",
       "      <td>3.20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>5</td>\n",
       "      <td>1.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.90</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-943bbd92-413c-40e5-b038-6f460b2336b1')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-943bbd92-413c-40e5-b038-6f460b2336b1 button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-943bbd92-413c-40e5-b038-6f460b2336b1');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(forecast_baseline\",\n  \"rows\": 30,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 1,\n        \"samples\": [\n          \"ADB02098\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"denomination\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 2,\n        \"samples\": [\n          \"AED100\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"hour\",\n      \"properties\": {\n        \"dtype\": \"int32\",\n        \"num_unique_values\": 24,\n        \"samples\": [\n          8\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"p50\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.30512857662936466,\n        \"min\": 1.0,\n        \"max\": 2.0,\n        \"num_unique_values\": 2,\n        \"samples\": [\n          2.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"p80\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.6282616044914419,\n        \"min\": 1.0,\n        \"max\": 3.0,\n        \"num_unique_values\": 8,\n        \"samples\": [\n          1.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"p95\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.8434262363075705,\n        \"min\": 1.0,\n        \"max\": 4.0,\n        \"num_unique_values\": 14,\n        \"samples\": [\n          3.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 20 — Simple probabilistic baseline\ndef baseline_quantiles(hourly_df):\n    if hourly_df.empty:\n        return pd.DataFrame()\n    x = hourly_df.copy()\n    # group by ATM + denomination + hour-of-day\n    stats = (\n        x.groupby([\"atm_id\",\"denomination\",\"hour\"])[\"notes\"]\n        .agg(\n            p50=lambda s: float(np.quantile(s, 0.50)),\n            p80=lambda s: float(np.quantile(s, 0.80)),\n            p95=lambda s: float(np.quantile(s, 0.95))\n        )\n        .reset_index()\n    )\n    return stats\n\nforecast_baseline = baseline_quantiles(hourly)\ndisplay(forecast_baseline.head(30))\n",
   "id": "cVGQ_oIZ6ij1"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "6YVRvbTU6ij2"
   },
   "source": "# 21 — Classical replenishment benchmark\n\nA full MILP will be added after the real withdrawal data contract is connected. For the POC, we define the exact mathematical interface.\n\nFor ATM `a`, denomination `d`:\n\n\\[\nI_{a,d,t+1}=I_{a,d,t}+x_{a,d,t}-D_{a,d,t}\n\\]\n\nand the objective is:\n\n\\[\n\\min\n\\;\\lambda_1 StockoutRisk+\n\\lambda_2 IdleCash+\n\\lambda_3 CITCost+\n\\lambda_4 EmergencyCost\n\\]\n\nThe same instance will later be compiled to CQM/DQM/QUBO.\n",
   "id": "6YVRvbTU6ij2"
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 962
    },
    "id": "unL8TNZw6ij2",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557325754,
     "user_tz": -180,
     "elapsed": 101,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "b1faf9b8-977b-42e4-cb2e-8a11fad8d4ad"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "      atm_id denomination  current_notes  forecast_p50  forecast_p80  \\\n",
       "0   ADB02098        AED10              0      1.000000      1.425000   \n",
       "1   ADB02098        AED20              0      1.062500      1.391667   \n",
       "2   ADB02098        AED50              0      1.187500      2.266667   \n",
       "3   ADB02098       AED100              0      2.020833      3.233333   \n",
       "4   ADB02098       AED200              0      1.041667      1.475000   \n",
       "5   ADB02098       AED500              0      1.375000      2.033333   \n",
       "6   ADB02098      AED1000              0      1.062500      1.266667   \n",
       "7   ADB05181        AED10              0      1.000000      1.050000   \n",
       "8   ADB05181        AED20              0      1.045455      1.163636   \n",
       "9   ADB05181        AED50              0      1.104167      1.491667   \n",
       "10  ADB05181       AED100              0      1.166667      1.975000   \n",
       "11  ADB05181       AED200              0      1.041667      1.216667   \n",
       "12  ADB05181       AED500              0      1.041667      1.458333   \n",
       "13  ADB05181      AED1000              0      1.020833      1.150000   \n",
       "14  ADB05296        AED10              0      1.041667      1.308333   \n",
       "15  ADB05296        AED20              0      1.020833      1.175000   \n",
       "16  ADB05296        AED50              0      1.083333      1.575000   \n",
       "17  ADB05296       AED100              0      1.416667      2.275000   \n",
       "18  ADB05296       AED200              0      1.041667      1.250000   \n",
       "19  ADB05296       AED500              0      1.041667      1.841667   \n",
       "20  ADB05296      AED1000              0      1.000000      1.250000   \n",
       "21  ADB05445        AED10              0      1.083333      1.208333   \n",
       "22  ADB05445        AED20              0      1.000000      1.130435   \n",
       "23  ADB05445        AED50              0      1.041667      1.466667   \n",
       "24  ADB05445       AED100              0      1.250000      1.891667   \n",
       "25  ADB05445       AED200              0      1.041667      1.091667   \n",
       "26  ADB05445       AED500              0      1.020833      1.466667   \n",
       "27  ADB05445      AED1000              0      1.090909      1.181818   \n",
       "28  ADB05446        AED10              0      1.041667      1.383333   \n",
       "29  ADB05446        AED20              0      1.000000      1.241667   \n",
       "\n",
       "    forecast_p95  min_stock  max_stock  cassette_capacity  service_cost  \\\n",
       "0       1.920833         20        500               1200           1.0   \n",
       "1       1.750000         20        500               1200           1.0   \n",
       "2       2.837500         20        500               1200           1.0   \n",
       "3       4.341667         20        500               1200           1.0   \n",
       "4       1.860417         20        500               1200           1.0   \n",
       "5       3.033333         20        500               1200           1.0   \n",
       "6       1.660417         20        500               1200           1.0   \n",
       "7       1.158333         20        500               1200           1.0   \n",
       "8       1.236364         20        500               1200           1.0   \n",
       "9       1.939583         20        500               1200           1.0   \n",
       "10      2.614583         20        500               1200           1.0   \n",
       "11      1.456250         20        500               1200           1.0   \n",
       "12      1.975000         20        500               1200           1.0   \n",
       "13      1.225000         20        500               1200           1.0   \n",
       "14      1.535417         20        500               1200           1.0   \n",
       "15      1.362500         20        500               1200           1.0   \n",
       "16      2.210417         20        500               1200           1.0   \n",
       "17      3.245833         20        500               1200           1.0   \n",
       "18      1.687500         20        500               1200           1.0   \n",
       "19      2.333333         20        500               1200           1.0   \n",
       "20      1.520833         20        500               1200           1.0   \n",
       "21      1.466667         20        500               1200           1.0   \n",
       "22      1.195652         20        500               1200           1.0   \n",
       "23      1.966667         20        500               1200           1.0   \n",
       "24      2.710417         20        500               1200           1.0   \n",
       "25      1.204167         20        500               1200           1.0   \n",
       "26      1.983333         20        500               1200           1.0   \n",
       "27      1.286364         20        500               1200           1.0   \n",
       "28      1.952083         20        500               1200           1.0   \n",
       "29      1.527083         20        500               1200           1.0   \n",
       "\n",
       "    cash_holding_cost  stockout_cost  \n",
       "0               0.001           10.0  \n",
       "1               0.001           10.0  \n",
       "2               0.001           10.0  \n",
       "3               0.001           10.0  \n",
       "4               0.001           10.0  \n",
       "5               0.001           10.0  \n",
       "6               0.001           10.0  \n",
       "7               0.001           10.0  \n",
       "8               0.001           10.0  \n",
       "9               0.001           10.0  \n",
       "10              0.001           10.0  \n",
       "11              0.001           10.0  \n",
       "12              0.001           10.0  \n",
       "13              0.001           10.0  \n",
       "14              0.001           10.0  \n",
       "15              0.001           10.0  \n",
       "16              0.001           10.0  \n",
       "17              0.001           10.0  \n",
       "18              0.001           10.0  \n",
       "19              0.001           10.0  \n",
       "20              0.001           10.0  \n",
       "21              0.001           10.0  \n",
       "22              0.001           10.0  \n",
       "23              0.001           10.0  \n",
       "24              0.001           10.0  \n",
       "25              0.001           10.0  \n",
       "26              0.001           10.0  \n",
       "27              0.001           10.0  \n",
       "28              0.001           10.0  \n",
       "29              0.001           10.0  "
      ],
      "text/html": [
       "\n",
       "  <div id=\"df-c5c077f8-5b4f-4780-a5a5-ae7d8e9421fc\" class=\"colab-df-container\">\n",
       "    <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>atm_id</th>\n",
       "      <th>denomination</th>\n",
       "      <th>current_notes</th>\n",
       "      <th>forecast_p50</th>\n",
       "      <th>forecast_p80</th>\n",
       "      <th>forecast_p95</th>\n",
       "      <th>min_stock</th>\n",
       "      <th>max_stock</th>\n",
       "      <th>cassette_capacity</th>\n",
       "      <th>service_cost</th>\n",
       "      <th>cash_holding_cost</th>\n",
       "      <th>stockout_cost</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.425000</td>\n",
       "      <td>1.920833</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>1.062500</td>\n",
       "      <td>1.391667</td>\n",
       "      <td>1.750000</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>1.187500</td>\n",
       "      <td>2.266667</td>\n",
       "      <td>2.837500</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>2.020833</td>\n",
       "      <td>3.233333</td>\n",
       "      <td>4.341667</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.475000</td>\n",
       "      <td>1.860417</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>1.375000</td>\n",
       "      <td>2.033333</td>\n",
       "      <td>3.033333</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ADB02098</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>1.062500</td>\n",
       "      <td>1.266667</td>\n",
       "      <td>1.660417</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.050000</td>\n",
       "      <td>1.158333</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>1.045455</td>\n",
       "      <td>1.163636</td>\n",
       "      <td>1.236364</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>1.104167</td>\n",
       "      <td>1.491667</td>\n",
       "      <td>1.939583</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>1.166667</td>\n",
       "      <td>1.975000</td>\n",
       "      <td>2.614583</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.216667</td>\n",
       "      <td>1.456250</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.458333</td>\n",
       "      <td>1.975000</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>ADB05181</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>1.020833</td>\n",
       "      <td>1.150000</td>\n",
       "      <td>1.225000</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.308333</td>\n",
       "      <td>1.535417</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>1.020833</td>\n",
       "      <td>1.175000</td>\n",
       "      <td>1.362500</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>1.083333</td>\n",
       "      <td>1.575000</td>\n",
       "      <td>2.210417</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>1.416667</td>\n",
       "      <td>2.275000</td>\n",
       "      <td>3.245833</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.250000</td>\n",
       "      <td>1.687500</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.841667</td>\n",
       "      <td>2.333333</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>ADB05296</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.250000</td>\n",
       "      <td>1.520833</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>1.083333</td>\n",
       "      <td>1.208333</td>\n",
       "      <td>1.466667</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.130435</td>\n",
       "      <td>1.195652</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED50</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.466667</td>\n",
       "      <td>1.966667</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>24</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED100</td>\n",
       "      <td>0</td>\n",
       "      <td>1.250000</td>\n",
       "      <td>1.891667</td>\n",
       "      <td>2.710417</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED200</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.091667</td>\n",
       "      <td>1.204167</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>26</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED500</td>\n",
       "      <td>0</td>\n",
       "      <td>1.020833</td>\n",
       "      <td>1.466667</td>\n",
       "      <td>1.983333</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>27</th>\n",
       "      <td>ADB05445</td>\n",
       "      <td>AED1000</td>\n",
       "      <td>0</td>\n",
       "      <td>1.090909</td>\n",
       "      <td>1.181818</td>\n",
       "      <td>1.286364</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>ADB05446</td>\n",
       "      <td>AED10</td>\n",
       "      <td>0</td>\n",
       "      <td>1.041667</td>\n",
       "      <td>1.383333</td>\n",
       "      <td>1.952083</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29</th>\n",
       "      <td>ADB05446</td>\n",
       "      <td>AED20</td>\n",
       "      <td>0</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.241667</td>\n",
       "      <td>1.527083</td>\n",
       "      <td>20</td>\n",
       "      <td>500</td>\n",
       "      <td>1200</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.001</td>\n",
       "      <td>10.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "    <div class=\"colab-df-buttons\">\n",
       "\n",
       "  <div class=\"colab-df-container\">\n",
       "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-c5c077f8-5b4f-4780-a5a5-ae7d8e9421fc')\"\n",
       "            title=\"Convert this dataframe to an interactive table.\"\n",
       "            style=\"display:none;\">\n",
       "\n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
       "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
       "  </svg>\n",
       "    </button>\n",
       "\n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    .colab-df-buttons div {\n",
       "      margin-bottom: 4px;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "    <script>\n",
       "      const buttonEl =\n",
       "        document.querySelector('#df-c5c077f8-5b4f-4780-a5a5-ae7d8e9421fc button.colab-df-convert');\n",
       "      buttonEl.style.display =\n",
       "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "      async function convertToInteractive(key) {\n",
       "        const element = document.querySelector('#df-c5c077f8-5b4f-4780-a5a5-ae7d8e9421fc');\n",
       "        const dataTable =\n",
       "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                    [key], {});\n",
       "        if (!dataTable) return;\n",
       "\n",
       "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "          + ' to learn more about interactive tables.';\n",
       "        element.innerHTML = '';\n",
       "        dataTable['output_type'] = 'display_data';\n",
       "        await google.colab.output.renderOutput(dataTable, element);\n",
       "        const docLink = document.createElement('div');\n",
       "        docLink.innerHTML = docLinkHtml;\n",
       "        element.appendChild(docLink);\n",
       "      }\n",
       "    </script>\n",
       "  </div>\n",
       "\n",
       "\n",
       "    </div>\n",
       "  </div>\n"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "dataframe",
       "summary": "{\n  \"name\": \"display(opt_instance\",\n  \"rows\": 30,\n  \"fields\": [\n    {\n      \"column\": \"atm_id\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 5,\n        \"samples\": [\n          \"ADB05181\",\n          \"ADB05446\",\n          \"ADB05296\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"denomination\",\n      \"properties\": {\n        \"dtype\": \"category\",\n        \"num_unique_values\": 7,\n        \"samples\": [\n          \"AED10\",\n          \"AED20\",\n          \"AED500\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"current_notes\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 0,\n        \"max\": 0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"forecast_p50\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.1999424016994399,\n        \"min\": 1.0,\n        \"max\": 2.0208333333333335,\n        \"num_unique_values\": 14,\n        \"samples\": [\n          1.0208333333333333\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"forecast_p80\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.47003909147179407,\n        \"min\": 1.05,\n        \"max\": 3.233333333333333,\n        \"num_unique_values\": 29,\n        \"samples\": [\n          1.3833333333333335\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"forecast_p95\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.7282653507219576,\n        \"min\": 1.1583333333333332,\n        \"max\": 4.341666666666666,\n        \"num_unique_values\": 30,\n        \"samples\": [\n          1.2863636363636364\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"min_stock\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 20,\n        \"max\": 20,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          20\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"max_stock\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 500,\n        \"max\": 500,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          500\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cassette_capacity\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0,\n        \"min\": 1200,\n        \"max\": 1200,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          1200\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"service_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 1.0,\n        \"max\": 1.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          1.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"cash_holding_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 6.616421313379338e-19,\n        \"min\": 0.001,\n        \"max\": 0.001,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          0.001\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"stockout_cost\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 0.0,\n        \"min\": 10.0,\n        \"max\": 10.0,\n        \"num_unique_values\": 1,\n        \"samples\": [\n          10.0\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
      }
     },
     "metadata": {}
    }
   ],
   "source": "# 22 — Optimization instance builder\ndef build_optimization_instance(\n    inventory_df,\n    forecast_df,\n    min_stock_default=20,\n    max_stock_default=500,\n    cassette_capacity_default=1200\n):\n    if inventory_df.empty:\n        return pd.DataFrame()\n\n    x = inventory_df.copy()\n    if not forecast_df.empty:\n        x = x.merge(\n            forecast_df,\n            on=[\"atm_id\",\"denomination\"],\n            how=\"left\",\n            suffixes=(\"\",\"_f\")\n        )\n    for c in [\"p50\",\"p80\",\"p95\"]:\n        if c not in x.columns:\n            x[c] = 0.0\n\n    # Conservative defaults; production values must come from ATM master data.\n    x[\"forecast_p50\"] = x[\"p50\"].fillna(0.0)\n    x[\"forecast_p80\"] = x[\"p80\"].fillna(0.0)\n    x[\"forecast_p95\"] = x[\"p95\"].fillna(0.0)\n    x[\"min_stock\"] = min_stock_default\n    x[\"max_stock\"] = max_stock_default\n    x[\"cassette_capacity\"] = cassette_capacity_default\n    x[\"service_cost\"] = 1.0\n    x[\"cash_holding_cost\"] = 0.001\n    x[\"stockout_cost\"] = 10.0\n\n    return x[\n        [\"atm_id\",\"denomination\",\"current_notes\",\n         \"forecast_p50\",\"forecast_p80\",\"forecast_p95\",\n         \"min_stock\",\"max_stock\",\"cassette_capacity\",\n         \"service_cost\",\"cash_holding_cost\",\"stockout_cost\"]\n    ]\n\nforecast_for_merge = (\n    forecast_baseline.groupby([\"atm_id\",\"denomination\"], as_index=False)\n    .agg(\n        p50=(\"p50\",\"mean\"),\n        p80=(\"p80\",\"mean\"),\n        p95=(\"p95\",\"mean\")\n    )\n) if not forecast_baseline.empty else pd.DataFrame()\n\nopt_instance = build_optimization_instance(\n    opt[[\"atm_id\",\"denomination\",\"current_notes\"]],\n    forecast_for_merge\n)\n\ndisplay(opt_instance.head(30))\n",
   "id": "unL8TNZw6ij2"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4egSTYPY6ij3"
   },
   "source": "# 23 — QUBO / CQM formulation contract\n\nProduction recommendation:\n\n- **D-Wave:** prefer CQM/DQM or another appropriate hybrid formulation; avoid forcing every integer/capacity constraint into a hand-crafted QUBO.\n- **IBM / Rigetti / AQT:** derive a binary QUBO/Ising form for QAOA research.\n- **QuEra / neutral-atom:** create graph/constraint subproblems where the physical model is a natural fit.\n\nBinary replenishment encoding example:\n\n\\[\nx=\\sum_{k=0}^{K-1}2^k b_k\n\\]\n\nQUBO:\n\n\\[\nQ(b)=Cost(b)+P\\cdot ConstraintViolation(b)\n\\]\n\nThis notebook keeps the mathematical contract independent from the backend.\n",
   "id": "4egSTYPY6ij3"
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "64zfoVJP6ij4",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557331438,
     "user_tz": -180,
     "elapsed": 21,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "e120cfe9-e118-49d7-a9f3-102efd2f467d"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{\n",
      "  \"name\": \"atm_replenishment_demo\",\n",
      "  \"problem_type\": \"QUBO\",\n",
      "  \"num_variables\": 4,\n",
      "  \"variables\": [\n",
      "    \"b0\",\n",
      "    \"b1\",\n",
      "    \"b2\",\n",
      "    \"b3\"\n",
      "  ],\n",
      "  \"linear_terms\": {\n",
      "    \"b0\": 1.0,\n",
      "    \"b1\": 2.0,\n",
      "    \"b2\": 4.0,\n",
      "    \"b3\": 8.0\n",
      "  },\n",
      "  \"quadratic_terms\": {},\n",
      "  \"constraints\": [\n",
      "    {\n",
      "      \"name\": \"capacity\",\n",
      "      \"sense\": \"<=\",\n",
      "      \"rhs\": 500\n",
      "    }\n",
      "  ],\n",
      "  \"metadata\": {\n",
      "    \"currency\": \"AED\",\n",
      "    \"purpose\": \"replenishment\"\n",
      "  }\n",
      "}\n"
     ]
    }
   ],
   "source": "# 24 — Backend-neutral quantum problem contract\nfrom dataclasses import dataclass, asdict\nfrom typing import Dict, Any, List\n\n@dataclass\nclass QuantumProblem:\n    name: str\n    problem_type: str\n    num_variables: int\n    variables: List[str]\n    linear_terms: Dict[str, float]\n    quadratic_terms: Dict[str, float]\n    constraints: List[Dict[str, Any]]\n    metadata: Dict[str, Any]\n\nproblem_example = QuantumProblem(\n    name=\"atm_replenishment_demo\",\n    problem_type=\"QUBO\",\n    num_variables=4,\n    variables=[\"b0\",\"b1\",\"b2\",\"b3\"],\n    linear_terms={\"b0\": 1.0, \"b1\": 2.0, \"b2\": 4.0, \"b3\": 8.0},\n    quadratic_terms={},\n    constraints=[{\n        \"name\":\"capacity\",\n        \"sense\":\"<=\",\n        \"rhs\":500\n    }],\n    metadata={\"currency\":\"AED\", \"purpose\":\"replenishment\"}\n)\n\nprint(json.dumps(asdict(problem_example), indent=2))\n",
   "id": "64zfoVJP6ij4"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "l78UjvFU6ij4"
   },
   "source": "## 25 — Quantum backend abstraction\n\nThe production platform should expose one interface and many adapters:\n\n```text\nDWaveBackend\nIBMBackend\nRigettiBackend\nAQTBackend\nQuEraBackend\nAtomBackend\nXanaduBackend\nWukongBackend\nHanyuanBackend\nSaxonQBackend\nClassicalBackend\n```\n\nThe orchestrator chooses a backend based on problem characteristics, deadline, current availability and benchmark evidence.\n",
   "id": "l78UjvFU6ij4"
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "3oKMoRJ86ij5",
    "executionInfo": {
     "status": "ok",
     "timestamp": 1789557337159,
     "user_tz": -180,
     "elapsed": 64,
     "user": {
      "displayName": "Joseph Volkan ÖZCAN",
      "userId": "11389530967736397480"
     }
    },
    "outputId": "b87a9020-28cd-49f8-d253-1e313a7c60c0"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "{'backend': 'classical_mock', 'status': 'MOCK', 'objective': None, 'constraint_violations': None}\n"
     ]
    }
   ],
   "source": "# 26 — Minimal backend abstraction\nclass QuantumBackend:\n    name = \"abstract\"\n\n    def supports(self, problem: QuantumProblem) -> bool:\n        return False\n\n    def compile(self, problem: QuantumProblem):\n        return problem\n\n    def solve(self, compiled_problem):\n        raise NotImplementedError\n\n    def metrics(self):\n        return {}\n\nclass ClassicalMockBackend(QuantumBackend):\n    name = \"classical_mock\"\n\n    def supports(self, problem):\n        return True\n\n    def solve(self, compiled_problem):\n        # Placeholder: exact/heuristic solver added in next iteration.\n        return {\n            \"backend\": self.name,\n            \"status\": \"MOCK\",\n            \"objective\": None,\n            \"constraint_violations\": None\n        }\n\nbackend = ClassicalMockBackend()\nprint(backend.solve(problem_example))\n",
   "id": "3oKMoRJ86ij5"
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "at-xLNuz6ij6"
   },
   "source": "# 27 — Next integration gate\n\nBefore claiming real forecasting accuracy or optimization improvement, connect:\n\n1. **Withdrawal / dispense EJ feed**\n2. **CIT replenishment records**\n3. **ATM master & cassette configuration**\n4. **Service windows / route constraints**\n5. **Historical ATM inventory snapshots**\n\nAfter that, the next notebook version can implement:\n\n- real demand forecast,\n- rolling backtesting,\n- MILP/CP-SAT optimum,\n- binary QUBO encoding,\n- D-Wave CQM/DQM execution,\n- IBM QAOA simulator + Warm-start QAOA,\n- hardware adapters,\n- and quantum-vs-classical benchmark reports.\n",
   "id": "at-xLNuz6ij6"
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}