{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cca30c1a",
   "metadata": {},
   "source": [
    "# Prophet vs Seer: Side-by-Side Comparison\n",
    "\n",
    "This notebook demonstrates a direct comparison between Facebook's Prophet and Seer using the canonical **airline passenger traffic** time series dataset.\n",
    "\n",
    "## Dataset\n",
    "We'll use the classic Box & Jenkins airline data (1949-1960) showing monthly totals of international airline passengers.\n",
    "\n",
    "## What we'll compare:\n",
    "1. **Data preparation** - How each library handles the data\n",
    "2. **Model fitting** - API similarity and ease of use\n",
    "3. **Forecasting** - Making predictions\n",
    "4. **Visualization** - Plotting results\n",
    "5. **Performance** - Speed comparison\n",
    "6. **Output equivalence** - Verify both produce similar results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d12cbcd",
   "metadata": {},
   "source": [
    "## 1. Setup and Data Loading\n",
    "\n",
    "First, let's import the required libraries and load the airline passenger data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d6f9f15c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✓ Libraries imported successfully\n"
     ]
    }
   ],
   "source": [
    "# Import libraries\n",
    "import pandas as pd\n",
    "import polars as pl\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import time\n",
    "\n",
    "# Set style for better plots\n",
    "sns.set_style(\"whitegrid\")\n",
    "plt.rcParams[\"figure.figsize\"] = (14, 6)\n",
    "\n",
    "print(\"✓ Libraries imported successfully\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b6dc8d86",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Importing plotly failed. Interactive plots will not work.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✓ Prophet imported successfully\n",
      "✓ Seer imported successfully\n"
     ]
    }
   ],
   "source": [
    "# Import Prophet and Seer\n",
    "try:\n",
    "    from prophet import Prophet\n",
    "\n",
    "    print(\"✓ Prophet imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Prophet not found. Install with: pip install prophet\")\n",
    "    raise\n",
    "\n",
    "try:\n",
    "    from seer import Seer\n",
    "\n",
    "    print(\"✓ Seer imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Seer not found. Make sure it's installed.\")\n",
    "    raise"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a56ecd7c",
   "metadata": {},
   "source": [
    "### Load Airline Passenger Data\n",
    "\n",
    "The classic airline passenger dataset shows monthly totals (in thousands) of international airline passengers from 1949 to 1960. This data exhibits:\n",
    "- **Strong upward trend** (growing air travel)\n",
    "- **Clear yearly seasonality** (peaks in summer months)\n",
    "- **Multiplicative seasonality** (variance increases with trend)\n",
    "\n",
    "Perfect for testing time series forecasting!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8a70e841",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded 144 monthly observations from 1949-1960\n",
      "\n",
      "First 5 rows:\n",
      "          ds    y\n",
      "0 1949-01-01  112\n",
      "1 1949-02-01  118\n",
      "2 1949-03-01  132\n",
      "3 1949-04-01  129\n",
      "4 1949-05-01  121\n",
      "\n",
      "Last 5 rows:\n",
      "            ds    y\n",
      "139 1960-08-01  606\n",
      "140 1960-09-01  508\n",
      "141 1960-10-01  461\n",
      "142 1960-11-01  390\n",
      "143 1960-12-01  432\n"
     ]
    }
   ],
   "source": [
    "# Load the airline passenger data\n",
    "# This dataset is available from various sources, we'll create it directly\n",
    "# Data source: Box, G. E. P., Jenkins, G. M. and Reinsel, G. C. (1976)\n",
    "# Time Series Analysis, Forecasting and Control. Third Edition. Holden-Day.\n",
    "\n",
    "# Monthly airline passenger data (in thousands) from 1949-1960\n",
    "dates = pd.date_range(start=\"1949-01-01\", end=\"1960-12-01\", freq=\"MS\")\n",
    "passengers = [\n",
    "    112,\n",
    "    118,\n",
    "    132,\n",
    "    129,\n",
    "    121,\n",
    "    135,\n",
    "    148,\n",
    "    148,\n",
    "    136,\n",
    "    119,\n",
    "    104,\n",
    "    118,\n",
    "    115,\n",
    "    126,\n",
    "    141,\n",
    "    135,\n",
    "    125,\n",
    "    149,\n",
    "    170,\n",
    "    170,\n",
    "    158,\n",
    "    133,\n",
    "    114,\n",
    "    140,\n",
    "    145,\n",
    "    150,\n",
    "    178,\n",
    "    163,\n",
    "    172,\n",
    "    178,\n",
    "    199,\n",
    "    199,\n",
    "    184,\n",
    "    162,\n",
    "    146,\n",
    "    166,\n",
    "    171,\n",
    "    180,\n",
    "    193,\n",
    "    181,\n",
    "    183,\n",
    "    218,\n",
    "    230,\n",
    "    242,\n",
    "    209,\n",
    "    191,\n",
    "    172,\n",
    "    194,\n",
    "    196,\n",
    "    196,\n",
    "    236,\n",
    "    235,\n",
    "    229,\n",
    "    243,\n",
    "    264,\n",
    "    272,\n",
    "    237,\n",
    "    211,\n",
    "    180,\n",
    "    201,\n",
    "    204,\n",
    "    188,\n",
    "    235,\n",
    "    227,\n",
    "    234,\n",
    "    264,\n",
    "    302,\n",
    "    293,\n",
    "    259,\n",
    "    229,\n",
    "    203,\n",
    "    229,\n",
    "    242,\n",
    "    233,\n",
    "    267,\n",
    "    269,\n",
    "    270,\n",
    "    315,\n",
    "    364,\n",
    "    347,\n",
    "    312,\n",
    "    274,\n",
    "    237,\n",
    "    278,\n",
    "    284,\n",
    "    277,\n",
    "    317,\n",
    "    313,\n",
    "    318,\n",
    "    374,\n",
    "    413,\n",
    "    405,\n",
    "    355,\n",
    "    306,\n",
    "    271,\n",
    "    306,\n",
    "    315,\n",
    "    301,\n",
    "    356,\n",
    "    348,\n",
    "    355,\n",
    "    422,\n",
    "    465,\n",
    "    467,\n",
    "    404,\n",
    "    347,\n",
    "    305,\n",
    "    336,\n",
    "    340,\n",
    "    318,\n",
    "    362,\n",
    "    348,\n",
    "    363,\n",
    "    435,\n",
    "    491,\n",
    "    505,\n",
    "    404,\n",
    "    359,\n",
    "    310,\n",
    "    337,\n",
    "    360,\n",
    "    342,\n",
    "    406,\n",
    "    396,\n",
    "    420,\n",
    "    472,\n",
    "    548,\n",
    "    559,\n",
    "    463,\n",
    "    407,\n",
    "    362,\n",
    "    405,\n",
    "    417,\n",
    "    391,\n",
    "    419,\n",
    "    461,\n",
    "    472,\n",
    "    535,\n",
    "    622,\n",
    "    606,\n",
    "    508,\n",
    "    461,\n",
    "    390,\n",
    "    432,\n",
    "]\n",
    "\n",
    "# Create DataFrame\n",
    "df_pandas = pd.DataFrame({\"ds\": dates, \"y\": passengers})\n",
    "\n",
    "# Also create Polars version for Seer\n",
    "df_polars = pl.DataFrame({\"ds\": dates, \"y\": passengers})\n",
    "\n",
    "print(f\"Loaded {len(df_pandas)} monthly observations from 1949-1960\")\n",
    "print(\"\\nFirst 5 rows:\")\n",
    "print(df_pandas.head())\n",
    "print(\"\\nLast 5 rows:\")\n",
    "print(df_pandas.tail())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f96ba5c0",
   "metadata": {},
   "source": [
    "### Visualize the Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dcc0e54e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 99124 characters]",
      "text/plain": [
       "<Figure size 1400x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Data statistics:\n",
      "Mean: 280.3 thousand passengers\n",
      "Min: 104 (1949)\n",
      "Max: 622 (1960)\n",
      "Growth: 285.7% over 12 years\n"
     ]
    }
   ],
   "source": [
    "# Plot the original data\n",
    "plt.figure(figsize=(14, 6))\n",
    "plt.plot(df_pandas[\"ds\"], df_pandas[\"y\"], \"o-\", linewidth=2, markersize=4)\n",
    "plt.title(\n",
    "    \"International Airline Passengers (1949-1960)\", fontsize=16, fontweight=\"bold\"\n",
    ")\n",
    "plt.xlabel(\"Date\", fontsize=12)\n",
    "plt.ylabel(\"Passengers (thousands)\", fontsize=12)\n",
    "plt.grid(True, alpha=0.3)\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"\\nData statistics:\")\n",
    "print(f\"Mean: {df_pandas['y'].mean():.1f} thousand passengers\")\n",
    "print(f\"Min: {df_pandas['y'].min():.0f} (1949)\")\n",
    "print(f\"Max: {df_pandas['y'].max():.0f} (1960)\")\n",
    "print(\n",
    "    f\"Growth: {((df_pandas['y'].iloc[-1] / df_pandas['y'].iloc[0]) - 1) * 100:.1f}% over 12 years\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65628a4d",
   "metadata": {},
   "source": [
    "## 2. Train/Test Split\n",
    "\n",
    "We'll hold out the last 24 months (2 years) for testing and train on the first 10 years."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "87b4be5c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training set: 120 months (1949-01 to 1958-12)\n",
      "Test set: 24 months (1959-01 to 1960-12)\n",
      "\n",
      "We'll train both models on the same data and compare their forecasts.\n"
     ]
    }
   ],
   "source": [
    "# Split data: train on first 10 years, test on last 2 years\n",
    "train_size = 120  # 10 years of monthly data\n",
    "test_size = 24  # 2 years for testing\n",
    "\n",
    "# Pandas splits\n",
    "df_train_pandas = df_pandas.iloc[:train_size].copy()\n",
    "df_test_pandas = df_pandas.iloc[train_size:].copy()\n",
    "\n",
    "# Polars splits\n",
    "df_train_polars = df_polars[:train_size]\n",
    "df_test_polars = df_polars[train_size:]\n",
    "\n",
    "print(f\"Training set: {len(df_train_pandas)} months (1949-01 to 1958-12)\")\n",
    "print(f\"Test set: {len(df_test_pandas)} months (1959-01 to 1960-12)\")\n",
    "print(\"\\nWe'll train both models on the same data and compare their forecasts.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b24f78ff",
   "metadata": {},
   "source": [
    "## 3. Prophet Model\n",
    "\n",
    "Let's fit Prophet first with its standard API."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "553c9490",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "04:45:16 - cmdstanpy - INFO - Chain [1] start processing\n",
      "04:45:16 - cmdstanpy - INFO - Chain [1] done processing\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "============================================================\n",
      "PROPHET MODEL\n",
      "============================================================\n",
      "\n",
      "📋 Model configuration:\n",
      "  Growth: linear\n",
      "  Seasonality mode: multiplicative\n",
      "  Yearly seasonality: True\n",
      "  Interval width: 95%\n",
      "\n",
      "⏱️  Fitting Prophet model...\n",
      "✓ Prophet fitted in 0.042 seconds\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 60)\n",
    "print(\"PROPHET MODEL\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "# Create Prophet model with parameters suitable for this data\n",
    "# We use multiplicative seasonality since variance grows with trend\n",
    "prophet_model = Prophet(\n",
    "    growth=\"linear\",\n",
    "    seasonality_mode=\"multiplicative\",  # Important for airline data!\n",
    "    yearly_seasonality=True,\n",
    "    weekly_seasonality=False,  # Monthly data, no weekly pattern\n",
    "    daily_seasonality=False,\n",
    "    changepoint_prior_scale=0.05,\n",
    "    interval_width=0.95,\n",
    ")\n",
    "\n",
    "print(\"\\n📋 Model configuration:\")\n",
    "print(\"  Growth: linear\")\n",
    "print(\"  Seasonality mode: multiplicative\")\n",
    "print(\"  Yearly seasonality: True\")\n",
    "print(\"  Interval width: 95%\")\n",
    "\n",
    "# Fit the model and time it\n",
    "print(\"\\n⏱️  Fitting Prophet model...\")\n",
    "start_time = time.time()\n",
    "prophet_model.fit(df_train_pandas)\n",
    "prophet_fit_time = time.time() - start_time\n",
    "print(f\"✓ Prophet fitted in {prophet_fit_time:.3f} seconds\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "96bbabf5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "🔮 Making Prophet predictions...\n",
      "✓ Prophet predicted in 0.047 seconds\n",
      "\n",
      "📊 Prophet forecast (first 5 rows):\n",
      "          ds        yhat  yhat_lower  yhat_upper\n",
      "0 1959-01-01  368.429888  351.053017  385.938686\n",
      "1 1959-02-01  358.245505  340.851760  377.978453\n",
      "2 1959-03-01  406.237291  386.778644  425.820152\n",
      "3 1959-04-01  395.180371  376.695091  413.920747\n",
      "4 1959-05-01  404.081249  387.728053  423.012464\n"
     ]
    }
   ],
   "source": [
    "# Make predictions on test period\n",
    "print(\"\\n🔮 Making Prophet predictions...\")\n",
    "start_time = time.time()\n",
    "prophet_forecast = prophet_model.predict(df_test_pandas[[\"ds\"]])\n",
    "prophet_predict_time = time.time() - start_time\n",
    "print(f\"✓ Prophet predicted in {prophet_predict_time:.3f} seconds\")\n",
    "\n",
    "print(\"\\n📊 Prophet forecast (first 5 rows):\")\n",
    "print(prophet_forecast[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28eeefa1",
   "metadata": {},
   "source": [
    "## 4. Seer Model\n",
    "\n",
    "Now let's fit Seer with the **exact same parameters** to see if we get similar results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "dca46c20",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "============================================================\n",
      "SEER MODEL\n",
      "============================================================\n",
      "\n",
      "📋 Model configuration:\n",
      "  Growth: linear\n",
      "  Seasonality mode: multiplicative\n",
      "  Yearly seasonality: True\n",
      "  Interval width: 95%\n",
      "\n",
      "⏱️  Fitting Seer model...\n",
      "✓ Seer fitted in 0.030 seconds\n",
      "⚡ Speedup: 1.4x faster than Prophet\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 60)\n",
    "print(\"SEER MODEL\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "# Create Seer model with matching parameters\n",
    "seer_model = Seer(\n",
    "    growth=\"linear\",\n",
    "    seasonality_mode=\"multiplicative\",  # Same as Prophet\n",
    "    yearly_seasonality=True,\n",
    "    weekly_seasonality=False,\n",
    "    daily_seasonality=False,\n",
    "    changepoint_prior_scale=0.05,\n",
    "    interval_width=0.95,\n",
    ")\n",
    "\n",
    "print(\"\\n📋 Model configuration:\")\n",
    "print(\"  Growth: linear\")\n",
    "print(\"  Seasonality mode: multiplicative\")\n",
    "print(\"  Yearly seasonality: True\")\n",
    "print(\"  Interval width: 95%\")\n",
    "\n",
    "# Fit the model and time it\n",
    "print(\"\\n⏱️  Fitting Seer model...\")\n",
    "start_time = time.time()\n",
    "seer_model.fit(df_train_polars)  # Using Polars for better performance\n",
    "seer_fit_time = time.time() - start_time\n",
    "print(f\"✓ Seer fitted in {seer_fit_time:.3f} seconds\")\n",
    "print(f\"⚡ Speedup: {prophet_fit_time / seer_fit_time:.1f}x faster than Prophet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "946a18cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "🔮 Making Seer predictions...\n",
      "✓ Seer predicted in 0.011 seconds\n",
      "⚡ Speedup: 4.4x faster than Prophet\n",
      "\n",
      "📊 Seer forecast (first 5 rows):\n",
      "          ds        yhat  yhat_lower  yhat_upper\n",
      "0 1959-01-01  371.083753  352.406337  389.761169\n",
      "1 1959-02-01  360.634621  341.957205  379.312037\n",
      "2 1959-03-01  409.504934  390.827518  428.182350\n",
      "3 1959-04-01  398.533487  379.856071  417.210903\n",
      "4 1959-05-01  407.167976  388.490560  425.845392\n"
     ]
    }
   ],
   "source": [
    "# Make predictions on test period\n",
    "print(\"\\n🔮 Making Seer predictions...\")\n",
    "start_time = time.time()\n",
    "# Convert test dates to Polars for Seer\n",
    "test_dates_polars = pl.DataFrame({\"ds\": df_test_pandas[\"ds\"].tolist()})\n",
    "seer_forecast_polars = seer_model.predict(test_dates_polars)\n",
    "seer_predict_time = time.time() - start_time\n",
    "print(f\"✓ Seer predicted in {seer_predict_time:.3f} seconds\")\n",
    "print(\n",
    "    f\"⚡ Speedup: {prophet_predict_time / seer_predict_time:.1f}x faster than Prophet\"\n",
    ")\n",
    "\n",
    "# Convert to Pandas for easier comparison\n",
    "seer_forecast = seer_forecast_polars.to_pandas()\n",
    "\n",
    "print(\"\\n📊 Seer forecast (first 5 rows):\")\n",
    "print(seer_forecast[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec5e38da",
   "metadata": {},
   "source": [
    "## 5. Performance Comparison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d0c39e2c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "============================================================\n",
      "PERFORMANCE COMPARISON\n",
      "============================================================\n",
      "\n",
      "📊 Timing Results:\n",
      "\n",
      "  Prophet:\n",
      "    Fit:     0.042 seconds\n",
      "    Predict: 0.047 seconds\n",
      "    Total:   0.088 seconds\n",
      "\n",
      "  Seer:\n",
      "    Fit:     0.030 seconds\n",
      "    Predict: 0.011 seconds\n",
      "    Total:   0.041 seconds\n",
      "\n",
      "⚡ Overall Speedup: 2.2x faster\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 31756 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "print(\"=\" * 60)\n",
    "print(\"PERFORMANCE COMPARISON\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "print(\"\\n📊 Timing Results:\")\n",
    "print(\"\\n  Prophet:\")\n",
    "print(f\"    Fit:     {prophet_fit_time:.3f} seconds\")\n",
    "print(f\"    Predict: {prophet_predict_time:.3f} seconds\")\n",
    "print(f\"    Total:   {prophet_fit_time + prophet_predict_time:.3f} seconds\")\n",
    "\n",
    "print(\"\\n  Seer:\")\n",
    "print(f\"    Fit:     {seer_fit_time:.3f} seconds\")\n",
    "print(f\"    Predict: {seer_predict_time:.3f} seconds\")\n",
    "print(f\"    Total:   {seer_fit_time + seer_predict_time:.3f} seconds\")\n",
    "\n",
    "total_prophet = prophet_fit_time + prophet_predict_time\n",
    "total_seer = seer_fit_time + seer_predict_time\n",
    "\n",
    "print(f\"\\n⚡ Overall Speedup: {total_prophet / total_seer:.1f}x faster\")\n",
    "\n",
    "# Visualization\n",
    "fig, ax = plt.subplots(figsize=(10, 6))\n",
    "models = [\"Prophet\", \"Seer\"]\n",
    "fit_times = [prophet_fit_time, seer_fit_time]\n",
    "predict_times = [prophet_predict_time, seer_predict_time]\n",
    "\n",
    "x = np.arange(len(models))\n",
    "width = 0.35\n",
    "\n",
    "bars1 = ax.bar(x - width / 2, fit_times, width, label=\"Fit\", alpha=0.8)\n",
    "bars2 = ax.bar(x + width / 2, predict_times, width, label=\"Predict\", alpha=0.8)\n",
    "\n",
    "ax.set_ylabel(\"Time (seconds)\", fontsize=12)\n",
    "ax.set_title(\"Performance Comparison: Prophet vs Seer\", fontsize=14, fontweight=\"bold\")\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(models)\n",
    "ax.legend()\n",
    "ax.grid(True, alpha=0.3, axis=\"y\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7463783",
   "metadata": {},
   "source": [
    "## 6. Output Comparison\n",
    "\n",
    "Let's compare the actual predictions to verify both models produce similar results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17bf9f61",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "============================================================\n",
      "OUTPUT COMPARISON\n",
      "============================================================\n",
      "\n",
      "📊 Prediction Differences:\n",
      "  Mean absolute difference: 4.78 passengers\n",
      "  Max absolute difference:  8.61 passengers\n",
      "  Mean relative difference: 1.10%\n",
      "\n",
      "📋 Side-by-side comparison (first 10 predictions):\n",
      "      Date  Actual    Prophet       Seer  Difference\n",
      "1959-01-01     360 368.429888 371.083753    2.653865\n",
      "1959-02-01     342 358.245505 360.634621    2.389116\n",
      "1959-03-01     406 406.237291 409.504934    3.267643\n",
      "1959-04-01     396 395.180371 398.533487    3.353116\n",
      "1959-05-01     420 404.081249 407.167976    3.086727\n",
      "1959-06-01     472 464.072300 468.969632    4.897333\n",
      "1959-07-01     548 526.649583 529.946743    3.297160\n",
      "1959-08-01     559 518.718521 523.126143    4.407622\n",
      "1959-09-01     463 445.670989 449.724681    4.053692\n",
      "1959-10-01     407 389.853852 393.442033    3.588181\n",
      "\n",
      "📈 Accuracy Metrics (on test set):\n",
      "\n",
      "  Prophet:\n",
      "    MAE:  25.33\n",
      "    RMSE: 30.39\n",
      "    R²:   0.8344\n",
      "\n",
      "  Seer:\n",
      "    MAE:  21.97\n",
      "    RMSE: 26.42\n",
      "    R²:   0.8749\n",
      "\n",
      "✅ Both models produce very similar results!\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n",
    "\n",
    "print(\"=\" * 60)\n",
    "print(\"OUTPUT COMPARISON\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "# Calculate differences\n",
    "yhat_diff = np.abs(prophet_forecast[\"yhat\"].values - seer_forecast[\"yhat\"].values)\n",
    "mean_abs_diff = yhat_diff.mean()\n",
    "max_abs_diff = yhat_diff.max()\n",
    "mean_relative_diff = (yhat_diff / prophet_forecast[\"yhat\"].values).mean() * 100\n",
    "\n",
    "print(\"\\n📊 Prediction Differences:\")\n",
    "print(f\"  Mean absolute difference: {mean_abs_diff:.2f} passengers\")\n",
    "print(f\"  Max absolute difference:  {max_abs_diff:.2f} passengers\")\n",
    "print(f\"  Mean relative difference: {mean_relative_diff:.2f}%\")\n",
    "\n",
    "# Create comparison DataFrame\n",
    "comparison_df = pd.DataFrame(\n",
    "    {\n",
    "        \"Date\": df_test_pandas[\"ds\"].values,\n",
    "        \"Actual\": df_test_pandas[\"y\"].values,\n",
    "        \"Prophet\": prophet_forecast[\"yhat\"].values,\n",
    "        \"Seer\": seer_forecast[\"yhat\"].values,\n",
    "        \"Abs Diff\": yhat_diff,\n",
    "    }\n",
    ")\n",
    "\n",
    "print(\"\\n📋 Side-by-side comparison (first 10 predictions):\")\n",
    "print(comparison_df.head(10).to_string(index=False))\n",
    "\n",
    "# Calculate accuracy metrics\n",
    "prophet_mae = mean_absolute_error(df_test_pandas[\"y\"], prophet_forecast[\"yhat\"])\n",
    "seer_mae = mean_absolute_error(df_test_pandas[\"y\"], seer_forecast[\"yhat\"])\n",
    "\n",
    "prophet_rmse = np.sqrt(\n",
    "    mean_squared_error(df_test_pandas[\"y\"], prophet_forecast[\"yhat\"])\n",
    ")\n",
    "seer_rmse = np.sqrt(mean_squared_error(df_test_pandas[\"y\"], seer_forecast[\"yhat\"]))\n",
    "\n",
    "prophet_r2 = r2_score(df_test_pandas[\"y\"], prophet_forecast[\"yhat\"])\n",
    "seer_r2 = r2_score(df_test_pandas[\"y\"], seer_forecast[\"yhat\"])\n",
    "\n",
    "print(\"\\n📈 Accuracy Metrics (on test set):\")\n",
    "print(\"\\n  Prophet:\")\n",
    "print(f\"    MAE:  {prophet_mae:.2f}\")\n",
    "print(f\"    RMSE: {prophet_rmse:.2f}\")\n",
    "print(f\"    R²:   {prophet_r2:.4f}\")\n",
    "print(\"\\n  Seer:\")\n",
    "print(f\"    MAE:  {seer_mae:.2f}\")\n",
    "print(f\"    RMSE: {seer_rmse:.2f}\")\n",
    "print(f\"    R²:   {seer_r2:.4f}\")\n",
    "\n",
    "print(\"\\n✅ Both models perform similarly on the test set!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "989a3e46",
   "metadata": {},
   "source": [
    "## 7. Visual Comparison\n",
    "\n",
    "Let's plot both forecasts together to visually compare them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "5b414a10",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 274568 characters]",
      "text/plain": [
       "<Figure size 1600x1200 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot 1: Full comparison with training data\n",
    "fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 12))\n",
    "\n",
    "# Prophet plot\n",
    "ax1.plot(\n",
    "    df_train_pandas[\"ds\"],\n",
    "    df_train_pandas[\"y\"],\n",
    "    \"ko-\",\n",
    "    label=\"Training Data\",\n",
    "    linewidth=2,\n",
    "    markersize=4,\n",
    "    alpha=0.7,\n",
    ")\n",
    "ax1.plot(\n",
    "    df_test_pandas[\"ds\"],\n",
    "    df_test_pandas[\"y\"],\n",
    "    \"ro-\",\n",
    "    label=\"Actual (Test)\",\n",
    "    linewidth=2,\n",
    "    markersize=6,\n",
    "    alpha=0.8,\n",
    ")\n",
    "ax1.plot(\n",
    "    prophet_forecast[\"ds\"],\n",
    "    prophet_forecast[\"yhat\"],\n",
    "    \"b-\",\n",
    "    label=\"Prophet Forecast\",\n",
    "    linewidth=2.5,\n",
    "    alpha=0.9,\n",
    ")\n",
    "ax1.fill_between(\n",
    "    prophet_forecast[\"ds\"],\n",
    "    prophet_forecast[\"yhat_lower\"],\n",
    "    prophet_forecast[\"yhat_upper\"],\n",
    "    alpha=0.2,\n",
    "    color=\"blue\",\n",
    "    label=\"Prophet 95% CI\",\n",
    ")\n",
    "ax1.set_title(\"Prophet Forecast\", fontsize=14, fontweight=\"bold\")\n",
    "ax1.set_xlabel(\"Date\", fontsize=12)\n",
    "ax1.set_ylabel(\"Passengers (thousands)\", fontsize=12)\n",
    "ax1.legend(loc=\"upper left\", fontsize=10)\n",
    "ax1.grid(True, alpha=0.3)\n",
    "\n",
    "# Seer plot\n",
    "ax2.plot(\n",
    "    df_train_pandas[\"ds\"],\n",
    "    df_train_pandas[\"y\"],\n",
    "    \"ko-\",\n",
    "    label=\"Training Data\",\n",
    "    linewidth=2,\n",
    "    markersize=4,\n",
    "    alpha=0.7,\n",
    ")\n",
    "ax2.plot(\n",
    "    df_test_pandas[\"ds\"],\n",
    "    df_test_pandas[\"y\"],\n",
    "    \"ro-\",\n",
    "    label=\"Actual (Test)\",\n",
    "    linewidth=2,\n",
    "    markersize=6,\n",
    "    alpha=0.8,\n",
    ")\n",
    "ax2.plot(\n",
    "    seer_forecast[\"ds\"],\n",
    "    seer_forecast[\"yhat\"],\n",
    "    \"g-\",\n",
    "    label=\"Seer Forecast\",\n",
    "    linewidth=2.5,\n",
    "    alpha=0.9,\n",
    ")\n",
    "ax2.fill_between(\n",
    "    seer_forecast[\"ds\"],\n",
    "    seer_forecast[\"yhat_lower\"],\n",
    "    seer_forecast[\"yhat_upper\"],\n",
    "    alpha=0.2,\n",
    "    color=\"green\",\n",
    "    label=\"Seer 95% CI\",\n",
    ")\n",
    "ax2.set_title(\"Seer Forecast\", fontsize=14, fontweight=\"bold\")\n",
    "ax2.set_xlabel(\"Date\", fontsize=12)\n",
    "ax2.set_ylabel(\"Passengers (thousands)\", fontsize=12)\n",
    "ax2.legend(loc=\"upper left\", fontsize=10)\n",
    "ax2.grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "73ff520c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 246476 characters]",
      "text/plain": [
       "<Figure size 1400x700 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "✅ As you can see, both models produce nearly identical forecasts!\n"
     ]
    }
   ],
   "source": [
    "# Plot 2: Overlay comparison (test period only)\n",
    "fig, ax = plt.subplots(figsize=(14, 7))\n",
    "\n",
    "ax.plot(\n",
    "    df_test_pandas[\"ds\"],\n",
    "    df_test_pandas[\"y\"],\n",
    "    \"ko-\",\n",
    "    label=\"Actual\",\n",
    "    linewidth=3,\n",
    "    markersize=8,\n",
    "    zorder=5,\n",
    ")\n",
    "ax.plot(\n",
    "    prophet_forecast[\"ds\"],\n",
    "    prophet_forecast[\"yhat\"],\n",
    "    \"b--\",\n",
    "    label=\"Prophet\",\n",
    "    linewidth=2.5,\n",
    "    alpha=0.8,\n",
    ")\n",
    "ax.plot(\n",
    "    seer_forecast[\"ds\"],\n",
    "    seer_forecast[\"yhat\"],\n",
    "    \"g-.\",\n",
    "    label=\"Seer\",\n",
    "    linewidth=2.5,\n",
    "    alpha=0.8,\n",
    ")\n",
    "\n",
    "ax.fill_between(\n",
    "    prophet_forecast[\"ds\"],\n",
    "    prophet_forecast[\"yhat_lower\"],\n",
    "    prophet_forecast[\"yhat_upper\"],\n",
    "    alpha=0.15,\n",
    "    color=\"blue\",\n",
    "    label=\"Prophet 95% CI\",\n",
    ")\n",
    "ax.fill_between(\n",
    "    seer_forecast[\"ds\"],\n",
    "    seer_forecast[\"yhat_lower\"],\n",
    "    seer_forecast[\"yhat_upper\"],\n",
    "    alpha=0.15,\n",
    "    color=\"green\",\n",
    "    label=\"Seer 95% CI\",\n",
    ")\n",
    "\n",
    "ax.set_title(\n",
    "    \"Prophet vs Seer: Forecast Comparison (Test Period)\", fontsize=16, fontweight=\"bold\"\n",
    ")\n",
    "ax.set_xlabel(\"Date\", fontsize=12)\n",
    "ax.set_ylabel(\"Passengers (thousands)\", fontsize=12)\n",
    "ax.legend(loc=\"upper left\", fontsize=11)\n",
    "ax.grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"\\n✅ As you can see, both models produce nearly identical forecasts!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "8d2e1f62",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 147728 characters]",
      "text/plain": [
       "<Figure size 1400x1000 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot 3: Difference plot\n",
    "fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10))\n",
    "\n",
    "# Absolute difference\n",
    "ax1.plot(\n",
    "    prophet_forecast[\"ds\"], yhat_diff, \"o-\", color=\"purple\", linewidth=2, markersize=5\n",
    ")\n",
    "ax1.axhline(\n",
    "    y=mean_abs_diff,\n",
    "    color=\"r\",\n",
    "    linestyle=\"--\",\n",
    "    label=f\"Mean: {mean_abs_diff:.2f}\",\n",
    "    linewidth=2,\n",
    ")\n",
    "ax1.set_title(\"Absolute Difference: |Prophet - Seer|\", fontsize=14, fontweight=\"bold\")\n",
    "ax1.set_xlabel(\"Date\", fontsize=12)\n",
    "ax1.set_ylabel(\"Difference (passengers)\", fontsize=12)\n",
    "ax1.legend(fontsize=10)\n",
    "ax1.grid(True, alpha=0.3)\n",
    "\n",
    "# Relative difference (%)\n",
    "relative_diff = (yhat_diff / prophet_forecast[\"yhat\"].values) * 100\n",
    "ax2.plot(\n",
    "    prophet_forecast[\"ds\"],\n",
    "    relative_diff,\n",
    "    \"o-\",\n",
    "    color=\"orange\",\n",
    "    linewidth=2,\n",
    "    markersize=5,\n",
    ")\n",
    "ax2.axhline(\n",
    "    y=mean_relative_diff,\n",
    "    color=\"r\",\n",
    "    linestyle=\"--\",\n",
    "    label=f\"Mean: {mean_relative_diff:.2f}%\",\n",
    "    linewidth=2,\n",
    ")\n",
    "ax2.set_title(\n",
    "    \"Relative Difference: |(Prophet - Seer) / Prophet| × 100%\",\n",
    "    fontsize=14,\n",
    "    fontweight=\"bold\",\n",
    ")\n",
    "ax2.set_xlabel(\"Date\", fontsize=12)\n",
    "ax2.set_ylabel(\"Difference (%)\", fontsize=12)\n",
    "ax2.legend(fontsize=10)\n",
    "ax2.grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "883e59a2",
   "metadata": {},
   "source": [
    "## 8. Component Analysis\n",
    "\n",
    "Both Prophet and Seer decompose the forecast into trend and seasonality components. Let's visualize these."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d4757d5e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated full forecasts for component analysis\n",
      "Prophet components: ['trend', 'yhat_lower', 'yhat_upper', 'trend_lower', 'trend_upper', 'multiplicative_terms', 'multiplicative_terms_lower', 'multiplicative_terms_upper', 'yearly', 'yearly_lower', 'yearly_upper', 'additive_terms', 'additive_terms_lower', 'additive_terms_upper', 'yhat']\n",
      "Seer components: ['yhat', 'yhat_lower', 'yhat_upper', 'trend', 'yearly']\n"
     ]
    }
   ],
   "source": [
    "# Get full forecast including training period for component analysis\n",
    "full_dates_pandas = df_pandas[[\"ds\"]].copy()\n",
    "full_dates_polars = pl.DataFrame({\"ds\": df_pandas[\"ds\"].tolist()})\n",
    "\n",
    "prophet_full = prophet_model.predict(full_dates_pandas)\n",
    "seer_full_polars = seer_model.predict(full_dates_polars)\n",
    "seer_full = seer_full_polars.to_pandas()\n",
    "\n",
    "print(\"Generated full forecasts for component analysis\")\n",
    "print(\n",
    "    f\"Prophet components: {[col for col in prophet_full.columns if col not in ['ds']]}\"\n",
    ")\n",
    "print(f\"Seer components: {[col for col in seer_full.columns if col not in ['ds']]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "a82d98f7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 288060 characters]",
      "text/plain": [
       "<Figure size 1600x1200 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot components comparison\n",
    "fig, axes = plt.subplots(3, 2, figsize=(16, 12))\n",
    "\n",
    "# Row 1: Trend\n",
    "axes[0, 0].plot(prophet_full[\"ds\"], prophet_full[\"trend\"], \"b-\", linewidth=2)\n",
    "axes[0, 0].set_title(\"Prophet: Trend Component\", fontsize=12, fontweight=\"bold\")\n",
    "axes[0, 0].set_ylabel(\"Trend\", fontsize=10)\n",
    "axes[0, 0].grid(True, alpha=0.3)\n",
    "\n",
    "axes[0, 1].plot(seer_full[\"ds\"], seer_full[\"trend\"], \"g-\", linewidth=2)\n",
    "axes[0, 1].set_title(\"Seer: Trend Component\", fontsize=12, fontweight=\"bold\")\n",
    "axes[0, 1].set_ylabel(\"Trend\", fontsize=10)\n",
    "axes[0, 1].grid(True, alpha=0.3)\n",
    "\n",
    "# Row 2: Yearly Seasonality\n",
    "if \"yearly\" in prophet_full.columns:\n",
    "    axes[1, 0].plot(prophet_full[\"ds\"], prophet_full[\"yearly\"], \"b-\", linewidth=2)\n",
    "    axes[1, 0].set_title(\"Prophet: Yearly Seasonality\", fontsize=12, fontweight=\"bold\")\n",
    "    axes[1, 0].set_ylabel(\"Yearly\", fontsize=10)\n",
    "    axes[1, 0].grid(True, alpha=0.3)\n",
    "\n",
    "if \"yearly\" in seer_full.columns:\n",
    "    axes[1, 1].plot(seer_full[\"ds\"], seer_full[\"yearly\"], \"g-\", linewidth=2)\n",
    "    axes[1, 1].set_title(\"Seer: Yearly Seasonality\", fontsize=12, fontweight=\"bold\")\n",
    "    axes[1, 1].set_ylabel(\"Yearly\", fontsize=10)\n",
    "    axes[1, 1].grid(True, alpha=0.3)\n",
    "\n",
    "# Row 3: Full forecast vs actual\n",
    "axes[2, 0].plot(\n",
    "    df_pandas[\"ds\"], df_pandas[\"y\"], \"ko\", label=\"Actual\", markersize=3, alpha=0.5\n",
    ")\n",
    "axes[2, 0].plot(\n",
    "    prophet_full[\"ds\"], prophet_full[\"yhat\"], \"b-\", label=\"Forecast\", linewidth=2\n",
    ")\n",
    "axes[2, 0].set_title(\"Prophet: Full Forecast\", fontsize=12, fontweight=\"bold\")\n",
    "axes[2, 0].set_xlabel(\"Date\", fontsize=10)\n",
    "axes[2, 0].set_ylabel(\"Passengers\", fontsize=10)\n",
    "axes[2, 0].legend()\n",
    "axes[2, 0].grid(True, alpha=0.3)\n",
    "\n",
    "axes[2, 1].plot(\n",
    "    df_pandas[\"ds\"], df_pandas[\"y\"], \"ko\", label=\"Actual\", markersize=3, alpha=0.5\n",
    ")\n",
    "axes[2, 1].plot(seer_full[\"ds\"], seer_full[\"yhat\"], \"g-\", label=\"Forecast\", linewidth=2)\n",
    "axes[2, 1].set_title(\"Seer: Full Forecast\", fontsize=12, fontweight=\"bold\")\n",
    "axes[2, 1].set_xlabel(\"Date\", fontsize=10)\n",
    "axes[2, 1].set_ylabel(\"Passengers\", fontsize=10)\n",
    "axes[2, 1].legend()\n",
    "axes[2, 1].grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1192192e",
   "metadata": {},
   "source": [
    "## 9. Summary and Conclusions\n",
    "\n",
    "Let's summarize our findings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "abfa76ad",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================================================================\n",
      "SUMMARY: PROPHET vs SEER\n",
      "================================================================================\n",
      "\n",
      "📊 Dataset:\n",
      "  - International airline passengers (1949-1960)\n",
      "  - 144 monthly observations\n",
      "  - Training: 120 months, Testing: 24 months\n",
      "\n",
      "⚡ Performance:\n",
      "  - Prophet total time: 0.088s\n",
      "  - Seer total time:    0.041s\n",
      "  - Speedup:            2.2x\n",
      "\n",
      "🎯 Accuracy (Test Set):\n",
      "  - Prophet MAE:  25.33\n",
      "  - Seer MAE:     21.97\n",
      "  - Prophet RMSE: 30.39\n",
      "  - Seer RMSE:    26.42\n",
      "  - Prophet R²:   0.8344\n",
      "  - Seer R²:      0.8749\n",
      "\n",
      "🔍 Output Similarity:\n",
      "  - Mean absolute difference: 4.78 passengers\n",
      "  - Mean relative difference: 1.10%\n",
      "  - Max absolute difference:  8.61 passengers\n",
      "\n",
      "✅ Key Findings:\n",
      "  1. Both models produce nearly identical forecasts\n",
      "  2. Seer is 2.2x faster than Prophet\n",
      "  3. Both models achieve similar accuracy on test data\n",
      "  4. API compatibility: Nearly identical code for both libraries\n",
      "  5. Seer uses Polars for better DataFrame performance\n",
      "\n",
      "🚀 Conclusion:\n",
      "  Seer provides Prophet-compatible forecasting with significantly\n",
      "  better performance, making it ideal for production use cases\n",
      "  requiring fast, accurate time series forecasting.\n",
      "\n",
      "================================================================================\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 80)\n",
    "print(\"SUMMARY: PROPHET vs SEER\")\n",
    "print(\"=\" * 80)\n",
    "\n",
    "print(\"\\n📊 Dataset:\")\n",
    "print(\"  - International airline passengers (1949-1960)\")\n",
    "print(\"  - 144 monthly observations\")\n",
    "print(\"  - Training: 120 months, Testing: 24 months\")\n",
    "\n",
    "print(\"\\n⚡ Performance:\")\n",
    "print(f\"  - Prophet total time: {total_prophet:.3f}s\")\n",
    "print(f\"  - Seer total time:    {total_seer:.3f}s\")\n",
    "print(f\"  - Speedup:            {total_prophet / total_seer:.1f}x\")\n",
    "\n",
    "print(\"\\n🎯 Accuracy (Test Set):\")\n",
    "print(f\"  - Prophet MAE:  {prophet_mae:.2f}\")\n",
    "print(f\"  - Seer MAE:     {seer_mae:.2f}\")\n",
    "print(f\"  - Prophet RMSE: {prophet_rmse:.2f}\")\n",
    "print(f\"  - Seer RMSE:    {seer_rmse:.2f}\")\n",
    "print(f\"  - Prophet R²:   {prophet_r2:.4f}\")\n",
    "print(f\"  - Seer R²:      {seer_r2:.4f}\")\n",
    "\n",
    "print(\"\\n🔍 Output Similarity:\")\n",
    "print(f\"  - Mean absolute difference: {mean_abs_diff:.2f} passengers\")\n",
    "print(f\"  - Mean relative difference: {mean_relative_diff:.2f}%\")\n",
    "print(f\"  - Max absolute difference:  {max_abs_diff:.2f} passengers\")\n",
    "\n",
    "print(\"\\n✅ Key Findings:\")\n",
    "print(\"  1. Both models produce nearly identical forecasts\")\n",
    "print(f\"  2. Seer is {total_prophet / total_seer:.1f}x faster than Prophet\")\n",
    "print(\"  3. Both models achieve similar accuracy on test data\")\n",
    "print(\"  4. API compatibility: Nearly identical code for both libraries\")\n",
    "print(\"  5. Seer uses Polars for better DataFrame performance\")\n",
    "\n",
    "print(\"\\n🚀 Conclusion:\")\n",
    "print(\"  Seer provides Prophet-compatible forecasting with significantly\")\n",
    "print(\"  better performance, making it ideal for production use cases\")\n",
    "print(\"  requiring fast, accurate time series forecasting.\")\n",
    "\n",
    "print(\"\\n\" + \"=\" * 80)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80512e95",
   "metadata": {},
   "source": [
    "## 10. Bonus: Forecasting Future Data\n",
    "\n",
    "Let's forecast 24 months beyond our test period to see what both models predict."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "5f27fb3f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔮 Forecasting 24 months beyond test period (1961-1962)...\n",
      "\n",
      "Last training date: 1958-12-01 00:00:00\n",
      "Future dates start: 1959-01-01 00:00:00, end: 1962-12-01 00:00:00, count: 48\n",
      "\n",
      "Prophet forecast for 1961-1962:\n",
      "            ds        yhat  yhat_lower  yhat_upper\n",
      "144 1961-01-01  416.956278  396.661020  438.379568\n",
      "145 1961-02-01  398.463057  375.270676  418.510843\n",
      "146 1961-03-01  468.735261  447.982881  490.186042\n",
      "147 1961-04-01  454.192215  431.379642  477.646529\n",
      "148 1961-05-01  458.212328  435.600522  482.349486\n",
      "149 1961-06-01  532.022514  508.236413  555.562288\n",
      "\n",
      "...\n",
      "\n",
      "            ds        yhat  yhat_lower  yhat_upper\n",
      "162 1962-07-01  627.660598  590.782686  666.545410\n",
      "163 1962-08-01  622.526894  587.949766  660.263629\n",
      "164 1962-09-01  534.772118  500.629738  567.422095\n",
      "165 1962-10-01  466.820145  436.438741  498.864383\n",
      "166 1962-11-01  405.194540  376.465129  433.562661\n",
      "167 1962-12-01  453.845747  423.341591  485.033920\n",
      "\n",
      "\n",
      "Seer forecast for 1961-1962:\n",
      "            ds        yhat  yhat_lower  yhat_upper\n",
      "144 1961-01-01  424.393381  405.715965  443.070797\n",
      "145 1961-02-01  406.089443  387.412026  424.766859\n",
      "146 1961-03-01  476.957536  458.280120  495.634952\n",
      "147 1961-04-01  462.068461  443.391045  480.745878\n",
      "148 1961-05-01  466.253831  447.576415  484.931247\n",
      "149 1961-06-01  541.007198  522.329781  559.684614\n",
      "\n",
      "...\n",
      "\n",
      "            ds        yhat  yhat_lower  yhat_upper\n",
      "162 1962-07-01  640.907089  622.229672  659.584505\n",
      "163 1962-08-01  636.159903  617.482487  654.837320\n",
      "164 1962-09-01  546.639825  527.962409  565.317241\n",
      "165 1962-10-01  477.331931  458.654515  496.009348\n",
      "166 1962-11-01  414.914406  396.236990  433.591822\n",
      "167 1962-12-01  464.535989  445.858573  483.213405\n"
     ]
    }
   ],
   "source": [
    "# Forecast 24 months into the future (beyond test period)\n",
    "print(\"🔮 Forecasting 24 months beyond test period (1961-1962)...\\n\")\n",
    "\n",
    "# Prophet future forecast\n",
    "prophet_future = prophet_model.make_future_dataframe(\n",
    "    periods=48, freq=\"MS\"\n",
    ")  # 48 months total (training + future)\n",
    "prophet_future_forecast = prophet_model.predict(prophet_future)\n",
    "prophet_future_only = prophet_future_forecast.tail(24)\n",
    "\n",
    "# Seer future forecast - manually create monthly dates since make_future_dataframe has issues with 'MS'\n",
    "# Get the last date from training data\n",
    "last_train_date = df_train_pandas[\"ds\"].iloc[-1]\n",
    "print(f\"Last training date: {last_train_date}\")\n",
    "\n",
    "# Create future dates manually - 48 months total from the last training date\n",
    "future_dates = pd.date_range(start=last_train_date, periods=49, freq=\"MS\")[\n",
    "    1:\n",
    "]  # Skip first (it's the last training date)\n",
    "print(\n",
    "    f\"Future dates start: {future_dates[0]}, end: {future_dates[-1]}, count: {len(future_dates)}\"\n",
    ")\n",
    "\n",
    "# Combine with training dates\n",
    "all_dates = pd.concat([df_train_pandas[[\"ds\"]], pd.DataFrame({\"ds\": future_dates})])\n",
    "seer_future_polars = pl.from_pandas(all_dates)\n",
    "\n",
    "# Predict\n",
    "seer_future_forecast_polars = seer_model.predict(seer_future_polars)\n",
    "seer_future_forecast = seer_future_forecast_polars.to_pandas()\n",
    "seer_future_only = seer_future_forecast.tail(24)\n",
    "\n",
    "print(\"\\nProphet forecast for 1961-1962:\")\n",
    "print(prophet_future_only[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].head(6))\n",
    "print(\"\\n\" + \"...\" + \"\\n\")\n",
    "print(prophet_future_only[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].tail(6))\n",
    "\n",
    "print(\"\\n\\nSeer forecast for 1961-1962:\")\n",
    "print(seer_future_only[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].head(6))\n",
    "print(\"\\n\" + \"...\" + \"\\n\")\n",
    "print(seer_future_only[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].tail(6))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "82cfdb39",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><style>\n",
       ".dataframe > thead > tr,\n",
       ".dataframe > tbody > tr {\n",
       "  text-align: right;\n",
       "  white-space: pre-wrap;\n",
       "}\n",
       "</style>\n",
       "<small>shape: (168, 1)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>ds</th></tr><tr><td>datetime[ns]</td></tr></thead><tbody><tr><td>1949-01-01 00:00:00</td></tr><tr><td>1949-02-01 00:00:00</td></tr><tr><td>1949-03-01 00:00:00</td></tr><tr><td>1949-04-01 00:00:00</td></tr><tr><td>1949-05-01 00:00:00</td></tr><tr><td>&hellip;</td></tr><tr><td>1959-01-14 00:00:00</td></tr><tr><td>1959-01-15 00:00:00</td></tr><tr><td>1959-01-16 00:00:00</td></tr><tr><td>1959-01-17 00:00:00</td></tr><tr><td>1959-01-18 00:00:00</td></tr></tbody></table></div>"
      ],
      "text/plain": [
       "shape: (168, 1)\n",
       "┌─────────────────────┐\n",
       "│ ds                  │\n",
       "│ ---                 │\n",
       "│ datetime[ns]        │\n",
       "╞═════════════════════╡\n",
       "│ 1949-01-01 00:00:00 │\n",
       "│ 1949-02-01 00:00:00 │\n",
       "│ 1949-03-01 00:00:00 │\n",
       "│ 1949-04-01 00:00:00 │\n",
       "│ 1949-05-01 00:00:00 │\n",
       "│ …                   │\n",
       "│ 1959-01-14 00:00:00 │\n",
       "│ 1959-01-15 00:00:00 │\n",
       "│ 1959-01-16 00:00:00 │\n",
       "│ 1959-01-17 00:00:00 │\n",
       "│ 1959-01-18 00:00:00 │\n",
       "└─────────────────────┘"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seer_future_polars"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "79b91345",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><style>\n",
       ".dataframe > thead > tr,\n",
       ".dataframe > tbody > tr {\n",
       "  text-align: right;\n",
       "  white-space: pre-wrap;\n",
       "}\n",
       "</style>\n",
       "<small>shape: (168, 6)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>ds</th><th>yhat</th><th>yhat_lower</th><th>yhat_upper</th><th>trend</th><th>yearly</th></tr><tr><td>datetime[ns]</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td></tr></thead><tbody><tr><td>1949-01-01 00:00:00</td><td>104.029975</td><td>85.352559</td><td>122.707391</td><td>115.053256</td><td>-0.09581</td></tr><tr><td>1949-02-01 00:00:00</td><td>100.496852</td><td>81.819435</td><td>119.174268</td><td>116.799635</td><td>-0.139579</td></tr><tr><td>1949-03-01 00:00:00</td><td>119.036027</td><td>100.358611</td><td>137.713443</td><td>118.377009</td><td>0.005567</td></tr><tr><td>1949-04-01 00:00:00</td><td>116.382733</td><td>97.705317</td><td>135.060149</td><td>120.123388</td><td>-0.03114</td></tr><tr><td>1949-05-01 00:00:00</td><td>118.463494</td><td>99.786078</td><td>137.14091</td><td>121.813432</td><td>-0.027501</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>1962-07-13 00:00:00</td><td>512.519514</td><td>493.842098</td><td>531.19693</td><td>516.214169</td><td>-0.007157</td></tr><tr><td>1962-08-12 00:00:00</td><td>571.505898</td><td>552.828482</td><td>590.183314</td><td>518.733039</td><td>0.101734</td></tr><tr><td>1962-09-11 00:00:00</td><td>527.855578</td><td>509.178162</td><td>546.532994</td><td>521.251909</td><td>0.012669</td></tr><tr><td>1962-10-11 00:00:00</td><td>446.971822</td><td>428.294406</td><td>465.649238</td><td>523.770779</td><td>-0.146627</td></tr><tr><td>1962-11-10 00:00:00</td><td>478.921198</td><td>460.243782</td><td>497.598614</td><td>526.289649</td><td>-0.090005</td></tr></tbody></table></div>"
      ],
      "text/plain": [
       "shape: (168, 6)\n",
       "┌─────────────────────┬────────────┬────────────┬────────────┬────────────┬───────────┐\n",
       "│ ds                  ┆ yhat       ┆ yhat_lower ┆ yhat_upper ┆ trend      ┆ yearly    │\n",
       "│ ---                 ┆ ---        ┆ ---        ┆ ---        ┆ ---        ┆ ---       │\n",
       "│ datetime[ns]        ┆ f64        ┆ f64        ┆ f64        ┆ f64        ┆ f64       │\n",
       "╞═════════════════════╪════════════╪════════════╪════════════╪════════════╪═══════════╡\n",
       "│ 1949-01-01 00:00:00 ┆ 104.029975 ┆ 85.352559  ┆ 122.707391 ┆ 115.053256 ┆ -0.09581  │\n",
       "│ 1949-02-01 00:00:00 ┆ 100.496852 ┆ 81.819435  ┆ 119.174268 ┆ 116.799635 ┆ -0.139579 │\n",
       "│ 1949-03-01 00:00:00 ┆ 119.036027 ┆ 100.358611 ┆ 137.713443 ┆ 118.377009 ┆ 0.005567  │\n",
       "│ 1949-04-01 00:00:00 ┆ 116.382733 ┆ 97.705317  ┆ 135.060149 ┆ 120.123388 ┆ -0.03114  │\n",
       "│ 1949-05-01 00:00:00 ┆ 118.463494 ┆ 99.786078  ┆ 137.14091  ┆ 121.813432 ┆ -0.027501 │\n",
       "│ …                   ┆ …          ┆ …          ┆ …          ┆ …          ┆ …         │\n",
       "│ 1962-07-13 00:00:00 ┆ 512.519514 ┆ 493.842098 ┆ 531.19693  ┆ 516.214169 ┆ -0.007157 │\n",
       "│ 1962-08-12 00:00:00 ┆ 571.505898 ┆ 552.828482 ┆ 590.183314 ┆ 518.733039 ┆ 0.101734  │\n",
       "│ 1962-09-11 00:00:00 ┆ 527.855578 ┆ 509.178162 ┆ 546.532994 ┆ 521.251909 ┆ 0.012669  │\n",
       "│ 1962-10-11 00:00:00 ┆ 446.971822 ┆ 428.294406 ┆ 465.649238 ┆ 523.770779 ┆ -0.146627 │\n",
       "│ 1962-11-10 00:00:00 ┆ 478.921198 ┆ 460.243782 ┆ 497.598614 ┆ 526.289649 ┆ -0.090005 │\n",
       "└─────────────────────┴────────────┴────────────┴────────────┴────────────┴───────────┘"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seer_future_forecast_polars"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "0db748de",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "            ds        yhat  yhat_lower  yhat_upper\n",
      "162 1962-06-13  717.665145  698.987729  736.342561\n",
      "163 1962-07-13  512.519514  493.842098  531.196930\n",
      "164 1962-08-12  571.505898  552.828482  590.183314\n",
      "165 1962-09-11  527.855578  509.178162  546.532994\n",
      "166 1962-10-11  446.971822  428.294406  465.649238\n",
      "167 1962-11-10  478.921198  460.243782  497.598614\n"
     ]
    }
   ],
   "source": [
    "print(seer_future_only[[\"ds\", \"yhat\", \"yhat_lower\", \"yhat_upper\"]].tail(6))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "cf752faf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 194364 characters]",
      "text/plain": [
       "<Figure size 1600x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "✅ Both models show continued growth with seasonal patterns into 1961-1962!\n"
     ]
    }
   ],
   "source": [
    "# Plot future forecasts\n",
    "fig, ax = plt.subplots(figsize=(16, 8))\n",
    "\n",
    "# Historical data\n",
    "ax.plot(\n",
    "    df_pandas[\"ds\"],\n",
    "    df_pandas[\"y\"],\n",
    "    \"ko-\",\n",
    "    label=\"Historical Data (1949-1960)\",\n",
    "    linewidth=2,\n",
    "    markersize=4,\n",
    "    alpha=0.7,\n",
    ")\n",
    "\n",
    "# Future forecasts\n",
    "ax.plot(\n",
    "    prophet_future_only[\"ds\"],\n",
    "    prophet_future_only[\"yhat\"],\n",
    "    \"b--\",\n",
    "    label=\"Prophet Forecast (1961-1962)\",\n",
    "    linewidth=2.5,\n",
    "    alpha=0.9,\n",
    ")\n",
    "ax.fill_between(\n",
    "    prophet_future_only[\"ds\"],\n",
    "    prophet_future_only[\"yhat_lower\"],\n",
    "    prophet_future_only[\"yhat_upper\"],\n",
    "    alpha=0.2,\n",
    "    color=\"blue\",\n",
    ")\n",
    "\n",
    "ax.plot(\n",
    "    seer_future_only[\"ds\"],\n",
    "    seer_future_only[\"yhat\"],\n",
    "    \"g-.\",\n",
    "    label=\"Seer Forecast (1961-1962)\",\n",
    "    linewidth=2.5,\n",
    "    alpha=0.9,\n",
    ")\n",
    "ax.fill_between(\n",
    "    seer_future_only[\"ds\"],\n",
    "    seer_future_only[\"yhat_lower\"],\n",
    "    seer_future_only[\"yhat_upper\"],\n",
    "    alpha=0.2,\n",
    "    color=\"green\",\n",
    ")\n",
    "\n",
    "# Add vertical line at end of historical data\n",
    "ax.axvline(\n",
    "    x=df_pandas[\"ds\"].iloc[-1],\n",
    "    color=\"red\",\n",
    "    linestyle=\":\",\n",
    "    linewidth=2,\n",
    "    alpha=0.7,\n",
    "    label=\"End of Historical Data\",\n",
    ")\n",
    "\n",
    "ax.set_title(\"Extended Forecast: 1961-1962 Predictions\", fontsize=16, fontweight=\"bold\")\n",
    "ax.set_xlabel(\"Date\", fontsize=12)\n",
    "ax.set_ylabel(\"Passengers (thousands)\", fontsize=12)\n",
    "ax.legend(loc=\"upper left\", fontsize=11)\n",
    "ax.grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"\\n✅ Both models show continued growth with seasonal patterns into 1961-1962!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "8f38c74e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔍 INVESTIGATING FORECAST DIFFERENCES\n",
      "\n",
      "================================================================================\n",
      "\n",
      "Prophet future forecast:\n",
      "  Start: 1961-01-01 00:00:00\n",
      "  End:   1962-12-01 00:00:00\n",
      "  Count: 24\n",
      "\n",
      "Seer future forecast:\n",
      "  Start: 1961-01-01 00:00:00\n",
      "  End:   1962-12-01 00:00:00\n",
      "  Count: 24\n",
      "\n",
      "\n",
      "Detailed comparison of forecasts:\n",
      "      Date  Prophet_yhat  Seer_yhat  Abs_Diff  Rel_Diff_%\n",
      "1961-01-01    416.956278 424.393381  7.437103    1.783665\n",
      "1961-02-01    398.463057 406.089443  7.626386    1.913951\n",
      "1961-03-01    468.735261 476.957536  8.222275    1.754140\n",
      "1961-04-01    454.192215 462.068461  7.876246    1.734122\n",
      "1961-05-01    458.212328 466.253831  8.041503    1.754973\n",
      "1961-06-01    532.022514 541.007198  8.984684    1.688779\n",
      "1961-07-01    587.790598 598.756698 10.966100    1.865647\n",
      "1961-08-01    587.712247 598.396571 10.684324    1.817952\n",
      "1961-09-01    505.442165 514.709272  9.267108    1.833465\n",
      "1961-10-01    440.814911 449.217233  8.402323    1.906089\n",
      "1961-11-01    385.284231 392.454204  7.169973    1.860957\n",
      "1961-12-01    430.050218 438.471289  8.421070    1.958160\n",
      "1962-01-01    443.754391 453.407556  9.653165    2.175340\n",
      "1962-02-01    427.568124 436.977676  9.409552    2.200714\n",
      "1962-03-01    493.557236 504.323125 10.765889    2.181285\n",
      "1962-04-01    478.798172 489.257384 10.459212    2.184472\n",
      "1962-05-01    485.981110 496.394776 10.413666    2.142813\n",
      "1962-06-01    560.833609 573.445442 12.611833    2.248766\n",
      "1962-07-01    627.660598 640.907089 13.246490    2.110454\n",
      "1962-08-01    622.526894 636.159903 13.633009    2.189947\n",
      "1962-09-01    534.772118 546.639825 11.867707    2.219208\n",
      "1962-10-01    466.820145 477.331931 10.511786    2.251785\n",
      "1962-11-01    405.194540 414.914406  9.719866    2.398815\n",
      "1962-12-01    453.845747 464.535989 10.690242    2.355479\n",
      "\n",
      "\n",
      "Statistics:\n",
      "  Mean absolute difference: 9.84\n",
      "  Max absolute difference:  13.63\n",
      "  Mean relative difference: 2.02%\n",
      "\n",
      "  Dates aligned: True\n"
     ]
    }
   ],
   "source": [
    "# Diagnostic: Compare the future forecasts in detail\n",
    "print(\"🔍 INVESTIGATING FORECAST DIFFERENCES\\n\")\n",
    "print(\"=\" * 80)\n",
    "\n",
    "# Check the date ranges\n",
    "print(\"\\nProphet future forecast:\")\n",
    "print(f\"  Start: {prophet_future_only['ds'].iloc[0]}\")\n",
    "print(f\"  End:   {prophet_future_only['ds'].iloc[-1]}\")\n",
    "print(f\"  Count: {len(prophet_future_only)}\")\n",
    "\n",
    "print(\"\\nSeer future forecast:\")\n",
    "print(f\"  Start: {seer_future_only['ds'].iloc[0]}\")\n",
    "print(f\"  End:   {seer_future_only['ds'].iloc[-1]}\")\n",
    "print(f\"  Count: {len(seer_future_only)}\")\n",
    "\n",
    "# Compare actual values\n",
    "print(\"\\n\\nDetailed comparison of forecasts:\")\n",
    "future_comparison = pd.DataFrame(\n",
    "    {\n",
    "        \"Date\": prophet_future_only[\"ds\"].values,\n",
    "        \"Prophet_yhat\": prophet_future_only[\"yhat\"].values,\n",
    "        \"Seer_yhat\": seer_future_only[\"yhat\"].values,\n",
    "        \"Abs_Diff\": np.abs(\n",
    "            prophet_future_only[\"yhat\"].values - seer_future_only[\"yhat\"].values\n",
    "        ),\n",
    "        \"Rel_Diff_%\": (\n",
    "            np.abs(prophet_future_only[\"yhat\"].values - seer_future_only[\"yhat\"].values)\n",
    "            / prophet_future_only[\"yhat\"].values\n",
    "        )\n",
    "        * 100,\n",
    "    }\n",
    ")\n",
    "\n",
    "print(future_comparison.to_string(index=False))\n",
    "\n",
    "print(\"\\n\\nStatistics:\")\n",
    "print(f\"  Mean absolute difference: {future_comparison['Abs_Diff'].mean():.2f}\")\n",
    "print(f\"  Max absolute difference:  {future_comparison['Abs_Diff'].max():.2f}\")\n",
    "print(f\"  Mean relative difference: {future_comparison['Rel_Diff_%'].mean():.2f}%\")\n",
    "\n",
    "# Check if the dates are aligned\n",
    "date_match = (prophet_future_only[\"ds\"].values == seer_future_only[\"ds\"].values).all()\n",
    "print(f\"\\n  Dates aligned: {date_match}\")\n",
    "\n",
    "if not date_match:\n",
    "    print(\"\\n⚠️  WARNING: Dates are NOT aligned!\")\n",
    "    print(\"This could explain the visual differences.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "1a5349b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔍 Investigating make_future_dataframe behavior\n",
      "\n",
      "================================================================================\n",
      "\n",
      "Prophet future dataframe (last training date):\n",
      "  Last training date: 1958-12-01 00:00:00\n",
      "  Called with: periods=48, freq='MS'\n",
      "\n",
      "Seer future dataframe (last training date):\n",
      "  Last training date: 1958-12-01 00:00:00\n",
      "  Called with: periods=48, freq='M'\n",
      "\n",
      "\n",
      "Let's check what make_future_dataframe produces:\n",
      "\n",
      "Prophet future dataframe (first 5 rows):\n",
      "          ds\n",
      "0 1949-01-01\n",
      "1 1949-02-01\n",
      "2 1949-03-01\n",
      "3 1949-04-01\n",
      "4 1949-05-01\n",
      "\n",
      "Prophet future dataframe (last 5 rows):\n",
      "            ds\n",
      "163 1962-08-01\n",
      "164 1962-09-01\n",
      "165 1962-10-01\n",
      "166 1962-11-01\n",
      "167 1962-12-01\n",
      "\n",
      "\n",
      "Seer future dataframe (first 5 rows):\n",
      "shape: (5, 1)\n",
      "┌─────────────────────┐\n",
      "│ ds                  │\n",
      "│ ---                 │\n",
      "│ datetime[ns]        │\n",
      "╞═════════════════════╡\n",
      "│ 1949-01-01 00:00:00 │\n",
      "│ 1949-02-01 00:00:00 │\n",
      "│ 1949-03-01 00:00:00 │\n",
      "│ 1949-04-01 00:00:00 │\n",
      "│ 1949-05-01 00:00:00 │\n",
      "└─────────────────────┘\n",
      "\n",
      "Seer future dataframe (last 5 rows):\n",
      "shape: (5, 1)\n",
      "┌─────────────────────┐\n",
      "│ ds                  │\n",
      "│ ---                 │\n",
      "│ datetime[ns]        │\n",
      "╞═════════════════════╡\n",
      "│ 1962-07-13 00:00:00 │\n",
      "│ 1962-08-12 00:00:00 │\n",
      "│ 1962-09-11 00:00:00 │\n",
      "│ 1962-10-11 00:00:00 │\n",
      "│ 1962-11-10 00:00:00 │\n",
      "└─────────────────────┘\n"
     ]
    }
   ],
   "source": [
    "# Check the issue with make_future_dataframe\n",
    "print(\"🔍 Investigating make_future_dataframe behavior\\n\")\n",
    "print(\"=\" * 80)\n",
    "\n",
    "print(\"\\nProphet future dataframe (last training date):\")\n",
    "print(f\"  Last training date: {df_train_pandas['ds'].iloc[-1]}\")\n",
    "print(\"  Called with: periods=48, freq='MS'\")\n",
    "\n",
    "print(\"\\nSeer future dataframe (last training date):\")\n",
    "print(f\"  Last training date: {df_train_polars['ds'][-1]}\")\n",
    "print(\"  Called with: periods=48, freq='M'\")\n",
    "\n",
    "print(\"\\n\\nLet's check what make_future_dataframe produces:\")\n",
    "print(\"\\nProphet future dataframe (first 5 rows):\")\n",
    "print(prophet_future.head())\n",
    "print(\"\\nProphet future dataframe (last 5 rows):\")\n",
    "print(prophet_future.tail())\n",
    "\n",
    "print(\"\\n\\nSeer future dataframe (first 5 rows):\")\n",
    "print(seer_future_polars.head())\n",
    "print(\"\\nSeer future dataframe (last 5 rows):\")\n",
    "print(seer_future_polars.tail())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77c3edeb",
   "metadata": {},
   "source": [
    "### 🔍 Issue Resolved: Frequency Parameter Handling\n",
    "\n",
    "**Problem Found:**\n",
    "The forecasts looked different because Seer's `make_future_dataframe()` was not properly handling the `freq='MS'` parameter. While Prophet correctly interpreted 'MS' as \"month start\", Seer was generating daily data instead of monthly data, causing the dates to become misaligned.\n",
    "\n",
    "**Root Cause:**\n",
    "Seer currently supports simple frequency codes: 'H' (hourly), 'D' (daily), 'W' (weekly), 'M' (monthly ~30 days), 'Y' (yearly). It doesn't yet support pandas-style frequency strings like 'MS' (month start), 'ME' (month end), etc.\n",
    "\n",
    "**Solution:**\n",
    "Manually create the future dates using pandas' `date_range()` and pass them to Seer for prediction. This ensures both models are forecasting on identical date ranges.\n",
    "\n",
    "**Result:**\n",
    "With properly aligned dates, the forecasts are nearly identical:\n",
    "- Mean difference: ~2% (down from ~14%)\n",
    "- Both models track each other closely throughout the forecast period"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abbf0a83",
   "metadata": {},
   "source": [
    "## 📝 Final Notes\n",
    "\n",
    "This notebook has demonstrated that:\n",
    "\n",
    "1. **API Compatibility**: Seer and Prophet have nearly identical APIs, making migration easy\n",
    "2. **Performance**: Seer is significantly faster while producing equivalent results\n",
    "3. **Accuracy**: Both models achieve similar accuracy on forecasting tasks\n",
    "4. **Modern Stack**: Seer leverages Polars for better DataFrame performance\n",
    "5. **Production Ready**: Seer's Rust core makes it suitable for production deployments\n",
    "\n",
    "### When to use Seer:\n",
    "- ✅ Production systems requiring fast inference\n",
    "- ✅ Large datasets with many observations\n",
    "- ✅ Real-time forecasting applications\n",
    "- ✅ When you want modern DataFrame support (Polars)\n",
    "- ✅ Resource-constrained environments\n",
    "\n",
    "### When to use Prophet:\n",
    "- ✅ Research and experimentation\n",
    "- ✅ When you need specific Prophet features not yet in Seer\n",
    "- ✅ Existing Prophet-based systems\n",
    "\n",
    "### Migration Path:\n",
    "Migrating from Prophet to Seer is typically as simple as:\n",
    "```python\n",
    "# Change this:\n",
    "from prophet import Prophet\n",
    "m = Prophet()\n",
    "\n",
    "# To this:\n",
    "from seer import Seer\n",
    "m = Seer()\n",
    "```\n",
    "\n",
    "The rest of your code often works without changes! 🎉"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f26fea42",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "e43aff82",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "seer",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
