【问题标题】:R: Selecting first of n consecutive rows above a certain threshold valueR:选择高于某个阈值的 n 个连续行中的第一行
【发布时间】:2015-07-12 22:35:18
【问题描述】:

我有一个包含 MRN、日期和测试值的数据框。

我需要选择每个 MRN 的所有 first 行,其中有 三个 连续值高于 0.5。

这是数据的示例版本:

   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')))

目前,我正在使用一种非常尴尬的方法,使用 lag 函数来计算日期差,然后过滤所有 >= 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 x 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,则应返回 FIRST 值的日期。

如果没有至少三个连续值 >= 0.5,则应返回 NA。

非常感谢任何帮助!

非常感谢!

【问题讨论】:

  • 看看cumany - 它非常适合这种情况。

标签: r dataframe row dplyr


【解决方案1】:

最简单的方法是将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 个值。

最后,我们通过适当的值进行过滤,并对每组进行第一次观察。

【讨论】:

    【解决方案2】:

    基本方法:

    使用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
    

    【讨论】:

    • 这还将找到长度为 3 的游程,其中 ANC =),并且会跳过长度大于 3 的游程(只会为您提供正好 3 次的游程)
    • @mathematical.coffee 固定
    【解决方案3】:

    我们可以创建一个辅助函数,它给定一个向量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
    

    【讨论】:

      【解决方案4】:

      这是一个ddply 解决方案(抱歉,我不了解%&gt;% 语法,但也许它也可以应用)。

      我不确定它是否像您所说的那样“优雅”,但在第二次阅读时会有意义(对我来说,这比单线更重要),并且对于错过日期很有效等等

      关键是使用rle(运行长度编码)来查找ANC &gt;= 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 &lt;- subset(df, MRN == '001') 并逐步查看rr.i 的外观。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-26
        • 2019-04-10
        • 2017-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多