{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "677c5e27",
   "metadata": {},
   "source": [
    "Starting with MLE and logistic regression model\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2520b517",
   "metadata": {},
   "source": [
    "Prerequisites\n",
    "Make sure you have the necessary Python libraries installed:\n",
    "pip install numpy pandas scikit-learn scipy matplotlib statsmodels\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97cb296e",
   "metadata": {},
   "source": [
    "1. Skealearn library\n",
    "Bootstrapping Method\n",
    "Mean S curve-should be corrected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "480f64ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Intercept (w0): -11.683190353932442\n",
      "Coefficient (w1): 5.687089258005263\n",
      "Standard errors: [3.50977855 1.95402341]\n",
      "95% CI for Intercept (w0): (-18.562229897321522, -4.804150810543363)\n",
      "95% CI for Coefficient (w1): (1.8572737464039735, 9.516904769606551)\n",
      "params:\n",
      "[-11.68319035   5.68708926]\n",
      "covariance matrix:\n",
      "[[12.31854544 -6.71118734]\n",
      " [-6.71118734  3.81820749]]\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 88916 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from scipy import io\n",
    "from scipy.stats import norm\n",
    "\n",
    "# Load the data\n",
    "data_path = \"../data/\"\n",
    "suv_file = data_path + \"suv_percentilesSLOthenUWM.mat\"\n",
    "flags_file = data_path + \"flags_combined.mat\"\n",
    "\n",
    "# Load .mat files\n",
    "suv_dict = io.loadmat(suv_file)\n",
    "flags_dict = io.loadmat(flags_file)\n",
    "\n",
    "# Extract relevant data\n",
    "suv = suv_dict['lung_SUVperc_COMBINED'][0:58, :, :]\n",
    "flags = flags_dict['flags'][0:58, 3]\n",
    "\n",
    "# Choose percentile of interest\n",
    "p = 94  # For lung and bowel, use 94 (0-indexed)\n",
    "X = np.nanmax(suv[:, :, p], axis=1).reshape(-1, 1)\n",
    "\n",
    "# Handle missing values\n",
    "valid_indices = ~np.isnan(X).flatten()\n",
    "X = X[valid_indices]\n",
    "flags = flags[valid_indices]\n",
    "\n",
    "# Fit logistic regression using sklearn\n",
    "logr = LogisticRegression(penalty=None, solver='lbfgs')\n",
    "logr.fit(X, flags)\n",
    "\n",
    "# Print model coefficients\n",
    "print(\"Intercept (w0):\", logr.intercept_[0])\n",
    "print(\"Coefficient (w1):\", logr.coef_[0][0])\n",
    "\n",
    "# Design matrix with intercept\n",
    "X_design = np.hstack([np.ones((X.shape[0], 1)), X])\n",
    "\n",
    "# Predicted probabilities\n",
    "pred_probs = logr.predict_proba(X)[:, 1]\n",
    "\n",
    "# Variance matrix\n",
    "V = np.diagflat(pred_probs * (1 - pred_probs))\n",
    "\n",
    "# Covariance matrix of parameters\n",
    "cov_matrix = np.linalg.inv(X_design.T @ V @ X_design)\n",
    "\n",
    "# Standard errors\n",
    "std_errors = np.sqrt(np.diag(cov_matrix))\n",
    "print(\"Standard errors:\", std_errors)\n",
    "\n",
    "# Combine parameters\n",
    "params = np.array([logr.intercept_[0], logr.coef_[0][0]])\n",
    "\n",
    "# 95% CI for parameters using normal approximation\n",
    "z = norm.ppf(0.975)  # 1.96 for 95% CI\n",
    "\n",
    "ci_intercept = (\n",
    "    params[0] - z * std_errors[0],\n",
    "    params[0] + z * std_errors[0]\n",
    ")\n",
    "\n",
    "ci_coef = (\n",
    "    params[1] - z * std_errors[1],\n",
    "    params[1] + z * std_errors[1]\n",
    ")\n",
    "\n",
    "print(f\"95% CI for Intercept (w0): {ci_intercept}\")\n",
    "print(f\"95% CI for Coefficient (w1): {ci_coef}\")\n",
    "\n",
    "print(\"params:\")\n",
    "print(params)\n",
    "\n",
    "print(\"covariance matrix:\")\n",
    "print(cov_matrix)\n",
    "\n",
    "# Define logistic function\n",
    "def logistic(x, params):\n",
    "    return 1 / (1 + np.exp(-(params[0] + params[1] * x)))\n",
    "\n",
    "# Generate bootstrap samples\n",
    "n_bootstraps = 1000\n",
    "bootstrap_params = np.random.multivariate_normal(params, cov_matrix, size=n_bootstraps)\n",
    "\n",
    "# Generate x values for plotting\n",
    "x_vals = np.linspace(X.min(), X.max(), 100)\n",
    "\n",
    "# Compute predicted probabilities for each bootstrap sample\n",
    "predicted_probs = np.array([logistic(x_vals, bp) for bp in bootstrap_params])\n",
    "\n",
    "# Calculate mean predicted probability across samples\n",
    "mean_predicted_probs = predicted_probs.mean(axis=0)\n",
    "\n",
    "# Calculate 95% confidence intervals\n",
    "lower_bounds = np.percentile(predicted_probs, 2.5, axis=0)\n",
    "upper_bounds = np.percentile(predicted_probs, 97.5, axis=0)\n",
    "\n",
    "# Plotting\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.title(\"Prediction Probability of AE Depending on maxSUV Value\")\n",
    "plt.xlabel(r\"$x = \\max_{visit} SUV(visit, p)$\")\n",
    "plt.ylabel(\"P(AE | X = x)\")\n",
    "\n",
    "# Scatter data points\n",
    "plt.scatter(X[flags == 0], flags[flags == 0], label=\"NC\", color='blue', alpha=0.6)\n",
    "plt.scatter(X[flags == 1], flags[flags == 1], label=\"AE\", color='red', alpha=0.6)\n",
    "\n",
    "# Plot the mean logistic curve\n",
    "plt.plot(x_vals, mean_predicted_probs, color='green', label=\"mean\")\n",
    "plt.plot(x_vals, logistic(x_vals, params), color='red', label=\"Logistic Regression (MLE)\")\n",
    "\n",
    "# Confidence interval band\n",
    "plt.fill_between(x_vals, lower_bounds, upper_bounds, color='green', alpha=0.2, label=\"95% CI (Parametric Bootstrap)\")\n",
    "\n",
    "plt.legend(loc='center right')\n",
    "plt.grid(True)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0807320",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4039de80",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "7deeceb4",
   "metadata": {},
   "source": [
    "Here, I applied some changes to make the curve smoothed. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96f9e51b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 87612 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "from scipy import io\n",
    "from scipy.stats import norm\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy.ndimage import gaussian_filter1d  # Critical import for smoothing\n",
    "\n",
    "# Load the data\n",
    "data_path = \"../data/\"\n",
    "suv_file = data_path + \"suv_percentilesSLOthenUWM.mat\"\n",
    "flags_file = data_path + \"flags_combined.mat\"\n",
    "\n",
    "suv_dict = io.loadmat(suv_file)\n",
    "flags_dict = io.loadmat(flags_file)\n",
    "\n",
    "# Extract and preprocess data\n",
    "suv = suv_dict['lung_SUVperc_COMBINED'][0:58, :, :]\n",
    "flags = flags_dict['flags'][0:58, 3]\n",
    "p = 94  # Target percentile\n",
    "X = np.nanmax(suv[:, :, p], axis=1).reshape(-1, 1)\n",
    "\n",
    "# Remove NaN values\n",
    "valid_indices = ~np.isnan(X).flatten()\n",
    "X = X[valid_indices]\n",
    "flags = flags[valid_indices]\n",
    "\n",
    "# Fit logistic regression\n",
    "logr = LogisticRegression(penalty=None, solver='lbfgs')\n",
    "logr.fit(X, flags)\n",
    "\n",
    "# Model parameters and covariance\n",
    "X_design = np.hstack([np.ones((X.shape[0], 1)), X])\n",
    "pred_probs = logr.predict_proba(X)[:, 1]\n",
    "V = np.diagflat(pred_probs * (1 - pred_probs))\n",
    "cov_matrix = np.linalg.inv(X_design.T @ V @ X_design)\n",
    "params = np.insert(logr.coef_[0], 0, logr.intercept_[0])\n",
    "\n",
    "# Bootstrap configuration\n",
    "n_bootstraps = 20000  # Increased for stability\n",
    "bootstrap_params = np.random.multivariate_normal(params, cov_matrix, size=n_bootstraps)\n",
    "x_vals = np.linspace(X.min(), X.max(), 500)  # Reduced resolution\n",
    "\n",
    "# Generate bootstrap predictions\n",
    "predicted_probs = 1 / (1 + np.exp(-(bootstrap_params[:, 0:1] + bootstrap_params[:, 1:2] * x_vals)))\n",
    "\n",
    "# --- Key Smoothing Step 1: Smooth individual bootstrap curves ---\n",
    "sigma_curve = 2.5  # Controls smoothness of each bootstrap curve\n",
    "smoothed_bootstrap_probs = np.array([\n",
    "    gaussian_filter1d(prob_curve, sigma=sigma_curve) \n",
    "    for prob_curve in predicted_probs\n",
    "])\n",
    "\n",
    "# Calculate the mean of the smoothed bootstrap probabilities (mean curve)\n",
    "mean_smoothed_probs = smoothed_bootstrap_probs.mean(axis=0)\n",
    "\n",
    "# Calculate CIs from smoothed curves\n",
    "lower_bounds = np.percentile(smoothed_bootstrap_probs, 2.5, axis=0)\n",
    "upper_bounds = np.percentile(smoothed_bootstrap_probs, 97.5, axis=0)\n",
    "\n",
    "# --- Key Smoothing Step 2: Light post-smoothing of CI bounds ---\n",
    "sigma_ci = 1.2  # Final touch for CI smoothness\n",
    "smoothed_lower = gaussian_filter1d(lower_bounds, sigma=sigma_ci)\n",
    "smoothed_upper = gaussian_filter1d(upper_bounds, sigma=sigma_ci)\n",
    "\n",
    "# --- Plotting ---\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.title(\"Smoothed AE Probability with Confidence Bands\")\n",
    "plt.xlabel(r\"$x = \\max_{visit} SUV(visit, p)$\")\n",
    "plt.ylabel(\"P(AE | X = x)\")\n",
    "\n",
    "# Data points\n",
    "plt.scatter(X[flags == 0], flags[flags == 0], label=\"NC\", color='blue', alpha=0.6)\n",
    "plt.scatter(X[flags == 1], flags[flags == 1], label=\"AE\", color='red', alpha=0.6)\n",
    "\n",
    "# Mean logistic curve (no additional smoothing needed)\n",
    "plt.plot(x_vals, 1 / (1 + np.exp(-(params[0] + params[1] * x_vals))), \n",
    "         color='orange', linestyle='--', label=\"Logistic (MLE)\")\n",
    "\n",
    "# Plot the mean of the smoothed bootstrap curves\n",
    "plt.plot(x_vals, mean_smoothed_probs, color='green', label=\"Mean Bootstrap Curve\", linewidth=2)\n",
    "\n",
    "# Confidence bands\n",
    "plt.fill_between(x_vals, smoothed_lower, smoothed_upper,\n",
    "                 color='green', alpha=0.2, label=\"95% CI (Smoothed)\")\n",
    "\n",
    "plt.legend(loc='center right')\n",
    "plt.grid(True)\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b16bc57",
   "metadata": {},
   "source": [
    "The next model used delta for standard error....becuase it assumes that we have gussian dist....It is a standard method..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92b70025",
   "metadata": {},
   "source": [
    "My question? You mentined that it would be better not use regulization. Whay? Also, what about penalty?  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16b21d5a",
   "metadata": {},
   "source": [
    "2. Statmodels library\n",
    "Delta Method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "36e9c0c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\zahra\\anaconda3\\Lib\\site-packages\\statsmodels\\discrete\\discrete_model.py:2385: RuntimeWarning: overflow encountered in exp\n",
      "  return 1/(1+np.exp(-X))\n",
      "c:\\Users\\zahra\\anaconda3\\Lib\\site-packages\\statsmodels\\discrete\\discrete_model.py:2443: RuntimeWarning: divide by zero encountered in log\n",
      "  return np.sum(np.log(self.cdf(q * linpred)))\n",
      "c:\\Users\\zahra\\anaconda3\\Lib\\site-packages\\statsmodels\\base\\model.py:595: HessianInversionWarning: Inverting hessian failed, no bse or cov_params available\n",
      "  warnings.warn('Inverting hessian failed, no bse or cov_params '\n",
      "c:\\Users\\zahra\\anaconda3\\Lib\\site-packages\\statsmodels\\base\\model.py:595: HessianInversionWarning: Inverting hessian failed, no bse or cov_params available\n",
      "  warnings.warn('Inverting hessian failed, no bse or cov_params '\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Optimization terminated successfully.\n",
      "         Current function value: inf\n",
      "         Iterations 8\n",
      "                           Logit Regression Results                           \n",
      "==============================================================================\n",
      "Dep. Variable:                      y   No. Observations:                   58\n",
      "Model:                          Logit   Df Residuals:                       56\n",
      "Method:                           MLE   Df Model:                            1\n",
      "Date:                Thu, 17 Apr 2025   Pseudo R-squ.:                     inf\n",
      "Time:                        23:13:27   Log-Likelihood:                   -inf\n",
      "converged:                       True   LL-Null:                        0.0000\n",
      "Covariance Type:            nonrobust   LLR p-value:                     1.000\n",
      "==============================================================================\n",
      "                 coef    std err          z      P>|z|      [0.025      0.975]\n",
      "------------------------------------------------------------------------------\n",
      "const        -11.6839      3.510     -3.328      0.001     -18.564      -4.804\n",
      "x1             5.6870      1.954      2.910      0.004       1.857       9.517\n",
      "==============================================================================\n",
      "\n",
      "95% Confidence Intervals for Parameters:\n",
      " [[-18.56416559  -4.8035785 ]\n",
      " [  1.85686085   9.51707004]]\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 71160 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy import io\n",
    "import statsmodels.api as sm\n",
    "\n",
    "# Load the data\n",
    "data_path = \"../data/\"\n",
    "suv_file = data_path + \"suv_percentilesSLOthenUWM.mat\"\n",
    "flags_file = data_path + \"flags_combined.mat\"\n",
    "\n",
    "# Load .mat files\n",
    "suv_dict = io.loadmat(suv_file)\n",
    "flags_dict = io.loadmat(flags_file)\n",
    "\n",
    "# Extract relevant data\n",
    "suv = suv_dict['lung_SUVperc_COMBINED'][0:58, :, :]\n",
    "flags = flags_dict['flags'][0:58, 3]\n",
    "\n",
    "# Choose percentile of interest\n",
    "p = 94  # For lung and bowel, use 94 (0-indexed)\n",
    "X = np.nanmax(suv[:, :, p], axis=1).reshape(-1, 1)\n",
    "\n",
    "# Handle missing values\n",
    "valid_indices = ~np.isnan(X).flatten()\n",
    "X = X[valid_indices]\n",
    "flags = flags[valid_indices]\n",
    "\n",
    "# Add intercept term\n",
    "X_with_intercept = sm.add_constant(X)\n",
    "\n",
    "# Fit logistic regression using MLE\n",
    "model = sm.Logit(flags, X_with_intercept)\n",
    "result = model.fit()\n",
    "\n",
    "# Print model summary and parameter confidence intervals\n",
    "print(result.summary())\n",
    "print(\"\\n95% Confidence Intervals for Parameters:\\n\", result.conf_int())\n",
    "\n",
    "# Prepare values for plotting predictions\n",
    "x_vals = np.linspace(X.min(), X.max(), 100)\n",
    "x_vals_with_intercept = sm.add_constant(x_vals)\n",
    "\n",
    "# Compute predicted probabilities\n",
    "predicted_probs = result.predict(x_vals_with_intercept)\n",
    "\n",
    "# Compute confidence intervals using the delta method\n",
    "# Get the covariance matrix of the parameters\n",
    "cov_params = result.cov_params()\n",
    "\n",
    "# Compute the standard errors for the predicted probabilities\n",
    "# For each x, compute the gradient (Jacobian) of the logistic function\n",
    "def logistic(x):\n",
    "    return 1 / (1 + np.exp(-x))\n",
    "\n",
    "# Compute the linear predictor\n",
    "linear_pred = x_vals_with_intercept @ result.params\n",
    "\n",
    "# Compute the gradients\n",
    "gradients = x_vals_with_intercept * (logistic(linear_pred) * (1 - logistic(linear_pred)))[:, np.newaxis]\n",
    "\n",
    "# Compute the standard errors\n",
    "standard_errors = np.sqrt(np.sum(gradients @ cov_params * gradients, axis=1))\n",
    "\n",
    "# Compute the confidence intervals\n",
    "z = 1.96  # for 95% confidence interval\n",
    "lower_bounds = predicted_probs - z * standard_errors\n",
    "upper_bounds = predicted_probs + z * standard_errors\n",
    "\n",
    "# Ensure the bounds are within [0,1]\n",
    "lower_bounds = np.clip(lower_bounds, 0, 1)\n",
    "upper_bounds = np.clip(upper_bounds, 0, 1)\n",
    "\n",
    "# Plotting\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.title(\"Prediction Probability of AE Depending on maxSUV Value\")\n",
    "plt.xlabel(r\"$x = \\max_{visit} SUV(visit, p)$\")\n",
    "plt.ylabel(\"P(AE | X = x)\")\n",
    "\n",
    "# Scatter data points\n",
    "plt.scatter(X[flags == 0], flags[flags == 0], label=\"NC\", color='blue', alpha=0.6)\n",
    "plt.scatter(X[flags == 1], flags[flags == 1], label=\"AE\", color='red', alpha=0.6)\n",
    "\n",
    "# Plot the mean logistic curve (S-shape)\n",
    "plt.plot(x_vals, predicted_probs, color='green', label=\"Logistic Regression (MLE)\")\n",
    "\n",
    "# Confidence interval band\n",
    "plt.fill_between(x_vals, lower_bounds, upper_bounds, color='green', alpha=0.2, label=\"95% CI (Delta Method)\")\n",
    "\n",
    "plt.legend(loc='center right')\n",
    "plt.grid(True)\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e66de4c4",
   "metadata": {},
   "source": [
    "#to chech the CI of the parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cee11b36",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Intercept (w0): -11.683190353932442\n",
      "Coefficient (w1): 5.687089258005263\n",
      "Standard errors: [3.50977855 1.95402341]\n",
      "95% CI for Intercept (w0): (-18.562229897321522, -4.804150810543363)\n",
      "95% CI for Coefficient (w1): (1.8572737464039735, 9.516904769606551)\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 87480 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from scipy import io\n",
    "from scipy.stats import norm\n",
    "from scipy.ndimage import gaussian_filter1d  # For smoothing\n",
    "\n",
    "# --- Load the data ---\n",
    "data_path = \"../data/\"\n",
    "suv_file = data_path + \"suv_percentilesSLOthenUWM.mat\"\n",
    "flags_file = data_path + \"flags_combined.mat\"\n",
    "\n",
    "suv_dict = io.loadmat(suv_file)\n",
    "flags_dict = io.loadmat(flags_file)\n",
    "\n",
    "suv = suv_dict['lung_SUVperc_COMBINED'][0:58, :, :]\n",
    "flags = flags_dict['flags'][0:58, 3]\n",
    "\n",
    "p = 94  # Percentile index\n",
    "X = np.nanmax(suv[:, :, p], axis=1).reshape(-1, 1)\n",
    "\n",
    "# --- Handle missing values ---\n",
    "valid_indices = ~np.isnan(X).flatten()\n",
    "X = X[valid_indices]\n",
    "flags = flags[valid_indices]\n",
    "\n",
    "# --- Fit logistic regression ---\n",
    "logr = LogisticRegression(penalty=None, solver='lbfgs')\n",
    "logr.fit(X, flags)\n",
    "\n",
    "# --- Print diagnostics ---\n",
    "print(\"Intercept (w0):\", logr.intercept_[0])\n",
    "print(\"Coefficient (w1):\", logr.coef_[0][0])\n",
    "\n",
    "# Design matrix and predicted probabilities\n",
    "X_design = np.hstack([np.ones((X.shape[0], 1)), X])\n",
    "pred_probs = logr.predict_proba(X)[:, 1]\n",
    "V = np.diagflat(pred_probs * (1 - pred_probs))\n",
    "cov_matrix = np.linalg.inv(X_design.T @ V @ X_design)\n",
    "\n",
    "# Standard errors\n",
    "std_errors = np.sqrt(np.diag(cov_matrix))\n",
    "print(\"Standard errors:\", std_errors)\n",
    "\n",
    "# Combine parameters\n",
    "params = np.insert(logr.coef_[0], 0, logr.intercept_[0])\n",
    "\n",
    "# Confidence Intervals for parameters\n",
    "z = norm.ppf(0.975)\n",
    "ci_intercept = (params[0] - z * std_errors[0], params[0] + z * std_errors[0])\n",
    "ci_coef = (params[1] - z * std_errors[1], params[1] + z * std_errors[1])\n",
    "print(f\"95% CI for Intercept (w0): {ci_intercept}\")\n",
    "print(f\"95% CI for Coefficient (w1): {ci_coef}\")\n",
    "\n",
    "# --- Bootstrap setup ---\n",
    "n_bootstraps = 20000\n",
    "bootstrap_params = np.random.multivariate_normal(params, cov_matrix, size=n_bootstraps)\n",
    "x_vals = np.linspace(X.min(), X.max(), 500)\n",
    "\n",
    "# Vectorized logistic predictions\n",
    "predicted_probs = 1 / (1 + np.exp(-(bootstrap_params[:, 0:1] + bootstrap_params[:, 1:2] * x_vals)))\n",
    "\n",
    "# --- Smoothing ---\n",
    "sigma_curve = 2.5\n",
    "smoothed_bootstrap_probs = np.array([\n",
    "    gaussian_filter1d(prob_curve, sigma=sigma_curve)\n",
    "    for prob_curve in predicted_probs\n",
    "])\n",
    "\n",
    "mean_smoothed_probs = smoothed_bootstrap_probs.mean(axis=0)\n",
    "lower_bounds = np.percentile(smoothed_bootstrap_probs, 2.5, axis=0)\n",
    "upper_bounds = np.percentile(smoothed_bootstrap_probs, 97.5, axis=0)\n",
    "\n",
    "# Final CI smoothing\n",
    "sigma_ci = 1.2\n",
    "smoothed_lower = gaussian_filter1d(lower_bounds, sigma=sigma_ci)\n",
    "smoothed_upper = gaussian_filter1d(upper_bounds, sigma=sigma_ci)\n",
    "\n",
    "# --- Plotting ---\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.title(\"Smoothed AE Probability with Confidence Bands\")\n",
    "plt.xlabel(r\"$x = \\max_{visit} SUV(visit, p)$\")\n",
    "plt.ylabel(\"P(AE | X = x)\")\n",
    "\n",
    "# Scatter data points\n",
    "plt.scatter(X[flags == 0], flags[flags == 0], label=\"NC\", color='blue', alpha=0.6)\n",
    "plt.scatter(X[flags == 1], flags[flags == 1], label=\"AE\", color='red', alpha=0.6)\n",
    "\n",
    "# Mean logistic curve\n",
    "plt.plot(x_vals, 1 / (1 + np.exp(-(params[0] + params[1] * x_vals))),\n",
    "         color='orange', linestyle='--', label=\"Logistic (MLE)\")\n",
    "\n",
    "# Smoothed mean curve\n",
    "plt.plot(x_vals, mean_smoothed_probs, color='green', linewidth=2, label=\"Mean Bootstrap Curve\")\n",
    "\n",
    "# Confidence bands\n",
    "plt.fill_between(x_vals, smoothed_lower, smoothed_upper, color='green', alpha=0.2, label=\"95% CI (Smoothed)\")\n",
    "\n",
    "plt.legend(loc='center right')\n",
    "plt.grid(True)\n",
    "plt.show()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "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.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
