I have a function like this
Squ <- function(x, expected.result){
result <- x*x
if(result != expected.result){
stop("We have some error /n/n")
return(NULL)
}
return(result)
}
I am running below three lines.
Squ(2, 4)
Squ(3, 7)
Squ(4, 16)
Ideally i expected this script will stop in second line i.e.Squ(3, 7) as if condition is true in the function but it didn't stop & runs the script completely. How to write the script to get stop if conditions met true.
Thanks,
Mani
Normally, running R code interactively means you'll keep running new lines of code even when there is an error. If you want the code to stop as soon as there's any kind of error, you should wrap the calls to Squ in a function. You'll have to edit the function if you want to save the values of each test, but it looks like you already know what each value should be and you're trying to find the first error. Using a function will give you the first error message and then halt execution.
test_Squ <- function() {
Squ(2, 4)
Squ(3, 7)
Squ(4, 16)
print("All code ran without throwing an error")
}
test_Squ()
# Error in Squ(3, 7) : We have some error /n/n
Let me know if you have a more specific use case in mind and I'll edit my answer.
Related
I would like to know if there is an R function that allows to prevent some lines of the code to be executed.
For instance, if this function exist and is called prevent, I could put on my code at line number 10 :
prevent(line12, line17)
and those line wouldn't be executed.
The application for me would pe to prevent some line which are in a loop to be executed, if a condition that I would test before going in the loop is not satisfyed.
It's a matter of optimization : I could test the condition inside the loop but I would rather test it before, since I already know wheter the condition is true or not.
Here is an example :
Condition=T
if (Condition){
Dont execute line 17
}
for (i in 1:n){
line 17 Print("I'm happy")
}
You could simply use an if statement:
condition <- T
for (i in 1:3) {
if (condition) {
cat("Execute this \n")
}
}
you could just add an "#" before the code line you don't want to execute
# 2+2
2+2
You can do it for multiple lines with cmd+shift+c in studio in mac, there should be an Linux or windows equivalent
On this code when I use for loop or the function lapply I get the following error
"Error in get_entrypoint (debug_port):
Cannot connect R to Chrome. Please retry. "
library(rvest)
library(xml2) #pull html data
library(selectr) #for xpath element
url_stackoverflow_rmarkdown <-
'https://stackoverflow.com/questions/tagged/r-markdown?tab=votes&pagesize=50'
web_page <- read_html(url_stackoverflow_rmarkdown)
questions_per_page <- html_text(html_nodes(web_page, ".page-numbers.current"))[1]
link_questions <- html_attr(html_nodes(web_page, ".question-hyperlink")[1:questions_per_page],
"href")
setwd("~/WebScraping_chrome_print_to_pdf")
for (i in 1:length(link_questions)) {
question_to_pdf <- paste0("https://stackoverflow.com",
link_questions[i])
pagedown::chrome_print(question_to_pdf)
}
Is it possible to build a for loop() or use lapply to repeat the code from where it break? That is, from the last i value without breaking the code?
Many thanks
I edited #Rui Barradas idea of tryCatch().
You can try to do something like below.
The IsValues will get either the link value or bad is.
IsValues <- list()
for (i in 1:length(link_questions)) {
question_to_pdf <- paste0("https://stackoverflow.com",
link_questions[i])
IsValues[[i]] <- tryCatch(
{
message(paste("Converting", i))
pagedown::chrome_print(question_to_pdf)
},
error=function(cond) {
message(paste("Cannot convert", i))
# Choose a return value in case of error
return(i)
})
}
Than, you can rbind your values and extract the bad is:
do.call(rbind, IsValues)[!grepl("\\.pdf$", do.call(rbind, IsValues))]
[1] "3" "5" "19" "31"
You can read more about tryCatch() in this answer.
Based on your example, it looks like you have two errors to contend with. The first error is the one you mention in your question. It is also the most frequent error:
Error in get_entrypoint (debug_port): Cannot connect R to Chrome. Please retry.
The second error arises when there are links in the HTML that return 404:
Failed to generate output. Reason: Failed to open https://lh3.googleusercontent.com/-bwcos_zylKg/AAAAAAAAAAI/AAAAAAAAAAA/AAnnY7o18NuEdWnDEck_qPpn-lu21VTdfw/mo/photo.jpg?sz=32 (HTTP status code: 404)
The key phrase in the first error is "Please retry". As far as I can tell, chrome_print sometimes has issues connecting to Chrome. It seems to be fairly random, i.e. failed connections in one run will be fine in the next, and vice versa. The easiest way to get around this issue is to just keep trying until it connects.
I can't come up with any fix for the second error. However, it doesn't seem to come up very often, so it might make sense to just record it and skip to the next URL.
Using the following code I'm able to print 48 of 50 pages. The only two I can't get to work have the 404 issue I describe above. Note that I use purrr::safely to catch errors. Base R's tryCatch will also work fine, but I find safely to be a little more convient. That said, in the end it's really just a matter of preference.
Also note that I've dealt with the connection error by utilizing repeat within the for loop. R will keep trying to connect to Chrome and print until it is either successful, or some other error pops up. I didn't need it, but you might want to include a counter to set an upper threshold for the number of connection attempts:
quest_urls <- paste0("https://stackoverflow.com", link_questions)
errors <- NULL
safe_print <- purrr::safely(pagedown::chrome_print)
for (qurl in quest_urls){
repeat {
output <- safe_print(qurl)
if (is.null(output$error)) break
else if (grepl("retry", output$error$message)) next
else {errors <- c(errors, `names<-`(output$error$message, qurl)); break}
}
}
I apologize that I can not tell you what these functions are form the start.
I have a function CheckOutCell. It takes one argument and that is the number 764. So every time I run the function it looks like this in it's entirety: CheckOutCell(764).
Now many times the function will give me an error:
Error in checkInCell(764) :
The function is currently locked; try again in a minute.
Which is a custom error message and the details are not important to this question.
Now this function could be locked from anywhere from 30 seconds to an hour. I want to be able to automatically run CheckOutCell(764) till it goes through, and then stop running it. That is, run it till I do not get an error, then stop.
I think a start would be using
while(capture.output(checkInCell(764)) == "Error in checkInCell(764) :
The function is currently locked; try again in a minute."){
do something}
However this just produces
Error in checkInCell(764) :
The function is currently locked; try again in a minute.
because the function is still locked, so no output can be captured.
How would I test for while(error = T)
Assume the source code of the function cannot be modified.
Even is.error(CheckInCell(764)) will just produce the same error message
So it seems that this code works in a way
wrapcheck <- function(x){
repeatCheck =tryCatch(checkOutCell(764),
error = function(cond)"skip")
SudoCheck = ifelse(repeatCheck=="skip",repeatCheck, checkOutCell(764))
while(SudoCheck == "skip"){
repeatCheck
}
}
wrapcheck(764)
Basically this checks for an error and then keeps running the function till the error is not produced. In fact I am fairly confident that this would work with any funciton you wanted to put in place of CheckOutCell.
The main problem is that when the function is locked, that it not really an error, it is locked. Therefore this above block will not work. This above block will work when errors other than a lock are produced.
I have a lot of different operations running on quite a big dataframe. It starts to be a pain for maintenance, especially with some data being improperly formatted, and I'm looking at some options to make my life easier.
The problem is that at one point in the flow of operations NAs are introduced in several lines, including the id (certainly due to some bad subsetting). Now I cannot find the culprit easily because I have each time to str() it, or to view() it in Rstudio... This takes time and I already did it once without finding the bad operation...
So I'm curious if there is some package answering to this problem or a way to program something "daemon-like", to pop up a warning message when a specific value appears.
A while loop doesn't help, because it evaluates all the statements, and of course at one point the condition is not true and it doesn't print when it stops ...
while(nrow(df[is.na(df$id),]) > 0){
statements OK
breaking statement
other OK statements
}
I'll look for other options but I wanted to ask before...
EDIT : thanks for the useful comments, I'll definitely will look more into those functions. However I tried also to build myself a watch function (see my answer).
Ok, I guess I have finally built something quite like it :
This is a function to source a file line per line until a given condition is met :
watchIt <- function(file,watchexpression,startwatchline){
line <- 1
sourceList <- scan(file = "source_test.R", what="character", sep="\n", blank.lines.skip = FALSE)
maxLines <- length(sourceList)
while(startwatchline > line && maxLines >= line){
cat("l")
eval(parse(text=sourceList[line]))
line <- line+1
cat(line)
cat(" ")
}
while(eval(parse(text=watchexpression)) == FALSE && maxLines >= line){
cat(" L")
eval(parse(text=sourceList[line]))
line <- line+1
cat(line)
cat(" ")
}
if(maxLines <= line) {
cat("End of file reached without condition getting TRUE")
}
else{
cat("Condition evaluated to TRUE on line :")
cat(line)
cat("\n")
cat(sourceList[line])
}
}
So this is how I use it :
watchIt("source_test.R","nrow(df[is.na(df$id),]) > 0",10)
This puts "source_test.R" in a list, each line a new list item, and, starting from line 10, I test if the resultant dataframe as NAs in the id field. The execution stops either when the condition evaluates TRUE or when the end of the list items is reached.
Still I'm waiting for some other/better answers... Also, this is kind of my fourth function I managed to create in R, so I guess there might be ameliorations to be made to it...
I'm trying to make my code ask me for a "TRUE" or "FALSE" value before proceeding.
It currently works fine if I run it one line at a time, however when I run all the code at once in RStudio it simply proceeds without waiting for user input and records a value of "" for my parameter.
raw <- readline("TRUE or FALSE -- this is a validation run: ")
if (raw == "F" | raw == "FALSE" | raw == "False"){
validation <- F
} else{
validation <- T
}
rm(raw)
Ideally, I would like an answer that works regardless of how I run it -- RScript, source within RStudio, or running it (i.e. selecting the code and pressing run or ctrl-enter).
If you want to do this in interactive mode then you already have answers but not for use with Rscript. For that instance you need to send messages to the console with cat:
If this test file is named 'prompt.r' and is in the directory where you are working in a system console session:
cat("a string please: ");
a <- readLines("stdin",n=1);
cat("You entered")
str(a);
cat( "\n" )
Then you can run it from the command line as
$ Rscript prompt.r
If you want an all-purpose script then this would run your script under interactive conditions and my script for non-interactive ones:
if (interactive() ){raw <-
readline("TRUE or FALSE -- this is a validation run: ")
if (raw == "F" | raw == "FALSE" | raw == "False"){
validation <- F
} else{
validation <- T
}
rm(raw) } else{
# non-interactive
cat("a string please: ");
a <- readLines("stdin",n=1);
cat("You entered")
str(a);
cat( "\n" )}
Are you running the code by highlighting the lines and clicking run? If so, that could be your problem because R is line-by-line entering your code in the terminal.
Instead, write your script (or comment out the parts you're not testing) and click the source button. Then R waits for user response instead of inputting the line after readline() into readline().
I had the same problem as you, which prompted me to look for an answer. But then I tried this different method of executing the code and it worked.
The reason why readline is "not waiting for input" is, according to the manual (?readline):
In non-interactive use the result is as if the response was RETURN and
the value is "".
You have stumbled on one of the "surprising" features of R.
<rant>
Why readline() works in "interactive mode" only is a complete mystery for me, since it's a perfectly acceptable use case to ask for user input from within a script. Python, for instance, gives you the input(prompt) function which you may invoke whenever you need it.
</rant>
A relatively convenient way of getting around this mess is to define a function ("Every programming problem can be solved by yet another level of indirection"...):
user.input <- function(prompt) {
if (interactive()) {
return(readline(prompt))
} else {
cat(prompt)
return(readLines("stdin", n=1))
}
}
A positive side-effect is that you can add all sorts of nice input validation in user.input(). But I doubt that that was the intention behind the bizarre behaviour of readline().
Although it requires interactive mode, I found the askYesNo() function within the utils package to provide a nice, simple user interface for TRUE/FALSE (or more accurately Yes/No) questions. For example, the following code would produce the following dialog.
Yes returns TRUE
No returns FALSE
Cancel returns NA
Using svDialogs may let you do this.
It's not exactly using readline, but does achieve a similar outcome to what you're looking for.
The code won't run until the dialog box has been filled:
library(svDialogs)
raw <- dlgInput("TRUE or FALSE -- this is a validation run: ",Sys.info()["raw"])$res
if (raw == "F" | raw == "FALSE" | raw == "False"){
validation <- F
} else{
validation <- T
}
rm(raw)
You have to create a function set an "if" conditional sentence with "is.na()" then execute "readline()" again if the value entered is null.
see below:
readinteger <- function()
{
n <- readline(prompt="Enter an integer: ")
n <- as.integer(n)
if (is.na(n)){
n <- readinteger()
}
return(n)
}
print(readinteger())
rm(list=ls())