R使用if语句而不是for循环

时间:2018-03-01 19:33:12

标签: r for-loop if-statement apply

我试图遍历数据框中的每个值,并根据该值从另一个数据框中提取信息。我有一些代码可用于执行嵌套for循环,但我正在使用运行时间太长而无法实现的大型数据集。

为简化起见,我将提供最初只有一行的样本数据:

ind_1 <- data.frame("V01" = "pp", "V02" = "pq", "V03" = "pq")
ind_1
#  V01 V02 V03
#1 pp  pq  pq

我也有这个数据框:

stratum <- rep(c("A", "A", "B", "B", "C", "C"), 3)
locus <- rep(c("V01", "V02", "V03"), each = 6)
allele <- rep(c("p", "q"), 9)
value <- rep(c(0.8, 0.2, 0.6, 0.4, 0.3, 0.7, 0.5, 0.5, 0.6), 2)
df <- as.data.frame(cbind(stratum, locus, allele, value))
head(df)
#   stratum locus allele value
#1        A   V01      p   0.8
#2        A   V01      q   0.2
#3        B   V01      p   0.6
#4        B   V01      q   0.4
#5        C   V01      p   0.3
#6        C   V01      q   0.7

每个基因座有两个等位基因值,每个基因座的层也有三个值,因此每个基因座有六个不同的值。 ind_1的列名对应locus中的df列。对于ind_1中的每个条目,我想根据dflocus中的列名称)返回从ind_1中的值列中提取的值列表,数据条目(pppq)。对于ind_1中的每个条目,列表中将有三个返回值,每个stratum位于df

我尝试的代码如下:

library(dplyr)
library(magrittr)
pop.prob <- function(df, ind_1){
  p <-  df %>%
    filter( locus == colnames(ind_1), allele == "p")
  p <- as.numeric(as.character(p$value))
  if( ind_1 == "pp") {
    prob <- (2 * p * (1-p))
    return(prob)
  } else if ( ind_1 == "pq") {
    prob <- (p^2)
    return(prob)
  } 
}
test <- sapply(ind_1, function(x) {pop.prob(df, ind_1)} )

此代码提供的值包含不正确的值:

      V01  V02  V03
[1,] 0.32 0.32 0.32
[2,] 0.32 0.32 0.32
[3,] 0.42 0.42 0.42

以及警告信息:

# 1: In if (ind_1 == "pp") { :
# the condition has length > 1 and only the first element will be used

理想情况下,我会得到以下输出:

> test
# $V01
# 0.32 0.48 0.42
#
# $V02
# 0.25 0.36 0.04
#
# $V03
# 0.16 0.49 0.25

我一直试图弄清楚如何在我的代码中不使用for循环,因为我一直在使用嵌套for循环,这需要花费过多的时间。任何帮助确定如何为这个简化的数据集做这个将不胜感激。一旦我这样做,我就可以将其应用于数据框,例如ind_1有多行

谢谢大家,如果示例数据不清楚,请告诉我

修改

这是我的代码,它适用于for循环:

pop.prob.for <- function(df, ind_1){
  prob.list <- list()
  for( i in 1:length(ind_1)){
    p <-  df %>%
      filter( locus == colnames(ind_1[i]), allele == "p")
    p <- as.numeric(as.character(p$value))
    if( ind_1[i] == "pp") {
      prob <- (2 * p * (1-p))
    } else if ( ind_1[i] == "pq") {
      prob <- (p^2)
    } 
    prob.list[[i]] <- prob
  }
  return(prob.list)
}
pop.prob.for(df, ind_1)

对于我的实际数据,我将添加一个额外的循环来遍历类似于ind_1的数据框中的多个行,并保存作为.rdata文件生成的每个列表的迭代

2 个答案:

答案 0 :(得分:1)

您的代码存在两个问题。一个是你应用函数正在错误的对象上运行,另一个是你无法通过sapply

访问元素的名称

现在sapply(ind_1, function(x) {pop.prob(df, ind_1)})使用df和全部 ind_1&#为pop.probind_1的每个元素说&#34; 34;因此矩阵输出不正确。要在ind_1上按元素操作,您需要编写sapply(ind_1, function(x) {pop.prob(df, ind_1)})

此更改不起作用,因为您在函数中提取列名称,而"pp"(第一个元素)没有列名。要使用您编写的函数,您需要编写:

test <- sapply(1:dim(ind_1)[2], function(x) {pop.prob(df, ind_1[x])})

这样您就可以以与for循环相同的方式进行迭代。另请注意,由于sapply尝试将lapply输出强制转换为矢量或矩阵,因此您需要获取矩阵。如果您想要一个列表,只需使用lapply

答案 1 :(得分:0)

这是一个矢量化apply解决方案。应该 forlibrary(data.table) setDT(df)[, value := as.numeric(as.character(value))] df[allele=='p', .(prob = {if (ind_1[.GRP]=='pp') 2*value*(1-value) else value^2}), by = locus] # locus prob # 1: V01 0.32 # 2: V01 0.48 # 3: V01 0.42 # 4: V02 0.25 # 5: V02 0.36 # 6: V02 0.04 # 7: V03 0.16 # 8: V03 0.49 # 9: V03 0.25 版本更快。更不用说简洁了。

schedule: every mon,tue,wed,thu,fri 00:10