Quick start¶
Installation¶
Probly can be installed using pip from GitHub as follows:
pip install git+https://github.com/bencwallace/probly#egg=probly
Note
Probly makes use of NumPy, SciPy, and Matplotlib.
Getting started¶
We begin by importing probly
.
>>> import probly as pr
Next, we initialize some pre-packaged random variables.
>>> # A Bernoulli random variable with parameter 0.5
>>> X = pr.Ber()
>>> # A Bernoulli random variable independent of X with parameter 0.9
>>> Y = pr.Ber(0.9)
>>> # A uniform random variable on the interval [-10, 10]
>>> Z = pr.Unif(-10, 10)
Calling a random variable produces a random sample from its distribution. In order to obtain reproducible results, we pass a seed as an argument to the random variable. Calling the same random variable with the same seed will produce the same result.
>>> seed = 99 # An arbitrary but fixed seed
>>> Z(seed)
-4.340731821079555
>>> Z(seed)
-4.340731821079555
Note
An entire Probly session can be seeded by using pr.seed
. This will determine the sequence of outputs produced
by sampling a sequence of random variables initialized in a given order with a given sequence of seeds; it is
distinct from seeding the random variables themselves.
Random variables can be combined via arithmetical operations.
>>> W = (1 + X) * Z / (5 + Y)
>>> # W is a new random object
>>> type(W)
<class 'probly.core.RandomVariable'>
The result of such operations is itself a random variable whose distribution may not be know explicitly. We can nevertheless sample from this unknown distribution!
>>> W(seed)
-1.4469106070265185
We can also compute properties of a random variable, such as its mean.
>>> W.mean()
0.023611159797914952
Dependence¶
Note that W
is dependent on X
, Y
, and Z
.
This essentially means that the following must output True
.
>>> x = X(seed)
>>> y = Y(seed)
>>> z = Z(seed)
>>> w = W(seed)
>>> w == (1 + x) * z / (5 + y)
True
Independent copies¶
Separate instantiations of a random variable will produce independent copies: for instance, samples from two instantiations of a normal random variable will be independent of one another, even with the same seed.
>>> pr.Normal()(seed)
-0.8113001427396095
>>> pr.Normal()(seed)
0.09346601550504334
Independent copies of a random variable can also be produced as follows.
>>> Wcopy = W.copy()
>>> Wcopy(seed)
2.430468450181704
Random matrices¶
Random NumPy arrays (in particular, random matrices) can be formed from other random variables.
>>> M = pr.array([[X, Z], [W, Y]])
>>> type(M)
<class 'probly.core.RandomVariable'>
Random arrays can be manipulated like ordinary NumPy arrays.
>>> M[0, 0](seed) == X(seed)
True
>>> import numpy as np
>>> S = np.sum(M)
>>> S(seed) == X(seed) + Z(seed) + W(seed) + Y(seed)
True
Function application¶
Any functions can be lifted to a map between random variables
using the @pr.lift
decorator.
>>> from numpy.linalg import det
>>> det = pr.lift(det)
An equivalent way of doing this is as follows:
@pr.lift
def det(m):
return np.linalg.det(m)
The function det
can now be applied to M
.
>>> D = det(M)
>>> D(seed)
-5.280650914177544
Conditioning¶
Random variables can be conditioned as in the following example:
>>> C = W.given(Y == 1, Z > 0)
>>> C(seed)
1.97965814796514
Any boolean-valued random variable can be used as a condition.
Random parameters¶
Random variables can themselves be used to parameterize other random variables, as in the following example:
>>> U = pr.Unif()
>>> B = pr.Ber(U)
>>> B(seed)
0
Custom models¶
Custom models can be constructed by applying the pr.model
decorator, evaluated on a list of parameter names,
to a function of these parameters whose return value is a sampler (a function from a random seed to a random sample).
>>> @pr.model('a', 'b')
>>> def SquareOfUniform(a, b):
>>> def sampler(seed):
>>> np.random.seed()
>>> return np.random.uniform(a, b) ** 2
>>> return sampler
This makes SquareOfUniform
into a class whose instances are random variable objects that can be manipulated as
above. To construct classes of random variables with additional functionality (e.g. built-in mean, variance, etc.),
one can directly subclass Distribution
as in the example at Custom distributions.