2015-07-12 47 views
4

我有一個包含MRN,日期和測試值的數據幀。R:選擇某一閾值以上的n個連續行的第一行

我需要選擇所有第一每具有高於0.5連續值MRN行。

這是數據的示例版本:

MRN Collected_Date ANC 
1 001  2015-01-02 0.345 
2 001  2015-01-03 0.532 
3 001  2015-01-04 0.843 
4 001  2015-01-05 0.932 
5 002  2015-03-03 0.012 
6 002  2015-03-05 0.022 
7 002  2015-03-06 0.543 
8 002  2015-03-07 0.563 
9 003  2015-08-02 0.343 
10 003  2015-08-03 0.500 
11 003  2015-08-04 0.734 
12 003  2015-08-05 0.455 
13 004  2014-01-02 0.001 
14 004  2014-01-03 0.500 
15 004  2014-01-04 0.562 
16 004  2014-01-05 0.503 

示例代碼:

df <- data.frame(MRN = c('001','001','001','001', 
         '002','002','002','002', 
         '003','003','003','003', 
         '004','004','004','004'), 
       Collected_Date = as.Date(c('01-02-2015','01-03-2015','01-04-2015','01-05-2015', 
              '03-03-2015','03-05-2015','03-06-2015','03-07-2015', 
              '08-02-2015','08-03-2015','08-04-2015','08-05-2015', 
              '01-02-2014','01-03-2014','01-04-2014','01-05-2014'), 
              format = '%m-%d-%Y'), 
       ANC = as.numeric(c('0.345','0.532','0.843','0.932', 
         '0.012','0.022','0.543','0.563', 
         '0.343','0.500','0.734','0.455', 
         '0.001','0.500','0.562','0.503'))) 

目前,我使用的是很彆扭的方法使用滯後函數來計算的時間差,然後過濾所有>> = 0.5的值,然後總結這些值,這有助於選擇THIRD值的日期。然後我。減去兩天時間到達第一值的日期:

df %>% group_by(MRN) %>% 
    mutate(., days_diff = abs(Collected_Date[1] - Collected_Date)) %>% 
     filter(ANC >= 0.5) %>% 
      mutate(days = days_diff + lag((days_diff))) %>% 
       filter(days == 5) %>% 
        mutate(Collected_Date = Collected_Date - 2) %>% 
         select(MRN, Collected_Date) 

輸出:

來源:本地數據幀[2×2] 組:MRN

MRN Collected_Date 
1 001  2015-01-03 
2 004  2014-01-03 

有一定是一種更簡單/更優雅的方式。另外,如果測試日期之間存在差距,則不會給出準確的結果。

我給這例如期望的輸出是:

MRN Collected_Date ANC  
1 001  2015-01-03 0.532 
2 004  2014-01-03 0.500 

因此,如果至少三個連續的測試值> = 0.5,所述第一值的日期應被返回。

如果沒有至少三個連續值> = 0.5,則應返回NA。

任何幫助,非常感謝!

非常感謝!

+1

看看'cumany' - 這是完美的這種情況。 – hadley

回答

7

最簡單的方法是將zoo庫與dplyr結合使用。在zoo包內有一個叫做rollapply的函數,我們可以用它來計算一個時間窗口的函數值。

在這個例子中,我們可以應用窗口來計算下三個值的最小值,然後應用指定的邏輯。

df %>% group_by(MRN) %>% 
    mutate(ANC=rollapply(ANC, width=3, min, align="left", fill=NA, na.rm=TRUE)) %>% 
    filter(ANC >= 0.5) %>% 
    filter(row_number() == 1) 

# MRN Collected_Date ANC 
# 1 001  2015-01-03 0.532 
# 2 004  2014-01-03 0.500 

在上述我們的代碼已經使用rollapply計算最小接下來的3項。要看到這是如何工作的比較如下:

rollapply(1:6, width=3, min, align="left", fill=NA) # [1] 1 2 3 4 NA NA 
rollapply(1:6, width=3, min, align="center", fill=NA) # [1] NA 1 2 3 4 NA 
rollapply(1:6, width=3, min, align="right", fill=NA) # [1] NA NA 1 2 3 4 

因此在我們的例子中,我們從左邊對齊,因此從當前位置開始,並期待着在未來2倍的值。

