Function Repository Resource:

ConditionalCategoricalDistribution

Source Notebook

Obtain a conditional categorical distribution formed by restricting the domain of a categorical distribution

Contributed by: Seth J. Chandler

ResourceFunction["ConditionalCategoricalDistribution"][{pos1pattern1,pos2pattern2,},dist]

restricts the domain of dist to those elements whose value at each posi matches patterni.

ResourceFunction["ConditionalCategoricalDistribution"][{pattern1,pattern2, },dist]

restricts the domain of dist to those elements whose value at position i matches patterni.

Details and Options

Use _ as the pattern in order to let any value be acceptable at a given position of a domain element.
The pattern may be specified using a List of patterns whose length is shorter than the length of each domain element. When this happens, the function right-pads the List with _.
By default, if the result of the function is a univariate distribution, the resulting domain elements do not each have List wrapped around them. To wrap each domain element in List, set the "FlattenUnivariate" option to False.
By default, if a dimension of the result has only one possible value, the distribution "marginalizes out" that dimension. Setting the option "Marginalization" to False prevents that behavior.

Examples

Basic Examples (4) 

Start with a categorical distribution:

In[1]:=
cd = CategoricalDistribution[{{"A", "B", "C"}, {"D", "E"}}, {{1/21, 2/
    21}, {1/7, 4/21}, {5/21, 2/7}}]
Out[1]=

Condition that distribution on the first dimension taking on a value of "A":

In[2]:=
Information[
 Echo@ResourceFunction["ConditionalCategoricalDistribution"][{"A"}, cd], "Probabilities"]
Out[2]=

Find the probabilities from that categorical distribution conditioned on the second dimension taking on a value of "E":

In[3]:=
Information[
 ResourceFunction["ConditionalCategoricalDistribution"][{2 -> "E"}, cd], "Probabilities"]
Out[3]=

Use a list of patterns instead of rules to impose the same condition as above:

In[4]:=
Information[
 ResourceFunction["ConditionalCategoricalDistribution"][{_, "E"}, cd], "Probabilities"]
Out[4]=

Scope (3) 

Patterns used in the conditions can be complex:

In[5]:=
cd = CategoricalDistribution[{{"A", "B", "C"}, {"D", "E"}}, {{1/21, 2/
    21}, {1/7, 4/21}, {5/21, 2/7}}]
Out[5]=
In[6]:=
Information[
 ResourceFunction[
  "ConditionalCategoricalDistribution"][{1 -> "A" | "B"}, cd], "Probabilities"]
Out[6]=

The first category must be a letter in the word "ABLE":

In[7]:=
Information[
 Echo[ResourceFunction[
   "ConditionalCategoricalDistribution"][{1 -> x_String /; StringContainsQ["ABLE", x]}, cd]], "Probabilities"]
Out[7]=

A CategoricalDistribution of arbitrary dimension works with the function:

In[8]:=
cd6 = CategoricalDistribution[{CharacterRange["a", "f"], CharacterRange["g", "k"], CharacterRange["l", "o"], CharacterRange["p", "r"], CharacterRange["s", "t"], CharacterRange["u", "z"]}, RandomInteger[{0, 10}, {6, 5, 4, 3, 2, 6}]]
Out[8]=
In[9]:=
Information[
 ResourceFunction[
  "ConditionalCategoricalDistribution"][{1 -> "a" | "b", 2 -> "j", 5 -> "t", 3 -> "m" | "n", 6 -> "z"}, cd6], "ProbabilityTable"]
Out[9]=

The function works with univariate categorical distributions returning a CategoricalDistribution with potentially fewer categories:

In[10]:=
Information[
 Echo[ResourceFunction["ConditionalCategoricalDistribution"][
   "A" | "B", CategoricalDistribution[{"A", "B", "C"}, {0.6, 0.3, 0.1}]]], "Probabilities"]
Out[10]=

Options (2) 

The value of the "FlattenUnivariate" option (True by default) determines whether the result of a univariate ConditionalCategoricalDistribution has its categories described without a List wrapper:

In[11]:=
cd = CategoricalDistribution[{{"A", "B", "C"}, {"D", "E"}}, {{1/21, 2/
    21}, {1/7, 4/21}, {5/21, 2/7}}]
