Writing if / ifelse function in R - r

I am attempting to write a function in order to create a variable (BBDR) based on the conditions of another variable (Site0) using the if function. I have the following code using the if function.
x1 <- (africanaDamRate$BB6-africanaDamRate$BB0)/29
x2 <- (africanaDamRate$BB6-africanaDamRate$BB0)/22
x3 <- (africanaDamRate$BB6-africanaDamRate$BB0)/34
x4 <- (africanaDamRate$BB6-africanaDamRate$BB0)/30
F1 <- function(y){
if(africanaDamRate$Site0==1){africanaDamRate$BBDR<-x1}
if(africanaDamRate$Site0==2){africanaDamRate$BBDR<-x2}
if(africanaDamRate$Site0==3){africanaDamRate$BBDR<-x3}
if(africanaDamRate$Site0==4){africanaDamRate$BBDR<-x4}
}
africanaDamRate$BBDR<-F1(y)
But when I attempt this code I receive "The condition has length greater than 1..."
I have also attempted using the ifelse function with the following code:
africanaDamRate$BBDR<-ifelse(c(africanaDamRate$Site0==1, x1, NA), c(africanaDamRate$Site0==2, x2, NA), c(africanaDamRate$Site0==3, x3, NA), c(africanaDamRate$Site0==4, x4, NA))
But get the "unused argument" error.
Does anyone have any ideas of how I can do this (without subsetting)? Thanks so much!
Ryan

Your ifelse statement is wrong. It could be written like this:
africanaDamRate$BBDR <- ifelse(africanaDamRate$Site0 == 1, x1,
ifelse(africanaDamRate$Site0 == 2, x2,
ifelse(africanaDamRate$Site0 == 3, x3,
ifelse(africanaDamRate$Site0 == 4, x4, NA))))

Related

Apply method in R missing argument (missing argument error)

I am trying to use the apply method in R. But I keep getting the error: Error in FUN(newX[, i], ...) : missing argument in "b".
The code which produces the error:
my_data <- data.frame(x1 = 1:5, x2 = 2:6, x3 = 3)
myFunction <- function(a, b, c){
return(a + b + c)
}
results = apply(my_data, 1, myFunction) #this line is producing the error massage
If I change "myFunction" to "sum" for example. Then there is no error. How can I get rid of this error?
Either the function should be changed to
myFunction <- function(x) sum(x)
apply(my_data, 1, myFunction)
#[1] 6 8 10 12 14
Also, the sum operation by row is more efficient with rowSums
rowSums(my_data)
or specify the arguments separately by using a lambda/anonymous function in the OP's original function
apply(my_data, 1, function(x) myFunction(x[1], x[2], x[3]))
You can use mapply:
with(my_data, mapply(myFunction, x1, x2, x3))
Also note your data.frame is a list of 3 vectors. You can add vectors just like a scalar.
with(my_data, x1 + x2 + x3)

How to use a dataframe in a function in r

