簡體   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