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

问题描述 投票:0回答:2

我试图遍历数据框中的每个值,并基于该值从另一个数据框中提取信息。我有一些代码可用于执行嵌套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_1pp)从pq中的值列中提取的。对于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循环,因为我一直在使用嵌套的循环,这需要花费过多的时间。任何帮助确定如何为这个简化的数据集做这个将不胜感激。一旦我这样做,我可以将其应用于数据框,例如具有多行的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文件生成的每个列表的迭代

r for-loop if-statement apply
2个回答
1
投票

您的代码有两个问题。一个是你的应用函数是在错误的对象上运行,另一个是你无法通过sapply访问元素的名称

现在sapply(ind_1, function(x) {pop.prob(df, ind_1)})说“对于ind_1的每个元素使用df和所有的pop.probind_1”,因此不正确的矩阵输出。要在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


0
投票

这是一个矢量化的data.table解决方案。应该比applyfor版本快得多。更不用说更简洁了。

library(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
© www.soinside.com 2019 - 2024. All rights reserved.