2015-09-04 67 views
7

我是R的相對新手,所以我很抱歉如果有明顯的答案。我看過其他問題,我認爲「適用」就是答案,但在這種情況下我無法解決如何使用它。比'for'循環更有效的使用R的方法

我有一個縱向調查,每年邀請參與者。有些年代他們沒有參加,有時他們會死。從調查開始以來,我需要確定哪些參與者參加了一致的「連勝」(即,如果他們停止,他們會停下來)。

我已經用'for'循環完成了這個工作,它在下面的例子中工作正常。但是我有很多年和很多參與者,而且循環很慢。我可以使用更快的方法嗎?

在這個例子中,TRUE意味着他們參加了那一年。該循環創建了兩個向量 - 「finalyear」表示他們參加的最後一年,以及「連勝」表明他們是否已經完成了前幾年(即情況1,3和5)。

dat <- data.frame(ids = 1:5, "1999" = c(T, T, T, F, T), "2000" = c(T, F, T, F, T), "2001" = c(T, T, T, T, T), "2002" = c(F, T, T, T, T), "2003" = c(F, T, T, T, F)) 
finalyear <- NULL 
streak <- NULL 
for (i in 1:nrow(dat)) { 
    x <- as.numeric(dat[i,2:6]) 
    y <- max(grep(1, x)) 
    finalyear[i] <- y 
    streak[i] <- sum(x) == y 
} 
dat$finalyear <- finalyear 
dat$streak <- streak 

謝謝!

+0

很多答案 - 任何人都想創建一個更大的數據集並進行基準測試?數據集有多大,因此有可能爲基準測試做一個有代表性的測試集? – Spacedman

+0

約25萬個案件和25年。下面的所有答案解決了我的問題 - 謝謝大家!如果人們感興趣,我可以製作一個用於測試不同方法的代表性數據集。 –

回答

4

我們可以使用max.colrowSumsvectorized方法。

dat$finalyear <- max.col(dat[-1], 'last') 

如果有不TRUE值的行,我們可以確保通過與rowSums雙重否定乘以該行返回0。 FALSE將被強制爲0,並且乘以0會爲該行返回0。

dat$finalyear <- max.col(dat[-1], 'last')*!!rowSums(dat[-1]) 

然後,我們通過比較列rowSums 2創建「連勝」欄:與「finalyear」

dat$streak <- rowSums(dat[,2:6])==dat$finalyear 
dat 
# ids X1999 X2000 X2001 X2002 X2003 finalyear streak 
#1 1 TRUE TRUE TRUE FALSE FALSE   3 TRUE 
#2 2 TRUE FALSE TRUE TRUE TRUE   5 FALSE 
#3 3 TRUE TRUE TRUE TRUE TRUE   5 TRUE 
#4 4 FALSE FALSE TRUE TRUE TRUE   5 FALSE 
#5 5 TRUE TRUE TRUE TRUE FALSE   4 TRUE 

或者一個行代碼的6(它可能適合在一個-line,但決定通過2行顯示)@ColonelBeauvel建議

library(dplyr) 
mutate(dat, finalyear=max.col(dat[-1], 'last'), 
      streak=rowSums(dat[-1])==finalyear) 
+1

最簡潔和向量化的答案。 +1 –

+0

@ColonelBeauvel謝謝,我打算讓你升職,但它似乎被刪除了。 – akrun

+2

帶有mutate(dat,finalyear = max.col(dat [-1],'last'),streak = rowSums(dat [-1])== finalyear)的單線程' –

3

以下是dplyrtidyr的解決方案。

gather(data = dat,year,value,-ids) %>% 
    mutate(year=as.integer(gsub("X","",year))) %>% 
    group_by(ids) %>% 
    summarize(finalyear=last(year[value]), 
      streak=!any(value[first(year):finalyear] == FALSE)) 

輸出

ids finalyear streak 
1 1  2001 TRUE 
2 2  2003 FALSE 
3 3  2003 TRUE 
4 4  2003 FALSE 
5 5  2002 TRUE 
1

下面是使用apply循環基本版本在行和rle看到狀態變化的頻率。你的條件似乎是等同於啓動爲TRUE,只有不斷變化,以FALSE最多一次的狀態,所以我測試rle爲短於3和第一個值是TRUE

> dat$streak = apply(dat[,2:6],1,function(r){r[1] & length(rle(r)$length)<=2}) 
> 
> dat 
    ids X1999 X2000 X2001 X2002 X2003 streak 
1 1 TRUE TRUE TRUE FALSE FALSE TRUE 
2 2 TRUE FALSE TRUE TRUE TRUE FALSE 
3 3 TRUE TRUE TRUE TRUE TRUE TRUE 
4 4 FALSE FALSE TRUE TRUE TRUE FALSE 
5 5 TRUE TRUE TRUE TRUE FALSE TRUE 

有可能的負荷工作了finalyear的方式,這才發現其中每一行是TRUE的最後一個元素:

> dat$finalyear = apply(dat[,2:6], 1, function(r){max(which(r))}) 
> dat 
    ids X1999 X2000 X2001 X2002 X2003 streak finalyear 
1 1 TRUE TRUE TRUE FALSE FALSE TRUE   3 
2 2 TRUE FALSE TRUE TRUE TRUE FALSE   5 
3 3 TRUE TRUE TRUE TRUE TRUE TRUE   5 
4 4 FALSE FALSE TRUE TRUE TRUE FALSE   5 
5 5 TRUE TRUE TRUE TRUE FALSE TRUE   4 
4

for循環本質上並不壞R,但如果你的成長載體,他們是緩慢的反覆(如你正在做的)。通常有更好的方法來做事情。與只適用-功能的解決方案的實例:

dat$finalyear <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))}) 
dat$streak <- apply(dat[,2:7],MARGIN=1,function(x){sum(x[1:5])==x[6]}) 

或選項2,通過@Spacedman基於評論:

dat$finalyear <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))}) 
dat$streak <- apply(dat[,2:6],MARGIN=1,function(x){max(which(x))==sum(x)}) 

> dat 
    ids X1999 X2000 X2001 X2002 X2003 finalyear streak 
1 1 TRUE TRUE TRUE FALSE FALSE   3 TRUE 
2 2 TRUE FALSE TRUE TRUE TRUE   5 FALSE 
3 3 TRUE TRUE TRUE TRUE TRUE   5 TRUE 
4 4 FALSE FALSE TRUE TRUE TRUE   5 FALSE 
5 5 TRUE TRUE TRUE TRUE FALSE   4 TRUE 
+0

整潔,但要小心它取決於'finalyear'被直接添加在真/假數據後面,在這種情況下在列7中。 – Spacedman

+0

謝謝。我懷疑我是否應該這樣做,或者調用max(which(x))兩次。將編輯。 – Heroka