gpt4 book ai didi

r - 用不同的功能总结不同列的简洁方法

转载 作者:行者123 更新时间:2023-12-05 00:10:56 28 4
gpt4 key购买 nike

我的问题基于 similar one通过施加额外的约束,每个变量的名称应该只出现一次。

考虑一个数据框

library( tidyverse )
df <- tibble( potentially_long_name_i_dont_want_to_type_twice = 1:10,
another_annoyingly_long_name = 21:30 )

我要申请 mean到第一列和 sum到第二列,而不必两次输入每个列名。

正如我上面链接的问题所示, summarize允许您这样做,但要求每列的名称出现两次。另一方面, summarize_at允许您简洁地将多个函数应用于多个列,但它是通过调用所有指定列上的所有指定函数来实现的,而不是以一对一的方式进行。有没有办法结合 summarize 的这些独特的功能?和 summarize_at ?

我可以用 rlang 破解它,但我不确定它是否比只输入每个变量两次更干净:
v <- c("potentially_long_name_i_dont_want_to_type_twice",
"another_annoyingly_long_name")
f <- list(mean,sum)

## Desired output
smrz <- set_names(v) %>% map(sym) %>% map2( f, ~rlang::call2(.y,.x) )
df %>% summarize( !!!smrz )
# # A tibble: 1 x 2
# potentially_long_name_i_dont_want_to_type_twice another_annoyingly_long_name
# <dbl> <int>
# 1 5.5 255

编辑以解决一些哲学观点

我不认为想要避免 x=f(x)成语是不合理的。我可能对输入长名称有点过分热情,但真正的问题实际上是(相对)长名称彼此非常相似。示例包括核苷酸序列(例如, AGCCAGCGGAAACAGTAAGG)和 TCGA barcodes .在这种情况下,不仅自动完成功能有限,而且还可以编写诸如 AGCCAGCGGAAACAGTAAGG = sum( AGCCAGCGGAAACAGTAAGG ) 之类的东西。引入了不必要的耦合并增加了在开发和维护代码时分配的两侧可能意外不同步的风险。

我完全同意@MrFlick 关于 dplyr 的看法提高代码的可读性,但我不认为可读性应该以牺牲正确性为代价。类似 summarize_at 的函数和 mutate_at非常出色,因为它们在将操作放在操作数旁边(清晰)和保证结果写入正确的列(正确性)之间取得了完美的平衡。

出于同样的原因,我觉得完全删除变量提及的建议解决方案在另一个方向上摇摆得太远了。虽然天生聪明——我当然很欣赏它们节省的额外输入——我认为,通过消除函数和变量名称之间的关联,这样的解决方案现在依赖于变量的正确排序,这会产生意外错误的风险。

简而言之,我认为自变异/自总结操作应该只提及每个变量名称一次。

最佳答案

我提出了 2 个技巧来解决这个问题,请参阅底部的代码和两种解决方案的一些详细信息:

一个函数.at它返回一组变量的结果(这里只有一个一组变量),然后我们可以将其解开,因此我们可以从两个世界中受益,summarizesummarize_at :

df %>% summarize(
!!!.at(vars(potentially_long_name_i_dont_want_to_type_twice), mean),
!!!.at(vars(another_annoyingly_long_name), sum))

# # A tibble: 1 x 2
# potentially_long_name_i_dont_want_to_type_twice another_annoyingly_long_name
# <dbl> <dbl>
# 1 5.5 255
summarize的副词,用美元符号简写。
df %>%
..flx$summarize(potentially_long_name_i_dont_want_to_type_twice = ~mean(.),
another_annoyingly_long_name = ~sum(.))

# # A tibble: 1 x 2
# potentially_long_name_i_dont_want_to_type_twice another_annoyingly_long_name
# <dbl> <int>
# 1 5.5 255

.at 的代码

它必须在管道中使用,因为它使用 .在父环境中,凌乱但有效。
.at <- function(.vars, .funs, ...) {
in_a_piped_fun <- exists(".",parent.frame()) &&
length(ls(envir=parent.frame(), all.names = TRUE)) == 1
if (!in_a_piped_fun)
stop(".at() must be called as an argument to a piped function")
.tbl <- try(eval.parent(quote(.)))
dplyr:::manip_at(
.tbl, .vars, .funs, rlang::enquo(.funs), rlang:::caller_env(),
.include_group_vars = TRUE, ...)
}

我将它设计为结合 summarizesummarize_at :
df %>% summarize(
!!!.at(vars(potentially_long_name_i_dont_want_to_type_twice), list(foo=min, bar = max)),
!!!.at(vars(another_annoyingly_long_name), median))

# # A tibble: 1 x 3
# foo bar another_annoyingly_long_name
# <dbl> <dbl> <dbl>
# 1 1 10 25.5

..flx 的代码
..flx输出一个替换其公式参数的函数,例如 a = ~mean(.)通过电话 a = purrr::as_mapper(~mean(.))(a)在运行之前。用 summarize 方便和 mutate因为一列不能是公式,所以不会有任何冲突。

我喜欢使用美元符号作为速记,并使用以 .. 开头的名称。所以我可以命名这些“标签”(并给它们一个类 "tag" )并将它们视为不同的对象(仍在试验这个)。 ..flx(summarize)(...)不过也能用。
..flx <- function(fun){
function(...){
mc <- match.call()
mc[[1]] <- tail(mc[[1]],1)[[1]]
mc[] <- imap(mc,~if(is.call(.) && identical(.[[1]],quote(`~`))) {
rlang::expr(purrr::as_mapper(!!.)(!!sym(.y)))
} else .)
eval.parent(mc)
}
}

class(..flx) <- "tag"

`$.tag` <- function(e1, e2){
# change original call so x$y, which is `$.tag`(tag=x, data=y), becomes x(y)
mc <- match.call()
mc[[1]] <- mc[[2]]
mc[[2]] <- NULL
names(mc) <- NULL
# evaluate it in parent env
eval.parent(mc)
}

关于r - 用不同的功能总结不同列的简洁方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55640904/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com