Initial Exploration

RandomInteger[{0,1}, 20]

RandomVariate[UniformDistribution[{0,1}]]

Count[RandomInteger[{0,1}, 20], 0]

ListLinePlot[Table[Accumulate[RandomReal[{-1,1},250]],{3}],Filling->Axis,PlotLegends->{"one","two","three"}]

Accumulate[RandomInteger[{0,1}, 10]]

ListLinePlot[%]

Table[RandomInteger[{0,1},1],10]

Accumulate[RandomInteger[BernoulliDistribution[0.5],1000]]/Range[1000] // ListPlot

Accumulate[RandomVariate[NormalDistribution[],5000]]/Range[5000] // ListPlot

ListPlot[RandomVariate[NormalDistribution[],1000],Filling->Axis]

ListPlot[RandomVariate[GeometricDistribution[1/2],50],Filling->Axis]

Accumulate[RandomInteger[{0,1},100]]/Range[100] // ListPlot

Plot[6x(1-x),{x,0,1},Filling->Bottom]

Integrate[6x(1-x)x,{x,0,1}]

(∫_0^1)6x(1-x)x \[DifferentialD]x

data = SemanticImport["/Users/codingquark/Documents/Wolfram Mathematica/doing_bayesian_data_analysis/DBDA2Eprograms/HairEyeColor.csv"]

Mean[data]

Length[data]

Normal[data]

data[Histogram,"Count"]

data[GroupBy["Hair"],Total, "Count"]

data[GroupBy["Eye"],Total,"Count"]

data[All,"Count"] // Total

data[GroupBy["Hair"]]
data[GroupBy["Eye"]]

data[GroupBy["Hair"],Mean,"Count"]

data[GroupBy["Eye"],Mean,"Count"]

data

titanic= ExampleData[{"Dataset","Titanic"}]

titanic[Histogram,"age"]

titanic[GroupBy[Key["sex"]],Counts,"class"]

Watch https://www.wolfram.com/broadcast/video.php?c=377&p=2&v=1258

data[Histogram,"Count"]

data[ListPlot,"Count"]

data[GroupBy["Hair"],GroupBy["Eye"]]

data[Total,"Count"]
data[All,"Count"] //Total

data[Mean,"Count"]

data[Median,"Count"]

data[Counts,"Hair"]

data[Counts,"Eye"]

data[MinMax,"Count"]

data[StandardDeviation,"Count"]

data[GroupBy["Hair"],Total,"Count"]
data[GroupBy["Eye"],Total,"Count"]

PieChart[data[GroupBy["Eye"],Total,"Count"],ChartLegends->Automatic]

PieChart[data[GroupBy["Hair"],Total,"Count"],ChartLegends->Automatic]

data[GroupBy["Eye"]] //Normal

Given the structure so far, we need to figure out a way to have named rows, nested with further named rows. Like so: {“Brown Eye”->{“Black Hair”->4, “Blond Hair”->10}, “Blue Eye”->...}
Not sure if this should be done at import time, or as the next step after import.

Importing Data

How should it look like?

We want our Hair + Eye colour data to be structured “nicely” before we start doing analysis. What is this “nice” structure?

If imported simply, this is what it looks like:

path = "/Users/codingquark/Documents/Wolfram Mathematica/doing_bayesian_data_analysis/DBDA2Eprograms/HairEyeColor.csv";
dataset=SemanticImport[path]

This structure is not bad, it correctly figures out that the first row of the CSV is column names. But is this how we want things to be? Could we have more structure to it?

One approach can be to add some named rows. For example, we can imagine that the actual data point in the entire dataset is the `Count` column, rest are just names! Which means, we can have rows of rows.

Let’s try. What does the structure look like?

planets = ExampleData[{"Dataset","Planets"}]

planets//Normal

Okay, now that we know how data is represented, we can try manually creating a row or two for our data.

dataset=Dataset[<|"Black"-><|"Blue"-><|"Count"->20|>,<|"Brown"-><|"Count"->68|>|>|>|>]

Does this look good? We don’t have column names for Hair and Eye, but we do for Count. Is that normal? If you look at the Moons in the planet dataset, you can see that the names of of moons are not column-named, but their properties are column-named. Which is analogous to what we have for our dataset as well.

Let’s finalise on this structure for now, and find out how to convert the imported data.

Structuring Imported Dataset

So far, I have not found any simple way to morph the dataset. The important note found so far is that we cannot modify existing Dataset, but instead have to create new Dataset with the structure we want. What this means, is that we will have to define Column/Row structure and select proper Count columns for the new Dataset definition.

