如何在R中使用条件语句更改矩阵条目

16
我有一个矩阵的例子,我想根据条件语句将矩阵的条目更改为"YES""NO"
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)

我得到的输出是这样的,但是它是错误的。请告诉我问题出在哪里以及如何修复它?

      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"
5个回答

18

这里不需要使用循环。只需在调用x>5时使用整个矩阵即可。

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

这将对整个矩阵执行逻辑操作,并输出一个逻辑矩阵。

您可以使用空方括号[],从ifelse()的输出重新分配VALUES,同时保持MATSTRUCTURE,例如:

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

1
它按照我的预期工作良好,且相对于其他代码而言,这是最简单的代码,因此我接受了它。 - MK Huda
这里MAT后面的空括号是不必要的,因为ifelse返回的值与其test参数具有相同的形状。由于MAT>5是一个矩阵,因此ifelse的值也将是一个矩阵。 - Robert Hacken
是的,对于这个特定的情况,@Robert,你是正确的。但我认为在这里包括空括号是一个好的做法,因为它是一种更通用的方法,可以强制保留原始数据的类。Ifelse确实返回与“test”参数相同类型的对象。但“<”是一个将其强制转换为逻辑向量或矩阵的函数。如果MAT是一个数据框架,则需要空括号,否则MAT将被强制转换为矩阵。 - GuedesBF
1
@GuedesBF 这是一个很好的观点,但在你的回答中,你说 ifelse 的输出将是一个向量(从其 dim 属性中剥离的矩阵),这是不正确的。 - Robert Hacken
1
是的,答案中包含了不准确的陈述,感谢您指出这一点,@Robert。我根据您的观察纠正了答案。 - GuedesBF

10

失败的原因

你尝试失败的原因来自于这一部分:

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

你应该注意到MAT是数值型的,但是在if...else...语句中你却给MAT赋了字符类型的值,这将导致MAT被转换成字符矩阵。在这种情况下,当你运行MAT[i,j] > 5时,你正在将一个字符与一个数值进行比较,例如:"18" > 5,这会返回一个不正确的FALSE


解决方法

解决方法是使用另一个变量来存储if...else...后的值,而不是替换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"
    }
  }
}

这样的,以致于

> 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"

替代方案

针对这个问题已经有很多答案,以下给出另一种基于 R 语言的选项。

> `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"

2
作为替代方案,也可以编写 structure(as.character(...), dim = dim(MAT))array(as.character(...), dim = dim(MAT)) - user13963867
@Jean-ClaudeArbaut 是的,没错。 - ThomasIsCoding
@Jean-ClaudeArbaut,您能否检查一下我在这里发布的帖子 https://stackoverflow.com/questions/68793463/reading-a-csv-file-as-matrix-and-change-the-entries-using-conditional-if-in-r/68794730#68794730 ,因为当我将代码放入我创建的R函数中时,它不起作用。 - MK Huda

4

仅使用逻辑矩阵转换为数值索引

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

-输出

> 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" 

3

试试这个:

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" 

2

更新: 根据Jean-Claude Arbaut、ThomasIsCoding和GuedesBF的有益提示,请注意第一个答案是错误的,这里提供了一个使用dplyr的替代方案:

我们可以在将matrix更改为tibble类并在操作后重新更改为matrix之后,使用across

library(tibble)
library(dplyr)

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

第一个答案:警告!

不要使用这段代码。

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

正如Jean-Claude Arbaut、ThomasIsCoding和GuedesBF所指出的那样,第一次赋值后它将强制转换为字符,这可能会导致下游操作产生意外结果。


2
代码存在严重的漏洞。请注意,在第一条指令之后,MAT是一个字符矩阵。例如,"6e-12" <= 5是错误的。即使您没有意外更改类型,以这种方式编写代码也很危险。将“yes”替换为1,“no”替换为0,它仍然会失败。 - user13963867
2
这将在第一次赋值后强制转换为字符,这可能会导致意外的结果,在下游操作中被@Jean-ClaudeArbaut和ThomasIsCoding观察到。 - GuedesBF
谢谢大家的意见。我已经更新了我的答案。请告诉我是否应该删除答案或者保留它以产生反向学习效果。 - TarJae

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接