如何使用跨多列的 if 语句编写 for 循环?

How to write a for loop with if statements across multiple columns?

我的 R 数据框中的一列有一个方向(左、右、L 或 R)。如果此列中的一行是 Left 或 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)
      }
    }
  }
}

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

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

示例数据。

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

如果你有一个 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]) 而不是 is.numeric(df[i,11])==T。但是,您不想在进行行测试或行分配的外观中使用 is.numeric。矢量是全数字或非数字的。在循环外测试以获得更高的效率。我看到 r2evans 提供了一个 MCVE 用于测试。请注意,他将其命名为 dat,这是您应该遵循的做法。否则您将继续看到错误:Error in df[[2]] : object of type 'closure' is not subsettable 在您意识到 df 是 F 分布密度的函数名称之前,这没有任何意义。