I am creating a function in R for calculating compound interest using the equation - F=P\left(1+\frac{i}{100t}\right)^{ty}.
I need to create error messages if any of P, i, t, or y arguments are not numeric or negative, and an error if i is out of the range 0-100.
When I try to run the code i get the error message:
Error: unexpected ',' in:
" stop("Argument 'P','i','t' and 'y' must be numeric")
if(P,"
and am unsure how to fix it or what I have done wrong. The code so far is:
compound <- function(P,i,t=12,y,plotit=FALSE,...){
# 1. Check arguments and throw errors here using 'if' statements
if(!is.numeric(P,i,t,y)){
stop("Argument 'P','i','t' and 'y' must be numeric")
if(P,i,t,y<0){
stop("Argument 'P','i','t' and 'y' must be non-negative")
if(i<0||i>100){
stop("'i' must be within the range 0-100 percent")
}
}
}
if(plotit==TRUE){
# 2. Do plot if necessary here according to 'plotit' argument
y <- floor(y)
f <- P*((1+(i/100*t))^t*y)
for(i in length(y)){
plot(f, type = 's')
}
}
else {
final.result <- f
# Calculate final result here
return(final.result)
}
}
Any help on how to improve the code would be great!
The problem is that you are passing four arguments to is.numeric(P,i,t,y). It can take only one.
if(!all(sapply(list(P,i,t,y), is.numeric)))
That will check if all arguments are numeric.
A similar problem occurs with if(P,i,t,y<0), so use:
if(!all(sapply(list(P,i,t,y), function(x) x >= 0)))
Additionally, you have nested these if statements, which means the second and the third will not be evaluated. Move them out of your first if statement.
Related
I'm writing a code to solve a sudoku puzzle using a video found from YouTube that has coded the same algorithm through Python. This code requires three functions to
Find an empty square.
insert a number into the empty square.
Test whether this number is valid to solve the puzzle.
This is using a backtracking algorithm for the solver.
I am having an issue when calling the functions together where i get the error:
Error in free_squ(x) : argument "x" is missing, with no default
In addition: Warning message:
In if (empty_sq == FALSE) { :
the condition has length > 1 and only the first element will be used
Called from: free_squ(x)
This is confusing as I only get it when running thIS code. So I can write other functions to call the individual functions to analyse the argument inserted into the overlying function:
function1(argument){
function2(argument){
function3(argument){
***DO STUFF***}}}
Why for the following code does function within the main function not recognise the argument?
sudoku_solve <- function(x){
empty_sq <- free_squ(x) # Define a new object to give coordinates of empty square
if(empty_sq == FALSE){ # If no empty square can be found
return(x) # Return the matrix
} else{
empty_sq <- empty_sq # Pointless line kept for clarity
}
for(i in c(1:9)){ # Integers to insert into the found empty square
if(valid(x, i, empty_sq) == TRUE){ # can the intiger be placed in this square?
x[empty_sq[1], empty_sq[2]] = i # if i valid, insert into empty square
}
if(sudoku_solve()){ # are all i's valid?
return(TRUE) # All i's valid
} else{
x[empty_sq[1], empty_sq[2]] = 0 # reset the initial try and try again with another
}
}
return(FALSE)
}
I have named the sudoku puzzle 'puzzle', and call the function by the following:
sudoku_solve(puzzle)
I think in the following statement, you are not passing any value to the function and x does not have a default value either.
if(sudoku_solve()){ # are all i's valid?
return(TRUE) # All i's valid
}
Hence, although the argument is initially passed, when the function is called again after the loop, it is called without an argument. So you pass to free_sq(x) inside sudoku_solve(), and it gives an error.
empty_sq <- free_squ(x)
Make sure you are passing a value to sudoku_solve or else set the default value for x wither in sudoku_solve or in the free_squ class/function.
I created a a time v. concentration model in R for ggplot, the first set of data points did not have 0's so I thought'd it be ok. I then created one sheet with my Elapsed Days (x) and Concentrations (y) and created a for loop. The for loop comes up with an error because there are values inside some columns that have 0s. Id like for it to stop plotting when it hits a 0 in the for loop and move on to the next set of data points
Any help would be appreciated!
for(i in 1:24){
i = i + 2
x <- alldata1$Elapsed.Days
y <- as.matrix(alldata1[i])
df <- data.frame(x, y)
myFunction(x, y, df)
}
Error in lm.fit(x, y, offset = offset, singular.ok = singular.ok, ...) :
NA/NaN/Inf in 'y'
since there are zero values, but Im a bit unsure.
You have to add an if condition for where it finds 0 and move to next
ex:
for(n in c(1,2,4,0,6,0,8,0)) {
if(n==0) next # skip wherever it gets 0 to next iteration
print(n)
}
Output
1
2
4
6
8
Geekzeus' answer is perfectly fine, I would just like to bring to your attention that there are also slightly more formal ways of dealing with errors and warnings. If you would have a look at the code below:
output <- tryCatch({myFunction(x, y, df)},
warning = function(cond) {NULL},
error = function(cond) {NULL})
if (is.null(output)) {
next()
} else {
return(output)
}
What will happen here is that R tries to evaluate myFunction(x, y, df), when it encounters an error it will provide the error condition to the function(cond){NULL}, which will do nothing with cond but instead assigns NULL to output. Subsequently the code will continue.
Here is an example to play around with:
foo <- function(i) {if (i == 3) stop("Error")}
for(i in 1:5) {
print(i)
out <- tryCatch({foo(i)}, error = function(cond){NULL})
if (is.null(out)) next()
}
The above will print all 5 numbers, even though it encountered an error when i == 3.
Its a good way to specify what should happen when your code encounters an error, but is not a substitute for debugging your code to prevent errors in the first place.
I want the function to produce an error in a try block if a condition is faced, and output one of the inputs without any change in the case of this error. Please note that I do not wish to create a list output or anything as I need to have consistent output. My question is how can I check that the error does occur for some values using testthat? For example, for the below function:
myfunc = function(x,y){
try({
if(x<0)
stop("Negative X")
y = y+x
})
return(y)
}
I want to test and ensure error does occur for x=-1:
I have tried:
library(testthat)
expect_error(myfunc(-1,2))
But I get:
Error in try({ : Negative X
Error: `myfunc(-1, 2)` did not throw an error.
If my function has input (x,y,z) in R, I want to write a line of code which ensures data for x is of the right form.
i.e. x is just a real number rather than anything else such as a vector or a list. I assume the code would go something like
if ( ... ){stop("x must be a real number")}
I would like to know what goes inside of the bracket instead of ... ?
The reason is that if I write in a vector, the programme just take the first component of the vector as the input. R would give a warning about this, but I would like the programme to be stopped immediately.
If you want to "stop" if your argument is of length 1, and a real number
You could use stopifnot
foo <- function(x, y, z) {
stopifnot(length(x)==1L & is.numeric(x))
}
or perhaps
foo <- function(x, y, z){
if(!(length(x)==1L & is.numeric(x))) { stop("x must be a real number")}
}
stop allows you to specify the error message whereas stopifnot will return the condition that was tested. (Both have advantages)
The benefit of stopifnot is that it can tell you exactly which of multiple conditions failed.for example (noting that I am now feeding multiple expressions)
foo <- function(x, y, z) {
stopifnot(length(x)==1L , is.numeric(x))
}
foo('a')
# Error: is.numeric(x) is not TRUE
foo(c(1,2))
# Error: length(x) == 1L is not TRUE
Can someone tell me what is wrong with this if-else loop in R? I frequently can't get if-else loops to work. I get an error:
if(match('SubjResponse',names(data))==NA) {
observed <- data$SubjResponse1
}
else {
observed <- data$SubjResponse
}
Note that data is a data frame.
The error is
Error in if (match("SubjResponse", names(data)) == NA) { :
missing value where TRUE/FALSE needed
This is not a full example as we do not have the data but I see these issues:
You cannot test for NA with ==, you need is.na()
Similarly, the output of match() and friends is usually tested for NULL or length()==0
I tend to write } else { on one line.
As #DirkEddelbuettel noted, you can't test NA that way. But you can make match not return NA:
By using nomatch=0 and reversing the if clause (since 0 is treated as FALSE), the code can be simplified. Furthermore, another useful coding idiom is to assign the result of the if clause, that way you won't mistype the variable name in one of the branches...
So I'd write it like this:
observed <- if(match('SubjResponse',names(data), nomatch=0)) {
data$SubjResponse # match found
} else {
data$SubjResponse1 # no match found
}
By the way if you "frequently" have problems with if-else, you should be aware of two things:
The object to test must not contain NA or NaN, or be a string (mode character) or some other type that can't be coerced into a logical value. Numeric is OK: 0 is FALSE anything else (but NA/NaN) is TRUE.
The length of the object should be exactly 1 (a scalar value). It can be longer, but then you get a warning. If it is shorter, you get an error.
Examples:
len3 <- 1:3
if(len3) 'foo' # WARNING: the condition has length > 1 and only the first element will be used
len0 <- numeric(0)
if(len0) 'foo' # ERROR: argument is of length zero
badVec1 <- NA
if(badVec1) 'foo' # ERROR: missing value where TRUE/FALSE needed
badVec2 <- 'Hello'
if(badVec2) 'foo' # ERROR: argument is not interpretable as logical