Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests for nhspy pandas_spc_calculations #16

Merged
merged 7 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading