2016-04-11 31 views
0

我試圖在數據框中創建一個變量來引用前一行(在創建的變量中)來派生一個值。我對R相對來說比較陌生,我從excel中獲得了這種類型的自我引用和迭代更新功能,非常簡單。在R中計算一個自引用變量

mydata <- data.frame(trial = c(1,1,1,1,1,1,1,1,2,2), 
fixation=c("","","aoi1","aoi1","","aoi3","aoi3","","",""), 
trial.marker=c("","","","","","","",1,"","")) 
mydata 

trial fixation trial.marker 
1      
1      
1  aoi1    
1  aoi1    
1      
1  aoi3    
1  aoi3    
1      1 
2      
2      

詳細背景:這是一個來自更大的數據集,我有眼動數據的樣本。每一行表示每13ms記錄一次觀察結果。固定變量指示了受試者在試驗中在該點看到的屏幕上的哪個位置。我的目標(現在)是計算每個試驗的首次注射指徵(每個試驗中受試者最先注意的是什麼)。我的方法是首先計算試驗標記以標記每次試驗的結束,然後計算第一個固定變量,該變量將掃描固定變量以獲得「aoi1」或「aoi3」的第一個「命中」,然後保留所有信息直到試驗結束(試驗標記),然後我用這個試驗標記爲數據框編制索引,爲每個試驗抽出一行摘要,我需要編寫各種類型的變量,我知道怎麼做的最好方法是通過計算這些類型的自引用變量

短背景:我需要計算一個變量,它將檢測每次試驗的固定變量中的第一個觀察結果, aoi1「或」aoi3「,並將該信息記錄在與trial.marker變量中的1值相同的行上。

我使用shift()函數來計算trial.marker,但是當引用同一個變量的前一行時,這不起作用。我已經得到了它的工作的唯一辦法是用可怕的循環:

for (i in 1:nrow(mydata)){ 

if(i == 1){mydata$first.fixation[i]<- ""} 

else if (mydata$trial.marker[i-1]==1){mydata$first.fixation[i]<-""} 

else if (mydata$first.fixation[i-1] == ""){ 

    if(mydata$fixation[i] == "aoi1"){mydata$first.fixation[i] <- "aoi1"} 
    else if (mydata$fixation[i] == "aoi3"){mydata$first.fixation[i] <- "aoi3"} 
    else mydata$first.fixation[i] <- "" 
    } 
else mydata$first.fixation[i] <- mydata$first.fixation[i-1] 
} 

mydata 

trial fixation trial.marker first.fixation 
    1          
    1          
    1  aoi1      aoi1 
    1  aoi1      aoi1 
    1         aoi1 
    1  aoi3      aoi1 
    1  aoi3      aoi1 
    1      1   aoi1 
    2          
    2  

我上運行該數據集有120萬行,並運行此花了約5個小時,所以我希望有一種更具計算效率的方法來處理它。

對不起,如果我的R語言很奇怪和/或我的帖子格式是可怕的。這是我的第一次堆棧溢出post =)希望事情足夠清楚,你可以理解我在這裏要做的事情。因爲我是R新手,我相信也可能有一種完全不同的方法來解決這個問題,但我不介意......但是誰知道。

+0

戴夫,for循環中的「恐懼」大多是過去的事情。但是,如果您的代碼在for循環中「增長」了一個向量或其他對象,那麼效率會顯着下降。在所有編程語言中,尤其是R,在循環之前創建存儲對象。 (例如,數字矢量:'數字(長度= MyLength)'或數字矩陣'矩陣(0,myRows,myCols)')。如果你只是在修改一個對象,那麼這不是一個問題。也就是說,你使用for循環可以通過其他路線大大提高。 – lmo

回答

0

這是我拍的照片。注意我不是R的專家(更多的只是將它作爲一個學習練習),所以我希望別人能夠在我的代碼中發表或至少批評我的代碼。

我在您的數據中添加了更多行以進行支票檢查。它仍然會循環,但這次只是應該更快的試驗次數。

理想情況下,你避免了R中的循環,因爲向量化操作幾乎總是更快。

mydata <- data.frame(trial = c(1,1,1,1,1,1,1,1,2,2), 
        fixation=c("","","aoi1","aoi1","","aoi3","aoi3","","aoi3",""), 
        trial.marker=c("","","","","","","",1,"","")) 
mydata 
#structure shows it produces factored data (which I don't know enough about to like) 
str(mydata) 

