{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3",
   "language": "python"
  },
  "language_info": {
   "name": "python"
  },
  "colab": {
   "name": "rv.ipynb"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "# random variable (RV) \u2014 Python demo\n\nNumerical companion to the entry [random variable (RV)](https://dictionaryofml.org/terms/rv.html) of the [Dictionary of Applied Machine Learning](https://dictionaryofml.org/): it recomputes what the entry states and prints one line per check.\n\nOne block per paragraph of the entry (marked [P...]), in order: each block verifies numerically what the corresponding paragraph asserts. Self-contained (numpy/matplotlib only), fixed seed.\n\nRequires NumPy and Matplotlib only, and uses fixed seeds, so the printed numbers reproduce exactly. Generated from [`pythondemos/rv.py`](https://dictionaryofml.org/terms/rv.py); CC BY 4.0."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# Notebook shim: the script resolves output paths relative to __file__,\n# which a notebook kernel does not define; everything lands in the\n# working directory instead.\nimport os\n__file__ = os.path.join(os.getcwd(), \"rv.py\")\nos.makedirs(\"pythondemos\", exist_ok=True)"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "\"\"\"\nrv.py \u2014 numerical companion to the glossary entry 'random variable (RV)'.\n\nOne block per paragraph of the entry (marked [P...]), in order: each block\nverifies numerically what the corresponding paragraph asserts. Self-contained\n(numpy/matplotlib only), fixed seed.\n\nBlocks\n------\n[P-weather] The maximum daytime temperature in Krems, read at 13:00, is a\n            real-valued RV; each day is one outcome of a random experiment and\n            the recorded temperature is a single realization. Thirty days are\n            modeled as iid RVs with a common distribution; the realizations are\n            written to rv_stem.csv for the entry's stem-plot figure.\n[P-def]     An RV is a function on the sample space of a random experiment: for\n            a fair-die experiment (sample space {1,...,6}) the RV\n            x(omega) = 1{omega is even} is an explicit function; simulating the\n            experiment and applying the function reproduces the probability\n            P(x = 1) = 1/2 implied by the uniform distribution on the sample\n            space.\n[P-types]   The types listed in the entry \u2014 binary RV, discrete RV, real-valued\n            RV, random vector, random matrix \u2014 are instantiated as functions of\n            the same underlying experiment, with values in {0, 1}, a countable\n            set, R, R^d, and R^{m x d}, respectively.\n\nOutputs\n-------\nrv_stem.csv : figure data for the entry's pgfplots stem plot (day, temp).\nrv.png      : preview figure (checking only).\n\nData generated by pythondemos/rv.py.\n\"\"\"\n\nimport numpy as np\nimport matplotlib\n\nmatplotlib.use(\"Agg\")\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\n\nOUT_DIR = Path(__file__).parent\n\nrng = np.random.default_rng(42)\nreport = []\n\n\ndef check(name, ok):\n    report.append((name, bool(ok)))\n    print(f\"  [{'ok' if ok else 'FAIL'}] {name}\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "**[P-weather]** The maximum daytime temperature in Krems, read at 13:00, is a real-valued RV; each day is one outcome of a random experiment and the recorded temperature is a single realization. Thirty days are modeled as iid RVs with a common distribution; the realizations are written to rv_stem.csv for the entry's stem-plot figure."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "print(\"[P-weather] daily 13:00 temperatures as realizations of a real-valued RV\")\nmu, sigma = 20.0, 4.0                 # center and spread of the common distribution (degC)\nn_days = 30\ndays = np.arange(1, n_days + 1)\ntemps = mu + sigma * rng.standard_normal(n_days)\nnp.savetxt(OUT_DIR / \"rv_stem.csv\",\n           np.column_stack([days, temps]),\n           fmt=[\"%d\", \"%.3f\"], delimiter=\",\",\n           header=\"day,temp\", comments=\"\")\ncheck(\"one realization written per day\", days.size == n_days)\ncheck(\"every realization is a finite real number\", np.all(np.isfinite(temps)))"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "**[P-def]** An RV is a function on the sample space of a random experiment: for a fair-die experiment (sample space {1,...,6}) the RV x(omega) = 1{omega is even} is an explicit function; simulating the experiment and applying the function reproduces the probability P(x = 1) = 1/2 implied by the uniform distribution on the sample space."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "print(\"[P-def] an RV is a function on the sample space\")\nsample_space = np.arange(1, 7)                   # fair die\nx_of = lambda omega: (omega % 2 == 0).astype(int)  # RV: even -> 1\noutcomes = rng.choice(sample_space, size=10**6)  # run the experiment\nx_real = x_of(outcomes)                          # realizations of the RV\ncheck(\"x is a deterministic function of the outcome\",\n      np.array_equal(x_real, x_of(outcomes)))\ncheck(\"P(x = 1) = 1/2 from the uniform experiment\",\n      abs(x_real.mean() - 0.5) < 2e-3)\ncheck(\"the same outcome always maps to the same value\",\n      x_of(np.array([4]))[0] == 1 and x_of(np.array([3]))[0] == 0)"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "**[P-types]** The types listed in the entry \u2014 binary RV, discrete RV, real-valued RV, random vector, random matrix \u2014 are instantiated as functions of the same underlying experiment, with values in {0, 1}, a countable set, R, R^d, and R^{m x d}, respectively."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "print(\"[P-types] binary / discrete / real-valued / vector / matrix RVs\")\nomega = rng.uniform(size=10**4)                  # one underlying experiment\nbinary = (omega > 0.5).astype(int)\ndiscrete = np.floor(10 * omega).astype(int)      # values in {0,...,9}\nrealval = -np.log(omega)                         # values in R\nvec = np.stack([omega, omega**2, np.sin(omega)], axis=1)      # R^3\nmat = omega[:, None, None] * np.ones((1, 2, 3))               # R^{2x3}\ncheck(\"binary RV takes values in {0, 1}\",\n      set(np.unique(binary)) <= {0, 1})\ncheck(\"discrete RV takes values in a countable set\",\n      np.issubdtype(discrete.dtype, np.integer)\n      and len(np.unique(discrete)) <= 10)\ncheck(\"real-valued RV takes values in R (nonnegative here)\",\n      realval.dtype == float and np.all(realval >= 0))\ncheck(\"random vector maps outcomes to R^3\", vec.shape == (10**4, 3))\ncheck(\"random matrix maps outcomes to R^{2x3}\",\n      mat.shape == (10**4, 2, 3))\n\n# ------------------------------------------------------------ preview\n# The temperatures, the die outcomes, and the RV values live on different\n# sets, so they get one panel each: drawing them on shared x positions\n# would hide one series.\nfig, ax = plt.subplots(1, 3, figsize=(11.0, 3.0))\nmarkerline, stemlines, baseline = ax[0].stem(days, temps)\nplt.setp(markerline, markersize=4)\nplt.setp(baseline, visible=False)\nax[0].set_xlabel(\"day\")\nax[0].set_ylabel(\"temperature at 13:00 (degC)\")\nax[0].set_title(\"[P-weather] daily 13:00 temperatures\")\nvals, counts = np.unique(outcomes[:600], return_counts=True)\nax[1].bar(vals, counts / 600, width=0.6, color=\"0.75\", edgecolor=\"black\")\nax[1].set_xticks(range(1, 7))\nax[1].set_xlabel(\"outcome of the die roll\")\nax[1].set_ylabel(\"relative frequency\")\nax[1].set_title(\"[P-def] the 6 outcomes\")\nax[2].bar([0, 1], [np.mean(x_real == 0), np.mean(x_real == 1)],\n          width=0.6, color=\"white\", edgecolor=\"black\", hatch=\"///\")\nax[2].set_xticks([0, 1])\nax[2].set_xlabel(\"value of the RV 1{even}\")\nax[2].set_ylabel(\"relative frequency\")\nax[2].set_title(\"[P-def] the 2 values it takes\")\nfig.tight_layout()\nfig.savefig(OUT_DIR / \"rv.png\", dpi=110)\nprint(f\"\\n{sum(ok for _, ok in report)}/{len(report)} checks passed\")\nassert all(ok for _, ok in report)"
  }
 ]
}