MK Huda
MK Huda

Reputation: 687

How to change matrix entries using conditional if in R

I have this example matrix and I want to change the entries of the matrix with "YES" or "NO" based on a conditional if statement.

a<-c(5,1,0,3,2,0.6,1.6,7,9,0)
b<-c(11,0,1,18,11,11,0,13,20,10)
c<-c(10,20,0.7,0.8,0.3,0.4,0,0.9,1,1)

MAT<-cbind(a,b,c)
MAT

for (i in 1:nrow(MAT)){
  for (j in 1:ncol(MAT)){
  if (MAT[i,j]>5){
    MAT[i,j]="YES"
    } else {
    MAT[i,j]="NO"
    }
  }
}
print(MAT)

The output I got is like this and its wrong. Please help tell me what's wrong and how to fix it?

      a     b    c   
[1,] "NO"  "NO" "NO"
[2,] "NO"  "NO" "NO"
[3,] "NO"  "NO" "NO"
[4,] "NO"  "NO" "NO"
[5,] "NO"  "NO" "NO"
[6,] "NO"  "NO" "NO"
[7,] "NO"  "NO" "NO"
[8,] "YES" "NO" "NO"
[9,] "YES" "NO" "NO"
[10,] "NO"  "NO" "NO"

Upvotes: 16

Views: 3036

Answers (5)

GuedesBF
GuedesBF

Reputation: 9858

You do not need loops here. Just use the whole matrix in your call to x>5

ifelse(MAT>5, "YES", "NO")

This will do the logical operation over the entire matrix, and output a logical matrix.

You can reassign the VALUES from the output of ifelse() while keeping the STRUCTURE of MAT by using the empty brackets [], as in:

MAT[]<-ifelse(MAT>5, "YES", "NO")

Upvotes: 18

TarJae
TarJae

Reputation: 78927

Update: After the helpful notes of Jean-Claude Arbaut, ThomasIsCoding and GuedesBF please note that the first answer is wrong, here is an alternative with dplyr:

We could use across after changing matrix to tibble class and rechange to matrix after our operation:

library(tibble)
library(dplyr)

MAT <- MAT %>% 
  as_tibble() %>% 
  mutate(across(everything(), ~ifelse(. > 5, "YES", "NO"))) %>% 
  as.matrix()

First answer: Warning!

Do not use this code

MAT[MAT>5] <- "yes"
MAT[MAT<=5] <- "no"

as Jean-Claude Arbaut, ThomasIsCoding and GuedesBF indicated, it will coerce to character after the first assignment, which could lead to unexpected outcomes in downstream operations.

Upvotes: 2

ThomasIsCoding
ThomasIsCoding

Reputation: 101403

Reason of Failure

The reason you failed in your attempt comes from this part:

  if (MAT[i,j]>5){
    MAT[i,j]="YES"
    } else {
    MAT[i,j]="NO"
    }
  }

You should have be aware of that MAT is numerical, but you are assigning characters to MAT with in if...else... statement, which will make MAT converted to a character matrix. In this case, when you run MAT[i,j] > 5, you are comparing a character with a numeric value, e.g., "18" > 5, which returns an undesired FALSE.


Workaround

A workaround is using another variable to store the values after if...else..., instead of replacing values in MAT:

a <- c(5, 1, 0, 3, 2, 0.6, 1.6, 7, 9, 0)
b <- c(11, 0, 1, 18, 11, 11, 0, 13, 20, 10)
c <- c(10, 20, 0.7, 0.8, 0.3, 0.4, 0, 0.9, 1, 1)

MAT <- cbind(a, b, c)
out <- MAT

for (i in 1:nrow(MAT)) {
  for (j in 1:ncol(MAT)) {
    if (MAT[i, j] > 5) {
      out[i, j] <- "YES"
    } else {
      out[i, j] <- "NO"
    }
  }
}

such that

> out
      a     b     c
 [1,] "NO"  "YES" "YES"
 [2,] "NO"  "NO"  "YES"
 [3,] "NO"  "NO"  "NO"
 [4,] "NO"  "YES" "NO"
 [5,] "NO"  "YES" "NO"
 [6,] "NO"  "YES" "NO"
 [7,] "NO"  "NO"  "NO"
 [8,] "YES" "YES" "NO"
 [9,] "YES" "YES" "NO"
[10,] "NO"  "YES" "NO"

Alternative

There are already many answers to this question, and below is another base R option

> `dim<-`(as.character(factor(MAT > 5, labels = c("NO", "YES"))), dim(MAT))
      [,1]  [,2]  [,3]
 [1,] "NO"  "YES" "YES"
 [2,] "NO"  "NO"  "YES"
 [3,] "NO"  "NO"  "NO"
 [4,] "NO"  "YES" "NO"
 [5,] "NO"  "YES" "NO"
 [6,] "NO"  "YES" "NO"
 [7,] "NO"  "NO"  "NO"
 [8,] "YES" "YES" "NO"
 [9,] "YES" "YES" "NO"
[10,] "NO"  "YES" "NO"

Upvotes: 10

akrun
akrun

Reputation: 887148

Using just logical matrix converted to numeric index

MAT[] <- c("NO", "YES")[1 + (MAT > 5)]

-ouptut

> MAT
      a     b     c    
 [1,] "NO"  "YES" "YES"
 [2,] "NO"  "NO"  "YES"
 [3,] "NO"  "NO"  "NO" 
 [4,] "NO"  "YES" "NO" 
 [5,] "NO"  "YES" "NO" 
 [6,] "NO"  "YES" "NO" 
 [7,] "NO"  "NO"  "NO" 
 [8,] "YES" "YES" "NO" 
 [9,] "YES" "YES" "NO" 
[10,] "NO"  "YES" "NO" 

Upvotes: 5

user438383
user438383

Reputation: 6206

Try this instead:

apply(MAT, 2, function(x) ifelse(x > 5, "YES", "NO"))
      a     b     c    
 [1,] "NO"  "YES" "YES"
 [2,] "NO"  "NO"  "YES"
 [3,] "NO"  "NO"  "NO" 
 [4,] "NO"  "YES" "NO" 
 [5,] "NO"  "YES" "NO" 
 [6,] "NO"  "YES" "NO" 
 [7,] "NO"  "NO"  "NO" 
 [8,] "YES" "YES" "NO" 
 [9,] "YES" "YES" "NO" 
[10,] "NO"  "YES" "NO" 

Upvotes: 3

Related Questions