Constructors and examples
Besides the Distribution constructors on Construction,
dit ships helpers for building joints and a catalog of named examples.
dit.distconst
These are imported at the package root:
uniform()/uniform_distribution()/uniform_like()mixture_distribution()— convex combination of distributionsproduct_distribution()— product of specified marginals of a jointinsert_rvf()/RVFunctions— append a deterministic function of existing variablesnoisy()/erasure()— pass each variable through a noisy or erasure channelrandom_distribution()/simplex_grid()— random or gridded pmfsdistribution_from_bayesnet()— joint from a NetworkX Bayesian network whose nodes carry local distributions
In [1]: import dit
In [2]: d = dit.uniform_distribution(2, 2)
In [3]: print(d)
Class: Distribution
Alphabet: ((0, 1), (0, 1))
Base: linear
x p(X0,X1)
(0, 0) 0.25
(0, 1) 0.25
(1, 0) 0.25
(1, 1) 0.25
In [4]: xor = dit.insert_rvf(d, lambda x: (int(x[0]) ^ int(x[1]),))
In [5]: print(xor)
Class: Distribution
Alphabet: ((0, 1), (0, 1), (0, 1))
Base: linear
x p(X0,X1,X2)
(0, 0, 0) 0.25
(0, 1, 1) 0.25
(1, 0, 1) 0.25
(1, 1, 0) 0.25
dit.example_dists
Parametric and hand-built examples:
Circuits:
Xor(),And(),Or(),Rdn(),Unq(), and related PID illustrationsCounts:
uniform(),bernoulli(),binomial(),multinomial()Structured joints:
giant_bit(),n_mod_m(),dyadic(),triadic(),pr_box()
Empirical constructors under dit.example_dists.empirical
(titanic(), penguins(),
blood_types(), congress(),
student(), car(),
bach(), corelli())
fetch their source data at call time and return the estimated joint.
The music examples additionally require dit[music].
API
- uniform(outcomes, base=None)[source]
Produces a uniform distribution over outcomes.
- Parameters:
outcomes (iterable) – The set of outcomes with which to construct the distribution.
base (float, 'linear', 'e') – The desired base for the distribution probabilities.
- Returns:
d – A uniform distribution over outcomes.
- Return type:
Distribution
- Raises:
ditException – Raised if the elements of outcomes do not all have the same length.
TypeError – Raised if outcomes is not iterable.
- uniform_distribution(outcome_length, alphabet_size, base=None)[source]
Returns a uniform distribution.
- Parameters:
outcome_length (int) – The length of the outcomes.
alphabet_size (int, list of lists) – The alphabets used to construct the outcomes of the distribution. If an integer, then the alphabet for each random variable will be the same, consisting of integers from 0 to k-1 where k is the alphabet size. If a list, then the elements are used as the alphabet for each random variable. If the list has a single element, then it will be used as the alphabet for each random variable.
base (float, 'linear', 'e') – The desired base for the distribution probabilities.
- Returns:
d – A uniform distribution.
- Return type:
Distribution.
Examples
Each random variable has the same standardized alphabet: [0,1] >>> d = dit.uniform_distribution(2, 2)
Each random variable has its own alphabet. >>> d = dit.uniform_distribution(2, [[0,1],[1,2]])
Both random variables have [‘H’,’T’] as an alphabet. >>> d = dit.uniform_distribution(2, [[‘H’,’T’]])
- mixture_distribution(dists, weights, merge=False)[source]
Create a mixture distribution: \(\sum p_i d_i\)
- Parameters:
dists ([Distribution]) – List of distributions to mix. Each distribution is assumed to have the same base and sample space.
weights ([float]) – List of weights to use while mixing dists. The weights are assumed to be probability represented in the base of the distributions.
merge (bool) – If True then distributions will be mixed even if they do not share the same sample space. The idea is that each of the input distributions is reinterpreted on a common, merged sample space. If False, then an exception will be raised if incompatible distributions are mixed.
- Returns:
mix – The mixture distribution.
- Return type:
Distribution
- Raises:
DitException – Raised if there dists and weights have unequal lengths.
InvalidNormalization – Raised if the weights do not sum to unity.
InvalidProbability – Raised if the weights are not valid probabilities.
IncompatibleOutcome – Raised if the sample spaces for each distribution are not compatible.
- product_distribution(dist, rvs=None, base=None)[source]
Returns a new distribution which is the product of marginals.
- Parameters:
dist (distribution) – The original distribution.
rvs (sequence) – A sequence whose elements are also sequences. Each inner sequence defines the marginal distribution used to create the new distribution.
base (float, 'linear', 'e') – The desired base for the distribution probabilities.
- Returns:
d – The product distribution.
- Return type:
Distribution
- insert_rvf(d, func, index=-1)[source]
Returns a new distribution with an added random variable at index index.
The new random variable must be a function of the other random variables. By this, we mean that the entropy of the new random variable conditioned on the original random variables should be zero.
- Parameters:
dist (Distribution) – The distribution used to construct the new distribution.
func (callable | list of callable) – A function which takes a single argument—the value of the previous random variables—and returns a new random variable. Note, the return value will be added to the outcome using __add__, and so it should be a hashable, orderable sequence (as every outcome must be). If a list of callables is provided, then multiple random variables are added simultaneously and will appear in the same order as the list.
index (int) – The index at which to insert the random variable. A value of -1 is will append the random variable to the end.
- Returns:
d – The new distribution.
- Return type:
Distribution
Examples
>>> d = dit.Distribution(['00', '01', '10', '11'], [1/4]*4) >>> def xor(outcome): ... return str(int(outcome[0] != outcome[1])) ... >>> d2 = dit.insert_rvf(d, xor) >>> d.outcomes ('000', '011', '101', '110')
- distribution_from_bayesnet(digraph, nodes=None, sample_space=None, attr='dist')[source]
Returns a distribution built from a Bayesian network.
Each node represents a random variable
X_i. Every node must store its conditional probability distributionP(X_i | Y_i)whereY_irepresents the parents ofX_i. If a node has no in-degree, then it must store the probability distributionP(X_i).- Parameters:
digraph (NetworkX digraph) – A directed graph, representing the Bayesian graphical model.
nodes (list, None) – The order of the nodes that will determine the random variable order. If None, then we use sorted(digraph.nodes()), which assumes the nodes are sortable. The reason we assume they are sortable is because the parent values must correspond to the node order and thus, we need an unambiguous ordering that the user could have known ahead of time.
sample_space (SampleSpace) – If provided, this specifies the outcomes of the distribution to be constructed. The distributions stored on the nodes are assumed to be compatible with this space. If functions are stored on the nodes, then this parameter must be provided.
attr (str) –
The attribute for each node that holds the conditional distributions. The attribute value can take a variety of forms.
It can be a function. The function must take two arguments. The first is the value of random variable for the current node. The second is a dictionary of keyed by parents whose values are the values of the random variables corresponding to the parents. The function should return the probability P(node_val|parent_vals).
It can be a list, such as [parents, dists], that holds the parents and the conditional distributions: dists[i] = P(X | Y_i = parents[i]). It can also be a dict-like structure so that dists[y] is a distribution representing P(X | Y = y)`. If the node has no in-degree, then the attribute value should store the distribution only. When using distributions, each should have random variable names assigned that match the nodes in the graph, or alternatively, all nodes in the graph should be integers and then random variable names are not necessary. The order of elements within these lists (or the dict) does not matter, but for nodes that have parents, the order of the random variables that specify the parents, must match the order of
nodes. So for example, if the node order is [2, 1, 0] and node 1 has parents 0 and 2. Then the parents for node 1 will be such that the first element corresponds to node 2 and the second to node 0, since 2 precedes 0 in the node order. All distributions will be assumed to have the same base, and this base will determine the base of the constructed distribution.
- Returns:
dist – The joint distribution.
- Return type:
Distribution
Examples
The Xor logic gate has the following structure:
>>> g = nx.DiGraph() >>> g.add_edge(0, 2) >>> g.add_edge(1, 2)
Let’s add distributions to it using functions.
>>> uniform = lambda node_val, parents: 0.5 >>> def xor(node_val, parents): ... if '1' == parents[0] == parents[1]: ... desired_output = '1' ... else: ... desired_output = '0' ... return int(node_val == desired_output) ... >>> g.nodes[0]['dist'] = uniform >>> g.nodes[1]['dist'] = uniform >>> g.nodes[2]['dist'] = xor >>> ss = ['000', '001', '010', '011', '100', '101', '110', '111'] >>> d = dit.distribution_from_bayesnet(g, sample_space=ss)
Alternatively, we could add distributions using Distribution objects.
>>> uniform = dit.uniform_distribution(1, 2) >>> sample_space1 = [(0,), (1,)] >>> one = dit.Distribution(sample_space1, [0, 1]) >>> zero = dit.Distribution(sample_space1, [1, 0]) >>> sample_space2 = [(0, 0), (0, 1), (1, 0), (1, 1)] >>> xor = [ sample_space2, [zero, one, one, zero]] >>> g.nodes[0]['dist'] = uniform >>> g.nodes[1]['dist'] = uniform >>> g.nodes[2]['dist'] = xor >>> d = dit.distribution_from_bayesnet(g)
We can add noise whenever the output would normally be 1.
>>> noisy = dit.Distribution(sample_space, [.1, .9]) >>> dists = [zero, noisy, noisy, zero] >>> g.nodes[2]['dist'][1] = dists >>> d = dit.distribution_from_bayesnet(g)