【问题标题】:How to make a For loop that keeps the original row value如何制作一个保留原始行值的 For 循环
【发布时间】:2020-04-13 15:12:37
【问题描述】:

我试图在一个循环中运行多个条件语句。我的第一个条件是 if, else if 有 3 个条件(如果没有匹配项,技术上是 4 个)。我的第二个确实只需要一个条件,如果不满足该条件,我想保留原始行值。问题是我的输出与行号不匹配,我不确定如何只输出到循环中的特定行。

我想遍历每一列,并且在每一列中我使用sapply 来检查每个值是否落在range1 之外(标记为4),在range1 之内(标记为1) ,is.na(标记为9),否则标记为-999。然后将使用更窄的范围,如果列中的每个值都在 range2 内,则用3 标记,否则不要更新。

我的部分工作代码和一个可重现的示例如下。我的输入和第一个循环是:

df <- structure(list(A = c(-2, 3, 5, 10, NA), A.c = c(NA, NA, NA, NA, NA), B = c(2.2, -55, 3, NA, 99), B.c = c(NA, NA, NA, NA, NA)), class = "data.frame", row.names = c(NA, -5L))

> df
   A A.c     B B.c
1 -2  NA   2.2  NA
2  3  NA -55.0  NA
3  5  NA   3.0  NA
4 10  NA    NA  NA
5 NA  NA  99.0  NA

min1 <- 0
max1 <- 8

test1.func <- function(x) {
  val <- if (!is.na(x) & is.numeric(x) & (x < min1 | x > max1){
    num = 4
  } else if (!is.na(x) & is.numeric(x) & x >= min1 & x <= max1){
    num = 1
  } else if (is.na(x)){# TODO it would be better to make this just what is already present in the row
  } else {
    num = -999
  }
  val
}

Test1 <- function(x) {
  i <- NA
  for(i in seq(from = 1, to = ncol(x), by = 2)){
    x[, i + 1] <- sapply(x[[i]], test1.func)
  }
  x
}

df_result <- Test1(df)

> df_result
   A A.c     B B.c
1 -2   4   2.2   1
2  3   1 -55.0   4
3  5   1   3.0   1
4 10   4    NA   9
5 NA   9  99.0   4

下一个循环和条件(任何现有的 4 或 9 值都将保留):

min2 <- 3
max2 <- 5

test2.func <- function(x) {
  val <- if (!is.na(x) & is.numeric(x) & (x < min2 | x > max2){
    num = 3
  }
  val
}

Test2 <- function(x) {
  i <- NA
  for(i in seq(from = 1, to = ncol(x), by = 2)){
    x[, i + 1] <- sapply(x[[i]], test2.func)
  }
  x
}

df_result2 <- Test2(df_result)
# Only 2.2 matches, if working correctly would output
> df_result2
   A A.c     B B.c
1 -2   4   2.2   3
2  3   1 -55.0   4
3  5   1   3.0   1
4 10   4    NA   9
5 NA   9  99.0   4

当前代码错误,因为只有一个匹配项:

Warning messages:
1: In `[<-.data.frame`(`*tmp*`, , i + 1, value = list(3, NULL, NULL,  :
  provided 5 variables to replace 1 variables

【问题讨论】:

  • 你能用一两句话概括一下逻辑吗?
  • 该错误发生在哪里?顺便说一句,在if 语句中使用&amp;(单个)是不好的做法,请改用&amp;&amp;(和||)(参见stackoverflow.com/q/16027840/3358272?Logic)。
  • @NelsonGon 逻辑是,(循环1)如果在min1和max1之外,返回4(失败),如果是NA,返回9,如果通过返回1 ,否则返回 -999。在循环 2 中,跳过循环 1 返回 4 或 9 的任何行,如果值超出 min2 和 max2,则返回 3。
  • 另外,我强烈建议您在复杂条件下使用一些括号。 !is.na(x) &amp; is.numeric(x) &amp; x &lt; min1 | !is.na(x) &amp; is.numeric(x) &amp; x &gt; max1 会比 (!is.na(x) &amp; is.numeric(x) &amp; x &lt; min1) | (!is.na(x) &amp; is.numeric(x) &amp; x &gt; max1) 更清晰(如果这就是您的意思),然后可以将其简化为 !is.na(x) &amp; is.numeric(x) &amp; (x &lt; min1 | x &gt; max1)... 如果您首先检查数字,然后检查缺失,然后检查数字条件,则可能会进一步简化反之亦然。
  • 但是,总的来说,当 Nelson 要求提供摘要时,如果您从 之类的内容开始会很有帮助。我想遍历每一列。在每一列中,我使用 sapply检查每个值以查看..."。听到您陈述这些内容将有助于我们确认您的代码尝试符合您的意图。

标签: r for-loop


【解决方案1】:

一些想法。

  1. for 循环不是必须的,最好利用 R 的向量化操作;
  2. 看来您的 43 值实际上类似于“带外 1”和“带外 2”,在这种情况下,这可以在一个函数中解决。
  3. == "NA" 的测试有点偏离...如果列中的一个值是字符串"NA"(而不是R 的NA 值),那么该列中的所有值都是字符串并且您有其他问题。正因为如此,我没有明确检查 is.numeric,尽管重新开始工作并不难。

试试这个:

func <- function(x, range1, range2) {
  ifelse(is.na(x), 9L,
         ifelse(x < range1[1] | x > range1[2], 4L,
                ifelse(x < range2[1] | x > range2[2], 3L,
                       1L)))
}

df[,c("A.c", "B.c")] <- lapply(df[,c("A", "B")], func, c(0, 8), c(3, 5))
df
#    A A.c     B B.c
# 1 -2   4   2.2   3
# 2  3   1 -55.0   4
# 3  5   1   3.0   1
# 4 10   4    NA   9
# 5 NA   9  99.0   4

我遇到的一个问题是它使用了一个 3 嵌套的 ifelse 循环。虽然这工作正常,但很难跟踪和排除故障(ifelse 有其自身的问题)。如果您有其他条件要合并,最好使用dplyr::case_when

func2 <- function(x, range1, range2) {
  dplyr::case_when(
    is.na(x)                      ~ 9L,
    x < range1[1] | x > range1[2] ~ 4L,
    x < range2[1] | x > range2[2] ~ 3L,
    TRUE                          ~ 1L
  )
}

我发现第二种方法更容易阅读,尽管它确实增加了 dplyr 的依赖项(虽然它确实具有优势和优势,但包括大量其他依赖项)。但是,如果您已经在工作流程中使用了任何 tidyverse 包,那么这可能是更好的解决方案。

【讨论】:

  • 感谢您的详细说明。 dplyr::case_when 那时可能最好使用,因为在我的 2 个示例测试之后会有更多。我试图确定逻辑,然后看看data.table 是否有任何解决方案,但case_when 可能会处理得很好,并且更容易编写所有这些门。
  • data.table::fcase 怎么样?
  • (该功能还没有在 CRAN 中,它在 1.12.9 中。)
  • 接下来我看看data.table::fcase
  • 感谢data.table::fcase 的建议。它比 dplyr::case_when 快约 3 倍,当然比标准 for 循环快约 20 倍。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多