chenchenmomo
chenchenmomo

Reputation: 233

How to use a function containing if-else structure to every column using R

The data frame sg is as follwoing:

 v1 v2 v3 
 A  B  C  
 B  A  B  
 C  A  A  

I used a function

definition <- funciton(x){
  if (x =='A') definition <- paste(x, ": MINIMUM_RED")   
  else if (x =='B') definition <- paste(x, ": PASSIVE_RED")   ## Passive red (no green demand during red)
  else if (x =='C') definition <- paste(x, ": RED_REQUEST")   ## During red the group has a green demand
  else if (x =='D') definition <- paste(x, ": RED_PRIORITY")  
  else if (x =='E') definition <- paste(x, ": RED_PRIVILEGE")  ## During red possilbe to go green
  else  definition <- paste(x, ": RED_WAIT")   ## 
}

I want to use the function definition for every value in the data frame sg, so I used apply function:

sgdf <- apply(sg,2,defination)

The result is as following:

     [,1]              [,2]              [,3]             
[1,] "A : MINIMUM_RED" "B : PASSIVE_RED" "C : RED_REQUEST"
[2,] "B : MINIMUM_RED" "A : PASSIVE_RED" "B : RED_REQUEST"
[3,] "C : MINIMUM_RED" "A : PASSIVE_RED" "A : RED_REQUEST"

Obviously, the function only works on the first element in every column, is any way I can get the right definition of every value?

Upvotes: 2

Views: 379

Answers (5)

shadow
shadow

Reputation: 22343

There are some better suggested solutions, but if you want to change your current code as little as possible, just adding a sapply in your last line should work:

sgdf <- apply(sg, 2, sapply, definition)

Another possibility is to use match with nomatch=6 as an indicator variable. This has the advantage over some of the suggestions that it also works for matrix entries that are not in A-E, i.e. the case of paste(x, ": RED_WAIT").

newvals <- c(": MINIMUM_RED",  ": PASSIVE_RED", ": RED_REQUEST",
             ": RED_PRIORITY", ": RED_PRIVILEGE", ": RED_WAIT")
ind <- match(as.matrix(sg), c("A", "B", "C", "D", "E"), nomatch=6)
matrix(paste(as.matrix(sg), newvals[ind]), ncol = ncol(sg))

Upvotes: 3

Rich Scriven
Rich Scriven

Reputation: 99371

How about a switch statement? They're nice and clean, and switch is a .Primitive

definition <- function(data)
{
    m <- as.matrix(data)
    for(i in seq_along(m)){
        if(m[i] %in% LETTERS[1:5]){
            m[i] <- switch(m[i], 
                   A = paste(m[i], ": MINIMUM_RED"),
                   B = paste(m[i], ": PASSIVE_RED"),
                   C = paste(m[i], ": RED_REQUEST"),
                   D = paste(m[i], ": RED_PRIORITY"),
                   E = paste(m[i], ": RED_PRIVILEGE")
                   )
        } else {
            m[i] =  paste(m[i], ": RED_WAIT") 
        }
    }
    return(m)
}

The resulting matrix is

definition(dat)
#      v1                v2                v3               
# [1,] "A : MINIMUM_RED" "B : PASSIVE_RED" "C : RED_REQUEST"
# [2,] "B : PASSIVE_RED" "A : MINIMUM_RED" "B : PASSIVE_RED"
# [3,] "C : RED_REQUEST" "A : MINIMUM_RED" "A : MINIMUM_RED"

and dat is

dat <-
structure(list(v1 = structure(1:3, .Label = c("A", "B", "C"), class = "factor"), 
    v2 = structure(c(2L, 1L, 1L), .Label = c("A", "B"), class = "factor"), 
    v3 = structure(c(3L, 2L, 1L), .Label = c("A", "B", "C"), class = "factor")), .Names = c("v1", 
"v2", "v3"), class = "data.frame", row.names = c(NA, -3L))

Better yet, let's just vectorize it instead. Here's a new data frame with a few values that won't be matched

DF <- structure(list(v1 = structure(c(1L, 2L, 3L, 4L, 1L),
.Label = c("A", "B", "C", "F"), class = "factor"), 
v2 = structure(c(2L, 1L, 1L, 3L, 4L), .Label = c("A", "B", "D", "E"), class = "factor"),
v3 = structure(c(3L, 2L, 1L, 4L, 5L), .Label = c("A", "B", "C", "G", "R"), class = "factor")),
.Names = c("v1", "v2", "v3"), row.names = c(NA, -5L), class = "data.frame")
> DF
#   v1 v2 v3
# 1  A  B  C
# 2  B  A  B
# 3  C  A  A
# 4  F  D  G
# 5  A  E  R

