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
RunTSNEapplied 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
perplexityvalue of50results 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 tryingperplexityparameters 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
iwould 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.