Using mocking with apply R - r

I am currently mocking in some unit tests, using the packages testthatand mockery. I am trying to understand how the function expect_argsfrom the mockerypackage works when the mocked function is actually called in a function using apply. Here is an example where the test is successful.
myMean <- function(A){
apply(A,1,mean)
}
myMat = matrix(rep(1,6), nrow = 2, ncol = 3)
test_that("myMean calls base::mean correctly",{
m <- mock(1, cycle = TRUE)
with_mock(
`base::mean` = m,
myMean(myMat),
expect_args(m, 1, as.double(myMat[1,])))
})
Let's take now a slightly more complicated example, where the argument of myMeanis actually a data.frame and needs to be converted to a matrix within the function.
myMean <- function(A){
B = as.matrix(A)
apply(B,1,mean)
}
myMat = as.data.frame(myMat)
test_that("myMean calls base::mean correctly",{
m <- mock(1, cycle = TRUE)
with_mock(
`base::mean` = m,
myMean(myMat),
expect_args(m, 1, as.double(myMat[1,])))
})
I then get the following error message:
Error: Test failed: 'myMeanSimple calls base::mean correct number of times
* 1st actual argument not equal to 1st expected argument.
names for target but not for current
This error is explained on the vignette of the mockery package. Nevertheless I do not manage to find which argument name I should associate with as.double(myMat[1,]).

First of all, I'm happy this small utility became useful! Second of all, the error you see results from how your transformations are carried out and how expect_args compares results. Internally, we call expect_equal which requires all of the names of the matrix to be present there.
After calling your second example I run this:
> mock_args(m)
[[1]]
[[1]][[1]]
V1 V2 V3
1 1 1
[[2]]
[[2]][[1]]
V1 V2 V3
1 1 1
So you can see that in the first call a single named raw was passed, and the same is true for the second call - there are names assigned to each column. This is because as.matrix preserves column names. So this is not about argument names, this is about names in the data that's compared.
Now, when you run your final comparison using expect_args you actually use as.double which doesn't preserve names. Thus, the error you see. To fix it you can simply change your expectation to:
expect_args(m, 1, as.matrix(myMat)[1,])
I hope this solves your problem.

Related

How are these expressions named: a = 1 and (a = 1)

I came across an object with round brackets like: (a = 1)
In contrast to a = 1, with (a = 1) the output is presented in the console.
a = 1 # a=1 on console
(a = 1) # a=1 and [1] 1 on console
My question is how do you call this situation? Object in round brackets?
The assignment operators (= and <-, to name two of several) both do their job (assign to a variable) and invisibly return the value assigned. In some cases, this allows "chaining" assignment,
aa <- ab <- ac <- 5
but has some other uses. Some incorrectly assume that it returns the entire LHS variable, but it only (invisibly) returns the value passed through it.
vec <- 1:4
(vec[2] <- 99)
# [1] 99
It is sometimes used in answers (here on SO and elsewhere) as a short-hand for both assigning something and showing what that is. For instance, see the difference in presentation only between the following two commands:
dat <- data.frame(a=1, b=2)
### (nothing printed)
(dat <- data.frame(a=1, b=2))
# a b
# 1 1 2
To your question "how do you call this", I'm not sure there's a great answer for that. Because it is using invisible(.) in the return value, the console's default print methods are not called. By wrapping it in parens, you are subverting that intent, so it is doing the default console thing of printing a value.
A good way to describe it is "an assignment wrapped in parentheses".

Assign a variable in R using another variable

