From f0a6978ede7b4c44c90e8e45d44b3cab7f8a5702 Mon Sep 17 00:00:00 2001
From: "Rossum, Bart-Jan van"
Date: Mon, 31 May 2021 12:47:19 +0200
Subject: [PATCH] Added tests for detectSerieOut (#79)
---
R/detectSerieOut.R | 16 +-
inst/tinytest/test_detectSerieOut.R | 237 +++++++++++++++++++++++++++
inst/tinytest/test_detectSingleOut.R | 2 +-
serieOut | Bin 0 -> 11999 bytes
4 files changed, 246 insertions(+), 9 deletions(-)
create mode 100644 inst/tinytest/test_detectSerieOut.R
create mode 100644 serieOut
diff --git a/R/detectSerieOut.R b/R/detectSerieOut.R
index e69efc8..2f50f68 100644
--- a/R/detectSerieOut.R
+++ b/R/detectSerieOut.R
@@ -115,8 +115,8 @@ detectSerieOut <- function(corrDat,
if (!all(genotypes %in% corrDat[["genotype"]])) {
stop("all genotypes should be in corrDat")
}
- if (!is.numeric(thrCor) || any(thrCor < 0) || any(thrCor > 1)) {
- stop("thrCor should be a numerical vector with values between 0 and 1.\n")
+ if (!is.numeric(thrCor) || any(thrCor < -1) || any(thrCor > 1)) {
+ stop("thrCor should be a numerical vector with values between -1 and 1.\n")
}
if (!is.numeric(thrPca) || any(thrPca < 0)) {
stop("thrPca should be a numerical vector with positive values.\n")
@@ -211,13 +211,13 @@ detectSerieOut <- function(corrDat,
formula = type ~ plotId,
value.var = "obj.coefficients")
## Remove intercept.
- plantDat <- plantDat[-1, ]
+ plantDat <- plantDat[-1, , drop = FALSE]
## Remove plants with only NA.
NAplants <- apply(X = plantDat, MARGIN = 2,
FUN = function(plant) {
all(is.na(plant))
})
- plantDat <- plantDat[, !NAplants]
+ plantDat <- plantDat[, !NAplants, drop = FALSE]
## Add geno.decomp as attribute for later use.
if (!is.null(geno.decomp)) {
attr(plantDat, which = "genoDecomp") <-
@@ -271,7 +271,7 @@ detectSerieOut <- function(corrDat,
combn(x = colnames(plantDat), m = 2, FUN = function(plants) {
## Fit linear model and extract slope.
modForm <- formula(paste(plants, collapse = "~"))
- slope <- coef(lm(modForm, data = plantDat))[2]
+ slope <- abs(coef(lm(modForm, data = plantDat))[2])
if (slope > 1) slope <- 1 / slope
return(slope)
}, simplify = TRUE)
@@ -702,7 +702,7 @@ removeSerieOut <- function(dat = NULL,
}
if (!is.null(fitSpline)) {
if (!inherits(fitSpline, "HTPSpline")) {
- stop("dat should be an object of class HTPSpline.\n")
+ stop("fitSpline should be an object of class HTPSpline.\n")
}
}
if (!inherits(serieOut, "data.frame")) {
@@ -720,8 +720,8 @@ removeSerieOut <- function(dat = NULL,
} else if (!is.null(fitSpline)) {
fitSpline$coefDat[fitSpline$coefDat[["plotId"]] %in%
serieOut[["plotId"]], "obj.coefficients"] <- NA
- fitSpline$predDat[fitSpline$predDat[["plotId"]] %in%
- serieOut[["plotId"]], c("pred.value", "deriv")] <- NA
+ fitSpline$predDat[fitSpline$predDat[["plotId"]] %in% serieOut[["plotId"]],
+ c("pred.value", "deriv", "deriv2")] <- NA
modDat <- attr(x = fitSpline, which = "modDat")
for (trait in traits) {
modDat[modDat[["plotId"]] %in% serieOut[["plotId"]], trait] <- NA
diff --git a/inst/tinytest/test_detectSerieOut.R b/inst/tinytest/test_detectSerieOut.R
new file mode 100644
index 0000000..9073475
--- /dev/null
+++ b/inst/tinytest/test_detectSerieOut.R
@@ -0,0 +1,237 @@
+### Test detectSingleOut.
+
+### Test fitSpline
+
+Sys.setlocale("LC_COLLATE", "C")
+
+## Read test data from .csv
+testDat <- read.csv("testDat.csv", stringsAsFactors = FALSE)
+# Set Replicate to 1 for all observations of check1 so geno.decomp can
+# actually be used for testing.
+testDat[testDat[["Genotype"]] == "check1", "Replicate"] <- 1
+
+## Create TP object.
+testTP <- createTimePoints(dat = testDat, experimentName = "testExp",
+ genotype = "Genotype", timePoint = "timepoints",
+ plotId = "pos", rowNum = "y", colNum = "x")
+
+## Fit models.
+testFitMod <- fitModels(testTP, trait = "t1", quiet = TRUE)
+testFitModGD <- fitModels(testTP, trait = "t1", geno.decomp = "Replicate",
+ quiet = TRUE)
+
+## Get corrected values.
+corr <- getCorrected(testFitMod)
+corrGD <- getCorrected(testFitModGD)
+
+## Fit a splines on the corrected values.
+expect_warning(splineRes <- fitSpline(inDat = corr, trait = "t1_corr"))
+expect_warning(splineResGD <- fitSpline(inDat = corrGD, trait = "t1_corr"))
+
+## get coefDat and predDat.
+coefDat <- splineRes[["coefDat"]]
+predDat <- splineRes[["predDat"]]
+
+coefDatGD <- splineResGD[["coefDat"]]
+predDatGD <- splineResGD[["predDat"]]
+
+## Check that general checks in detectSingleOut function correctly.
+expect_error(detectSerieOut(trait = 1),
+ "trait should be a character string of length 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = 1),
+ "corrDat should be a data.frame")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr, predDat = 1),
+ "predDat should be a data.frame")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = 1),
+ "coefDat should be a data.frame")
+expect_error(detectSerieOut(trait = "t2_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat),
+ "corrDat should at least contain the following columns")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = coefDat, coefDat = coefDat),
+ "predDat should at least contain the following columns")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = predDat),
+ "coefDat should at least contain the following columns")
+
+## Check that checks for genotypes function correctly
+expect_warning(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat),
+ "have less than 3 plotIds and are skipped in the outlier")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = 1),
+ "genotypes should be a character vector")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "a"),
+ "all genotypes should be in predDat")
+expect_error(detectSerieOut(trait = "t1_corr",
+ corrDat = corr[corr[["genotype"]] != "G12", ],
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "G12"),
+ "all genotypes should be in corrDat")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat,
+ coefDat = coefDat[coefDat[["genotype"]] != "G12", ],
+ genotypes = "G12"),
+ "all genotypes should be in coefDat")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "G12"),
+ "No genotypes left for performing outlier detection")
+expect_silent(serieOut1 <- detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1"))
+
+## Check that general structure of the output is correct.
+expect_inherits(serieOut1, c("serieOut", "data.frame"))
+expect_equal(dim(serieOut1), c(7, 4))
+expect_equal(colnames(serieOut1),
+ c("plotId", "genotype", "reason", "value"))
+
+## Check that full output content is correct.
+expect_equal_to_reference(serieOut1, file = "serieOut")
+
+## Check that parameter thrCor functions correctly.
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrCor = "a"),
+ "thrCor should be a numerical vector with values between -1 and 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrCor = 2),
+ "thrCor should be a numerical vector with values between -1 and 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrCor = 0:1),
+ "thrCor should be a vector of length 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corrGD,
+ predDat = predDatGD, coefDat = coefDatGD,
+ genotypes = "check1", geno.decomp = "geno.decomp",
+ thrCor = 0:1),
+ "thrCor should be a named vector, with names matching the levels")
+
+## Check that parameter thrPca functions correctly.
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrPca = "a"),
+ "thrPca should be a numerical vector with positive values")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrPca = -1),
+ "thrPca should be a numerical vector with positive values")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrPca = 0:1),
+ "thrPca should be a vector of length 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corrGD,
+ predDat = predDatGD, coefDat = coefDatGD,
+ genotypes = "check1", geno.decomp = "geno.decomp",
+ thrPca = 0:1),
+ "thrPca should be a named vector, with names matching the levels")
+
+
+## Check that parameter thrPca functions correctly.
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrSlope = "a"),
+ "thrSlope should be a numerical vector with values between 0 and 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrSlope = -1),
+ "thrSlope should be a numerical vector with values between 0 and 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrSlope = 0:1),
+ "thrSlope should be a vector of length 1")
+expect_error(detectSerieOut(trait = "t1_corr", corrDat = corrGD,
+ predDat = predDatGD, coefDat = coefDatGD,
+ genotypes = "check1", geno.decomp = "geno.decomp",
+ thrSlope = 0:1),
+ "thrSlope should be a named vector, with names matching the levels")
+
+# Set all three to extreme values should result in either 0 or many outliers.
+serieOut2 <- detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrCor = 0, thrPca = 0,
+ thrSlope = 1)
+expect_equal(dim(serieOut2), c(9, 4))
+
+serieOut3 <- detectSerieOut(trait = "t1_corr", corrDat = corr,
+ predDat = predDat, coefDat = coefDat,
+ genotypes = "check1", thrCor = -1, thrPca = 180,
+ thrSlope = 0)
+expect_equivalent(serieOut3, data.frame())
+
+## Check that detecting outliers functions correctly for models with geno.deco
+expect_silent(serieOutGD <-
+ detectSerieOut(trait = "t1_corr", corrDat = corrGD,
+ predDat = predDatGD, coefDat = coefDatGD,
+ genotypes = "check1", geno.decomp = "geno.decomp"))
+expect_equal(dim(serieOutGD), c(6, 5))
+
+### Check plotting of detectSerieOut results.
+
+## Check that general checks in plot function correctly.
+expect_error(plot(serieOut1, genotypes = "a"),
+ "a character vector of genotypes used for outlier detection")
+
+## Check that general output structure is correct.
+expect_silent(p <- plot(serieOut1))
+expect_inherits(p, "list")
+expect_equal(length(p), 1)
+expect_inherits(p[[1]], "gtable")
+
+## Check that option useTimeNumber functions correctly.
+expect_error(plot(serieOut1, useTimeNumber = TRUE, timeNumber = 1),
+ "timeNumber should be a character string of length 1")
+expect_silent(plot(serieOut1, useTimeNumber = TRUE, timeNumber = "timeNumber"))
+
+## Check that option title functions correctly.
+expect_silent(plot(serieOut1, genotypes = "check1", title = "bla"))
+
+## Check that plotting functions correctly with geno.decomp.
+expect_error(plot(serieOutGD, genotypes = "check1", geno.decomp = "2"),
+ "All selected geno.decomp levels should be in the data")
+expect_silent(plot(serieOutGD, genotypes = "check1", geno.decomp = "1"))
+
+### Check removal of outliers detected by detectSerieOut
+
+## Check that general checks in plot function correctly.
+expect_error(removeSerieOut(dat = 1),
+ "dat should be a data.frame")
+expect_error(removeSerieOut(fitSpline = 1),
+ "fitSpline should be an object of class HTPSpline")
+expect_error(removeSerieOut(dat = corr, fitSpline = splineRes),
+ "Specify exactly one of dat and fitSpline as inputs")
+expect_error(removeSerieOut(dat = corr[, colnames(corr) != "plotId"]),
+ "dat should at least contain the column plotId")
+expect_error(removeSerieOut(dat = corr, serieOut = 1),
+ "serieOut should be a data.frame")
+expect_error(removeSerieOut(dat = corr,
+ serieOut = serieOut1[, colnames(serieOut1) != "plotId"]),
+ "serieOut should at least contain the column plotId")
+
+## Check that outliers are removed from data.frame.
+corrOut1 <- removeSerieOut(dat = corr, serieOut = serieOut1)
+expect_true(all(is.na(corrOut1[corrOut1[["plotId"]] == "c12r1", "t1_corr"])))
+
+corrOut2 <- removeSerieOut(dat = corr, serieOut = serieOut1,
+ traits = c("t1", "t1_corr"))
+expect_true(all(is.na(corrOut2[corrOut2[["plotId"]] == "c12r1",
+ c("t1", "t1_corr")])))
+
+## Check that outliers are removed from fitted spline.
+corrOut3 <- removeSerieOut(fitSpline = splineRes, serieOut = serieOut1)
+coefOut3 <- corrOut3$coefDat
+predOut3 <- corrOut3$predDat
+modOut3 <- attr(corrOut3, "modDat")
+
+expect_true(all(is.na(coefOut3[coefOut3[["plotId"]] == "c12r1",
+ "obj.coefficients"])))
+expect_true(all(is.na(predOut3[predOut3[["plotId"]] == "c12r1",
+ c("pred.value", "deriv", "deriv2")])))
+expect_true(all(is.na(modOut3[modOut3[["plotId"]] == "c12r1", "t1_corr"])))
+
diff --git a/inst/tinytest/test_detectSingleOut.R b/inst/tinytest/test_detectSingleOut.R
index f61e4d9..94aa511 100644
--- a/inst/tinytest/test_detectSingleOut.R
+++ b/inst/tinytest/test_detectSingleOut.R
@@ -47,7 +47,7 @@ expect_equal(colnames(singleOut),
c("plotId", "timePoint", "EffpsII", "yPred", "sd_yPred",
"lwr", "upr", "outlier"))
-## Check that full output contenct is correct.
+## Check that full output content is correct.
expect_equal_to_reference(singleOut, file = "singleOut")
## Check that parameter confIntSize functions correctly.
diff --git a/serieOut b/serieOut
new file mode 100644
index 0000000000000000000000000000000000000000..e3f2264d7fc3c1a80365bb7e1d1d08f8b26bcecc
GIT binary patch
literal 11999
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