Skip to content

Commit

Permalink
Merge pull request #56 from CraigP17/main
Browse files Browse the repository at this point in the history
Added Triangular distribution support
  • Loading branch information
hot9cups authored Nov 28, 2020
2 parents 2d6f995 + 0c9211b commit ddc9d90
Show file tree
Hide file tree
Showing 6 changed files with 391 additions and 1 deletion.
43 changes: 43 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,46 @@
# plot bar graph of probability distribution function of data
>>> bernoulli.plot_bar_pdf()
```

## For Triangular Distribution
```
>>> from probdists import Triangular
# By default Triangular will create a triangular dist. with minimum a=0, maximum b=1 and mode=0.5
# This is useful if you want a simple Triangular distribution
>>> triangle = Triangular()
# To specify your own lower (a) and upper (b) limit, and mode
# Note that a <= mode <= b
>>> triangle = Triangular(a=2, b=12, mode=7)
>>> triangle.read_data_file('demo_triangular_data')
>>> triangle.replace_stats_with_data()
# pass in your filename to read data from filename and update values
# to calculate mean:
>>> print(triangle.calculate_mean())
0.5
# to calculate standard deviation:
>>> print(triangle.calculate_stddev())
0.2
# to calculate pdf:
>>> print(triangle.calculate_pdf(0.5))
2
# to calculate cdf
>>> print(triangle.calculate_cdf(0.5))
0.5
>>> print(triangle.calculate_cdf(1))
1
# to access data
>>> print(triangle.data)
[1, 2, 3, 4, 5, 5, 6, 8, 9, 13]
# plot pdf of triangular distribution
>>> triangle.plot_bar_pdf()
```
3 changes: 2 additions & 1 deletion probdists/Generaldistribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def read_data_file(self, file_name, separator='\\n', header=None):
'demo_exponential_data': 'numbers_exponential.txt',
'demo_gamma_data': 'numbers_gamma.txt',
'demo_uniform_data': 'numbers_uniform.txt',
'demo_bernoulli_data': 'numbers_bernoulli.txt'
'demo_bernoulli_data': 'numbers_bernoulli.txt',
'demo_triangular_data': 'numbers_triangular.txt'
}
if file_name in file_name_map:
dirname = Path(__file__).parent.parent.absolute()
Expand Down
239 changes: 239 additions & 0 deletions probdists/Triangulardistribution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import math
import matplotlib.pyplot as plt
from .Generaldistribution import Distribution
from collections import Counter


class Triangular(Distribution):
"""
Triangular distribution class for calculating and visualizing the
triangular distribution: a continuous probability distribution shaped
like a triangle
Note: a <= mode <= b
Attributes:
a (float): the minimum lower limit value
b (float): the maximum upper limit value
mode (float): the mode, where min <= mode <= max
mean (float): the mean value of the distribution
stdev (float): the standard deviation of the distribution
"""

def __init__(self, a=0, b=1, mode=0.5):
if b < mode < a or a == b:
raise ValueError

if a == b or a == mode or b == mode:
raise TriangularValueException()

self.a = a
self.b = b
self.mode = mode

Distribution.__init__(self, self.calculate_mean(),
self.calculate_stdev())

def calculate_mean(self, round_to=2):
"""
Method to calculate the mean from the min, max and mode
Args:
round_to (int): Round the mean value. Defaults to 2.
Returns:
float: mean of the data set
"""

self.mean = 1/3 * (self.a + self.b + self.mode)

return round(self.mean, round_to)

def calculate_stdev(self, round_to=2):
"""
Method to calculate the standard deviation from the min, max and mode
Args:
round_to (int): Round the mean value. Defaults to 2.
Returns:
float: standard deviation of the data set
"""

summation = (self.a ** 2) + (self.b ** 2) + (self.mode ** 2) - \
(self.a * self.b) - (self.a * self.mode) - \
(self.b * self.mode)
variance = summation / 18
self.stdev = math.sqrt(variance)

return round(self.stdev, round_to)

def replace_stats_with_data(self):
""" Method to calculate a, b, mode from the data set
Args:
None
Returns:
float: a, the minimum value
float: b, the maximum value
float: mode, the mode of the dataset
"""
if not self.data:
# Use default values
min_a, max_b, mode = 0, 1, 0.5
else:
min_a = min(self.data)
max_b = max(self.data)
mode = self.calculate_mode()

if min == max or min == mode or max == mode:
raise TriangularValueException()

self.a = min_a
self.b = max_b
self.mode = mode

return self.a, self.b, self.mode

def calculate_mode(self, round_to=2):
"""
Calculates the mode of a dataset
If no single mode, it will approximate the mode using the mean
Args:
round_to (int): Round the mode value. [Default value: 2]
Returns:
float: mode of data
"""
frequency_dict = dict(Counter(self.data))
max_frequency = max(list(frequency_dict.values()))

# Create list of modes from data
mode = [k for k, v in frequency_dict.items() if v == max_frequency]

if len(mode) == 1:
return mode[0]
else:
# Multiple modes
msg = f"""Multiple modes found: {str(mode)}, Triangular Distribution requires single mode"""
raise TriangularValueException(msg)

def calculate_pdf(self, x, round_to=2):
"""
Probability density function calculator for the Triangular distribution.
Args:
x (float): point for calculating the probability density function
round_to (int): Round the pdf value. [Default value: 2]
Returns:
float: probability density function
"""
# Check equivalence
if self.a == self.b or self.a == self.mode or self.b == self.mode:
raise TriangularValueException()

value = 0 # default value for when x < min or x > max
if self.a <= x < self.mode:
value = (2 * (x - self.a)) / (
(self.b - self.a) * (self.mode - self.a))
elif self.mode == x:
value = 2 / (self.b - self.a)
elif self.mode < x <= self.b:
value = (2 * (self.b - x)) / (
(self.b - self.a) * (self.b - self.mode))

self.pdf = value
return round(self.pdf, round_to)

def calculate_cdf(self, x, round_to=2):
"""
Cumulative density function calculator for the Triangular distribution.
Args:
x (float): point for calculating the cumulative density function
round_to (int): Round the value. [Default value: 2]
Returns:
float: cumulative density function output
"""
# Check equivalence
if self.a == self.b or self.a == self.mode or self.b == self.mode:
raise TriangularValueException()

if x < self.a:
value = 0
elif self.a <= x <= self.mode:
num = ((x - self.a) ** 2)
den = (self.b - self.a) * (self.mode - self.a)
value = num / den
elif self.mode < x <= self.b:
num = ((self.b - x) ** 2)
den = (self.b - self.a) * (self.b - self.mode)
value = 1 - (num / den)
else:
value = 1

self.cdf = value
return round(self.cdf, round_to)

def plot_bar_pdf(self):
"""
Method to plot the pdf of the triangular distribution.
Args:
self
Returns:
None
"""
x = [self.a, self.mode, self.b]

peak = 2 / (self.b - self.a)
y = [0, peak, 0]

plt.plot(x, y)
plt.title('Probability Density Plot for Triangular Distribution')
plt.xlabel('Probability')
plt.ylabel('x')

plt.show()

return x, y

def __repr__(self):
"""
Outputs the characteristics of the Triangular Distribution instance.
Args:
self
Returns:
string: characteristics of the Triangle
"""

return f"minimum: {self.a}, maximum: {self.b}, mode: {self.mode}, " \
f"mean: {self.mean}, standard deviation: {self.stdev}"


class TriangularValueException(Exception):
"""
Defines Exception raised when minimum, maximum or mode values are equal
and TriangularDistribution instance cannot be created
Attributes:
message (str): Error message to return
"""

def __init__(self, msg=None):
if msg is not None:
self.message = msg
else:
self.message = "Minimum, Maximum, or Mode cannot be equivalent"

def __str__(self):
if self.message:
return f"""TriangularValueException: {self.message}"""
return f"""TriangularValueException Raised"""
1 change: 1 addition & 0 deletions probdists/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from .Gammadistribution import Gamma
from .Bernoullidistribution import Bernoulli
from .Uniformdistribution import Uniform
from .Triangulardistribution import Triangular, TriangularValueException
36 changes: 36 additions & 0 deletions probdists/numbers_triangular.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
2
3
3
4
4
4
5
5
5
5
6
6
6
6
6
7
7
7
7
7
7
8
8
8
8
8
9
9
9
9
10
10
10
11
11
12
Loading

0 comments on commit ddc9d90

Please sign in to comment.