I have to run 10's of different permutations with same structure but different base names for the output. to avoid having to keep replacing the whole character names within each formula, I was hoping to great a variable then use paste function to assign the variable to the name of the output..
Example:
var<-"Patient1"
(paste0("cells_", var, sep="") <- WhichCells(object=test, expression = test > 0, idents=c("patient1","patient2"))
The expected output would be a variable called "cells_Patient1"
Then for subsequent runs, I would just copy and paste these 2 lines and change var <-"Patient1" to var <-"Patient2"
[please note that I am oversimplifying the above step of WhichCells as it entails ~10 steps and would rather not have to replace "Patient1" by "Patient2" using Search and Replaced
Unfortunately, I am unable to crate the variable "cells_Patient1" using the above command. I am getting the following error:
Error in variable(paste0("cells_", var, sep = "")) <-
WhichCells(object = test, : target of assignment expands to
non-language object
Browsing stackoverflow, I couldn't find a solution. My understanding of the error is that R can't assign an object to a variable that is not a constant. Is there a way to bypass this?
1) Use assign like this:
var <- "Patient1"
assign(paste0("cells_", var), 3)
cells_Patient1
## [1] 3
2) environment This also works.
e <- .GlobalEnv
e[[ paste0("cells_", var) ]] <- 3
cells_Patient1
3) list or it might be better to make these variables into a list:
cells <- list()
cells[[ var ]] <- 3
cells[[ "Patient1" ]]
## [1] 3
Then we could easily iterate over all such variables. Replace sqrt with any suitable function.
lapply(cells, sqrt)
## $Patient1
## [1] 1.732051

Assign to list index in specific environment using `get`