Out[11]=
In[12]:=
AssociationMap[
 Information[
   ResourceFunction["ConditionalCategoricalDistribution"][{1 -> "B"}, cd, "FlattenUnivariate" -> #], "Probabilities"] &, {True, False}]
Out[12]=

Setting the "Marginalize" option to False preserves the dimensionality of the original distribution:

In[13]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/47fe7db1-42ea-4503-9a67-e5fdd633ed99"]
Out[13]=

Applications (2) 

Here is the joint distribution of persons in group A or B who have or do not have some disease, and to whom a test classifies as negative or positive for the disease. Find the joint distribution of persons in group A with respect to disease and test result:

In[14]:=
health = CategoricalDistribution[{{"A", "B"}, {"healthy", "sick"}, {"negative", "positive"}}, {{{64, 58}, {95, 32}}, {{39, 85}, {44, 62}}}]
Out[14]=
In[15]:=
Information[
 ahealth = ResourceFunction["ConditionalCategoricalDistribution"][{"A"}, health], "ProbabilityTable"]
Out[15]=

Find the probability that a person in group A who tests positive is actually sick:

In[16]:=
PDF[ahealth, {"sick", "positive"}]/
 PDF[MarginalDistribution[ahealth, 2], "positive"]
Out[16]=

Compute the fractions of true positives (sensitivity), true negatives (specificity) and false positives (1-specificity) for a mixture of categorical distributions:

In[17]:=
tp[dist_] := PDF[ResourceFunction[
   "ConditionalCategoricalDistribution"][{"Actual1"}, dist], "ClassifyAs1"]
In[18]:=
tn[dist_] := PDF[ResourceFunction[
   "ConditionalCategoricalDistribution"][{"Actual0"}, dist], "ClassifyAs0"]
In[19]:=
fp[dist_] := PDF[ResourceFunction[
   "ConditionalCategoricalDistribution"][{"Actual0"}, dist], "ClassifyAs1"]
In[20]:=
mixed = ResourceFunction[
ResourceObject[<|"Name" -> "MixtureCategoricalDistribution", "ShortName" -> "MixtureCategoricalDistribution", "UUID" -> "560477fe-b58d-40e2-9fd8-a914de0cbec2", "ResourceType" -> "Function", "Version" -> None, "Description" -> "Creates a mixture distribution of CategoricalDistributions but outputs it as a new CategoricalDistribution", "SymbolName" -> "FunctionRepository`$560477feb58d40e29fd8a914de0cbec2`MixtureCategoricalDistribution", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/schandler/Resources/560/560477fe-b58d-40e2-9fd8-a914de0cbec2/download/DefinitionData"]|>, ResourceSystemBase -> Automatic]][
  CategoricalDistribution[{"Small", "Big"}, {0.3, 0.7}], {CategoricalDistribution[{{"Actual0", "Actual1"}, {"ClassifyAs0", "ClassifyAs1"}}, {{80, 20}, {30, 70}}],
   CategoricalDistribution[{{"Actual0", "Actual1"}, {"ClassifyAs0", "ClassifyAs1"}}, {{300, 80}, {50, 370}}]}]
Out[20]=
In[21]:=
Map[(ResourceFunction["ConditionalCategoricalDistribution"][{#}, mixed] &)/*(Through[{tp, tn, fp}[#]] &), {"Small", "Big"}]
Out[21]=

Properties and Relations (1) 

The resource function MixtureCategoricalDistribution increases the dimensionality of a CategoricalDistribution, whereas ConditionalCategoricalDistribution reduces the dimensionality:

In[22]:=
mixed = ResourceFunction[
ResourceObject[<|"Name" -> "MixtureCategoricalDistribution", "ShortName" -> "MixtureCategoricalDistribution", "UUID" -> "560477fe-b58d-40e2-9fd8-a914de0cbec2", "ResourceType" -> "Function", "Version" -> None, "Description" -> "Creates a mixture distribution of CategoricalDistributions but outputs it as a new CategoricalDistribution", "SymbolName" -> "FunctionRepository`$560477feb58d40e29fd8a914de0cbec2`MixtureCategoricalDistribution", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/schandler/Resources/560/560477fe-b58d-40e2-9fd8-a914de0cbec2/download/DefinitionData"]|>, ResourceSystemBase -> Automatic]][
  CategoricalDistribution[{"Small", "Big"}, {3/10, 7/10}], {CategoricalDistribution[{{"Actual0", "Actual1"}, {"ClassifyAs0", "ClassifyAs1"}}, {{2/5, 1/10}, {3/
      20, 7/20}}],
   CategoricalDistribution[{{"Actual0", "Actual1"}, {"ClassifyAs0", "ClassifyAs1"}}, {{3/2, 2/5}, {1/4, 37/20}}]}]
Out[22]=
In[23]:=
Information[
 Echo[ResourceFunction[
   "ConditionalCategoricalDistribution"][{1 -> "Small"}, mixed]], "ProbabilityTable"]
Out[23]=

Neat Examples (4) 

The following application comes from the field of causal inference, which is sometimes referred to as "do-calculus". Assume the joint probability distribution of the size of a kidney stone, the treatment one receives for it and how the outcome of that treatment is distributed as set forth below:

In[24]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/40647d3b-f3e3-4ad7-be73-67c21d41bdbc"]

Compute the probability of a good outcome conditioned on the treatment. It will appear that B has better outcomes than A:

In[25]:=
AssociationMap[
 treatment |-> Information[
    MarginalDistribution[
     ResourceFunction[
      "ConditionalCategoricalDistribution"][{2 -> treatment}, kidneys], 2], "Probabilities"] // N, {"A", "B"}]
Out[25]=

Now synthesize a randomized controlled trial and derive the interventional distribution when one forces the treatment to be "A" by computing a MixtureCategoricalDistribution over stone size in which the components are the conditional categorical distributions based on the stone size and the treatment being A:

In[26]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/1d3c1a6a-d31d-4a7b-9a4e-436fb02d83cf"]
Out[26]=

Do exactly the same thing but force the treatment to be "B":

In[27]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/7b3baaa2-1a96-428e-8618-58ad9a062229"]
Out[27]=

Although the observational distribution might suggest treatment B is superior, in fact, treatment A is superior and the observational distribution is distorted by the fact that small kidney stones, which generally have better outcomes, are more frequently treated with treatment B.

Publisher

Seth J. Chandler

Version History

  • 2.0.0 – 02 August 2021
  • 1.0.0 – 21 May 2020

Related Resources

License Information