r-在tibble中编写这个条件行函数的最佳代码是什么



我想使用tidyverse计算tibble中两个向量的几何平均值。两个变量的计算平均值应按行进行。为此,我写了下面的函数,它起了作用,但我只是想知道如何以更高效的编码方式完成或编写此操作,更高效我指的是更少的代码,更快、更整洁。有更好的主意吗?只是想一想,map_*()在这种情况下能实现吗?我也知道使用rowwise(),但据我最近所知,这篇文章的作者哈德利·威克姆在策略上淡化了rowwise()的使用。

一个最小的代表性例子如下:

Reprex

df <- tribble(
~v1, ~ v2,
4, 5,
NA, 7,
2, 2,
3, NA,
NA, NA,
9, 9)

建议功能

gMean <- function (df, v1, v2){
output <- vector ("double", nrow (df))
for (i in 1:nrow(df)){
output[[i]] <- case_when (!is.na(df$v1[i]) && !is.na(df$v2[i]) ~ ((df$v1[i] * df$v2[i]) ^ 0.5), 
is.na (df$v1[i]) && is.na (df$v2[i]) ~ 1, 
!is.na(df$v1[i]) && is.na(df$v2[i]) ~ df$v1[i], 
is.na(df$v1[i]) && !is.na(df$v2[i]) ~ df$v2[i]
)
}
output
}

输出

df %>%
gMean (v1, v2)
[1] 4.472136 7.000000 2.000000 3.000000 1.000000 9.000000

您也可以(只(使用mutate,而不是在每一行上循环。

在您的情况下,不需要map或使用rowwise,而且由于case_when是从下到上进行评估的,因此您也可以简化is.na调用。

df |> mutate(gMean = case_when(is.na(v1) & is.na(v2) ~ 1,
is.na(v1) ~ v2,
is.na(v2) ~ v1,
TRUE ~ sqrt(v1 * v2)))

然而,如果我们想使用rowwise()map2_dbl(),我们可以使用prod来允许na.rm-选项,并且只取平方根(^(1/2)(,其中有2个值可用。利用1/0定义为R中的Inf1^Inf定义为1

df |>
rowwise() |>
mutate(gMean = prod(v1, v2, na.rm = TRUE) ^ (1 / sum(c(!is.na(v1), !is.na(v2))))) |>
ungroup()
df |>
mutate(gMean = map2_dbl(v1, v2, ~ prod(.x, .y, na.rm = TRUE) ^ (1 / sum(c(!is.na(.x), !is.na(.y))))))

输出:

# A tibble: 6 × 3
v1    v2 gMean
<dbl> <dbl> <dbl>
1     4     5  4.47
2    NA     7  7   
3     2     2  2   
4     3    NA  3   
5    NA    NA  1   
6     9     9  9  

另一种可能的解决方案:

library(tidyverse)
df %>% 
mutate(gMean = map2_dbl(v1, v2, ~ sqrt(.x * .y)) %>% 
coalesce(v1, v2) %>% if_else(is.na(.), 1, .))
#> # A tibble: 6 × 3
#>      v1    v2 gMean
#>   <dbl> <dbl> <dbl>
#> 1     4     5  4.47
#> 2    NA     7  7   
#> 3     2     2  2   
#> 4     3    NA  3   
#> 5    NA    NA  1   
#> 6     9     9  9

最新更新