I'm running into issues trying to catch errors thrown from Seurat::RunTSNE()
inside of a function. When users input a small dataset we've noticed that this function will fail with the error:
Error in .checktsneparams(nrow(X), dims = dims, perplexity = perplexity, : perplexity is too large for the number of samples
To handle this condition I would like to run an alternative function and give the user a warning.
Typically one does so using try()
or tryCatch()
, for example:
exception_handling <- function() {
tryCatch(
expr = {
message(RunTSNE(object = dataSet, dims.use = 1:10, do.fast = TRUE))
message("Successfully executed the RunTSNE call.")
},
error = function(e) {
message("Caught an error!")
print(e)
},
warning = function(w) {
message("Caught a warning!")
print(w)
},
finally = {
message("Reduce perplexity")
warning_msg <- "Lowering perplexity to the lowest recommendation: 5."
print(warning_msg)
# Works with some functions
lmfit <- lm(mpg ~ wt, mtcars)
return(lmfit)
}
)
}
The output from exception_handling()
is:
Call:
lm(formula = mpg ~ wt, data = mtcars)
Coefficients:
(Intercept) wt
37.285 -5.344
However, I run into an error when trying this with Seurat::RunTSEN()
:
exception_handling2 <- function(x) {
tryCatch(
expr = {
message(RunTSNE(object = dataSet, dims.use = 1:10, do.fast = TRUE))
message("Successfully executed the RunTSNE call.")
},
error = function(e) {
message("Caught an error!")
print(e)
},
warning = function(w) {
message("Caught a warning!")
print(w)
},
finally = {
message("Reduce perplexity")
warning_msg <- "Lowering perplexity to the lowest recommendation: 5."
print(warning_msg)
# Doesn't work for some function?
dataSet_tSNE <- RunTSNE(object = dataSet, dims.use = 1:10, do.fast = TRUE, perplexity = 5)
return(dataSet_tSNE)
}
)
}
Error in UseMethod(generic = "RunTSNE", object = object) : no applicable method for
RunTSNE
applied to an object of class "try-error"
Ultimately, I think I want to run a while-loop
that reduces the number of perplexity until the function runs successfully (but not sure if this is the optimal way to do it)....
Maybe something like this:
dataSet <- try(RunTSNE(object = dataSet, dims.use = 1:10, do.fast = TRUE))
while (class(dataSet) == "try-error") {
cat("Caught an error relating to 'perplexity', reducing the number from default = 50")
for (i in 49:5) {
dataSet <- RunTSNE(object = dataSet, dims.use = 1:10, do.fast = TRUE, perplexity = i)
}
}
Hi Martin,
I didn't realize Seurat wasn't under the Bioconductor umbrella and apologize for not including a
reprex
- I've done so now.Thank you very much for providing a very helpful answer anyways!
This suggestion to provide a 'recovery' value (or function) in case of an error is almost 100% of the way there. If a
perplexity
value of50
results in the error perplexity is too large for the number of samples a back-up function runs.Ideally I would like a vectorized solution in the
error = function(e){}
that keeps trying lower values of perplexity until "it works", something like yoursapply(49:5, f)
example - although I cannot figure out how to implement this.Only how to wrap multiple
tryCatch()
's together but this is not optimal if your recursively tryingperplexity
parameters from 50 -> 5....If I had a function that only 'worked' for some small value of
i
, e.g.,Naively I could write a loop that tried larger and then smaller values until it 'worked'
and after the loop
i
would be the first value to succeedA better approach would be to write a function that calls the function you're interested in, but returns a numerical value indicating how close one is to the 'best' result (e.g., largest value that returns a computed result)
You can see how
g()
evaluates for different values ofi
-- it's maximized at the largest value below 10.We can find this maximum efficiently using
optimize()
or other base R functions (e.g.,nlm()
,uniroot()
, perhaps re-definingg()
), usingI think though you should reflect on the overall strategy of choosing perplexity in this way.