This manual approach is too much hassle. And figuring out “script-like” way to change the structure is not attractive right now.

Hence, we will try to roll with what we have and see what happens when we try doing operations on the data.

References

https://www.wolfram.com/broadcast/video.php?c=377&p=2&v=1258

https://www.youtube.com/watch?v=XN8Y177doEE

https://www.wolfram.com/wolfram-u/courses/data-science/becoming-a-data-curator-dat906/

Basic Analysis

Exercises from 4.6

Exercise 4.1

The eye-color hair-color data is frequencies of eye and hair color for males and females. Run the (some, we are going to ignore them) R code.

In your write-up, include each line above and its results. Explain what each line does (in a bit more detail than the inline comments). Extend above commands by also computing the probabilities of the hair colors given brown eyes, and the probabilities of the eye colors given Brown hair.

First, let’s import the data and show it.

path = "/Users/codingquark/Documents/Wolfram Mathematica/doing_bayesian_data_analysis/DBDA2Eprograms/HairEyeColor.csv";
dataset=SemanticImport[path]

Run equivalent of: 
EyeHairFreq = apply (HairEyeColor, c ("Eye", "Hair"), sum)
EyeHairProp = EyeHairFreq / sum (EyeHairFreq)
show (round (EyeHairProp, 2))

Funnily enough, the R code given by the book loads the CSV, generates Male/Female split, and then store in the variable “HairEyeColor”. The “sum” application then gives the data we have got already!

Next line is dividing each cell by the total.