#To avoid factors use stringsAsFactors = FALSE, also added blank column for first.fixation 
mydata <- data.frame(trial = c(1,1,1,1,1,1,1,1,2,2,3,3), 
        fixation=c("","","aoi1","aoi1","","aoi3","aoi3","","","","aoi3",""), 
        trial.marker=c("","","","","","","",1,"",2,"",""), 
        first.fixation="", 
        stringsAsFactors = FALSE) 
mydata 
str(mydata) 


trials<-unique(mydata$trial) 

#which returns the indices that match the criteria, function not used for anything just for demonstration 
which(mydata$fixation!="" & mydata$trial==1) 

#loop through trials 
for (i in 1:length(trials)){ 
    trial<-trials[i] 
    #If there are no fixation it would error out so if statement 
    if(length(which(mydata$fixation!="" & mydata$trial==trial))>0){ 
    #Find the last row with the given trial number 
    rowmax <- max(which(mydata$trial==trial)) 
    #Find the first row with given trial number and fixation 
    rowmin <- min(which(mydata$fixation!="" & mydata$trial==trial)) 
    #fill the data in 
    mydata$first.fixation[rowmin:rowmax] = mydata$fixation[rowmin] 
    } 
} 
mydata 
0

我會用data.table來解決這個問題,它通常會給出非常好的表現。雖然我沒有爲音量運行基準。這將是解決方案。

library(data.table) 
dt <- data.table(mydata) 
f <- function(fixation) { 
    if (length(which(fixation != "")) == 0) { 
    return(rep("", length(fixation))) 
    } 
    min_informed <- min(which(fixation != "")) 
    return(c(rep("", min_informed-1), rep(fixation[min_informed], length(fixation)-min_informed+1))) 
} 
dt[, fist.fixation:=f(fixation), by=list(trial)] 

和輸出

trial fixation trial.marker fist.fixation 
1:  1          
2:  1          
3:  1  aoi1      aoi1 
4:  1  aoi1      aoi1 
5:  1        aoi1 
6:  1  aoi3      aoi1 
7:  1  aoi3      aoi1 
8:  1      1   aoi1 
9:  2          
10:  2      2    
11:  3  aoi3      aoi3 
12:  3        aoi3 

猜你不熟悉data.table,代碼的一些解釋:在dt[, fist.fixation:=f(fixation), by=list(trial)],第一個參數是查詢,在這種情況下,所有的元素,第二個參數是根據函數f的結果創建新列first.fixation,並且第三個參數是按試驗進行分組=>因此函數f接收具有針對每個試驗的所有注視的向量。一旦你有了這個矢量,在函數f中,很容易知道哪一個是第一個被通知的,等等。

如果你決定檢查你的大數據框架,如果你發佈時間,這將是很好的。我認爲它應該按照幾分鐘的順序(儘管可能有幾分鐘)。

無論如何希望它有幫助。

+0

當我發現shift()時,我剛剛碰到data.table。看起來好像有很多潛力超越了data.frame,所以我一定會學習如何在有機會時使用它。 – Dave

+0

嗨。根據我的經驗(實際上並不那麼龐大),在大多數情況下,當我面對像你這樣有點複雜的轉換時,我嘗試使用'aggregate'和'plyr'等其他函數來完成它,我最終使用'data .table「,我得到的最終代碼更清晰和更快 - 儘管圖書館確實更難以學習。就你而言,如果你發佈的解決方案需要幾秒鐘的時間,我不認爲'data.table'會打敗它,因此沒有進一步調查的意義。乾杯! – lrnzcig

0

所以我很確定我用不同的方法解決了這個問題。鍵入我的問題說得很清楚,我認爲我正在尋找一個試用總結,所以我做了以下內容:

first.match <- function(x,y){ 
match.list <- sort(match(x,y),decreasing=FALSE) 
y[match.list[1]] 
} 

ff.data <-aggregate(x=exp2data$aoifixation, 
by=list(exp2data$subject,exp2data$trial), 
FUN=function(x) first.match(c("AOI1","AOI3"),x)) 

這給了我總結了每個試驗(每個主題的首屆固定;在本例中未顯示但是)。然後,我使用試驗標記技術對原始數據集進行索引,並將聚集的第一個固定矢量添加到修剪後的數據集中。

ff.data <- ff.data[order(ff.data$Group.1,ff.data$Group.2),] 
exp2data.trial <- exp2data[exp2data$trialmarker==1,] 
exp2data.trial$ff <- ff.data[,3] 

我仍然三重檢查,以確保它編碼正確,但它似乎是好的。最好的部分是它跑了幾秒鐘!我還沒有嘗試過,但我想通過修改聚合中的自定義函數,我能夠獲得所需的所有奇怪的眼動追蹤變量,這些變量只有在試用版數據中才需要。