查找非NA的最后一列(tidyverse)

查找非NA的最后一列(tidyverse),r,tidyverse,R,Tidyverse,不确定我做错了什么,但我正在努力获取最后一列(在多个列中)的每行索引,这不是NA 使用tidyverse和Cross,我得到了与输入列一样多的输出列,其中我希望有一个单独的输出列,其中包含相应列的索引 dat <- data.frame(id = c(1, 2, 3), x = c(1, NA, NA), y = c(NA, NA, NA), z = c(3, 1, NA))

不确定我做错了什么,但我正在努力获取最后一列(在多个列中)的每行索引,这不是NA

使用tidyverse和Cross,我得到了与输入列一样多的输出列,其中我希望有一个单独的输出列,其中包含相应列的索引

dat <- data.frame(id = c(1, 2, 3),
                  x  = c(1, NA, NA),
                  y  = c(NA, NA, NA),
                  z  = c(3, 1, NA))
预期结果将是:

  id  x  y  z  last
1  1  1 NA  3  3
2  2 NA NA  1  3
3  3 NA NA NA  NA

当前流程的问题:

  • cross
    将一次向函数/表达式传递一列;您的代码需要一行或一个矩阵/帧。为此,跨的
    是不合适的

  • 最后一行所需的
    NA
    输出与逻辑不一致:
    !is.na(.x)
    应该返回
    c(F,F,F)
    ,它仍然有一个max。然后,您的逻辑需要一个自定义函数,因为您需要以不同的方式处理它

  • 尝试将
    max.col
    改编为自定义函数:

    max.col.notnaR基溶液:

    dat$last = apply(dat[,2:4], 1, 
                     FUN = function(x) ifelse(max(which(is.na(x))) == length(x), NA, max(which(is.na(x)))+1 ))
    
    dat
    
    # id  x  y  z last
    # 1  1  1 NA  3    3
    # 2  2 NA NA  1    3
    # 3  3 NA NA NA   NA
    

    tidyverse
    实际上不适合行操作。大多数情况下,将数据重塑为长格式(如@Rui Barradas answer所示)是一种很好的方法

    下面是一种使用
    rowwise
    保持数据宽度的方法

    library(dplyr)
    
    dat %>%
      rowwise() %>%
      mutate(last = {ind = which(!is.na(c_across(x:z))); 
                    if(length(ind)) tail(ind, 1) else NA})
    
    #    id     x   y        z  last
    #  <dbl> <dbl> <lgl> <dbl> <int>
    #1     1     1 NA        3     3
    #2     2    NA NA        1     3
    #3     3    NA NA       NA    NA
    
    库(dplyr)
    dat%>%
    行()
    突变(last={ind=which(!is.na(c_横跨(x:z)));
    if(长度(ind))尾(ind,1)else NA})
    #最后一个id x y z
    #      
    #1 NA 3 3
    #2 NA NA 1 3
    #3-3-NA-NA-NA-NA
    
    您希望使用
    c_-cross()
    rowwise()
    来执行此操作
    rowwise()
    的工作原理与
    groupby\u all()
    类似,只是更为明确
    c_Cross()
    创建列外的平面向量(而
    Cross()
    创建TIBLES)

    如果我们首先单独定义一个函数来提取最后一个非
    NA
    值,或者如果没有,则返回
    NA

    get_last <- function(x){
      y <- c(NA,which(!is.na(x)))
      y[length(y)]
    }
    
    
    
    base
    R

    df 2 NA 1 3
    #>3-3-NA-NA-NA-NA
    

    由(v0.3.0)于2020-12-29创建,从NAs向量开始,您可以单步通过每个列,如果给定元素通过您的
    检查,返回
    TRUE
    ,则将该列的索引分配给该元素。与其他答案的不同之处在于,这不会逐行检查条件,也不会根据数据创建矩阵。但不确定为每列创建两个新的临时向量是否比首先将整个数据转换为矩阵好/坏

    library(tidyverse) # purrr and dplyr
    
    last_matching_ind <- function(dat, check_fun){
      check_fun <- as_mapper(check_fun)
      reduce2(dat, seq_along(dat), .init = NA_integer_,
              function(prev, dat, ind) if_else(check_fun(dat), ind, prev) )
    }
    
    dat %>% 
      mutate(last = last_matching_ind(dat[-1], ~ !is.na(.x)))
    
    #   id  x  y  z last
    # 1  1  1 NA  3    3
    # 2  2 NA NA  1    3
    # 3  3 NA NA NA   NA
    
    library(tidyverse)#purrr和dplyr
    
    最后一个匹配ind
    max.col
    需要一个
    矩阵(ref:),而
    .x
    是一个向量,因此第一个有效调用是
    max.col(c(1,1,NA))
    (它返回一个长度为3的向量,
    c(1,1,NA)< /代码>。我知道。但是,改变x。to将导致获得多个新的列。可能你应该考虑<代码> dAT%> %cBin(最后= max .CL(是.N.(.[-1)],Ti.Mease=“最后”))< /代码>,其次,你的第三行<代码> NA<代码>与<代码> max *<代码>不一致:即使<代码>!是NA(x)
    对于所有三个都为false,三个false中的最大值(类似于
    max(c(0,0,0))
    )仍然有效,因此它仍将返回3。听起来您需要特殊的逻辑,可能是自定义函数。您几乎肯定不想使用
    而不是
    。x
    是的,rowwise在tidyverse中不太好,但您的解决方案可以工作(而且速度很快)。我还通过重塑找到了一个解决方案,但考虑到我的数据集的大小,它的速度非常慢。有趣的解决方案,感谢您的解释。我现在知道wy Cross不起作用。不过,我接受了@Ronak Shah的解决方案,因为它既短又简单。这很好,有时
    按行
    是最简单/最好的解决方法o开始。意识到对于较大的数据,
    rowwise
    的性能非常糟糕。只要您的数据在数百行以下,这应该是好的(远不止这些,您可能会感觉到差异)。是的,完全同意。这就是为什么我通常尝试避免rowwise和c_交叉,并使用hbyrid
    apply(交叉(选择变量),1,我的函数)
    ,但这在这里不起作用。幸运的是,即使使用我当前的55k行,解决方案也只需要2-3秒左右。
    dat %>%
      rowwise() %>%
      mutate(last = get_last(c_across(x:z)))
    
    library(tidyverse) # purrr and dplyr
    
    last_matching_ind <- function(dat, check_fun){
      check_fun <- as_mapper(check_fun)
      reduce2(dat, seq_along(dat), .init = NA_integer_,
              function(prev, dat, ind) if_else(check_fun(dat), ind, prev) )
    }
    
    dat %>% 
      mutate(last = last_matching_ind(dat[-1], ~ !is.na(.x)))
    
    #   id  x  y  z last
    # 1  1  1 NA  3    3
    # 2  2 NA NA  1    3
    # 3  3 NA NA NA   NA