Repository: rohitsanj/doex

Issue:
BUG: reduce degree of freedom of error with the number of missing values
Reported by @nitin-kamath


Discussion:

Patch:
diff --git a/doex/latin_square.py b/doex/latin_square.py
index ee3453b..7209dc5 100644
--- a/doex/latin_square.py
+++ b/doex/latin_square.py
@@ -65,7 +65,9 @@ class LatinSquare:
         self.dof_columns = n_cols - 1
         self.dof_treatments = len(self.treatments) - 1
         self.dof_total = N - 1
-        self.dof_error = self.dof_total - (self.dof_rows + self.dof_columns + self.dof_treatments)
+        self.dof_error = self.dof_total - (
+            self.dof_rows + self.dof_columns + self.dof_treatments + num_missing
+        )
 
         # Calculate Mean Sum of Squares
         self.mss_rows = self.ss_rows / self.dof_rows
diff --git a/doex/rcbd.py b/doex/rcbd.py
index ff83010..f7ff63d 100644
--- a/doex/rcbd.py
+++ b/doex/rcbd.py
@@ -9,6 +9,11 @@ class RandomizedCompleteBlockDesign:
 
         n_treatments, n_blocks = self.data.shape
 
+        if hasattr(self, "num_missing"):
+            num_missing = self.num_missing
+        else:
+            num_missing = 0
+
         N = 0
         for entry in self.data:
             N += len(entry)
@@ -32,7 +37,7 @@ class RandomizedCompleteBlockDesign:
         self.dof_treatments = n_treatments - 1
         self.dof_blocks = n_blocks - 1
         self.dof_total = N - 1
-        self.dof_error = self.dof_total - (self.dof_treatments + self.dof_blocks)
+        self.dof_error = self.dof_total - (self.dof_treatments + self.dof_blocks + num_missing)
 
         # Calculate Mean Sum of Squares
         self.mss_treatments = self.ss_treatments / self.dof_treatments
@@ -101,11 +106,11 @@ class RandomizedCompleteBlockDesign_MissingValues(RandomizedCompleteBlockDesign)
 
         n_treatments, n_blocks = self.data.shape
 
-        num_missing = np.count_nonzero(np.isnan(self.data))
+        self.num_missing = np.count_nonzero(np.isnan(self.data))
         missing_locations = np.argwhere(np.isnan(self.data))
         self.handle_missing(self.data, missing_locations)
 
-        print("Data after adjusting for {} missing value(s)".format(num_missing))
+        print("Data after adjusting for {} missing value(s)".format(self.num_missing))
         print(self.data)
 
         # Continue with RCBD analysis


Test patch:
diff --git a/doex/tests/test_latin_square.py b/doex/tests/test_latin_square.py
index ae0d36d..4a2daa2 100644
--- a/doex/tests/test_latin_square.py
+++ b/doex/tests/test_latin_square.py
@@ -84,9 +84,9 @@ class TestLatinSquare:
         )
 
         abs_tol = 10 ** -3
-        assert math.isclose(exp.f_treatments, 15.0143, abs_tol=abs_tol)
-        assert math.isclose(exp.f_rows, 2.5857, abs_tol=abs_tol)
-        assert math.isclose(exp.f_columns, 1.3714, abs_tol=abs_tol)
+        assert math.isclose(exp.f_treatments, 12.5119, abs_tol=abs_tol)
+        assert math.isclose(exp.f_rows, 2.1548, abs_tol=abs_tol)
+        assert math.isclose(exp.f_columns, 1.1429, abs_tol=abs_tol)
 
     def test_latin_square_multiple_comparisons(self):
         exp = LatinSquare(
diff --git a/doex/tests/test_rcbd.py b/doex/tests/test_rcbd.py
index c6ceb4f..134fe49 100644
--- a/doex/tests/test_rcbd.py
+++ b/doex/tests/test_rcbd.py
@@ -48,16 +48,30 @@ class TestRCBDMissing:
             ]
         )
         abs_tol = 10 ** -3
-        assert math.isclose(exp.f_treatments, 0.8102, abs_tol=abs_tol)
-        assert math.isclose(exp.f_blocks, 2.2349, abs_tol=abs_tol)
+        assert math.isclose(exp.f_treatments, 0.7561, abs_tol=abs_tol)
+        assert math.isclose(exp.f_blocks, 2.0859, abs_tol=abs_tol)
 
     def test_rcbd_missing_2(self):
         exp = RandomizedCompleteBlockDesign_MissingValues(
             [[12, 14, 12], [10, float("nan"), 8], [float("nan"), 15, 10]]
         )
 
-        assert math.isclose(exp.f_treatments, 9.5, abs_tol=10 ** -3)
-        assert math.isclose(exp.f_blocks, 15.5, abs_tol=10 ** -3)
+        assert math.isclose(exp.f_treatments, 4.7500, abs_tol=10 ** -3)
+        assert math.isclose(exp.f_blocks, 7.7500, abs_tol=10 ** -3)
+
+    def test_rcbd_missing_3(self):
+        exp = RandomizedCompleteBlockDesign_MissingValues(
+            [
+                [90.3, 89.2, 98.2, 93.9, 87.4, 97.9],
+                [92.5, 89.5, 90.6, float("nan"), 87, 95.8],
+                [85.5, 90.8, 89.6, 86.2, 88, 93.4],
+                [82.5, 89.5, 85.6, 87.4, 78.9, 90.7],
+            ]
+        )
+
+        abs_tol = 10 ** -3
+        assert math.isclose(exp.f_treatments, 7.6241, abs_tol=abs_tol)
+        assert math.isclose(exp.f_blocks, 5.2181, abs_tol=abs_tol)
 
     def test_rcbd_missing_throw_error(self):
         with pytest.raises(Exception):