If I make an environment with a list in it, and want to assign values to that list, why does the following fail when using get and assign?
res <- new.env()
res$calls <- vector("list", 100)
res$counter <- 1
## works fine
res$calls[[1]] <- 1
## Fails, why?
get("calls", envir=res)[[get("counter", envir=res)]] <- 2
## doesnt make the assignment
val <- get("calls", envir=res)[[get("counter", envir=res)]]
assign("val", 2, envir=res)
I think the following will address your issue:
get("calls", envir=res)[[get("counter", envir=res)]] <- 2 fails because get is not a replacement function. On the other hand res$calls[[1]] <- 1 is actually a replacement function which you can see if you type help('[[<-'). This is the function used when you make an assignment. I think the reason why get has no replacement counterpart i.e. (get<-) is that there is a specific function to do this, which is called assign (as per #TheTime 's comment).
For the second case val <- get("calls", envir=res)[[get("counter", envir=res)]] is created in the global environment. When you use assign("val", 2, envir=res) a res$val variable is created inside the res environment which you can see below:
> res$val
[1] 2
However, val remains the same on the global environment as 1:
> val
[1] 1
So, You probably won't be able to do the assignment with either get or assign. get won't allow it because it is not a replacement function and ?assign mentions:
assign does not dispatch assignment methods, so it cannot be used to set elements of vectors, names, attributes, etc.
So, you can just use the normal [[<- assignment method. #Frank in the comments provides a nice way like:
res[[ "calls" ]][[ res[["counter"]] ]] <- 2

R: passing by parameter to function and using apply instead of nested loop and recursive indexing failed

I have two lists of lists. humanSplit and ratSplit. humanSplit has element of the form::
> humanSplit[1]
$Fetal_Brain_408_AGTCAA_L001_R1_report.txt
humanGene humanReplicate alignment RNAtype
66 DGKI Fetal_Brain_408_AGTCAA_L001_R1_report.txt 6 reg
68 ARFGEF2 Fetal_Brain_408_AGTCAA_L001_R1_report.txt 5 reg
If you type humanSplit[[1]], it gives the data without name $Fetal_Brain_408_AGTCAA_L001_R1_report.txt
RatSplit is also essentially similar to humanSplit with difference in column order. I want to apply fisher's test to every possible pairing of replicates from humanSplit and ratSplit. Now I defined the following empty vector which I will use to store the informations of my fisher's test
humanReplicate <- vector(mode = 'character', length = 0)
ratReplicate <- vector(mode = 'character', length = 0)
pvalue <- vector(mode = 'numeric', length = 0)
For fisher's test between two replicates of humanSplit and ratSplit, I define the following function. In the function I use `geneList' which is a data.frame made by reading a file and has form:
> head(geneList)
human rat
1 5S_rRNA 5S_rRNA
2 5S_rRNA 5S_rRNA
Now here is the main function, where I use a function getGenetype which I already defined in other part of the code. Also x and y are integers :
fishertest <-function(x,y) {
ratReplicateName <- names(ratSplit[x])
humanReplicateName <- names(humanSplit[y])
## merging above two based on the one-to-one gene mapping as in geneList
## defined above.
mergedHumanData <-merge(geneList,humanSplit[[y]], by.x = "human", by.y = "humanGene")
mergedRatData <- merge(geneList, ratSplit[[x]], by.x = "rat", by.y = "ratGene")
## [here i do other manipulation with using already defined function
## getGenetype that is defined outside of this function and make things
## necessary to define following contingency table]
contingencyTable <- matrix(c(HnRn,HnRy,HyRn,HyRy), nrow = 2)
fisherTest <- fisher.test(contingencyTable)
humanReplicate <- c(humanReplicate,humanReplicateName )
ratReplicate <- c(ratReplicate,ratReplicateName )
pvalue <- c(pvalue , fisherTest$p)
}
After doing all this I do the make matrix eg to use in apply. Here I am basically trying to do something similar to double for loop and then using fisher
eg <- expand.grid(i = 1:length(ratSplit),j = 1:length(humanSplit))
junk = apply(eg, 1, fishertest(eg$i,eg$j))
Now the problem is, when I try to run, it gives the following error when it tries to use function fishertest in apply
Error in humanSplit[[y]] : recursive indexing failed at level 3
Rstudio points out problem in following line:
mergedHumanData <-merge(geneList,humanSplit[[y]], by.x = "human", by.y = "humanGene")
Ultimately, I want to do the following:
result <- data.frame(humanReplicate,ratReplicate, pvalue ,alternative, Conf.int1, Conf.int2, oddratio)
I am struggling with these questions:
In defining fishertest function, how should I pass ratSplit and humanSplit and already defined function getGenetype?
And how I should use apply here?
Any help would be much appreciated.
Up front: read ?apply. Additionally, the first three hits on google when searching for "R apply tutorial" are helpful snippets: one, two, and three.
Errors in fishertest()
The error message itself has nothing to do with apply. The reason it got as far as it did is because the arguments you provided actually resolved. Try to do eg$i by itself, and you'll see that it is returning a vector: the corresponding column in the eg data.frame. You are passing this vector as an index in the i argument. The primary reason your function erred out is because double-bracket indexing ([[) only works with singles, not vectors of length greater than 1. This is a great example of where production/deployed functions would need type-checking to ensure that each argument is a numeric of length 1; often not required for quick code but would have caught this mistake. Had it not been for the [[ limit, your function may have returned incorrect results. (I've been bitten by that many times!)
BTW: your code is also incorrect in its scoped access to pvalue, et al. If you make your function return just the numbers you need and the aggregate it outside of the function, your life will simplify. (pvalue <- c(pvalue, ...) will find pvalue assigned outside the function but will not update it as you want. You are defeating one purpose of writing this into a function. When thinking about writing this function, try to answer only this question: "how do I compare a single rat record with a single human record?" Only after that works correctly and simply without having to overwrite variables in the parent environment should you try to answer the question "how do I apply this function to all pairs and aggregate it?" Try very hard to have your function not change anything outside of its own environment.
Errors in apply()
Had your function worked properly despite these errors, you would have received the following error from apply:
apply(eg, 1, fishertest(eg$i, eg$j))
## Error in match.fun(FUN) :
## 'fishertest(eg$i, eg$j)' is not a function, character or symbol
When you call apply in this sense, it it parsing the third argument and, in this example, evaluates it. Since it is simply a call to fishertest(eg$i, eg$j) which is intended to return a data.frame row (inferred from your previous question), it resolves to such, and apply then sees something akin to:
apply(eg, 1, data.frame(...))
Now that you see that apply is being handed a data.frame and not a function.
The third argument (FUN) needs to be a function itself that takes as its first argument a vector containing the elements of the row (1) or column (2) of the matrix/data.frame. As an example, consider the following contrived example:
eg <- data.frame(aa = 1:5, bb = 11:15)
apply(eg, 1, mean)
## [1] 6 7 8 9 10
# similar to your use, will not work; this error comes from mean not getting
# any arguments, your error above is because
apply(eg, 1, mean())
## Error in mean.default() : argument "x" is missing, with no default
Realize that mean is a function itself, not the return value from a function (there is more to it, but this definition works). Because we're iterating over the rows of eg (because of the 1), the first iteration takes the first row and calls mean(c(1, 11)), which returns 6. The equivalent of your code here is mean()(c(1, 11)) will fail for a couple of reasons: (1) because mean requires an argument and is not getting, and (2) regardless, it does not return a function itself (in a "functional programming" paradigm, easy in R but uncommon for most programmers).
In the example here, mean will accept a single argument which is typically a vector of numerics. In your case, your function fishertest requires two arguments (templated by my previous answer to your question), which does not work. You have two options here:
Change your fishertest function to accept a single vector as an argument and parse the index numbers from it. Bothing of the following options do this:
fishertest <- function(v) {
x <- v[1]
y <- v[2]
ratReplicateName <- names(ratSplit[x])
## ...
}
or
fishertest <- function(x, y) {
if (missing(y)) {
y <- x[2]
x <- x[1]
}
ratReplicateName <- names(ratSplit[x])
## ...
}
The second version allows you to continue using the manual form of fishertest(1, 57) while also allowing you to do apply(eg, 1, fishertest) verbatim. Very readable, IMHO. (Better error checking and reporting can be used here, I'm just providing a MWE.)
Write an anonymous function to take the vector and split it up appropriately. This anonymous function could look something like function(ii) fishertest(ii[1], ii[2]). This is typically how it is done for functions that either do not transform as easily as in #1 above, or for functions you cannot or do not want to modify. You can either assign this intermediary function to a variable (which makes it no longer anonymous, figure that) and pass that intermediary to apply, or just pass it directly to apply, ala:
.func <- function(ii) fishertest(ii[1], ii[2])
apply(eg, 1, .func)
## equivalently
apply(eg, 1, function(ii) fishertest(ii[1], ii[2]))
There are two reasons why many people opt to name the function: (1) if the function is used multiple times, better to define once and reuse; (2) it makes the apply line easier to read than if it contained a complex multi-line function definition.
As a side note, there are some gotchas with using apply and family that, if you don't understand, will be confusing. Not the least of which is that when your function returns vectors, the matrix returned from apply will need to be transposed (with t()), after which you'll still need to rbind or otherwise aggregrate.
This is one area where using ddply may provide a more readable solution. There are several tutorials showing it off. For a quick intro, read this; for a more in depth discussion on the bigger picture in which ddply plays a part, read Hadley's Split, Apply, Combine Strategy for Data Analysis paper from JSS.

unused arguments and invalid subscript type list error in R

i am struggling with an assignment and i would like your input.
note: this is a homework but when i tried to add the tag it said not to add it..
i don't want the resulting code, just suggestions on how to get this working :)
so, i have a t.test function as such:
my.t.test <- function(x,s1,s2){
x1 <- x[s1]
x2 <- x[s2]
x1 <- as.numeric(x1)
x2 <- as.numeric(x2)
t.out <- t.test(x1,x2,alternative="two.sided",var.equal=T)
out <- as.numeric(t.out$p.value)
return(out)
}
a matrix 30cols x 12k rows called data and an annotation file containing col names and data on the colums named dataAnn
dataAnn first column contains a list of M (male) or F (female) corresponding to the samples (or cols) in data (that follow the same order as in dataAnn), i have to run a t.test comparing the two samples and get the p values out
when i call
raw.pValue <- apply(data,1,my.t.test,s1=dataAnn[,1]=="M",s2=dataAnn[,1]=="F")
i get the error
Error in t.test(x1, x2, alternative = "two.sided", var.equal = T) :
unused argument(s) (alternative = "two.sided", var.equal = T)
i even tried to use
raw.pValue <- apply(data,1,my.t.test,s1=unlist(data[,1:18]),s2=unlist(data[,19:30]))
to divide the cols i want to compare but in this case i get the error
Error in x[s1] : invalid subscript type 'list'
i have been looking online, i understand that the second error is caused by an indices being a list...but this didn't really clarify it for me...
any input would be appreciated!!
You have overwritten the t.test function. Try calling it something like my.t.test, or when you want to call the original one use stats::t.test (this calls the one from the stats namespace). Remember that when you have overwritten a function you need to rm it from your workspace before you can use the original one without specifying the namespace.

Resources