Reputation: 687
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
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
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
Reputation: 101403
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
.
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"
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
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
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