Skip to content

Commit

Permalink
Merge pull request #16 from v-Morriss/main
Browse files Browse the repository at this point in the history
Unit tests for nhspy pandas_spc_calculations
  • Loading branch information
craig-shenton authored Jul 11, 2024
2 parents a7cc1c4 + 7364077 commit bfdefe4
Show file tree
Hide file tree
Showing 9 changed files with 566 additions and 11 deletions.
6 changes: 2 additions & 4 deletions nhspy_plotthedots/pandas_spc_calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def special_cause_flag(values: List[float],
Parameters:
- values (List[float]): List of float numbers
outside_limits (List[bool]): List of boolean values representing whether
- outside_limits (List[bool]): List of boolean values representing whether
an element is outside the limits or not
- close_to_limits (List[bool]): List of boolean values representing whether
an element is close to a limit or not
Expand All @@ -210,10 +210,8 @@ def limits_calculations(fix_values: List[float]) -> Tuple[float, float, float, f
Calculates the limits for a given list of values.
Parameters:
- values (List[float]): The list of values for which the special cause
- fix_values (List[float]): The list of values for which the special cause
limits need to be calculated.
- fix_after_n_points (Optional[int]): The number of values after which
the mean and other calculations should be fixed.
Returns:
Tuple[float, float, float, float]: A tuple containing the following values:
Expand Down
80 changes: 80 additions & 0 deletions tests/unittests/test_limits_calculations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Python source
# -------------------------------------------------------------------------
# Copyright (c) 2023 NHS Python Community. All rights reserved.
# Licensed under the MIT License. See license.txt in the project root for
# license information.
# -------------------------------------------------------------------------

# FILE: test_limits_calculations.py
# DESCRIPTION: Tests for the limits_calculations() function.
# Given a list of floats (fix_values) it returns a tuple of floats:
# - mean: The mean of the input values
# - lpl: The lower process limit of the input values
# - upl: The upper process limit of the input values
# - nlpl: The near lower process limit of the input values
# - nupl: The near upper process limit of the input values

# CONTRIBUTORS: v.Morriss
# CONTACT: -
# CREATED: 9 Jul 2024
# VERSION: 0.0.1

# Imports
# -------------------------------------------------------------------------
# Python:
import unittest

# 3rd Party:
import numpy as np

# Local
from nhspy_plotthedots.pandas_spc_calculations import limits_calculations
# Define tests
# -------------------------------------------------------------------------
class LimitsCalculations(unittest.TestCase):
def test_increasing_trend(self):
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
expected = (5.5, 2.84, 8.16, 3.7266666666666666, 7.273333333333333)
self.assertEqual(limits_calculations(values), expected)

def test_decreasing_trend(self):
values = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
expected = (5.5, 2.84, 8.16, 3.7266666666666666, 7.273333333333333)
self.assertEqual(limits_calculations(values), expected)

def test_no_trend(self):
values = [1, 2, 3, 4, 5, 4, 3, 2, 1, 2]
expected = (2.7, 0.040000000000000036, 5.36, 0.9266666666666667, 4.473333333333334)
self.assertEqual(limits_calculations(values), expected)

def test_small_input(self):
values = [1, 2, 3]
expected = (2.0, -0.6600000000000001, 4.66, 0.22666666666666657, 3.7733333333333334)
self.assertEqual(limits_calculations(values), expected)

def test_large_input(self):
values = list(range(20))
expected = (9.5, 6.84, 12.16, 7.726666666666667, 11.273333333333333)
self.assertEqual(limits_calculations(values), expected)

def test_exact_seven_input(self):
values = [3, 1, 4, 1, 5, 9, 2]
expected = (3.5714285714285716, -6.625238095238096, 13.768095238095238, -3.2263492063492065, 10.36920634920635)
self.assertEqual(limits_calculations(values), expected)

def test_null_input(self):
values = []
np.isnan(limits_calculations(values)).all()

def test_mixed_input(self):
values = [1, 2, 3, 2, 1, 2, 3, 4, 5, 6, 7, 4, 3, 2, 1, 2]
expected = (3.0, -0.014666666666666828, 6.014666666666667, 0.9902222222222221, 5.009777777777778)
self.assertEqual(limits_calculations(values), expected)

def test_negative_input(self):
values = [-1, -2, -3, -2, -1, -2, -3, -4, -5, -6, -7, -4, -3, -2, -1, -2]
expected = (-3.0, -6.014666666666667, 0.014666666666666828, -5.009777777777778, -0.9902222222222221)
self.assertEqual(limits_calculations(values), expected)

if __name__ == '__main__':
unittest.main()
137 changes: 137 additions & 0 deletions tests/unittests/test_pandas_spc_x_calc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# -------------------------------------------------------------------------
# Copyright (c) 2023 NHS Python Community. All rights reserved.
# Licensed under the MIT License. See license.txt in the project root for
# license information.
# -------------------------------------------------------------------------

# FILE: test_pandas_spc_x_calc.py

# DESCRIPTION: Tests on the pandas_scp_x_calc() function. Given a pandas DataFrame,
# a string indicating the column name of the values to be analysed,
# and an optional integer representing the number of values after which
# the mean and other calculations should be fixed. It returns a pandas
# DataFrame with the same values and the Statistic Process Control (SPC) values.
# The SCP values const of:
# - mean: The mean of the input values
# - lpl: The lower process limit of the input values
# - upl: The upper process limit of the input values
# - outside_limits: A boolean list representing whether a value is
# outside the process limits
# - relative_to_mean: A list representing the relative value of an
# element to mean
# - close_to_limits: A boolean list representing whether a value is
# close to a limit or not
# - special_cause_flag: A boolean list representing whether a value
# is a special cause or not 'outside_limits' representing whether a
# value is outside the process limits
#
# CONTRIBUTORS: v.Morriss
# CONTACT: -
# CREATED: 9 Jul 2024
# VERSION: 0.0.1

# Imports
# -------------------------------------------------------------------------
# Python:
import unittest
import math

# 3rd party:
import pandas as pd

# Local
from nhspy_plotthedots.pandas_spc_calculations import pandas_spc_x_calc

# Define tests
# -------------------------------------------------------------------------

class TestPandasSpcXCal(unittest.TestCase):

def test_sample1(self):
df = pd.DataFrame(data = {"column" : [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]})
col = "column"
n_points = 3
expected = pd.DataFrame(data =
{
"column" : [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
"mean" : [2.0] * 8,
"lpl" : [-0.6600000000000001] * 8,
"upl" : [4.66] * 8,
"outside_limits" : [False] * 4 + [True] * 4,
"relative_to_mean" : [-1.0, 0.0] + [1.0] * 6,
"close_to_limits" : [False] * 3 + [True] + [False] * 4,
"special_cause_flag" : [True] * 8,
})
self.assertTrue(pandas_spc_x_calc(df,col,n_points).equals(expected))

def test_sample2(self):
df = pd.DataFrame(data = {"column" : [16.0, 9.0, 4.0, 2.0, 1.0]})
col = "column"
n_points = 11
expected = pd.DataFrame(data =
{
"column" : [16.0, 9.0, 4.0, 2.0, 1.0],
"mean" : [6.4] * 5,
"lpl" : [-3.575000000000001] * 5,
"upl" : [16.375] * 5,
"outside_limits" : [False] * 5,
"relative_to_mean" : [1.0] * 2 + [-1.0] * 3,
"close_to_limits" : [True] + [False] * 4,
"special_cause_flag" : [False] * 5,
})
self.assertTrue(pandas_spc_x_calc(df,col,n_points).equals(expected))

def test_sample3(self):
df = pd.DataFrame(data = {"column" : [1,6,1,8,0,3]})
col = "column"
n_points = None
expected = pd.DataFrame(data =
{
"column" : [1,6,1,8,0,3],
"mean" : [3.1666666666666665] * 6,
"lpl" : [-11.729333333333333] * 6,
"upl" : [18.062666666666665] * 6,
"outside_limits" : [False] * 6,
"relative_to_mean" : [-1.0, 1.0, -1.0, 1.0, -1.0, -1.0],
"close_to_limits" : [False] * 6,
"special_cause_flag" : [False] * 6,
})
self.assertTrue(pandas_spc_x_calc(df,col,n_points).equals(expected))

def test_nan(self):
df = pd.DataFrame(data = {"column" : [math.nan]})
col = "column"
n_points = None
expected = pd.DataFrame(data =
{
"column" : [math.nan],
"mean" : [math.nan],
"lpl" : [math.nan],
"upl" : [math.nan],
"outside_limits" : [False],
"relative_to_mean" : [math.nan],
"close_to_limits" : [False],
"special_cause_flag" : [False],
})
self.assertTrue(pandas_spc_x_calc(df,col,n_points).equals(expected))

def test_special_cause(self):
pd.set_option('display.max_columns', None)
df = pd.DataFrame(data = {"column" : [1,1,1,1,2]})
col = "column"
n_points = None
expected = pd.DataFrame(data =
{
"column" : [1,1,1,1,2],
"mean" : [1.2] * 5,
"lpl" : [1.2] * 5,
"upl" : [1.2] * 5,
"outside_limits" : [True] * 5,
"relative_to_mean" : [-1.0] * 4 + [1.0],
"close_to_limits" : [False] * 5,
"special_cause_flag" : [True] * 5,
})
self.assertTrue(pandas_spc_x_calc(df,col,n_points).equals(expected))

if __name__ == '__main__':
unittest.main()
22 changes: 21 additions & 1 deletion tests/unittests/test_part_of_seven_trend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# FILE: test_part_of_seven_trend.py

# DESCRIPTION: Tests on the part_of_seven_trend() function. Check if there
# DESCRIPTION: Tests on the part_of_seven_trend() function. Checks if there
# is a trend of 7 elements where at least one element has an absolute value of 1.
# It returns a A list of boolean values representing whether there is
# a trend of 7 elements where at least one element has an absolute
Expand Down Expand Up @@ -73,6 +73,26 @@ def test_negative_input(self):
expected = [True] * 5 + [False] * 3 + [True] * 7 + [False]
self.assertEqual(part_of_seven_trend(values), expected)

def test_perfect_pos(self):
values = [1, 1, 1, 1, 1, 1, 1]
expected = [True] * 7
self.assertEqual(part_of_seven_trend(values), expected)

def test_perfect_neg(self):
values = [-1, -1, -1, -1, -1, -1, -1]
expected = [True] * 7
self.assertEqual(part_of_seven_trend(values), expected)

def test_no_one(self):
values = list(range(2,12,2))
expected = [False] * 5
self.assertEqual(part_of_seven_trend(values), expected)

def test_parabola(self):
values = [0,1,6,9,10,9,6,1,0]
expected = [True] * 8 + [False]
self.assertEqual(part_of_seven_trend(values), expected)

if __name__ == "__main__":
unittest.main()

70 changes: 70 additions & 0 deletions tests/unittests/test_part_of_two_in_three.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Python source
# -------------------------------------------------------------------------
# Copyright (c) 2023 NHS Python Community. All rights reserved.
# Licensed under the MIT License. See license.txt in the project root for
# license information.
# -------------------------------------------------------------------------

# FILE: test_part_of_two_in_three.py
# DESCRIPTION: Tests for the part_of_two_in_three() function.
# part_of_two_in_three(), given two boolean lists, uses zip()
# to iterate over the two input lists and applies logical
# AND to corresponding elements.

# CONTRIBUTORS: v.Morriss
# CONTACT: -
# CREATED: 9 Jul 2024
# VERSION: 0.0.1


# Imports
# -------------------------------------------------------------------------
# Python:
import unittest

# Local
from nhspy_plotthedots.pandas_spc_calculations import part_of_two_in_three

# Define tests
# -------------------------------------------------------------------------

class PartOfTwoInThree(unittest.TestCase):

def false(self):
values1 = [False] * 10
values2 = [False] * 10
expected = [False] * 10
self.assertEqual(part_of_two_in_three(values1, values2), expected)

def true(self):
values1 = [True] * 10
values2 = [True] * 10
expected = [True] * 10
self.assertEqual(part_of_two_in_three(values1, values2), expected)

def mixed(self):
values1 = [False, True, False, True]
values2 = [False, False, True, True]
expected = [False, False, False, True]
self.assertEqual(part_of_two_in_three(values1, values2), expected)

def test_null_input(self):
values1 = []
values2 = []
expected = []
self.assertEqual(part_of_two_in_three(values1, values2), expected)

def left_uneven(self):
values1 = [True, True, True]
values2 = [True]
expected = [True]
self.assertEqual(part_of_two_in_three(values1, values2), expected)

def right_uneven(self):
values1 = [True]
values2 = [True,True,True]
expected = [True]
self.assertEqual(part_of_two_in_three(values1, values2), expected)

if __name__ == '__main__':
unittest.main()
Loading

0 comments on commit bfdefe4

Please sign in to comment.