Probability Estimation#

[1]:
%%capture
# execute the creation & training notebook first
%run "02-01-creation_and_training.ipynb"
# execute the outlier detection notebook
%run "02-05-outlier_detection.ipynb"

In the outlier detection section we saw how to detect outliers in a test data set and how the outlier threshold influenced the detection results. In the rank estimation section the underlying method of rank estimation was explained.

In this section we take a look at the .estimate_probabilities method, which calls the ProbabilityEstimator, the basis for both outlier detection and rank estimation.

The ProbabilityEstimator is used by the RankEstimator under the hood to estimate the probability of individual data points. Let us apply the probability estimator to the modified test data set from the outlier detection example.

[2]:
log_probabilities = causal_structure.estimate_probabilities(data=mod_test_data)
log_probabilities
[2]:
(a) (b|a) (c|a,b) graph
0 1.222395 -1.602953 1.237068 0.855358
1 1.413743 -1.012356 1.323633 1.725051
2 1.113812 -0.994486 1.244645 1.363961
3 0.006604 -1.219309 0.964815 -0.252899
4 1.405377 -0.984049 1.322710 1.744055
... ... ... ... ...
95 0.431912 -1.019023 0.986077 0.398876
96 1.329171 -1.179034 1.279977 1.429986
97 1.343442 -1.046909 1.310268 1.606940
98 1.408012 -1.011526 1.315708 1.712243
99 1.405317 -1.422481 1.324429 1.307159

100 rows × 4 columns

The numbers that are returned are the logarithmic probability densities. These values are unbound. They can assume floating point value.

Since they are densities their individual values do not tell us much about the likelihood of a given event. Only the comparison to other probability density values for the same parameter provides some insight.

For example let us extract the 5 data points with the lowest probability for the whole graph.

[3]:
log_probabilities.sort_values("graph").iloc[:5]
[3]:
(a) (b|a) (c|a,b) graph
50 0.987680 -13.097823 -245.485737 -262.044845
60 0.766149 -1.219600 -24.933954 -25.538929
74 -1.950167 -1.359456 0.267683 -3.049903
79 -0.471609 -2.757013 0.663875 -2.556750
13 -0.325763 -2.460974 0.718113 -2.064678

We see that the modified data points 50 and 60 indeed have very low logarithmic probability densities. Much lower than any of the unmodified data points. This is why they were easily picked up by the OutlierDetector.

One more thing is noticable when looking at the logarithmic probability densities. The sum of the ‘(a)’, ‘(b|a)’ and ‘(c|a,b)’ columns seems to be close to the ‘graph’ column.

[4]:
log_probability_sums = log_probabilities[['(a)', '(b|a)', '(c|a,b)']].sum(axis=1)
log_probabilities_with_sum = log_probabilities.copy()
log_probabilities_with_sum["sum of '(a)', '(b|a)', '(c|a,b)'"] = log_probability_sums
log_probabilities_with_sum.sort_values("graph").iloc[:5]
[4]:
(a) (b|a) (c|a,b) graph sum of '(a)', '(b|a)', '(c|a,b)'
50 0.987680 -13.097823 -245.485737 -262.044845 -257.595880
60 0.766149 -1.219600 -24.933954 -25.538929 -25.387404
74 -1.950167 -1.359456 0.267683 -3.049903 -3.041940
79 -0.471609 -2.757013 0.663875 -2.556750 -2.564747
13 -0.325763 -2.460974 0.718113 -2.064678 -2.068623

This is not by accident. The chain rule (or product rule) of conditional probabilities tells us that the product of the (correctly conditioned) probability densities is the total probability density. The graph column represents the total logarithmic probability density and for logarithmic probability densities the product becomes a sum.

The reason why the sum and the graph column do not match exactly is that the graph also contains the probabilites of the trained regression parameters, which enter the total sum.

For further details about the ProbabilityEstimator see the corresponding section in the core-documentation.