Dictionary of Applied Machine Learning · random variable

random variable — Python demo

Numerical companion to the entry random variable: it recomputes what the entry states and prints one line per check

One block per paragraph of the entry (marked [P...]): each block verifies numerically what the corresponding statement asserts. Self-contained (numpy/matplotlib only), fixed seed.

Run it with python3 rv.py, from any directory — it writes its output files into the current directory. Requires NumPy and Matplotlib only, and uses fixed seeds, so the printed numbers reproduce exactly. Download rv.py

The script, block by block

One cell per block of the script: the code, and what that code printed when it last ran here

setup

"""
rv.py — numerical companion to the glossary entry 'random variable (RV)'.

One block per paragraph of the entry (marked [P...]): each block verifies
numerically what the corresponding statement asserts. Self-contained
(numpy/matplotlib only), fixed seed.

Blocks
------
[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.
[P-types] The types listed in the entry — binary RV, discrete RV,
          real-valued RV, random vector, random matrix — 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.

Outputs
-------
rv.png : preview figure (checking only).

Data generated by pythondemos/rv.py.
"""

import numpy as np
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt

rng = np.random.default_rng(42)
report = []


def check(name, ok):
    report.append((name, bool(ok)))
    print(f"  [{'ok' if ok else 'FAIL'}] {name}")

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.

print("[P-def] an RV is a function on the sample space")
sample_space = np.arange(1, 7)                   # fair die
x_of = lambda omega: (omega % 2 == 0).astype(int)  # RV: even -> 1
outcomes = rng.choice(sample_space, size=10**6)  # run the experiment
x_real = x_of(outcomes)                          # realizations of the RV
check("x is a deterministic function of the outcome",
      np.array_equal(x_real, x_of(outcomes)))
check("P(x = 1) = 1/2 from the uniform experiment",
      abs(x_real.mean() - 0.5) < 2e-3)
check("the same outcome always maps to the same value",
      x_of(np.array([4]))[0] == 1 and x_of(np.array([3]))[0] == 0)
[P-def] an RV is a function on the sample space
  [ok] x is a deterministic function of the outcome
  [ok] P(x = 1) = 1/2 from the uniform experiment
  [ok] the same outcome always maps to the same value

P-types

The types listed in the entry — binary RV, discrete RV, real-valued RV, random vector, random matrix — 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.

print("[P-types] binary / discrete / real-valued / vector / matrix RVs")
omega = rng.uniform(size=10**4)                  # one underlying experiment
binary = (omega > 0.5).astype(int)
discrete = np.floor(10 * omega).astype(int)      # values in {0,...,9}
realval = -np.log(omega)                         # values in R (exponential)
vec = np.stack([omega, omega**2, np.sin(omega)], axis=1)      # R^3
mat = omega[:, None, None] * np.ones((1, 2, 3))               # R^{2x3}
check("binary RV takes values in {0, 1}",
      set(np.unique(binary)) <= {0, 1})
check("discrete RV takes values in a countable set",
      np.issubdtype(discrete.dtype, np.integer)
      and len(np.unique(discrete)) <= 10)
check("real-valued RV takes values in R (nonnegative here)",
      realval.dtype == float and np.all(realval >= 0))
check("random vector maps outcomes to R^3", vec.shape == (10**4, 3))
check("random matrix maps outcomes to R^{2x3}",
      mat.shape == (10**4, 2, 3))

# ------------------------------------------------------------ preview
# The outcomes and the RV values live on different sets, so they get one
# panel each: drawing them on shared x positions would hide one series.
fig, ax = plt.subplots(1, 2, figsize=(7.0, 3.0))
vals, counts = np.unique(outcomes[:600], return_counts=True)
ax[0].bar(vals, counts / 600, width=0.6, color="0.75", edgecolor="black")
ax[0].set_xticks(range(1, 7))
ax[0].set_xlabel("outcome of the die roll")
ax[0].set_ylabel("relative frequency")
ax[0].set_title("[P-def] the 6 outcomes")
ax[1].bar([0, 1], [np.mean(x_real == 0), np.mean(x_real == 1)],
          width=0.6, color="white", edgecolor="black", hatch="///")
ax[1].set_xticks([0, 1])
ax[1].set_xlabel("value of the RV 1{even}")
ax[1].set_ylabel("relative frequency")
ax[1].set_title("[P-def] the 2 values it takes")
fig.tight_layout()
fig.savefig("rv.png", dpi=110)
print(f"\n{sum(ok for _, ok in report)}/{len(report)} checks passed")
assert all(ok for _, ok in report)
[P-types] binary / discrete / real-valued / vector / matrix RVs
  [ok] binary RV takes values in {0, 1}
  [ok] discrete RV takes values in a countable set
  [ok] real-valued RV takes values in R (nonnegative here)
  [ok] random vector maps outcomes to R^3
  [ok] random matrix maps outcomes to R^{2x3}

8/8 checks passed
Preview figure produced by rv.py
The preview figure the block P-types writes when the script runs