最後,我們按適當的值進行過濾,並對每個組進行第一次觀察。

1

這是ddply解決方案(對不起,我沒有及時更新%>%語法,但也許可以應用它)。

我不確定在你的意思上它是否「優雅」,但它在第二次閱讀時會有意義(這對我來說比單線更重要),並且對失蹤很有幫助日期等

關鍵是使用rle(運行長度編碼)查找'運行'ANC >= 0.5其中運行至少是長度3.這需要照顧'連續'部分。我們將其保存到r

然後r.i給出第一次運行中的長度爲3或更長的索引,並且其中運行的值爲TRUE

要獲得指數x你剛纔sum運行長度可達但不包括運行我們感興趣的是,加1得到的開始(這是sum(r$lengths[1:(r.i - 1)])+1)。

ddply(df, 
.(MRN), 
function (x) { 
    r <- rle(x$ANC >= 0.5) # find 'runs' of x$ANC >= 0.5 
    # find index of first run of length >=3 with ANC >= .5 
    r.i <- which(r$lengths >= 3 & r$values)[1] 
    if (!is.na(r.i)) { 
     # get index of first row in that run and return it. 
     return(x[sum(r$lengths[seq_len(r.i - 1)]) + 1, ]) 
    } 
    return(NULL) 
}) 

如果你提取例如數據,它會更好。 x <- subset(df, MRN == '001')並逐步瞭解r,r.i的樣子。

2

我們可以創建給定x返回指示連續值高於給定閾值的數目的向量的向量的輔助功能:

high_run <- function(x, threshold) { 
    high <- x >= threshold 
    streak <- high[1] 
    for(h in high[2:length(high)]){ 
     streak <- c(streak, streak[length(streak)]*h + h) 
    } 
    run 
} 

以及返回在第一次運行的起始索引的函數特定長度的:

high_run_start <- function(x, threshold, run){ 
    match(run, high_run(x, threshold)) - run + 1 
} 

我們可以然後使用這個後一個函數來選擇原始數據幀的適當的行:

> df %>% group_by(MRN) %>% 
+ filter(row_number()==high_run_start(ANC,0.5,3)) 
Source: local data frame [2 x 3] 
Groups: MRN 

    MRN Collected_Date ANC 
1 001  2015-01-03 0.532 
2 004  2014-01-03 0.500 
3

基本方法:

使用rle發現的3個或更多序列,並搶第一個

df <- data.frame(MRN = c('001','001','001','001','002','002','002','002','003','003','003','003','004','004','004','004'), Collected_Date = as.Date(c('01-02-2015','01-03-2015','01-04-2015','01-05-2015', '03-03-2015','03-05-2015','03-06-2015','03-07-2015', '08-02-2015','08-03-2015','08-04-2015','08-05-2015', '01-02-2014','01-03-2014','01-04-2014','01-05-2014'), format = '%m-%d-%Y'), ANC = as.numeric(c('0.345','0.532','0.843','0.932', '0.012','0.022','0.543','0.563', '0.343','0.500','0.734','0.455', '0.001','0.500','0.562','0.503'))) 

df[as.logical(with(df, ave(ANC, MRN, FUN = function(x) 
    cumsum(x >= .5 & with(rle(x >= .5), rep(lengths, lengths)) >= 3) == 1))), ] 

# MRN Collected_Date ANC 
# 2 001  2015-01-03 0.532 
# 14 004  2014-01-03 0.500 

也許這個版本更容易理解

df[as.logical(with(df, ave(ANC, MRN, FUN = function(x) { 
    r <- rle(x >= .5) 
    r <- rep(r$lengths, r$lengths) 
    cumsum(r == 3 & x >= .5) == 1 
    }))), ] 

編輯

df <- df[c(1:4,4,4,4,5,5,5,5:16), ] 
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x) 
    cumsum(x >= .5 & with(rle(x >= .5), rep(lengths, lengths)) >= 3) == 1))), ] 

# MRN Collected_Date ANC 
# 2 001  2015-01-03 0.532 
# 14 004  2014-01-03 0.500 
+0

這也可以找到長度爲3的運行,其中ANC <0.5(不只是> =),並且將跳過長度大於3的運行(只會給你正好3的運行) –

+0

@ mathematical.coffee fixed – rawr

相關問題