Roz
Roz

Reputation: 325

Function with a nested loop in R

I'm trying to write a function that returns "Fail" if all 3 valves at a given location (x) are open (1), but returns "Pass" if any valve is closed (0). My code is below, but it doesn't work and I get the error---unexpected 'else' in:"else"--- And I can't figure out where my code is wrong.

systemFail <- function(x) {
  
if(df$valveOne[df$location == x] == 1) {        
    if(df$valveTwo[df$location == x] == 1) {  
      if(df$valveThree[df$location == x] == 1) {
        print("Fail")}}}
      else {            
        print ("Pass")}
    else {            
    Print("Pass")}
else {
  Print("Pass")}
}

Upvotes: 0

Views: 69

Answers (3)

AndrewGB
AndrewGB

Reputation: 16876

Here's a possibility that may work better (although unsure about structure of your data).

library(dplyr)

# Sample data.
df <- structure(
  list(
    location = 1:8,
    valveOne = c(0, 0, 0, 0, 1, 1, 1, 1),
    valveTwo = c(0, 0, 1, 1, 0, 1, 1, 1),
    valveThree = c(0, 1, 0, 1, 0, 1, 0, 1)), class = "data.frame", row.names = c(NA,-8L))

systemFail <- function(x, y) {
  station <- x %>%
    dplyr::filter(location == y) %>% 
    mutate(sum = sum(across(starts_with("valve")), na.rm = T))
  
  if (station$sum == 3) {
    message("FAIL")
  } else {
    message("PASS")
  }
  
}

An alternative is put your data into a long format with tidyr.

# Put data into long format.
df2 <- df %>%
  tidyr::pivot_longer(!location, names_to = "valve", values_to = "value")

# x is your dataframe, and y is the location of the valve.
systemFail <- function(x, y) {
  station <- x %>%
    dplyr::filter(location == y)
  
  if (all(station$value == 1) == TRUE) {
    message("FAIL")
  } else {
    message("PASS")
  }
  
}

systemFail(df2, 2)
# PASS

systemFail(df2, 8)
# FAIL

Upvotes: 0

Nullius in Verba
Nullius in Verba

Reputation: 1

You can also do it like this:

df <- data.frame(location=1:8,
   valveOne=c(0,0,0,0,1,1,1,1),
   valveTwo=c(0,0,1,1,0,0,1,1),
   valveThree=c(0,1,0,1,0,1,0,1))

SystemFail <- function(x) {
   ifelse((df$valveOne==1 & df$valveTwo==1 & df$valveThree==1)[df$location==x],"Fail","Pass")
}

df$SystemFail <- ifelse((df$valveOne==1 & df$valveTwo==1 & df$valveThree==1),"Fail","Pass")

The last line shows how to calculate all the values at once.

Upvotes: 0

IRTFM
IRTFM

Reputation: 263481

I think you had too many closing curley-braces at the innermost if-else, and not enough of them at the outer levels:

Try:

systemFail <- function(x) {
if(df$valveOne[df$location == x] == 1) {        
      if(df$valveTwo[df$location == x] == 1) {  
         if(df$valveThree[df$location == x] == 1) { print("Fail")}
         else { print ("Pass") } }
      else { print("Pass") } }
   else {print("Pass")} }

Wait.... that doesn't make sense. Why would there be a single "location" column for three different valves. Instead it should be:

df <- data.frame( valve2loc= sample(0:1,20, repl=TRUE), 
                  valve2loc= sample(0:1,20, repl=TRUE), 
                  valve3loc= sample(0:1,20, repl=TRUE))

So three valves with three open/close values let you decide whether all are open:

 df$pass_fail <- c("Pass","Fail")[ 1+ (rowSums(df)==3)]

That is a vectorized operation that calculated the sum of valve location values, determines where they are all "1" and if so adds one to the logical 0/1 Boolean result, which is then used as an index for the two possible results. (R is 1 based on indexing so need to add 1 to logical values (0/1) to make them useful for indexing another 2-valued vector.)

Upvotes: 1

Related Questions