【问题标题】:How to write a for loop with if statements across multiple columns?如何使用跨多列的 if 语句编写 for 循环?
【发布时间】:2021-09-20 00:21:51
【问题描述】:

R 中我的数据框中的一列有一个方向(左、右、L 或 R)。如果此列中的一行是左或 L,我试图将不同列中同一行中的数字转换为负值。这是我目前写的代码:

for(i in 1:nrow(df)){
  if(is.na(df[i,7]==F)){
    if(df[i,7]=="Left"  | df[i,7]=="L"){
      if(is.numeric(df[i,11])==T){
        lapply(df[i,11], all.neg)
      }
    }
  }
}

我不断收到以下错误消息:

Error in if (df[i, 7] == "Left" | df[i, 7] == "L") { : 
  missing value where TRUE/FALSE needed

我试图做 na.pass(df) 以避免在缺失值后停止,并且我包含了 is.na() 的第一个 if 语句,这似乎没有必要。我还检查并确保没有其他值(例如“Null”)未被正确编码为 NA。如果有人知道如何解决此问题,我将不胜感激 - 非常感谢!

这是数据的截图。基本上,如果 LSTOA 方向为左或 L,我需要将所有值更改为负数。 enter image description here

这是数据的头部:

structure(list(`LSTOA Direction` = c("Left", "Left", "Left", 
"Right", "Left", "Left"), `Preop PA` = c(NA, "6.5", "13.3", NA, 
NA, "11.0"), `1st Erect` = c(NA, NA, "2.8", NA, "7.6", "2.8"), 
    `6M PO PA` = c(NA_character_, NA_character_, NA_character_, 
    NA_character_, NA_character_, NA_character_), `1Y PO PA` = c("7.5", 
    NA, "3.3", "5.5", NA, NA), `2Y PO PA` = c(NA, NA, "0.1", 
    "5.8", "7.2", "2.5"), `5Y PO PA` = c(NA, NA, NA, "3.9", "4.4", 
    NA), `10Y PO PA` = c("7.8", NA, NA, "2.6", NA, NA), `15Y PO PA` = c(NA, 
    NA, NA, "3.2", NA, NA)), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))

【问题讨论】:

  • 嗨,我认为使用dplyr 可以更轻松地实现这一点。您能否提供一个可重复的输入和输出示例 - 它会让其他人更容易帮助您。谢谢。
  • 您要查找的dplyr 包中的函数是case_when。查看它的文档,看看是否有帮助!
  • (1) 在if 中,不要使用|(单管),除非你将它包裹在anyall 中,否则你的if 条件不正确。在if 中,条件的长度必须始终为 1。其他任何事情都会导致您看到的错误(如果是NANULL)或the condition has length > 1 ...。 (2) 拥有样本数据和预期输出将大有帮助。请参阅stackoverflow.com/q/5963269minimal reproducible examplestackoverflow.com/tags/r/info

标签: r dataframe for-loop if-statement na


【解决方案1】:

样本数据。

dat <- data.frame(A=c("L","Right","R","Left"), B=1:4, C=11:14, D=LETTERS[1:4])
dat
#       A B  C D
# 1     L 1 11 A
# 2 Right 2 12 B
# 3     R 3 13 C
# 4  Left 4 14 D

基础 R

mult <- 1-2*grepl("^L", dat$A)
mult
# [1] -1  1  1 -1
isnum <- sapply(dat, is.numeric)
isnum
#     A     B     C     D 
# FALSE  TRUE  TRUE FALSE 
dat[isnum] <- lapply(dat[isnum], `*`, must)
# *** output flushed ***
dat[isnum] <- lapply(dat[isnum], `*`, mult)
dat
#       A  B   C D
# 1     L -1 -11 A
# 2 Right  2  12 B
# 3     R  3  13 C
# 4  Left -4 -14 D

dplyr

library(dplyr)
dat %>%
  mutate(across(where(is.numeric), ~ if_else(grepl("^L", A), -1, 1) * .))
#       A  B   C D
# 1     L -1 -11 A
# 2 Right  2  12 B
# 3     R  3  13 C
# 4  Left -4 -14 D

【讨论】:

  • 我尝试在数据框上使用这两种方法,我现在发布了上面的屏幕截图。代码运行没有错误,但它不会将任何值更改为负数。我唯一改变的是 A 到 LSTOA 方向,因为这是我的专栏的名称。谢谢!
  • 没关系,但我无法处理数据图像。如果您希望人们能够使用您的数据,请编辑您的问题并将dput(head(x)) 的输出粘贴到code block
  • 我已更新以在代码块中包含数据头 - 谢谢!
  • 您查看过自己的数据吗?所有列都是character,没有任何数字。
  • 那行得通 - 我应该在一切都好之前检查一下 :)。感谢您的所有帮助!
【解决方案2】:

如果您有一个 data.frame,其中有两列,一列在第 7 位带有方向指示符,另一列在第 11 位带有数字值,您希望变为负数,然后尝试这种“矢量化”重新分配

 #Copy the column to be altered
 df$newval <- df[[11]]
 df$newval[ grepl("^L", df[[7]]) ] <- -abs( df$newval[ grepl("^L", df[[7]]) ])

LHS 和 RHS 都具有相同的逻辑索引,当第一个字母是大写字母“L”时将为 TRUE,因此只有这些值得到否定处理。现在它将是负数(即使一开始是负数。)如果您不希望出现这种情况,请删除abs,它将是符号反转值。如果使用矢量化方法绕过循环,我在这里做什么。有一个逻辑向量grepl("^L", df[[7]]),用于确定是否修改行值。

有几点可以改进您的编码。永远不要使用 T 和 F。如果你忘记它们可以用非逻辑值替换,它们会让你大吃一惊。 TRUE 和 FALSE 永远无法重新定义。当您使用is.numeric 时,测试是否等于 T 或 TRUE 是没有用的。而不是is.numeric(df[i,11])==T,只需使用is.numeric(df[i,11])。但是,您不会希望在执行行测试或行分配的外观中使用 is.numeric。向量都是数字或非数字的。在循环外进行测试以提高效率。我看到 r2evans 提供了一个 MCVE 用于测试。请注意,他将其命名为 dat,这是您应该遵循的做法。否则,您将继续看到错误:Error in df[[2]] : object of type 'closure' is not subsettable,直到您意识到 df 是 F 分布密度的函数名称,这才有意义。

【讨论】:

    猜你喜欢
    • 2018-03-16
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多