I need to insert the variables of a dataframe into a function in r. The function in question is "y=[1- (x1-x2) / x3]". When I write, and enter the variables manually it works, however, I need to use the random numbers from the dataframe.
#Original function
f<-function(x1, x2, x3)
+{}
f<-function(x1, x2, x3)
+{return(1-(x1-x2)/x3)}
f(0.9, 0.5, 0.5)```
#Dataframe function
f<-function(x1, x2, x3)
+{}
f<-function(x1, x2, x3)
+{return(1-(x1-x2)/x3)}
f(x1 = x1, x2 = x2, x3 = x3, DATA = DF)
The first output is ok, however, the second output appears the error message. Error in f(VMB = VMB, VMR = VMR, DATA = DATA1) : unused argument (DATA = DATA1) I know I'm not properly inserting the dataframe into the code, but I'm already circling, can anyone help me?
As the comments suggest, your problem is that the function doesn't contain a data argument. R doesn't know where x1, x2, x3 comes from and will only look at through the global environment trying to find them. If these are contained in a data frame, it doesn't know that it should take them from there, and will fail.
For example
f <- function(x,y,z)
1 + (x-y)/z
f(0.9, 0.5, 0.5)
will work, because it knows where to retrieve the values. So will
x1 <- 0.9
x2 <- 0.5
x3 <- 0.5
f(x1, x2, x3)
because it looks through these environemnts, but
df <- data.frame(x = 0.9, y = 0.5, z = 0.5)
f(x, y, z) #fails
fails, because it doesn't look for them in df. Instead you can use
f(df$x, df$y, df$z)
with(df, f(x, y, z)) #same
which lets R know where to get the variables. (Here i used x, y and z to avoid conflict names)
If this function should always take a data.frame and use columns x1, x2, x3 you could use rewrite it to incorporate this, as below.
f <- function(df){
with(df, 1 + (x1-x2)/x3)
}

Should attach be avoided in this situation?

Although there are some questions about this topic (e.g. this question), none of them answer my particular questions (as far as I could tell anyway).
Suppose I have a function which depends on a lot of parameters. For demonstration purposes I chose 3 parameters:
myfun <- function(x1, x2, x3){
some code containing x1, x2, x3
}
Often the input parameters are already contained in a list:
xlist <- list(x1 = 1, x2= 2, x3 = 3)
I want to run myfun with the inputs contained in xlist like this:
myfun(xlist$x1, xlist$x2, xlist$x3)
However this seems like too big of an effort (because of the high number of parameters).
So I decided to modify myfun: instead of all the input parameters. It now gets the whole list as one single input: at the beginning of the code I use attach in order to use the same code as above.
myfun2 <- function(xlist){
attach(xlist)
same code as in myfun containing x1, x2, x3
detach(xlist)
}
I thought that this would be quite a neat solution, but a lot of users advise to not use attach.
What do you think? Are there any arguments to prefer myfun over myfun2?
Thanks in advance.
I think you'd be better off using do.call. do.call will accept a list and convert them to arguments.
myfun <- function(x1, x2, x3){
x1 + x2 + x3
}
xlist <- list(x1 = 1, x2= 2, x3 = 3)
do.call(myfun, xlist)
This has the benefit of being explicit about what the arguments are, which makes it much easier to reason with the code, maintain it, and debug it.
The place where this gets tricky is if xlist has more values in it than just those required by the function. For example, the following throws an error:
xlist <- list(x1 = 1, x2 = 2, x3 = 3, x4 = 4)
do.call(myfun, xlist)
You can circumvent this by matching arguments with the formals
do.call(myfun, xlist[names(xlist) %in% names(formals(myfun))])
It's still a bit of typing, but if you're talking about 10+ arguments, it's still a lot easier than xlist$x1, xlist$x2, xlist$x3, etc.
LAP gives a useful solution as well, but would be better used to have with outside the call.
with(xlist, myfun(x1, x2, x3))
You could just use with():
xlist <- list(x1 = 1, x2= 2, x3 = 3)
FOO <- function(mylist){
with(mylist,
x1+x2+x3
)
}
> FOO(xlist)
[1] 6
I'm not convinced of this approach, though. The function would depend on the correctly named elements within the list.
My approach would be something like this:
testfun <- function (a_list)
{
args = a_list
print(args$x1)
print(args$x2)
print(args$x3)
}
my_list <- list(x1=2, x2=3, x3=4)
testfun(my_list)
However, you would need to know the names of the parameters within the function.
Perhaps the do.call() function can come into play here.
do.call('fun', list)
You could assign the list to the environment of the function:
myfun <- function(xlist) {
for (i in seq_along(xlist)) {
assign(names(xlist)[i], xlist[[i]], envir = environment())
}
# or if you dislike for-loops
# lapply(seq_along(xlist), function(i) assign(names(xlist)[i], xlist[[i]], envir = parent.env(environment())))
print(paste0(x2, x3)) # do something with x2 and x3
print(x1 * x3) # do something with x1 and x3
}
myfun(list(x1 = 4, x2 = "dc", x3 = c(3,45,21)))

Determine if a sequence has "gaps" in R

I would like to determine if a sequence contains any gaps or irregular steps? Not sure if this is the right way to phrase this and there's a good chance that this is duplicate (but I was unable to find a good question).
The following has_gap function gives me the correct results, but seems a bit clunky? Perhaps there is something built-in that I haven't discovered?
x1 <- c(1:5, 7:10)
x2 <- 1:10
x3 <- seq(1, 10, by = 2)
x4 <- c(seq(1, 6, by = 2), 6, seq(7, 10, by = 2))
has_gap <- function(vec) length(unique(diff(vec))) != 1
vecs <- list(x1, x2, x3, x4)
sapply(vecs, has_gap)
# [1] TRUE FALSE FALSE TRUE
library(zoo)
is.regular(x3, strict=TRUE)
is.regular(x3, strict=FALSE)
As noted by G. Grothendieck in the comments, one approach is:
has_gaps <- \(x)!!diff(range(diff(x)))
Another approach might be:
has_gaps2 <- \(x)var(diff(x))>0
If performance is an issue, rawr suggested:
has_gaps3 <- \(x)!isTRUE(all.equal(cor(x,seq_along(x)),1))

recode variables with NAs with ifelse() in R

I am recoding two binary variables into new variable such that any 1s in the first variable take a 0 in the new one and all digits in the second variable are preserved. The code below shows the logic that I would like to produce. However, when I run this code, the recoding using the ifelse() just recreates x2 without incorporating the first ifelse() line that uses x1's 1s as 0. Thoughts?
set.seed(123)
x1 <- sample(c(0,1,NA), 20, replace = TRUE)
x2 <- sample(c(0,1,NA), 20, replace = TRUE)
recode <- ifelse(x1 == 1, 0, NA)
recode <- ifelse(x2 == 1, 1, recode)
recode <- ifelse(x2 == 0, 0, recode)
table(recode); table(x2)
Thanks
Sorry, but it does what you wanted to do. The problem that you might have forgotten is that the result of comparison of NA with anything is also NA, so ifelse( x2 == 0, yes, no ) returns NA (instead of no) if x2 == NA.
Better try
recode <- rep( NA, length( x1 ) )
recode[ x1 == 1 ] <- 0
recode[ ! is.na( x2 ) ] <- x2[ ! is.na( x2 ) ]
Maybe you want this?
ifelse(is.na(x2), ifelse(x1 == 1, 0, NA), x2)
You overwrote those results. The relevant line from the Details section of the help('ifelse') page is:
Missing values in test give missing values in the result.
recode <- ifelse(x1 == 1, 0, NA)
recode[ !is.na(x2)] <- x2[!is.na(x2)]
I am posting this just to figure out if there is some reason this one liner was not suggested:
recode <- ifelse(x1 %in% 1 & is.na(x2), 0, x2)

Resources