And the replacement

> q <- c(": MINIMUM_RED", ": PASSIVE_RED", ": RED_REQUEST", ": RED_PRIORITY", ": RED_PRIVILEGE")
YES <- paste(LETTERS[1:5], q); NO <- "NA : RED_WAIT"
> m <- as.matrix(DF)
> for(i in seq(m)){ m[i] <- q[match(m[i], LETTERS[1:5])] }
> m[is.na(m)] <- NO
> m
#      v1                v2                  v3               
# [1,] "A : MINIMUM_RED" "B : PASSIVE_RED"   "C : RED_REQUEST"
# [2,] "B : PASSIVE_RED" "A : MINIMUM_RED"   "B : PASSIVE_RED"
# [3,] "C : RED_REQUEST" "A : MINIMUM_RED"   "A : MINIMUM_RED"
# [4,] "NA : RED_WAIT"   "D : RED_PRIORITY"  "NA : RED_WAIT"  
# [5,] "A : MINIMUM_RED" "E : RED_PRIVILEGE" "NA : RED_WAIT"  

Upvotes: 1

Anders Ellern Bilgrau
Anders Ellern Bilgrau

Reputation: 10263

You need to use the vectorized ifelse in your function definition instead of if and else or use gsub instead.

edit The following is the vectorized version for illustration, but I think the alterative answers by lookup are a much better solution. But this does illustrate that ifelse is vectorized while if is not.

definition <- function (x) {
  y <-
    ifelse(x =='A', "MINIMUM_RED",
           ifelse(x =='B', "PASSIVE_RED",
                  ifelse(x =='C', "RED_REQUEST", 
                         ifelse(x =='D', "RED_PRIORITY",
                                ifelse(x =='E', "RED_PRIVILEGE",  
                                       "RED_WAIT")))))
  return(paste(x, ":", y))
}


x <- LETTERS[1:5]
definition(x)
#[1] "A : MINIMUM_RED"   "B : PASSIVE_RED"   "C : RED_REQUEST"   "D : RED_PRIORITY" 
#[5] "E : RED_PRIVILEGE"

Upvotes: 3

zx8754
zx8754

Reputation: 56249

Using lookup variables:

#dummy data
df <- read.table(text="v1 v2 v3 
A  B  C  
B  A  B  
C  A  A  ", header=TRUE)

#make lookup variables
ind <- c("A","B","C","D","E")
def <- paste0(ind,":",
              c("MINIMUM_RED",
                "PASSIVE_RED",
                "RED_REQUEST",
                "RED_PRIORITY",
                "RED_PRIVILEGE"))
#result         
sapply(df,function(i){def[order(i)]})

#output
# v1              v2              v3             
# [1,] "A:MINIMUM_RED" "B:PASSIVE_RED" "C:RED_REQUEST"
# [2,] "B:PASSIVE_RED" "C:RED_REQUEST" "B:PASSIVE_RED"
# [3,] "C:RED_REQUEST" "A:MINIMUM_RED" "A:MINIMUM_RED"

Upvotes: 3

nico
nico

Reputation: 51680

You may just use a list to map each character to a definition.

For instance

sg <- data.frame(v1=c("A", "B", "C"),
                 v2=c("B", "A", "A"),
                 v3=c("C", "A", "A")) 

defs <- list("A" = "MINIMUM_RED",
         "B" = "PASSIVE_RED",
         "C" = "RED_REQUEST",
         "D" = "RED_PRIORITY",
         "E" = "RED_PRIVILEGE")

# Convert the data frame in a matrix, so we can use it to index the list
tmp <- as.matrix(sg)

# note that paste works on vectors
res <- matrix(paste(tmp, defs[tmp]), ncol=ncol(tmp))

Resulting in:

     [,1]            [,2]            [,3]           
[1,] "A MINIMUM_RED" "B PASSIVE_RED" "C RED_REQUEST"
[2,] "B PASSIVE_RED" "A MINIMUM_RED" "A MINIMUM_RED"
[3,] "C RED_REQUEST" "A MINIMUM_RED" "A MINIMUM_RED"

Upvotes: 5

Related Questions