嵌套for循环中向量之间的计算

Calculation between vectors in nested for loops

我正在努力解决与嵌套 for 循环和条件计算有关的问题。

假设我有一个这样的数据框:

df = data.frame("a" = c(2, 3, 3, 4), 
                "b" = c(4, 4, 4, 4), 
                "c" = c(5, 5, 4, 4), 
                "d" = c(3, 4, 4, 2))

有了这个 df,我想用一个条件比较向量之间的每个元素:如果两个元素之间的绝对差值小于 2(即 0 和 1 的差值),我想在 a 中给出 1新创建的向量,而两个元素之间的绝对差 >= 2,我想附加 0.

例如,对于向量“a”和其他向量“b”、“c”、“d”之间的计算,我想要这样的结果:0 0 1。第一个 0 是基于a1 和 b1 相差 2;第二个0是基于a1和c1的差3; 1基于a1和d1的差异。因此,我尝试制作一个嵌套的 for 循环,以将相同的行程也应用于以下行中的元素。

所以我的第一次试用是这样的:

list_all = list(df$a, df$b, df$c, df$d)

v0<-c()

for (i in list_all)
  for (j in list_all)
    if (i != j) {
      if(abs(i-j)<2) { 
        v0<-c(v0, 1) 
      } else { 
        v0<-append(v0, 0)
      }} else { 
        next}

结果是这样的:

v0
[1] 0 0 1 0 1 1 0 1 0 1 1 0

但是好像只计算了第一个元素,后面的元素没有计算

所以我的二审是这样的:

list = list(df$b, df$c, df$d)

v1<-c()

for (i in df$a){
  for (j in list){
    if(abs(i-j)<2) { 
      v1<-append(v1, 1) 
    } else { 
      v1<-append(v1, 0)
    }
  }
}
v1 

v1
[1] 0 0 1 1 0 1 1 0 1 1 1 1

似乎计算是在 df$a 的所有元素和其他元素的第一个元素之间进行的。所以这也不是我需要的。

当我把 df$b 而不是 list 放在嵌套的 for 循环中时,结果更加混乱。

v2<-c()

for (i in df$a){
  for (j in df$b){
    if(abs(i-j)<2) { 
      v2<-append(v2, 1) 
    } else { 
      v2<-append(v2, 0)
    }
  }
}
v2     
 [1] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1

好像不是在对应元素(同一行)之间计算,而是在所有向量之间计算,不分位置。

谁能告诉我如何解决这个问题?我不明白为什么嵌套 for 循环只适用于第一个元素。

提前致谢。

我不确定我是否理解正确,但是这个怎么样?

df = data.frame("a" = c(2, 3, 3, 4), 
                "b" = c(4, 4, 4, 4), 
                "c" = c(5, 5, 4, 4), 
                "d" = c(3, 4, 4, 2))

as.vector(apply(df, 1, \(x) ifelse(abs(x[1] - x[2:4]) < 2, 1, 0)))
#>  [1] 0 0 1 1 0 1 1 1 1 1 1 0

我认为您正在让自己的生活变得不必要地复杂化。如果我理解正确的话,你可以做你想做的,根本不需要嵌套循环。

要记住的关键是 R 默认是 向量化的 。这意味着 R 将同时修改向量的所有行。没有必要循环。因此,例如,如果 a 是一个具有值 12 的向量,我写 a + 1,结果将是一个具有值 23.

将此逻辑应用到您的案例中,您可以这样写:

df$diffB <- ifelse(abs(df$a-df$b) < 2, 1, 0)
df$diffC <- ifelse(abs(df$a-df$c) < 2, 1, 0)
df$diffD <- ifelse(abs(df$a-df$d) < 2, 1, 0)
df

给予

  a b c d diffB diffC diffD
1 2 4 5 3     0     0     1
2 3 4 5 4     1     0     1
3 3 4 4 4     1     1     1
4 4 4 4 2     1     1     0

如果您愿意,您可以编写一个循环遍历 ,Aron 在他的回答中给了您一个选择。

就个人而言,我发现使用 tidyverse 的代码比使用 base R 编写的代码更容易理解。这是因为我可以从左到右阅读 tidyverse 代码,而 base R 代码(通常)需要阅读从里到外。 Tidyverse 的语法也比基础 R 的语法更一致。

以下是我如何使用 tidyverse 解决您的问题:

library(tidyverse)

df %>% 
  mutate(
    diffB=ifelse(abs(a-b) < 2, 1, 0),
    diffC=ifelse(abs(a-c) < 2, 1, 0),
    diffD=ifelse(abs(a-d) < 2, 1, 0)
  )

并且“遍历列”变为

df %>% 
  mutate(
    across(
      c(b, c, d), 
      ~ifelse(abs(a-.x) < 2, 1, 0), 
      .names="diff{.col}"
    )
  )