counttotal=dataset[Total,"Count"];
haireyeproportion=dataset[All,Append[#,"Frequency"->#Count/counttotal]&]

To find total counts of each hair-color:

hairfrequencydataset=dataset[GroupBy["Hair"],Total,"Count"]

Like we added the Frequency column above for all rows, do the same but for the grouped data:

hairproportions=hairfrequencydataset[All,<|"Frequency"->#1/counttotal|>&]

Same operations now for eye colors:

eyefrequencydataset=dataset[GroupBy["Eye"],Total,"Count"]
eyeproportions=eyefrequencydataset[All,<|"Frequency"->#1/counttotal|>&]

Calculate conditional probability of Hair, given Blue eye color (table 4.2):

haireyeproportion[Select[#Eye=="Blue"&]][All,<|"Hair"->#Hair,"Frequency"->N[#Frequency/eyeproportions["Blue","Frequency"]]|>&]

Calculate conditional probability of hair colors given brown eyes:

haireyeproportion[Select[#Eye=="Brown"&]][All,<|"Hair"->#Hair,"Frequency"->N[#Frequency/eyeproportions["Brown","Frequency"]]|>&]

Calculate conditional probability of eye colors given brown hair:

haireyeproportion[Select[#Hair=="Brown"&]][All,<|"Eye"->#Eye,"Frequency"->N[#Frequency/hairproportions["Brown","Frequency"]]|>&]

Let’s now try to understand, verbally, what the exercise was about.

We were given raw counts for hair+eye colour combos. Just like one of the section in the chapter, we wanted to get to finding out conditional probabilities.

To get to conditional probabilities, we needed some basic information derived first:

Proportions of each hair+eye colour combo

Proportions of each hair colour

Proportions of each eye colour

When we calculate hair+eye proportions, we are calculating joint probabilities. For example, the probability of finding someone with both Brown eyes and Black hair is 0.11 i.e. 11% and so on.

When we calculate the proportions of only hair or only eye colours, we are measuring probability of finding, for example, a person with black hair; a person with blue eyes and so on. For example, the probability of finding a person with Black hair is 0.18 / 18%; a person with Brown eyes is 0.37 / 37%

Finally, we can use the derived information to calculate conditional probabilities. To calculate the probability of a person having Hazel eyes, given we know that the person has Blond hair we need to divide independent probability by marginal probability. So, for the specific case, the calculation is: 0.0168919/0.214527 = 0.078 (~7%)

Thus ends a successful Exercise 4.1! Notice how the entire exercise was about calculating proportions in various ways and it did not feel the way it did when reading the section of the book.

Exercise 4.2

Modify the coin flipping program in Section 4.5 to simulate a biased coin that has p(H)=0.8. Change the height of the reference line in the plot to match p(H). Comment your code.

Okay, this is more involved since it is mainly about R code. We will have to first write code to simulate coin flips, then make the coin flips biased.

Alright, let’s think about this. The R code uses `sample` function that takes probability values with with to take the samples. We need something similar in Mathematica. But what is our real goal? We want to flip a coin - [H,T]. And when it is flipped we want to specify how biased the coin is. Having flipped the coin N times, we want to plot the running proportion of H and know the end proportion.

First, we have RandomVariate function that can take some distribution. The one we might find useful is DiscreteUniformDistribution. Let’s try.

RandomVariate[DiscreteUniformDistribution[{0,1}],10] // Short

{1,0,0,0,1,1,0,0,1,1}

Histogram[RandomVariate[DiscreteUniformDistribution[{0,1}],500]]

tosses=RandomVariate[DiscreteUniformDistribution[{0,1}],500];

N[((Count[tosses,1])/500)]

N[((Count[tosses,0])/500)]

There, we got the end proportions of the fair die after 500 tosses.

manytosses=RandomVariate[DiscreteUniformDistribution[{0,1}],50000];

ListPlot[Accumulate[(manytosses/(Length[manytosses]))]]

ListLinePlot[Accumulate[Select[tosses,#==1&]]]

Still no idea how to get “running proportion”! FoldList works, but does not give access to current index of the list. MapIndex does not keep track of previous elements. Looks like we will have to write our own function. Not fun, I must say.

Stolen the following code from https://mathematica.stackexchange.com/questions/190503/how-to-make-foldlistindexed-ie-foldlist-with-an-index-or-iterator

foldIndexedList=Module[{i=1,f=#},FoldList[f[##,i++]&,##2]]&;
foldIndexedList[f,x,{a,b,c,d}]

fewtosses={1,1,0,1,1,0,1,0,0,0}

This approach may not be the thing we want. What we want is a list: {(1/1),(2/2),(2/3),(3/4),(4/5),(4/6),(5/7),(5/8),(5/9),(5/10)} and so on.

For[i=1;res={},i<=Length[tosses],i++,AppendTo[res,Last@((FoldList[Plus,0,tosses[[;;i]]])/i)]]

ListLinePlot[res]

Well what do we have here! It worked! It is the ugliest way to do things because it is not functional, but it got the job done. We will refine this somehow.

MapIndexed[((FoldList[Plus,0,fewtosses[[;;First[#2]]]])/(First[#2]))&,fewtosses]

MapIndexed[List[#1,First@#2]&,fewtosses]

MapIndexed[((Total[tosses[[;;First@#2]]])/(First@#2))&,tosses];

ListLinePlot[%]

There we go, it is now functional - nice and neat.

Now we need to bias the coin such that the probability of Heads is 0.8.

There’s BernoulliDistribution which “represents a Bernoulli distribution with probability parameter 𝓅. Also known as the coin toss distribution or Bernoulli trial”. Since it literally says “coin toss” in there, we should try it out.

RandomVariate[BernoulliDistribution[0.8],10]

biasedtosses=RandomVariate[BernoulliDistribution[0.8],500];
N[((Count[biasedtosses,1])/500)]
N[((Count[biasedtosses,0])/500)]

Yes! Looks good! Let’s plot!

ListLinePlot[MapIndexed[((Total[biasedtosses[[;;First@#2]]])/(First@#2))&,biasedtosses],PlotRange->All]

And thus concludes the exercise!

Exercise 4.4

Consider a spinner with a [0,1] scale on its circumference. Suppose that the spinner is slanted or magnetized or bent in some way such that it is biased, and its probability density function is 𝓅(𝓍) = 6𝓍(1-𝓍) over the interval 𝓍 ∈ [0,1].

Adapt the program IntegralOfDensity.R to plot this density function and approximate its integral. Comment your code. Be careful to consider values of 𝓍 only in the interval [0,1]. Hint: You can omit the first couple of lines regarding meanval and sdval, because those parameter values pertain only to the normal distribution. Then set xlow=0 and xhigh=1, and set dx to some small value.

Derive the exact integral using calculus.

Does this function satisfy equation 4.3?

From inspecting the graph, what is the maximal values of 𝓅(𝓍)?

Here is the R code from the file:

source("DBDA2E-utilities.R")
# Graph of normal probability density function, with comb of intervals.
meanval = 0.0               # Specify mean of distribution.
sdval = 0.2                 # Specify standard deviation of distribution.
xlow  = meanval - 3.5*sdval # Specify low end of x-axis.
xhigh = meanval + 3.5*sdval # Specify high end of x-axis.
dx = sdval/10               # Specify interval width on x-axis
# Specify comb of points along the x axis:
x = seq( from = xlow , to = xhigh , by = dx )
# Compute y values, i.e., probability density at each value of x:
y = ( 1/(sdval*sqrt(2*pi)) ) * exp( -.5 * ((x-meanval)/sdval)^2 )
# Plot the function. "plot" draws the intervals. "lines" draws the bell curve.
openGraph(width=7,height=5)
plot( x , y , type="h" , lwd=1 , cex.axis=1.5, xlab="x" , ylab="p(x)", cex.lab=1.5, main="Normal Probability Density" , cex.main=1.5 )
lines( x , y , lwd=3 ,  col="skyblue" )
# Approximate the integral as the sum of width * height for each interval.
area = sum( dx * y )
# Display info in the graph.
text( meanval-sdval , .9*max(y) , bquote( paste(mu ," = " ,.(meanval)) ), adj=c(1,.5) , cex=1.5 )
text( meanval-sdval , .75*max(y) , bquote( paste(sigma ," = " ,.(sdval)) ), adj=c(1,.5) , cex=1.5 )
text( meanval+sdval , .9*max(y) , bquote( paste(Delta , "x = " ,.(dx)) ), adj=c(0,.5) , cex=1.5 )
text( meanval+sdval , .75*max(y) ,bquote( sum({},x,{}) *" "* Delta *"x p(x) = "* .(signif(area,3)) ) ,adj=c(0,.5) , cex=1.5 )
# Save the plot to an EPS file.
saveGraph( file = "IntegralOfDensity" , type="eps" )

That’s a lot of code just to display graphs. Disregard those lines, and we are left with a few important lines:

# Specify comb of points along the x axis:
x = seq( from = xlow , to = xhigh , by = dx )

# Compute y values, i.e., probability density at each value of x:
y = ( 1/(sdval*sqrt(2*pi)) ) * exp( -.5 * ((x-meanval)/sdval)^2 )

# Approximate the integral as the sum of width * height for each interval.
area = sum( dx * y )

Fairly straight forward. Initially I thought we are going to use NormalDistribution function, but no. This is just calculating values of “probability density” in a range (0 to 1) with some interval and plots the values. Can we find some excuse to use NormalDistribution?

Anyway, here we go:

μ=0.0;
σ=0.2;

δ=0.02;
𝓎=Table[{x,6x(1-x)},{x,0,1,δ}];
Show[ListPlot[𝓎,Filling->Bottom],
ListLinePlot[𝓎]]

area=Total[𝓎*δ]

I am curious how the plot looked liked with the original equation.

μ=0.0;
σ=0.2;
δ=(σ/10);
𝓎=Table[{x,(1/(σ*√(2π)))*(E^((-0.5*(((((x-μ)/σ)))^2))))},{x,(μ-3.5*σ),(μ+3.5*σ),δ}];
Show[ListPlot[𝓎,Filling->Bottom],
ListLinePlot[𝓎,Filling->Axis]]

(∫_0^1)6x(1-x)\[DifferentialD]x

Max[𝓎]

Thus concludes the exercise.

However, there’s something more we can try. That is to see if we can find more Mathematica-like ways of doing this.
Thinking about it, we are given a probability distribution function 6𝓍(1-𝓍). And all we want is to see how it will behave for different values of the variable 𝓍. There is one potential function we can try.

𝒟=ProbabilityDistribution[6x(1-x),{x,0,1}];
Plot[PDF[𝒟,x],{x,0,1},Filling->Bottom]

Ah! Look what we got here! Perfect.

Max[PDF[𝒟,x]]

𝒟=ProbabilityDistribution[(1/(σ*√(2π)))*(E^((-0.5*(((((x-μ)/σ)))^2)))),{x,-.7,.7}];
Plot[PDF[𝒟,x],{x,-1,1},Filling->Bottom]

To get area under the curve we do CDF[a] - CDF[b]

𝒟=ProbabilityDistribution[6x(1-x),{x,0,1}];
CDF[𝒟][1]-CDF[𝒟][0]

𝒟=ProbabilityDistribution[(1/(σ*√(2π)))*(E^((-0.5*(((((x-μ)/σ)))^2)))),{x,-.7,.7}];
CDF[𝒟][.7]-CDF[𝒟][-.7]

As you can observe, this approach is much better. Though it may not be as easy to understand what’s really going on under the hood, it is cleaner once you already have understood what’s going on. For example, if we were to start directly with ProbabilityDistribution, we would not know how 6𝓍(1-𝓍) is executed and plotted.