{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "36074922",
   "metadata": {
    "vscode": {
     "languageId": "plaintext"
    }
   },
   "source": [
    "# Random Number Generation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b8006699",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.094149Z",
     "iopub.status.busy": "2026-08-31T06:00:40.093915Z",
     "iopub.status.idle": "2026-08-31T06:00:40.148572Z",
     "shell.execute_reply": "2026-08-31T06:00:40.148170Z"
    },
    "tags": [
     "hide-input",
     "thebe-init",
     "runnable"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{contents}\n",
    ":local:\n",
    ":depth: 2\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88919db7",
   "metadata": {},
   "source": [
    "## The numpy.random module \n",
    "\n",
    "(These functions discussed in this section are considered \"[legacy random generation](https://numpy.org/doc/stable/reference/random/legacy.html#legacy)\" and are for simple random data and for instructional purposes. The next section provides some information about the new random modules in numpy for your information.)\n",
    "\n",
    "The numpy.random module in NumPy provides tools for generating pseudo-random numbers and sampling from various probability distributions. Some functions in the `numpy.random` module are commonly used:\n",
    "- **np.random.rand()**: returns random floating-point numbers (**uniform distribution** over the interval [0, 1))\n",
    "- **np.random.randn()**: returns random floating-point numbers (**standard normal distribution** with mean of 0 and standard deviation of 1) (note that **np.random.random** is the same as random.rand(), except random.random() takes the shape argument as a single tuple; e.g., see [stackoverflow](https://stackoverflow.com/questions/47231852/np-random-rand-vs-np-random-random))\n",
    "- **np.random.randint()**: returns random integers from a specified range\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0ef08b8",
   "metadata": {},
   "source": [
    "As a summary, here's a comparison table of the commonly used methods in the numpy random module:\n",
    "\n",
    "| Function  | Distribution  | Typical Use     | Note                               | Example                   |\n",
    "| --------- | ------------- | --------------- | ---------------------------------- | ------------------------- |\n",
    "| `rand`    | Uniform [0,1) | Random decimals | uniform decimals                   | `np.random.rand()`        |\n",
    "| `random`  | Uniform [0,1) | Same as rand    | uniform decimals                   | `np.random.random(size=(2, 3))`|\n",
    "| `randn`   | Normal (0,1)  | Stats, ML       | bell curve; mean=0, SD=1           | `np.random.randn()`       |\n",
    "| `normal`  | Normal (μ, σ) | Simulations     | bell curve; mean & SD controllable | `np.random.normal(10, 2)` |\n",
    "| `randint` | Integers      | Dice, indexing  | whole numbers                      | `np.random.randint(1, 7)` |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a786b42",
   "metadata": {},
   "source": [
    "Simple examples of the random numbers are:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bc3c967",
   "metadata": {},
   "source": [
    "### random.rand( )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "695d7d5b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.150541Z",
     "iopub.status.busy": "2026-08-31T06:00:40.150380Z",
     "iopub.status.idle": "2026-08-31T06:00:40.239752Z",
     "shell.execute_reply": "2026-08-31T06:00:40.239457Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.40301217, 0.84399089, 0.87490942],\n",
       "       [0.70088229, 0.20296399, 0.29242541]])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.rand()            ### rand: one number\n",
    "np.random.rand(3)           ### rand: 1D array\n",
    "np.random.rand(2, 3)        ### rand: 2D"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac0b6124",
   "metadata": {},
   "source": [
    "### random.random()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b3fdd663",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.241162Z",
     "iopub.status.busy": "2026-08-31T06:00:40.241033Z",
     "iopub.status.idle": "2026-08-31T06:00:40.243275Z",
     "shell.execute_reply": "2026-08-31T06:00:40.243066Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.34023756, 0.00731665, 0.47677721],\n",
       "       [0.77388367, 0.91412804, 0.65316238]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.random()          ### random: one number\n",
    "np.random.random(3)         ### random: 1D array\n",
    "np.random.random((2, 3))    ### random: 2D\n",
    "np.random.random(size=(2, 3))    ### random: 2D"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37489afa",
   "metadata": {},
   "source": [
    "### random.randn( )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "fe581acc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.244392Z",
     "iopub.status.busy": "2026-08-31T06:00:40.244314Z",
     "iopub.status.idle": "2026-08-31T06:00:40.246436Z",
     "shell.execute_reply": "2026-08-31T06:00:40.246250Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.37431238,  0.36886381,  0.69357478],\n",
       "       [-0.53748378,  0.1185337 , -0.06065889]])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.randn()           ### randn: 1 number, normal distribution\n",
    "np.random.randn(3)          ### randn: 1D array, normal distribution\n",
    "np.random.randn(2, 3)       ### randn: 2D array, normal distribution\n",
    "# np.random.randn(2, 3, 4)    ### randn: 3D array, normal distribution\n",
    "# np.random.randn(2, 5, 2, 3)   ### randn: 4D array, normal distribution"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a88a458b",
   "metadata": {},
   "source": [
    "### random.normal()\n",
    "\n",
    "`np.random.normal(loc=0.0, scale=1.0, size=None)`\n",
    "\n",
    "**loc = location of mean**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ff826d4a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.247616Z",
     "iopub.status.busy": "2026-08-31T06:00:40.247523Z",
     "iopub.status.idle": "2026-08-31T06:00:40.251375Z",
     "shell.execute_reply": "2026-08-31T06:00:40.251143Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-1.69344156,  0.20421139,  0.56043965],\n",
       "       [-0.81986677, -0.74497685,  1.99723415]])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.normal()        ### normal: one number, normal distribution\n",
    "np.random.normal(3)       ### normal: one number, mean=3\n",
    "np.random.normal(0, 1)    ### normal: mean=0, std=1\n",
    "np.random.normal(loc=0, scale=1)    ### normal: mean=0, std=1\n",
    "np.random.normal(0, 1, (2, 3))  ### normal: mean=0, std=1, shape=(2,3)\n",
    "# create a 3x3 array of normally distributed pseudorandom\n",
    "# values with mean 0 and standard deviation 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6218817",
   "metadata": {},
   "source": [
    "### random.randint( )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d5ac1f1e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.252467Z",
     "iopub.status.busy": "2026-08-31T06:00:40.252397Z",
     "iopub.status.idle": "2026-08-31T06:00:40.255694Z",
     "shell.execute_reply": "2026-08-31T06:00:40.255450Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[6, 9, 6],\n",
       "       [6, 6, 9],\n",
       "       [5, 6, 9]])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.randint( 10 )             ### 0 to 10 exclusive\n",
    "np.random.randint( 5, 10 )          ### 5 to 10 exclusive\n",
    "np.random.randint(5, 10, (3, 3))    ### normal: mean=0, std=1, shape=(3,3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9575148c",
   "metadata": {},
   "source": [
    "### choice( )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "13e01fc5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.256778Z",
     "iopub.status.busy": "2026-08-31T06:00:40.256718Z",
     "iopub.status.idle": "2026-08-31T06:00:40.258627Z",
     "shell.execute_reply": "2026-08-31T06:00:40.258444Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 1])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.choice([1, 2, 3, 4, 5], size = 2)  ### the choice function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b766c97",
   "metadata": {},
   "source": [
    "### size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "1a73d481",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.259584Z",
     "iopub.status.busy": "2026-08-31T06:00:40.259523Z",
     "iopub.status.idle": "2026-08-31T06:00:40.261262Z",
     "shell.execute_reply": "2026-08-31T06:00:40.261055Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[7, 9, 7, 5, 7],\n",
       "       [3, 7, 9, 3, 3],\n",
       "       [7, 5, 9, 5, 7]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.choice([3, 5, 7, 9], size=(3, 5)) ### the size parameter to generate N-D arrays"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e04442f",
   "metadata": {},
   "source": [
    "### rng.choice( )\n",
    "\n",
    "The modern form of `choice()` uses a random number generator, `rng`, instead of `np.random`:\n",
    "\n",
    "`rng.choice(a, size=None, replace=True, p=None)`\n",
    "\n",
    "- `a`: the values to draw from (a list, an array, or a pandas column)\n",
    "- `size`: how many values to draw\n",
    "- `replace`: whether a value can be drawn more than once (default `True`)\n",
    "- `p`: the probability of drawing each value; without it, every value is equally likely\n",
    "\n",
    "The seed in `default_rng(42)` makes the results the same every time the cell runs. You will use `rng.choice()` with `p` in Chapter 8 to simulate outcomes from a probability model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fd120eab",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "rng = np.random.default_rng(42)       ### create a generator with seed 42\n",
    "rng.choice([1, 2, 3, 4, 5], size=2)  ### same as np.random.choice\n",
    "rng.choice(['on time', 'late'], size=10, p=[0.9, 0.1])  ### p: 90% on time, 10% late"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "779a2669",
   "metadata": {},
   "source": [
    "**This section is for informational purposes only.**\n",
    "\n",
    "This section compares NumPy’s legacy random number generator with modern Generator API for reproducible randomness. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7146719-915b-4ee0-930e-4f3c30574d30",
   "metadata": {},
   "source": [
    "\n",
    "| Topic                  | **Legacy (global RNG)**                                 | **Modern (Generator API)**                                |\n",
    "| ---------------------- | ------------------------------------------------------- | --------------------------------------------------------- |\n",
    "| Main objects           | `np.random` module, `RandomState`                       | `np.random.Generator` (e.g., `rng = default_rng(seed)`)   |\n",
    "| Seeding                | `np.random.seed(123)` (global)                          | `rng = np.random.default_rng(123)` (local, explicit)      |\n",
    "| State scope            | **Global**, shared across code                          | **Isolated per `rng`**, easy to pass around               |\n",
    "| Typical calls          | `np.random.rand`, `randn`, `randint`, `random_sample`   | `rng.random`, `normal`, `integers`, `uniform`, …          |\n",
    "| Reproducibility        | Fragile (any call anywhere advances global state)       | Robust (each `rng` advances its own state only)           |\n",
    "| Performance/algorithms | Older (MT19937); OK speed                               | Newer bit-generators (PCG64 default), often faster/better |\n",
    "| Broadcasting/shape     | Functions accept `size=` or separate dims (`rand(2,3)`) | Consistent `size=` tuple; parameters broadcast cleanly    |\n",
    "| Thread/process safety  | Global contention possible                              | Use separate `Generator`s per thread/process              |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e47810a0-11ce-44d2-ac48-205184ed0525",
   "metadata": {},
   "source": [
    "| Legacy                                               | Modern (preferred)                                     |\n",
    "| ---------------------------------------------------- | ------------------------------------------------------ |\n",
    "| `np.random.seed(123)`                                | `rng = np.random.default_rng(123)`                     |\n",
    "| `np.random.rand(2,3)`                                | `rng.random((2,3))`                                    |\n",
    "| `np.random.randn(5)`                                 | `rng.normal(loc=0, scale=1, size=5)`                   |\n",
    "| `np.random.randint(0, 10, 4)`                        | `rng.integers(0, 10, size=4)` *(note: high exclusive)* |\n",
    "| `np.random.random_sample(3)` / `np.random.sample(3)` | `rng.random(3)`                                        |\n",
    "| `np.random.uniform(-1, 1, 100)`                      | `rng.uniform(-1, 1, size=100)`                         |\n",
    "| `np.random.binomial(n=10, p=0.3, size=6)`            | `rng.binomial(10, 0.3, size=6)`                        |\n",
    "| `np.random.poisson(lam=5, size=(3,3))`               | `rng.poisson(5, size=(3,3))`                           |\n",
    "| `np.random.permutation(n)`                           | `rng.permutation(n)`                                   |\n",
    "| `np.random.shuffle(a)`                               | `rng.shuffle(a)`                                       |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4678d767-405f-4df8-9ddc-7cc2306987fb",
   "metadata": {},
   "source": [
    "| Call / Purpose                          | **Legacy (global RNG)**                                  | **Modern (Generator API)**         | **Example (legacy → modern)**                                              |\n",
    "| --------------------------------------- | -------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------- |\n",
    "| **Seed**                                | `random.seed(123)`                                    | `rng = random.default_rng(123)` | `random.seed(123)` → `rng = random.default_rng(123)`                 |\n",
    "| **Uniform floats**                      | `random.rand(2, 3)`                                   | `rng.random((2, 3))`               | `random.rand(2,3)` → `rng.random((2,3))`                                |\n",
    "| **Standard normal**                     | `random.randn(5)`                                     | `rng.normal(0, 1, size=5)`         | `random.randn(5)` → `rng.normal(0,1,5)`                                 |\n",
    "| **Integers**                            | `random.randint(0, 10, 4)`                            | `rng.integers(0, 10, size=4)`      | `random.randint(0,10,4)` → `rng.integers(0,10,4)` *(high is exclusive)* |\n",
    "| **Random sample / sample alias**        | `random.random_sample(3)` *(= `random.sample(3)`)* | `rng.random(3)`                    | `random.random_sample(3)` → `rng.random(3)`                             |\n",
    "| **Uniform in [low, high)**              | `random.uniform(-1, 1, 6)`                            | `rng.uniform(-1, 1, size=6)`       | `random.uniform(-1,1,6)` → `rng.uniform(-1,1,6)`                        |\n",
    "| **Binomial**                            | `random.binomial(10, 0.3, size=6)`                    | `rng.binomial(10, 0.3, size=6)`    | `random.binomial(10,0.3,6)` → `rng.binomial(10,0.3,6)`                  |\n",
    "| **Poisson**                             | `random.poisson(5, size=(3, 3))`                      | `rng.poisson(5, size=(3,3))`       | `random.poisson(5,(3,3))` → `rng.poisson(5,(3,3))`                      |\n",
    "| **Permutation (returns permuted copy)** | `random.permutation(10)`                              | `rng.permutation(10)`              | `random.permutation(10)` → `rng.permutation(10)`                        |\n",
    "| **Shuffle (in place)**                  | `random.shuffle(a)`                                   | `rng.shuffle(a)`                   | `random.shuffle(a)` → `rng.shuffle(a)`                                  |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e00aab97",
   "metadata": {},
   "source": [
    "## Quick Reference: Modern Random API\n",
    "\n",
    "Use the modern generator API when you need reproducible random values for sampling or simulation.\n",
    "\n",
    "```python\n",
    "rng = np.random.default_rng(42)\n",
    "rng.random(5)                 # uniform [0, 1)\n",
    "rng.integers(1, 10, size=5)   # integers\n",
    "rng.normal(0, 1, size=5)      # normal values\n",
    "rng.choice([1, 2, 3], size=2) # sample values\n",
    "rng.permutation(5)            # permuted indexes\n",
    "rng.uniform(5, 10, size=3)    # uniform range\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-cd2e6310",
   "metadata": {},
   "source": [
    "\n",
    "## Section Practice\n",
    "\n",
    "Use this short exercise to check the main workflow from this section.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "cell-1a244a8b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.262372Z",
     "iopub.status.busy": "2026-08-31T06:00:40.262310Z",
     "iopub.status.idle": "2026-08-31T06:00:40.263660Z",
     "shell.execute_reply": "2026-08-31T06:00:40.263472Z"
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### Exercise: Practice Random Number Generation\n",
    "# Create a list named monthly_sales with the values 120, 135, 150, and 145.\n",
    "# Calculate the average monthly sales and print it rounded to one decimal place.\n",
    "### Your code starts here\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "137.5\n"
     ]
    }
   ],
   "source": [
    "### Solution\n",
    "monthly_sales = [120, 135, 150, 145]\n",
    "average_sales = sum(monthly_sales) / len(monthly_sales)\n",
    "print(round(average_sales, 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cell-2bcb45d8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-31T06:00:40.264700Z",
     "iopub.status.busy": "2026-08-31T06:00:40.264629Z",
     "iopub.status.idle": "2026-08-31T06:00:40.266157Z",
     "shell.execute_reply": "2026-08-31T06:00:40.265982Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "137.5\n"
     ]
    }
   ],
   "source": [
    "### Solution\n",
    "monthly_sales = [120, 135, 150, 145]\n",
    "average_sales = sum(monthly_sales) / len(monthly_sales)\n",
    "print(round(average_sales, 1))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.13.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
