【问题标题】:Counting number of rows since last observation that fulfills condition计算自上次观察以来满足条件的行数
【发布时间】:2019-02-07 09:40:57
【问题描述】:

我的Data Frame 看起来类似于此示例的前三列:

id    obs   value   newCol
a     1     uncool  NA
a     2     cool    1
a     3     uncool  NA
a     4     uncool  NA
a     5     cool    2
a     6     uncool  NA
a     7     cool    1
a     8     uncool  NA
b     1     cool    0

我需要的是一个列(上面的 newCol),它计算值为“cool”的观察值或组的第一行(按 id 分组)之间的“uncool”数量。

我该怎么做(最好使用dplyr)?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    我们可以通过从底部开始执行cumsum 来定义组,然后使用ave 为每个组构建一​​个向量:

    transform(dat, newCol = ave(
      value, id, rev(cumsum(rev(value=="cool"))),
      FUN = function(x) ifelse(x=="cool", length(x)-1, NA)))
    #   id obs  value newCol
    # 1  a   1 uncool   <NA>
    # 2  a   2   cool      1
    # 3  a   3 uncool   <NA>
    # 4  a   4 uncool   <NA>
    # 5  a   5   cool      2
    # 6  a   6 uncool   <NA>
    # 7  a   7   cool      1
    # 8  a   8 uncool   <NA>
    # 9  b   1   cool      0
    

    使用 dplyr :

    dat %>%
      group_by(id,temp = rev(cumsum(rev(value=="cool")))) %>%
      mutate(newCol = ifelse(value=="cool", n()-1, NA)) %>%
      ungroup() %>%
      select(-temp)
    # # A tibble: 9 x 4
    # id   obs  value newCol
    #   <chr> <int>  <chr>  <dbl>
    # 1     a     1 uncool     NA
    # 2     a     2   cool      1
    # 3     a     3 uncool     NA
    # 4     a     4 uncool     NA
    # 5     a     5   cool      2
    # 6     a     6 uncool     NA
    # 7     a     7   cool      1
    # 8     a     8 uncool     NA
    # 9     b     1   cool      0
    

    【讨论】:

      【解决方案2】:

      除了id,您还需要另一个分组变量,由grp = cumsum(dat$value == "cool") - (dat$value == "cool") 给出,如下所示。

      然后您可以使用mutate,我们将sum(value == "uncool") 分配给每个组内的value == "cool" 和NA 的观察值。

      library(dplyr)
      dat %>%
        group_by(id, grp = cumsum(dat$value == "cool") - (dat$value == "cool")) %>% 
        mutate(newCool = if_else(value == "cool", sum(value == "uncool"), NA_integer_))
      # A tibble: 9 x 6
      # Groups:   id, grp [5]
        id      obs value  newCol   grp newCool
        <chr> <int> <chr>   <int> <int>   <int>
      1 a         1 uncool     NA     0      NA
      2 a         2 cool        1     0       1
      3 a         3 uncool     NA     1      NA
      4 a         4 uncool     NA     1      NA
      5 a         5 cool        2     1       2
      6 a         6 uncool     NA     2      NA
      7 a         7 cool        1     2       1
      8 a         8 uncool     NA     3      NA
      9 b         1 cool        0     3       0
      

      数据

      dat <- structure(list(id = c("a", "a", "a", "a", "a", "a", "a", "a", 
      "b"), obs = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L), value = c("uncool", 
      "cool", "uncool", "uncool", "cool", "uncool", "cool", "uncool", 
      "cool"), newCol = c(NA, 1L, NA, NA, 2L, NA, 1L, NA, 0L)), .Names = c("id", 
      "obs", "value", "newCol"), class = "data.frame", row.names = c(NA, 
      -9L))
      

      【讨论】:

        【解决方案3】:

        我们可以创建一个辅助函数,它将基于cool/uncool 分组value,并计算cools,即

        library(tidyverse)
        
        f1 <- function(x) {
            i1 <- which(x == 'cool')
            v1 <- rep(seq_along(i1), c(i1[1], diff(i1)))
            if (tail(x, 1) != 'cool') {
                return(c(v1, tail(v1, 1) + 1))
            } else {
                return(v1)
            }
        }
        
        df %>% 
         group_by(id) %>% 
         mutate(new_grp = f1(value)) %>% 
         group_by(id, new_grp) %>% 
         mutate(new = length(value[value != 'cool']), 
                new = replace(new, value != 'cool', NA)) %>% 
         ungroup() %>% 
         select(-new_grp)
        

        给出,

        # A tibble: 9 x 5
          id      obs value  newCol   new
          <fct> <int> <fct>   <int> <int>
        1 a         1 uncool     NA    NA
        2 a         2 cool        1     1
        3 a         3 uncool     NA    NA
        4 a         4 uncool     NA    NA
        5 a         5 cool        2     2
        6 a         6 uncool     NA    NA
        7 a         7 cool        1     1
        8 a         8 uncool     NA    NA
        9 b         1 cool        0     0
        

        【讨论】:

          【解决方案4】:

          编写简单的函数来解决您的问题:

          # Your data
          data <- data.frame(id = c("a", "a", "a", "a", "a", "a" ,"a" ,"a", "b"), 
                             obs = c(1,2,3,4,5,6,7,8,1), 
                             value = c("uncool", "cool", "uncool", "uncool", "cool", "uncool" ,"cool" ,"uncool", "cool"), 
                             stringsAsFactors = FALSE)
          
          # Function for solving problem      
          cool_counter <- function(vector) {
            uncool <- FALSE
            count <- 0
            results <- list()
          
            for(i in 1:length(vector)) {
              if(i == 1) {
                uncool <- vector[i] == "uncool"
                results[[i]] <- NA
          
                if(uncool) {
                  count <- 1
                }
              }
              if(i > 1) {
                uncool <- vector[i] == "uncool"
                if(uncool) {
                  count <- count + 1
                  results[[i]] <- NA
                }
                if(!uncool) {
                  results[[i]] <- count
                  count <- 0
                }
              }
            }
          
            return(unlist(results))
          } 
          

          这给出了:

          # Running function
          library(dplyr)
          data <- data %>%
            group_by(id) %>%
            mutate(newCol = cool_counter(value))
          
          # Results
          data
            id      obs value  newCol
            <chr> <dbl> <chr>   <dbl>
          1 a         1 uncool     NA
          2 a         2 cool        1
          3 a         3 uncool     NA
          4 a         4 uncool     NA
          5 a         5 cool        2
          6 a         6 uncool     NA
          7 a         7 cool        1
          8 a         8 uncool     NA
          9 b         1 cool       NA
          

          【讨论】:

            猜你喜欢
            • 2018-04-06
            • 1970-01-01
            • 1970-01-01
            • 2021-12-24
            • 2016-01-29
            • 1970-01-01
            • 2013-10-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多