代码之家  ›  专栏  ›  技术社区  ›  L Tyrone Mario Cardoso

使用dplyr将值从正更改为负时的新组ID

  •  0
  • L Tyrone Mario Cardoso  · 技术社区  · 2 年前

    搜索了SO,很惊讶我找不到解决方案,希望不是重复的。也许这更多的是一个逻辑问题,而不是R问题本身,但希望能找到:

    1. 概述的行为发生的原因,以及;
    2. 如果我使用 ifelse() 具有 cumsum() 问题是,如何在 dplyr 不使用 ifelse() , cumsum() ,或两者兼而有之。
    library(dplyr)
    
    # Sample data
    df <- data.frame(X = c(-10.9, -8.2, -6.3, -2.2, -0.3, 1.3, 4.6, -4.9, 3.6, 0, 1.8, 4.2, 9.8))
    

    所需输出:

           X tile_x
    1  -10.9      0
    2   -8.2      0
    3   -6.3      0
    4   -2.2      0
    5   -0.3      0
    6    1.3      0
    7    4.6      0
    8   -4.9      1
    9   -3.6      1
    10   0.0      1
    11   1.8      1
    12   4.2      1
    13   9.8      1
    

    当我尝试时:

    df1 <- df %>%
      mutate(tile_x = cumsum(ifelse(X < 0 & dplyr::lag(X, n = 1) > 0,
                                    1, 0)))
    
    df1
           X tile_x
    1  -10.9     NA
    2   -8.2     NA
    3   -6.3     NA
    4   -2.2     NA
    5   -0.3     NA
    6    1.3     NA
    7    4.6     NA
    8   -4.9     NA
    9   -3.6     NA
    10   0.0     NA
    11   1.8     NA
    12   4.2     NA
    13   9.8     NA
    

    这个 ifelse() 没有 cumsum() 确定组的正确起始位置,但包括 cumsum() 产生NA。

    我可以使用以下方法实现所需输出:

    df1 <- df %>% 
      mutate(tile_x = cumsum(ifelse(X > 0 & dplyr::lead(X, n = 1) < 0,
                                    1, 0)),
             tile_x = dplyr::lag(tile_x),
             tile_x = replace(tile_x, is.na(tile_x ), 0))
    

    但这看起来有点不整洁 dplyr 注释 如果从1开始,tile_x中的第一个值不必是0,这更容易实现。每组的长度各不相同,但图案相同。

    2 回复  |  直到 2 年前
        1
  •  2
  •   ThomasIsCoding    2 年前

    希望 diff 对于 sign(X) 对你来说很有意义

    > df %>%
    +   mutate(tile_x = cumsum(c(FALSE, diff(sign(X)) < 0)))
           X tile_x
    1  -10.9      0
    2   -8.2      0
    3   -6.3      0
    4   -2.2      0
    5   -0.3      0
    6    1.3      0
    7    4.6      0
    8   -4.9      1
    9   -3.6      1
    10   0.0      1
    11   1.8      1
    12   4.2      1
    13   9.8      1
    

    findInterval

    > df %>%
    +   mutate(tile_x = findInterval(row_number(), 1 + which(diff(sign(X)) < 0)))
           X tile_x
    1  -10.9      0
    2   -8.2      0
    3   -6.3      0
    4   -2.2      0
    5   -0.3      0
    6    1.3      0
    7    4.6      0
    8   -4.9      1
    9   -3.6      1
    10   0.0      1
    11   1.8      1
    12   4.2      1
    13   9.8      1
    
        2
  •  1
  •   margusl    2 年前

    默认情况下,的第一个值 lag() 输出为 NA ,这会影响 cumsum() 结果:

    c(1,2,3) |> cumsum()
    #> [1] 1 3 6
    c(1,2,3) |> dplyr::lag()
    #> [1] NA  1  2
    c(1,2,3) |> dplyr::lag() |> cumsum()
    #> [1] NA NA NA
    

    这可以通过设置默认值来解决,即。 lag(..., default = 0) :

    c(1,2,3) |> dplyr::lag(default = 0) |> cumsum()
    #> [1] 0 1 3
    

    此外,不需要使用将逻辑矢量转换为数字 ifelse() :

    library(dplyr, warn.conflicts = FALSE)
    df <- data.frame(X = c(-10.9, -8.2, -6.3, -2.2, -0.3, 1.3, 4.6, -4.9, 3.6, 0, 1.8, 4.2, 9.8))
    df %>%
      mutate(tile_x = cumsum(X < 0 & lag(X, default = 0) > 0))
    #>        X tile_x
    #> 1  -10.9      0
    #> 2   -8.2      0
    #> 3   -6.3      0
    #> 4   -2.2      0
    #> 5   -0.3      0
    #> 6    1.3      0
    #> 7    4.6      0
    #> 8   -4.9      1
    #> 9    3.6      1
    #> 10   0.0      1
    #> 11   1.8      1
    #> 12   4.2      1
    #> 13   9.8      1
    

    创建于2023-11-19 reprex v2.0.2

    推荐文章