<jupyter_start><jupyter_text>The Numpy Random Package[Official documentation](https://numpy.org/doc/stable/reference/random/index.html)***  Table of Contents [1. Introduction](Intro) [2. Simple random data](Simple-random-data)&nbsp;&nbsp;&nbsp;&nbsp;[- Integers](Integers)&nbsp;&nbsp;&nbsp;&nbsp;[- Floats](Floats)&nbsp;&nbsp;&nbsp;&nbsp;[- Choice](Choice)&nbsp;&nbsp;&nbsp;&nbsp;[- Bytes](Bytes) [3. Permutations](Permutations)&nbsp;&nbsp;&nbsp;&nbsp;[- Shuffle](Shuffle)&nbsp;&nbsp;&nbsp;&nbsp;[- Permutation](Permutation)&nbsp;&nbsp;&nbsp;&nbsp;[- Permuted](Permuted) [4. Distributions](Distr)&nbsp;&nbsp;&nbsp;&nbsp;[- Normal](Normal)&nbsp;&nbsp;&nbsp;&nbsp;[- Uniform](Uniform)&nbsp;&nbsp;&nbsp;&nbsp;[- Binomial](Binomial)&nbsp;&nbsp;&nbsp;&nbsp;[- Multinomial](Multinomial)&nbsp;&nbsp;&nbsp;&nbsp;[- Geometric](Geometric) [5. Seeds](Seeds)  1. Introduction ***Random number generation has many applications in the real world such as science, gambling, statistics, computer simulation/gaming, cryptography and any other area that requires an unpredicatable outcome. Most applications use a pseudorandom number generator (PRNG) however, True Random Number Generators (TRNG) do exist. Such a device can generate random numbers via a physical process, such as radioactive decay or thermal noise in a semiconductor. Both instrinsically random processes as per quantum theory.[1] As most application do not rely on truly random generation, pseudorandom number generation is generally used. It is called pseudorandom because the sequence to generate a random sample relies on a specified value known as the seed. Numpy's random package produces pseudorandom numbers by combining a BitGenerator, which produces a sequence of random bits (typically 32 or 64 bits) and a Generator, that converts the sequence into a sequence of numbers. The Generator conforms to a particular probability distribution ie., uniform, normal or binomial. [2] Five such distributions will be discussed in the "Distributions" section. Random State vs GeneratorGenerator replaces the legacy version Random State, since being updated in version 1.17.0. Random State legacy version is still be used however, it is limited to a single bitGenerator. Generator has multiple bitGenerators - meaning many distinct probability distributions are available for use. A further benefit of Generator is that by default it utilises bits that are produced by PCG-64, a 28-bit execution of O’Neill’s permutation congruential generator, which provides good statistical algorithms for generating random numbers that are difficult to predict.[3][Random Generator Documentation](https://numpy.org/doc/stable/reference/random/generator.html)   Set up***  Import libraries<jupyter_code># Numerical arrays.
import numpy as np

# Plotting data.
import matplotlib.pyplot as plt

# Nice plot style. 
import seaborn as sns<jupyter_output><empty_output><jupyter_text>Set plot defaults<jupyter_code># Set default plot style.
plt.style.use('fivethirtyeight')

# Set default figure size.
plt.rcParams['figure.figsize'] = [10, 6]

# Renders plots nicely in notebook.
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Construct a Generator*** The variable `rng` will be used in place of `np.random.generator` in code examples throughout this notebook for simplification.<jupyter_code># Create a new generator with bitGenerator PCG64. 
rng = np.random.default_rng(1) # Setting seed for reproducible results.<jupyter_output><empty_output><jupyter_text>The integer value passed through as a parameter is known as a seed. Seeds will be discussed in more detail in section 5.  References:***[1] <NAME>. (2003). RANDy - A True-Random Generator Based On Radioactive Decay [Ebook]. Security and Cryptography Research Group Saarland University. Retrieved from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.110.9725&rep=rep1&type=pdf [2] Random sampling (numpy.random) — NumPy v1.21 Manual. (2021). Retrieved 2 November 2021, from https://numpy.org/doc/stable/reference/random/index.html [3] <NAME>. (2021). PCG, A Family of Better Random Number Generators. Retrieved 2 November 2021, from https://www.pcg-random.org/index.html  2. Simple random data This section introduces numpy's built-in functions for producing random samples.  `integers()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.integers.htmlnumpy.random.Generator.integers)Generates random integer values. *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.integers(low, high=None, size=None, dtype=np.int64, endpoint=False)` The range is specified using the low and high parameters, the low value is inclusive while the high value is exclusive. The returned integers are from the discrete uniform distribution meaning each n value has equal probability of being returned.  [1]<jupyter_code># Random integer array.
rng.integers(low=0, high=10, size=100)
# A single random integer. 
rng.integers(0, 10, 1)<jupyter_output><empty_output><jupyter_text>Specifying endpoint***With endpoint set to True the range of numbers is inclusive of high and low values.<jupyter_code># endpoint=True
ran_int = rng.integers(low=0, high=10, size=20, endpoint=True)
ran_int<jupyter_output><empty_output><jupyter_text>Default dytype***64 bits for high precision accuracy. However, 32 bits is sufficient for most functions.<jupyter_code># Check the type of data the function produces.
type(ran_int[0])<jupyter_output><empty_output><jupyter_text>Example: Roll a dice***<jupyter_code># Roll the dice 1000 times. 
rolls = rng.integers(1, 7, size=1000)
# Plot results.
sns.histplot(rolls)<jupyter_output><empty_output><jupyter_text>As can be seen with the plot above, each side appeared a similar number of times after 1000 rolls (between 160 & 180 times each). This is typical of discrete uniform distribution, where the probability of occurances of events are equally possible within a finite range of values.  `random()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.random.htmlnumpy.random.Generator.random)Generates random float values.*** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.random(size=None, dtype=np.float64, out=None)`<jupyter_code># Generate a single floating point number with size parameter set to None
rng.random(size=None)
# Generate an array of floats each using 32 bits of memory. 64 bits is default and used for high precisison. 
a = rng.random(size=100000, dtype=np.float32) # Default range is 0 - 0.9999. 
# Generate multiple arrays.
rng.random(size=(5, 5))<jupyter_output><empty_output><jupyter_text>`choice()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.choice.htmlnumpy.random.Generator.choice)Generates a random sample from an array. *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.choice(a, size=None, replace=True, p=None, axis=0, shuffle=True)`  Produce a sample from an array of strings***The default for values returned is discrete uniform, similar to the integer function above, each value has the same probability of being returned. The example below illustrates this ie., a single value being chosen multiple times. Unlike the integer function, however, this setting can be changed as we will see with the `replace` parameter explanation at the end of this section.<jupyter_code># Choose a colour at random 10 times and plot results.
x = rng.choice(["yellow", "red", "blue", "green"], size=10)
x<jupyter_output><empty_output><jupyter_text>Plot the choices***A visual representation of the selections made by the choice function.<jupyter_code># Histogram plot of x.
plt.title("No. of times colours selected")
sns.histplot(x, bins=8, edgecolor='white')<jupyter_output><empty_output><jupyter_text>The `replace` parameter***`replace=True` is the default setting and means that for each random selection all of the original values are included in the array. This is the default setting and can be observed in the example above. `replace=False` after a selection is made, the selected value will not be placed back into the array.<jupyter_code># Array of colours.
colours = ["yellow", "red", "blue", "green", "purple", "black", "orange", "white"]

# Replace=False. 
y = rng.choice(colours, size=[8], replace=False)
y<jupyter_output><empty_output><jupyter_text>Plot the choices***As can be seen below, each value can only be chosen once as it is removed from the array after its selection.<jupyter_code># Increase plot size. 
plt.rcParams['figure.figsize'] = [6, 6]

# Plot y on histogram. 
plt.title("No. of times colours selected")
sns.histplot(y, bins=8, edgecolor="black")
# Randomly selecting a single item from array.
rng.choice([1, 2, 3, 4, 5], size=1)<jupyter_output><empty_output><jupyter_text>The `p` parameter*** The `p` parameter allows the specification of probability of an item to be selected from the array. If the `p` parameter is not specified, the probability of selection is the same for each array item. That is, there is a uniform distribution of the data. The following code will show how the `p` parameter can be used to generate a non-uniform distribution sample.<jupyter_code># Set the p parameter.
p = rng.choice(colours[0:2], size=20, p=[0.2, 0.8])
p
# Code source: https://stackoverflow.com/questions/28663856/how-to-count-the-occurrence-of-certain-item-in-an-ndarray

# Count the occurances of each colour in array. 
unique, counts = np.unique(p, return_counts=True)

# Place in dict object.
dict(zip(unique, counts))

# Plot the results.
sns.displot(p, color="pink")<jupyter_output><empty_output><jupyter_text>Difference between integers and choice functions:***While the random.Generator.integers and random.Generator.choice both choose select values (integers from a range, and choice from a given array), the significant differences bring that choice allows:- the specification as to whether a replacement value placed in the array or not.- the specificaton of probability of an item being selected.(uniform distribution is default) [2]  `bytes()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.bytes.htmlnumpy.random.Generator.bytes)Generates random bytes. *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.bytes(length)` This function simply returns a string of random bytes with the size of the return specified by the length parameter.<jupyter_code># Generate random bytes.
x = rng.bytes(10)
x
# Check data type of x.
type(x)<jupyter_output><empty_output><jupyter_text>References:***[1] Discrete uniform distribution - Wikipedia. (2021). Retrieved 5 November 2021, from https://en.wikipedia.org/wiki/Discrete_uniform_distribution[2] How is numpy.choice different from numpy.randint? - Stack Overflow (2021). Retrieved 5 November 2021, from https://stackoverflow.com/questions/55772716/how-is-numpy-choice-different-from-numpy-randint***  3. Permutations  A permutation is the number of ways a sequence can be arranged where ordering is significant. It can be compared to combinations, on the contrary, where ordering is insignificant.  `shuffle()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.shuffle.htmlnumpy.random.Generator.shuffle)Randomly shuffles items of a given array in-place.*** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.shuffle(x, axis=0)` Shuffling is uselful in applications where it is important to avoid bias. Machine learning often uses shuffling in its operations.   Shuffle 1-dimensional array***<jupyter_code># Create a numpy array.
arr = np.arange(11)
arr
# Shuffle the array. 
rng.shuffle(arr)
arr<jupyter_output><empty_output><jupyter_text>Shuffle 2-dimensional array.***<jupyter_code># Generate 4 numpy arrays.
arr = np.arange(16).reshape((4, 4))

# Shuffle arrays. (Default axis = 0 & rearranges the rows)
rng.shuffle(arr)
arr<jupyter_output><empty_output><jupyter_text>`axis` parameter*** The axis default is 0. The following example illustrates the outcome when the axis is set to 1.<jupyter_code># Generate multiple arrays.
arr = np.arange(64).reshape(8, 8)

# Shuffle with parameter axis=1(rearranges the columns). 
rng.shuffle(arr, axis=1)
arr
# Shuffle 3-dimensional array. 
arr = np.arange(8).reshape(2, 2, 2)
rng.shuffle(arr, axis=2)
arr<jupyter_output><empty_output><jupyter_text>`permutation()` [Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.permutation.htmlnumpy.random.Generator.permutation)Randomly permutates items of a array, leaving the original array unchanged and storing the permutation in a new variable. *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.permutation(x, axis=0)`  Pass integer as parameter*** When an integer is passed through the function as x, it is treated as arange(x).<jupyter_code># Permutates range from 0(inclusive) - 60(exclusive).
rng.permutation(60)<jupyter_output><empty_output><jupyter_text>Pass array as parameter***<jupyter_code># Generate numpy array. 
arr = np.arange(60)

# Makes a copy of array and permutates it. 
rng.permutation(arr)<jupyter_output><empty_output><jupyter_text>Axis parameter*** Like the shuffle function, the permutate function can be set to permutate along a given axis. The default is 0.<jupyter_code># Generate 2-dimensional array.
arr = np.arange(60).reshape(10, 6)
arr

# Axis=1 rearranges the columns. 
rng.permutation(arr, axis=1) # Note bulk permutation, slices are shuffled together, not independently.<jupyter_output><empty_output><jupyter_text>`permuted()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.permuted.htmlnumpy.random.Generator.permuted)Randomly permutates an array along a given axis.*** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.permuted(x, axis=None, out=None)` The shuffle and permuted functions may seems as though they fulfil the same function, however, the difference between the two is that with the permuted function the slices of the array are shuffled independently of each other.<jupyter_code># Produce permuted sample from array.
rng.permuted([1, 2, 3, 4, 5])
# Generate multiple arrays.
x = np.arange(50).reshape(10,5)
x<jupyter_output><empty_output><jupyter_text>Slices are shuffled independently ***Unlike the permutation function, the slices are shuffled independently meaning that values within a slice are also shuffled.<jupyter_code># Permute array along axis 1. 
rng.permuted(x, axis=1, out=None)
rng.permuted(x, axis=None, out=None)<jupyter_output><empty_output><jupyter_text>Differences between the shuffle, permutation & permuted functions*** Regarding memory usage, the shuffle function uses in-place memory and the permutation function returns a copy of the array (out-of-place memory). With the permuted function, on the other-hand, whether the return be in-place or out-of-place can be specified with the `out` parameter.[1]Both the shuffle and permutation functions deals with slices of a given axis as if they are 1-dimensional arrays, while the purmuted function treats each slice independent of the other. I.e, Where axis=1 the columns will be shuffled. the permutation function will return the shuffled columns while keeping the same values in column whereas, the permuted function will also shuffle the items within the column.   References:[1] Random Generator — NumPy v1.21 Manual. (2021). Retrieved 15 November 2021, from https://numpy.org/doc/stable/reference/random/generator.html  4. Distributions***  Continuous Distributions Normal, uniform***  `normal()` [Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.htmlnumpy.random.Generator.normal)Generate random samples from the Gaussian normal distribution.*** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.normal(loc=0.0, scale=1.0, size=None)` The probability distribution function of the normal distribution (or Gaussian distribution) is also known as the bell curve due to its distinctive bell-like shape. Normal distribution is a phenomenon that often found in nature and is used frequently in probability problems. It is a symmetrical representation of data which focuses on the mean and standard deviation, in which most of the values lie, while the remaining values taper off gradually. [1] [Image source](https://www.google.com/search?q=normal+distribution&tbm=isch&source=iu&ictx=1&fir=PdECETJk-Nz0eM%252CNTVbhD7-dDkldM%252C_%253BtfalmXQH_1OaOM%252CIHBRA5u2SVyimM%252C_&vet=1&usg=AI4_-kQ6iJynd96JVmTgWJWK3JCHcMzbRg&sa=X&ved=2ahUKEwiIzbSHo7v0AhURYcAKHfCHDzgQ_h16BAhcEAEimgrc=tfalmXQH_1OaOM) The image above depicts the normal distribution, note the bell curve characteristic of the Gaussian ot normal distribution. The line in the middle of the curve represents the mean, while each line to the right or left of the mean represents a standard deviation. Paraphrase this! "Standard deviation is the measure of dispersion of a set of data from its mean. It measures the absolute variability of a distribution; the higher the dispersion or variability, the greater is the standard deviation and greater will be the magnitude of the deviation of the value from their mean."[https://economictimes.indiatimes.com/definition/standard-deviation]One standard deviation from the mean represents 68.27 percent of the data. 95 percent of the data is within two standard deviations from the mean. [2]  Formula*** Reference this!  Parameters*** The `loc` parameter specifies the mean of the data, the default value is 0 if no value is given.The `scale` parameter specifies the standard deviation of the normal distribution. While the `size` parameter specifies how many data points will be generated.   Generate normal samples***<jupyter_code># Generate a single normal value with default parameter values.
rng.uniform()
# Generate 5000 normal values with loc & size settings.
a = rng.normal(loc=50, size=10000)
# Check the mean.
a.mean()
# Check the standard deviation. 
a.std()<jupyter_output><empty_output><jupyter_text>The default standard deviation for the normal function is set to around 1 which represents approximately 68 percent of the data.<jupyter_code># Plot data on histogram with kernal density estimation. 
sns.histplot(a, kde=True)<jupyter_output><empty_output><jupyter_text>Specify scale***   Example: IQ Tests. [3]Assume the mean of IQ results are 100. Set the `loc` parameter to the mean IQ of 100.Assume the standard deviation is 15: Set `scale` to 15. The `size` in this case are the subjects(people), this is set to 1,000,000.<jupyter_code># Specify mean & standard deviation.
m = 100
s = 15

# Produce 1,000,000 normally distributed values.
x = rng.normal(loc=m, scale=s, size=1000000)
# Check the size of output array.
x.size
# Check standard deviation. 
x.std()
# Plot the data with kernal density estimation. 
sns.displot(x, kde=True, bins=30)
# Check highest value.
np.amax(x)
# Check lowest value. 
np.amin(x)<jupyter_output><empty_output><jupyter_text>Subplots with different scale values***<jupyter_code># Create normal arrays specifying different scale values.
a = rng.normal(loc=0, scale=3.0, size=1000)
b = rng.normal(loc=0, scale=1.0, size=1000)
# Create a pyplot figure. 
fig, ax = plt.subplots()

# Plot values with histogram. 
ax.hist(a, alpha=0.5, density=True)   # alpha parameter makes bins transparent.
ax.hist(b, alpha=0.5, density=True);  # density=true returns a probability density.<jupyter_output><empty_output><jupyter_text>As seen in the plot scale affects the spread of the data. - "a" array (BLUE) has a bigger spread. 99 percent of the data lies beyween -9 and 9 (3 standard deviations).- "b" array (ORANGE) has a smaller spread. 99 percent of the data lies between -3 and 3 (also 3 standard deviations).  `uniform()` [Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.normal.htmlnumpy.random.Generator.uniform)Generate random samples from the uniform distribution. *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.uniform(low=0.0, high=1.0, size=None)` Samples returned from this function "are uniformly distributed over the half-open interval [low, high)".[4] That is, that any value within the given range has equal probability of being selected. The low parameter value is included, however, the high value is excluded, denoted by the closed bracket before the low parameter and an open round bracket after the high parameter. That is, values very close to the upper bound can be returned but the high value will not be returned.As the values are within a range it is said to be a continuous distribution. When dealing with discrete values (i.e., arrays of values), Numpys's integers function would be suitable.Uniform distribution is used for calculating possibilities. This distributions is also known as rectangular distribution owing to its rectangular shape, see figure below.  Continuous uniform distribution function*** [5]<jupyter_code># Generate single number using default parameters.
rng.uniform() # The default low value is 0, while the high value is 1. 
# Generate 100 numbers within default range.
rng.uniform(size=100)<jupyter_output><empty_output><jupyter_text>Note how the high value is excluded.<jupyter_code># Generate sample specifying range. 
x = rng.uniform(low=10, high=100, size=10000)
x
# Plot results with kernal density estimation. 
sns.histplot(x, kde=True, bins=10)
# Highest value
np.amax(x)
# Lowest value
np.amin(x)<jupyter_output><empty_output><jupyter_text>Example: Deck of cards     ***        - Consider a deck of 52 cards.     - The following code will draw a card from the deck 100,000 times.     - As each card is unique, the probability of pulling any card from the deck is equally likely.     See the results below!<jupyter_code># Create uniform distribution.
cards = rng.uniform(1, 53, 100000)
# Plot occurances of cards. 
sns.histplot(cards, bins=52, kde=True)<jupyter_output><empty_output><jupyter_text>After 100,000 pulls from the deck, we can see that each card was selected a similar number of times. Note: the rectangler shape characteristic of a uniform distribution.   Discrete Distributions Binomial, Multinomial***  `binomial()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.binomial.htmlnumpy.random.Generator.binomial)Generate random samples from a binomial distribution. *** "The binomial distribution is a probability distribution that summarizes the likelihood that a value will take one of two independent values [typically 0 and 1] under a given set of parameters or assumptions."[6] We assume that for each trial there is just one outcome and further, that the probability of success for each trial is the same. Binomial distribution is a commonly used discrete distribution in statistics.   Formula*** [7]  ParametersThere are two non-optional parameters:- `n` must be integer or list of integers- `p` is specifies the probability of success. It must be a value between 0 and 1.   Flip a coin simulation***  The possible outcome `n` is either 0 or 1 (heads or tails) and the probability of it being either heads or tails is 50/50 as specified with `p`.<jupyter_code># Flip a coin 1,000 times. 
coin = rng.binomial(n=1, p=0.5, size=1000)
# Plot results with a count plot. 
plt.title("Flip a Coin")
sns.countplot(x = coin);<jupyter_output><empty_output><jupyter_text>As we can see, each side appeared a similar number of times.  Change probability `p`***<jupyter_code># Flip a coin 1,000 times. 
coin = rng.binomial(n=1, p=0.8, size=1000)
# Create a histogram with coin toss array.
plt.title("Flip a Coin")
sns.countplot(x = coin);<jupyter_output><empty_output><jupyter_text>The probability of one side of the coin appearing is 20/80.<jupyter_code># Check number of times each side occured. 
faces, counts = np.unique(coin, return_counts=True)
faces, counts
# View the percentages of occurances of heads vs tails.
percent = dict(zip(faces, counts * 100 / len(coin)))
percent<jupyter_output><empty_output><jupyter_text>`multinomial()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.multinomial.htmlnumpy.random.Generator.multinomial) Generate random samples from a multinomial distribution. *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.Generator.multinomial(n, pvals, size=None)` The multinomial probability distribution is a generalised version of the binomial distribution. However, the possible outcomes can be greater than two.  Simulate dice rolls***<jupyter_code># Probability of throwing the dice and landing on a given side. 
p = [1/6]

# Number of trials (throws).
n = 60
# Generate a sample from p & n values, throw 1 dice. 
count = rng.multinomial(n, pvals=p*6, size=1)
# Show result.
count<jupyter_output><empty_output><jupyter_text>The first item in the array represents the first face, the second item, the second face etc...<jupyter_code># Plot results.
plt.rcParams["figure.figsize"] = (8,6)
fig, ax = plt.subplots()

plt.title("60 Die Throws w/ 1 die")
plt.xlabel("No. of times dice landed on each face")
ax.hist(count, histtype="barstacked");

faces = ['1', '2', '3', '4', '5', '6']
plt.legend(faces, title="Faces");<jupyter_output><empty_output><jupyter_text>The `size` parameter***<jupyter_code># Generate a sample from p & n values above, throw 2 dice this time. 
count = rng.multinomial(n, pvals=p*6, size=2)
# Check the count. 
count<jupyter_output><empty_output><jupyter_text>The first row represents the first dice and 60 throws of it. While each item in the row represents how many times the die landed on that face. (The first item represents face one, the second item, face two etc...)The second row represents the second dice and 60 throws of it and how many times the second die landed on each face.<jupyter_code># Plot results.
plt.rcParams["figure.figsize"] = (8,6)
fig, ax = plt.subplots()

plt.title("60 Die Throws w/ 2 die")
plt.xlabel("No. of times dice landed on each face")
ax.hist(count, histtype="barstacked");

faces = ['1', '2', '3', '4', '5', '6']
plt.legend(faces, title="Faces")<jupyter_output><empty_output><jupyter_text>There are now TWELVE possibilites rather than six.This is because TWO dice are being rolled. That is, there are two opportunities to land on one, two opportunities to land on two, two opportunities to land on three etc...  `geometric()`[Documentation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.geometric.html) Generate random samples . *** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`random.geometric(p, size=None)` The geometric probability distribution represents the number of trials neccessary for a successful event to occur in a Bernoulli trial.[8] A Bernoulli trial is also known as a binomial trial and as mentioned above, it is a random experiment where only two outcomes are possible, namely, success or failure.  Formula:  Parameters:***- `p` represents the probability of a successful event.- `size` (k) is the number of trials that must be run in order to achieve success.  Geometric problem example:Source: [The Organic Chemistry Tutor](https://www.youtube.com/watch?v=d5iAWPnrH6w&t=369s)*** Q:  15% of all cars passing along a certain road are blue. What is the probability that the 200th car will be the first blue car that you see passing along this road?<jupyter_code># Probability of the car being blue.
p = 0.15

# Number of trials.
size = 200

# Draw 200 values from the geometric distribution.
geo = rng.geometric(p, size)
geo
# Successful trials.
(geo == 1).sum() / 200<jupyter_output><empty_output><jupyter_text>Probability of success***The figure returned above is the probability that the 200th car will be blue.  Plot the geometric probability distribution***<jupyter_code># Plot the data from geometric probability distribution. 
plot = sns.histplot(geo, kde=True, stat="probability")
plot.set(title='Probability that the 200th car is blue', ylabel = 'Probability');<jupyter_output><empty_output><jupyter_text>References:***[0] What is Standard Deviation? Definition of Standard Deviation - The Economic Times. (2021). Retrieved 21 November 2021, from https://economictimes.indiatimes.com/definition/standard-deviation[1] 9 Real Life Examples Of Normal Distribution – StudiousGuy. (2021). Retrieved 21 November 2021, from https://studiousguy.com/real-life-examples-normal-distribution/[2] <NAME>. (2021). Explaining the 68-95-99.7 rule for a Normal Distribution. Retrieved 22 November 2021, from https://towardsdatascience.com/understanding-the-68-95-99-7-rule-for-a-normal-distribution-b7b7cbf760c2[3] <NAME>., <NAME>. The Normal Distribution: An extended numeric example. Retrieved 22 November 2021, from https://mathstat.slu.edu/~may/ExcelCalculus/sec-7-6-NormalDistribution.html[4] numpy.random.Generator.uniform — NumPy v1.21 Manual. (2021). Retrieved 24 November 2021, from https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.uniform.htmlnumpy.random.Generator.uniform[5] Continuous uniform distribution - Wikipedia. (2021). Retrieved 24 November 2021, from https://en.wikipedia.org/wiki/Continuous_uniform_distribution[6] How Binomial Distribution Works. (2021). Retrieved 23 November 2021, from https://www.investopedia.com/terms/b/binomialdistribution.asp[7] Binomial distribution - Wikipedia. (2021). Retrieved 22 November 2021, from https://en.wikipedia.org/wiki/Binomial_distribution[8] What is a Geometric Distribution? - DeepAI: The front page of A.I. (2021). Retrieved 25 November 2021, from https://deepai.org/machine-learning-glossary-and-terms/geometric-distribution***  5. Seeds A random seed, also known as a seed state, is a piece of data (usually a number), used to format a pseudo-random number generator. The purpose of a seed is to yield pseudo-random data that can be reproduced. The seed value itself does not need to be random, however, it will be ignored when generating values. The values generated will be processed from a probability distribution conforming to a pseudo-random process. The seed determines the sequence of random number generation, therefore, if the same seed is entered to initialise the generator, the output of the generator will be the same each time. "Reproducibility is a very important concept that ensures that anyone who re-runs the code gets the exact same outputs."[1] Choosing a good random seed is critical in computer security. For instance, consider an encryption key that is generated using pseudo-random methods. If the seed is known one can access the key.[2]   `defaults_rng()`[Documentation](https://numpy.org/doc/stable/reference/random/generator.html)*** In the introduction section of this notebook, an instance of the Numpy's default Generator PCG64 was created using the function `numpy.random.defaults_rng()`. This function has one parameter `seed`, which can be an integer value, SeedSequence, BitGenerator, Generator or None. The `default_rng()` initialises the BitGenerator which "provides a stream of random bits based on different algorithms.[3] If no seed is specified the operating system will refer to a state (usually a number) in memory and use it as the seed each time the function is called. Time is often used to set the seed.<jupyter_code># Create a generator with bitGenerator PCG64. 
rng = np.random.default_rng(seed=42)
# Default generator type.
rng
# Create a random sample.
rng.random((5, 5))<jupyter_output><empty_output><jupyter_text>- Because the seed parameter was specified, the items generated will always be the same after re-starting the kernel.  Generate the same numbers***<jupyter_code>rng = np.random.default_rng(seed=42)
rng.random((5, 5))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10014709103996494, 'fraction_numerical': 0.02165680843739242, 'mean_word_length': 5.75703108479594, 'pattern_counts': {'":': 0, '<': 166, '<?xml version=': 0, '>': 166, 'https://': 34, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '40', 'dir': 'jupyter-structured-clean-dedup', 'id': '189781', 'license': 'MIT', 'n_tokens_mistral': 9798, 'n_tokens_neox': 9038, 'n_words': 3516, 'path': 'numpy-random.ipynb', 'repo_name': 'Izardo/Numpy-Random-Project', 'size': '31975'}