在列上迭代 for 循环时出错:"argument is of length zero"

Error when iterating for loop over columns: "argument is of length zero"

我有一个数据框"comp"。参考样本:

comp <- data.frame(A=c(1:5), B=c(1,0,1,0,0), C=c(5,2,0,0,NA), D=c(1,3,1,NA,0))

  A B  C D
1 1 1  5 1
2 2 0  2 3
3 3 1  0 1
4 4 0  0 NA
5 5 0 NA 0

我想在每一列(不包括前两列)上迭代一个 for 循环。基本上,循环应该根据该单元格中的值和该行第 2 列中的值打印特定字符串或 NA。在 C 中打印什么的规则是:

这些相同的规则也适用于 D 列(只需将上述规则中的 C 替换为 D)。对于我的示例数据,它看起来像这样:

  A B C              D
1 1 1 "Ysnp, Yphen"  "Ysnp, Yphen"
2 2 0 "Ysnp, Nphen"  "Ysnp, Nphen"
3 3 1 "Nsnp, Yphen"  "Ysnp, Yphen"
4 4 0 "Nsnp, Nphen"  NA
5 5 0 NA             "Nsnp, Nphen"

我的真实数据集有 50 多列,因此对每一列应用 for 循环很繁琐。这是我试过的:

sapply(comp[,-(1:2)], function(snp) {
  for (i in 1:nrow(comp)){
    if (comp$snp[i]!=0 & !is.na(comp$snp[i])){
      if (comp[i, 2]==1) comp$snp[i] <- "Ysnp, Yphen"
      else comp$snp[i] <- "Ysnp, Nphen"
    }
    else if (comp$snp[i]==0 & !is.na(comp$snp[i])){
      if (comp[i, 2]==1) comp$snp[i] <- "Nsnp, Yphen"
      else comp$snp[i] <- "Nsnp, Nphen"
    }
    else comp$snp[i] <- NA
  }
})

然而,当我 运行 这个循环时,我得到以下错误:

Error in if (comp$snp[i] != 0 & !is.na(comp$snp[i])) { : 
  argument is of length zero

我已经检查过我的数据框不包含任何 NULL 值,所以我不确定为什么循环会生成此错误。我还尝试在整个循环中将 comp$snp[i] 替换为 comp[i, snp],或者使用 apply 而不是 sapply,但这并没有解决问题。

这对case_when来说应该是一件简单的事情:

comp <- data.frame(A=c(1:5), B=c(1,0,1,0,0), C=c(5,2,0,0,NA))

library(tidyverse);
comp %>%
    mutate(C = case_when(
        C > 0 & B == 1 ~ "Ysnp, Yphen",
        C > 0 & B == 0 ~ "Ysnp, Nphen",
        C == 0 & B == 1 ~ "Nsnp, Yphen",
        C == 0 & B == 0 ~ "Nsnp, Nsnp",
        is.na(C) ~ "NA"));
#  A B           C
#1 1 1 Ysnp, Yphen
#2 2 0 Ysnp, Nphen
#3 3 1 Nsnp, Yphen
#4 4 0  Nsnp, Nsnp
#5 5 0          NA

规则:

  • 如果 C 为正且 B 为 1:"Ysnp, Yphen"
  • 如果 C 为正且 B 为 0:"Ysnp, Nphen"
  • 如果 C 为 0,B 为 1:"Nsnp, Yphen"
  • 如果 C 为 0,B 为 0:"Nsnp, Nsnp"
  • 如果 C 是 NA: NA

更新

对于任意数量的列,您可以使用 for 循环。 for 循环将非常快,因为您只是替换现有 data.frame 中的条目,并且没有动态内存(重新)分配。

comp <- data.frame(A=c(1:5), B=c(1,0,1,0,0), C=c(5,2,0,0,NA), D=c(1,3,1,NA,0))


df <- comp;
for (i in 3:ncol(df)) {
    df[, i] <- ifelse(is.na(df[, i]), "NA", paste(
        ifelse(df[, i] > 0, "Ysnp", "Nsnp"),
        ifelse(df$B == 1, "Yphen", "Nphen"), sep = ", "));
}
#  A B           C           D
#1 1 1 Ysnp, Yphen Ysnp, Yphen
#2 2 0 Ysnp, Nphen Ysnp, Nphen
#3 3 1 Nsnp, Yphen Ysnp, Yphen
#4 4 0 Nsnp, Nphen          NA
#5 5 0          NA Nsnp, Nphen

事实证明你甚至不需要 for 循环但可以使用直接索引。

df[, 3:ncol(df)] <- ifelse(is.na(df[, 3:ncol(df)]), "NA", paste(
    ifelse(df[, 3:ncol(df)] > 0, "Ysnp", "Nsnp"),
    ifelse(df$B == 1, "Yphen", "Nphen"), sep = ", "));
df;
#  A B           C           D
#1 1 1 Ysnp, Yphen Ysnp, Yphen
#2 2 0 Ysnp, Nphen Ysnp, Nphen
#3 3 1 Nsnp, Yphen Ysnp, Yphen
#4 4 0 Nsnp, Nphen          NA
#5 5 0          NA Nsnp, Nphen