{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# default_exp rl.agents.ab_test_agent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# AB Test Agents\n",
    "> RL Agents to Take actions in A/B Test Environments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#hide\n",
    "from nbdev.showdoc import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from typing import List\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class ABTestRunner:\n",
    "    \"\"\"\n",
    "    Class that is used to run simulations of split tests.\n",
    "\n",
    "    Attributes:\n",
    "        bandit_returns: List of average returns per bandit.\n",
    "        batch_size: Number of examples per batch.\n",
    "        batches: Number of batches.\n",
    "        simulations: Number of simulations.\n",
    "    \n",
    "    Methods:\n",
    "        init_bandits: Prepares everything for new simulation.\n",
    "        run: Runs the simulations and tracks performance.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, bandit_returns: List[float], batch_size: int=1000, batches: int=10, simulations: int=100):\n",
    "        \"\"\"\n",
    "        Initializes a new RunSplitTest class with passed parameters.\n",
    "\n",
    "        Args:\n",
    "            bandit_returns: List of average returns per bandit.\n",
    "            batch_size: Number of examples per batch.\n",
    "            batches: Number of batches.\n",
    "            simulations: Number of simulations.\n",
    "        \"\"\"\n",
    "\n",
    "        self.bandit_returns = bandit_returns\n",
    "        self.n_bandits = len(bandit_returns)\n",
    "        self.bandits = list(range(self.n_bandits))\n",
    "        self.bandit_positive_examples = [0] * self.n_bandits\n",
    "        self.bandit_total_examples = [0] * self.n_bandits\n",
    "\n",
    "        self.batch_size = batch_size\n",
    "        self.batches = batches\n",
    "        self.simulations = simulations\n",
    "\n",
    "        self.df_bids = pd.DataFrame(columns=self.bandit_returns)\n",
    "        self.df_clicks = pd.DataFrame(columns=self.bandit_returns)\n",
    " \n",
    "    def init_bandits(self):\n",
    "        \"\"\"\n",
    "        Prepares everything for new simulation.\n",
    "        \"\"\"\n",
    "\n",
    "        self.bandit_positive_examples = [0] * self.n_bandits\n",
    "        self.bandit_total_examples = [0] * self.n_bandits\n",
    "    \n",
    "    def run(self):\n",
    "        \"\"\"\n",
    "        Runs the simulations and tracks performance.\n",
    "        \"\"\"\n",
    "\n",
    "        for j in range(self.simulations):\n",
    "            self.init_bandits()\n",
    "            for i in range(self.batches):\n",
    "                examples = self.batch_size // self.n_bandits\n",
    "                for idx in self.bandits:\n",
    "                    self.bandit_total_examples[idx] += examples\n",
    "                    self.bandit_positive_examples[idx] += np.random.binomial(examples, self.bandit_returns[idx])\n",
    "                if self.df_bids.shape[0] < self.batches:\n",
    "                    self.df_bids.loc[i] = self.bandit_total_examples\n",
    "                    self.df_clicks.loc[i] = self.bandit_positive_examples\n",
    "                else:\n",
    "                    self.df_bids.loc[i] += self.bandit_total_examples\n",
    "                    self.df_clicks.loc[i] += self.bandit_positive_examples\n",
    "        self.df_bids /= self.simulations\n",
    "        self.df_clicks /= self.simulations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Author: Sparsh A.\n",
      "\n",
      "Last updated: 2021-12-26 10:35:14\n",
      "\n",
      "Compiler    : GCC 7.5.0\n",
      "OS          : Linux\n",
      "Release     : 5.4.144+\n",
      "Machine     : x86_64\n",
      "Processor   : x86_64\n",
      "CPU cores   : 2\n",
      "Architecture: 64bit\n",
      "\n",
      "pandas : 1.1.5\n",
      "IPython: 5.5.0\n",
      "numpy  : 1.19.5\n",
      "torch  : 1.10.0+cu111\n",
      "\n"
     ]
    }
   ],
   "source": [
    "#hide\n",
    "%reload_ext watermark\n",
    "%watermark -a \"Sparsh A.\" -m -iv -u -t -d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> References:\n",
    "1. https://nbviewer.org/github/sparsh-ai/stanza/blob/S543002/2021-06-19-methods-for-effective-online-testing.ipynb"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
