如何用R中的应用族函数替换多个嵌套for循环?

时间:2016-04-01 19:17:05

标签: r loops for-loop nested lapply

我的数据集(dat)中有四个主要变量。

  1. SubjectID
  2. 组(可以是Easy1,Easy2,Hard1,Hard2)
  3. 对象(x,y,z,w)
  4. 反应时间
  5. 对于变量1,2和3的每个组合,我想更改反应时间,以便将第3个四分位数+ 1.5IQR以上的所有值设置为第三个四分位数+ 1.5个IQR的值。

    TUK <- function (a,b,c) {
    ....
    }
    

    基本上,for循环逻辑是:

    for (i in dat$SubjectID):
    for (j in dat$Group):
    for (k in dat$Object) :
    TUK(i,j,k)
    

    如何使用apply function family?

    谢谢!

    添加可重复的示例:

    SubjectID <- c(3772113,3772468)
    Group <- c("Easy","Hard")
    Object <- c("A","B")
    dat <- data.frame(expand.grid(SubjectID,Group,Object))
    dat$RT <- rnorm(8,1500,700)
    colnames(dat) <- c("SubjectID","Group","Object","RT")
    
    TUK <- function (SUBJ,GROUP,OBJECT){
      p <- dat[dat$SubjectID==SUBJ & dat$Group== GROUP & dat$Object==OBJECT, "RT"]
    
      p[p$RT< 1000 | p$RT> 2000,] <- NA
    
      dat[dat$SubjectID==SUBJ & dat$Group== GROUP & dat$Object==OBJECT, "RT"]<<- p
    }
    

1 个答案:

答案 0 :(得分:1)

您的问题很大一部分是您的TUK功能可怕。以下是

的原因
  • 问题:它取决于在全局环境中有一个名为dat的数据框。更改数据的名称,它就会中断。

    • 解决方案:您应该传递所需的所有参数。在这种情况下,dat应该是一个参数。
  • 问题:应避免全局作业<<- 。某些高级案例中有必要(例如,有时在Shiny应用程序中),但一般情况下它会使函数以非常类似于R的方式运行。

    • 解决方案:只需return()一个值,并将其指定为任何其他正常R函数。
  • 问题:过于复杂。您通过传入SUBJ,GROUP和OBJECT但只使用它们来进行子集您正在尝试在您的函数内部执行dplyrdata.tablebase::ave擅长的“分组”位。这就好像你试图以某种方式构建你的函数,以便只能在这个特定的for循环中嵌入使用。

    • 解决方案:功能应该是简单的构建块。使其成为单个矢量的功能。它将更清洁,更容易调试。当它适用于单个向量时,请使用dplyrdata.tableave(或甚至是for循环)对其进行拆分应用组合。这也使您的功能更普遍有用,而不是粘合到这一特定情况。

考虑到上述情况,这是一次尝试重写:

TUK2 <- function (RT){
  RT[RT < 1000 | RT > 2000] <- NA
  return(RT)
}

看多少简单!现在,如果我们要将此函数应用于数据中的每个GROUP:SUBJ:OBJECT分组,并将RT列替换为结果,我们使用dplyr执行此操作:

library(dplyr)
group_by(dat, Group, SubjectID, Object) %>%
    mutate(new_RT = TUK2(RT))

dplyr对数据进行分组,即对数据进行分割,将简单函数应用于每个部分,并将它们全部组合在一起。

现在,在你的问题中,你说

  

对于变量1,2和3的每个组合,我想更改反应时间,以便将第3个四分位数+ 1.5IQR以上的所有值设置为第3个四分位数+ 1.5个IQR的值。

这听起来不像你的功能。仅基于此描述,我将其编码为

group_by(dat, Group, SubjectID, Object) %>%
    mutate(new_RT = pmin(RT, quantile(RT, probs = 0.75) + 1.5 * IQR(RT)))

pmin用于并行最小值,它是采用两个向量中较小的向量的矢量化方式。请尝试,例如pmin(1:10, 7),看看它的作用。

在这两个示例中,除非您使用dplyr等重新分配dat <- group_by(dat, ...)数据框,否则不会保存dplyr数据框。这是功能性编程方式 - 没有全球任务。

另外一个注意事项:使用重写功能,您仍然可以使用循环而不是dplyr。我不知道为什么你会 - dplyr语法肯定更好 - 但我只想说明小构建块功能通常很有用,它是不是< / strong>“烘烤”for (sub %in% unique(dat$SubjectID)) { for (obj %in% unique(dat$Object)) { for (grp %in% unique(dat$Group)) { dat[dat$SubjectID == sub & dat$Object == obj & dat$Group == grp, "RT"] <- TUK2( dat[dat$SubjectID == sub & dat$Object == obj & dat$Group == grp, "RT"] ) } } } 以原始函数“烘焙”特定for循环的方式。

import UIKit

class ContactCell: UITableViewCell {

    @IBOutlet var lblContactName: UILabel!
    @IBOutlet var lblContactTitle: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
}