查找非NA的最后一列(tidyverse)
不确定我做错了什么,但我正在努力获取最后一列(在多个列中)的每行索引,这不是NA 使用tidyverse和Cross,我得到了与输入列一样多的输出列,其中我希望有一个单独的输出列,其中包含相应列的索引查找非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))
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
最后一个匹配indmax.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_交叉,并使用hbyridapply(交叉(选择变量),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