繁体   English   中英

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

[英]How to replace mutiple nested for loops with apply family functions in R?

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

  1. 主题ID
  2. 组(可以是 Easy1、Easy2、Hard1、Hard2)
  3. 对象(x、y、z、w)
  4. 反应时间

对于变量 1、2 和 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 函数系列来做到这一点?

谢谢!

添加可重现的示例:

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
}

您的问题的很大一部分是您的TUK功能很糟糕 以下是一些原因

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

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

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

    • 解决方案:函数应该是简单的构建块。 使其成为仅单个向量的函数。 它会更干净,更容易调试。 当它在单个向量上工作时,使用dplyrdata.tableave (甚至是for循环)对其进行拆分-应用-组合。 这也使您的功能更普遍有用,而不是固定在这种特殊情况下。

考虑到上述情况,这里尝试重写:

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

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

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

dplyr对数据进行分组,数据的拆分,将简单的功能应用于每个部分,然后为我们将它们全部组合在一起。


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

对于变量 1、2 和 3 的每个组合,我想更改反应时间,以便将高于第三四分位数 + 1.5IQR 的所有值设置为第三四分位数 + 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原始函数那样“烘焙” dplyr在”特定的 for 循环中。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM