如何在dplyr中使用多个列来实现函数

3

此问题以前已经被提出:如何在R中对数据框的数字进行四舍五入并保留总和?

我还想在dplyr中实现这个功能,根据所需的数字位数进行四舍五入并保持整体一致性:

round_preserve_sum <- function(x, digits = 0) {
  up <- 10 ^ digits
  x <- x * up
  y <- floor(x)
  indices <- tail(order(x-y), round(sum(x)) - sum(y))
  y[indices] <- y[indices] + 1
  y / up
}

以下是一个数据框:

df <- data.frame(SAND = c(0.00000, 28.00000, 27.27273),
                 SILT = c(45.45455, 35.00000, 34.34343),
                 CLAY = c(54.54545, 37.00000, 38.38384))

使用这个函数和这些值分别,我得到了:
round_preserve_sum(c(0.00000, 45.45455, 54.54545), 0)

[1] 0 45 55

 round_preserve_sum(c(28.00000, 35.00000, 37.00000), 0)

[1] 28 35 37

 round_preserve_sum(c(27.27273, 34.34343, 38.38384), 0)

【1】27、34、39

这三个数的总和为100。

当我在dplyr中实现这个函数时:

df.Rd0 <-df %>% 
  mutate(across(c(SAND, SILT, CLAY), ~round_preserve_sum(.,0)),
         Sum = SAND + SILT + CLAY)

我会:

   SAND SILT CLAY  Sum 
1    0   46   55   101 
2   28   35   37   100 
3   27   34   38    99

不使用波浪符号:
df.Rd0 <-df %>% 
  mutate(across(c(SAND, SILT, CLAY), round_preserve_sum(.,0)),
         Sum = SAND + SILT + CLAY)

我收到了这个错误信息:
Error : Problem with `mutate()` input `..1`.
i `..1 = across(c(SAND, SILT, CLAY), round_preserve_sum(., 0))`.
x undefined columns selected

我猜这个函数没有为向量编程?

1个回答

3

~ 是一个lambda表达式,即function(.x)的简写形式。如果我们不使用它,则需要将格式参数指定为具名参数。

library(dplyr)
df %>% 
  mutate(across(c(SAND, SILT, CLAY), round_preserve_sum, digits = 0),
         Sum = SAND + SILT + CLAY)

-输出

  SAND SILT CLAY Sum
1    0   46   55 101
2   28   35   37 100
3   27   34   38  99

关于 OP 手动计算和为 100 的问题,它是按行而非按列进行的 - `across` 循环遍历列。 我们需要使用 `rowwise` 和 `c_across`。
df %>%
    rowwise %>% 
    mutate(Sum = sum(round_preserve_sum(c_across(everything()), 0))) %>%
    ungroup

-输出

# A tibble: 3 x 4
   SAND  SILT  CLAY   Sum
  <dbl> <dbl> <dbl> <dbl>
1   0    45.5  54.5   100
2  28    35    37     100
3  27.3  34.3  38.4   100

如果我们想要返回带有总和的舍入列,可以使用 pmap
library(purrr)
df %>%
    pmap_dfr(~ {tmp <- round_preserve_sum(c(...), 0)
      c(tmp, Sum = sum(tmp))})
# A tibble: 3 x 4
   SAND  SILT  CLAY   Sum
  <dbl> <dbl> <dbl> <dbl>
1     0    45    55   100
2    28    35    37   100
3    27    34    39   100

通过使用collapse中的dapply,可以使这个过程更快。

library(collapse)
df <- dapply(df, MARGIN = 1, FUN = round_preserve_sum, 0)
df$Sum <- rowSums(df, na.rm = TRUE)

感谢@akrun提供的两个解决方案。我正在尝试使用collapse,但收到了以下错误信息:Error in aplyfun(.Call(Cpp_mrtl, do.call(cbind, X), FALSE, 0L), FUN, ...) : la fonction 'Rcpp_precious_remove' n'existe pas dans le package 'Rcpp'。我加载了Rcpp并再次尝试,但出现了错误。 - Marc-Olivier Gasser
另外,在collapse:dapply中,是否有办法指定应用函数的列或变量。在参考文献中没有找到相关信息:linkhttps://sebkrantz.github.io/collapse/reference/dapply.html) - Marc-Olivier Gasser
1
再次感谢@akrun。重新安装了Rcpp,现在可以正常工作了。用于指定collapse中的列:get_vars(df, c("SAND", "SILT", "CLAY")) <- dapply(get_vars(df, c("SAND", "SILT", "CLAY")), FUN = round_preserve_sum, 0, MARGIN = 1)。df$Sum2 <- rowSums(get_vars(df, c("SAND", "SILT", "CLAY")), na.rm = TRUE)。还没有弄清楚如何使用purr:pmap_dfr。 - Marc-Olivier Gasser
@Marc-OlivierGasser 你的意思是你还没有尝试过 pmap 吗? - akrun
pmap 似乎可以工作。我只是不知道如何指定列。此外,在 collapse 中仍然存在 get_vars 的问题。我想在特定列上应用函数,无论是更改值还是创建新列。在 dplyr 中使用 across 是非常直接的。 - Marc-Olivier Gasser
使用 pmap,您可以使用以下代码:df %>% select(yourcols) %>% pmap_dfr(~ {tmp <- round_preserve_sum(c(...), 0) c(tmp, Sum = sum(tmp))})。如果有更多的列,则可以将其与其他列绑定。 - akrun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接