函数不能嵌入到另一个函数中

Function does not work embedded in another function

在我的数据集中,ID 具有不止一个不同的名称。为了检测它们,我构建了这个函数:

ddply(my_dataframe, ~ID_col, summarise, number_of_names = length(unique(names_col)))

效果很好,所以我得到了一个 table,第一列是 ID,第二列是不同名称的数量。

因为我需要对几个 ID/name-pairs 执行此操作,所以我决定将 ddply 函数放在一个函数中。我是这样做的:

function_name = function (source, id, name) {
  ddply(source, ~id, summarise, number_of_names = length(unique(name)))

不幸的是,当我使用它时会抛出一个错误:

function_name(my_dataframe, ID_col, names_col)
# Error in unique.default(x) : unique() applies only to vectors

如您所见,它与之前的代码完全相同,但嵌入到具有三个变量的函数中。我非常想修复它,真的很期待解决方案。

仅供参考: 在我的原始代码中,我没有使用 "source" 或 "name" 而是德语单词,因此对于现有的其他功能应该没有问题。我也已经尝试将变量放在引号中。

感谢您的帮助!

这是 DF 的样子:

my_dataframe <- data.frame(
  ID_col = c(letters[2:9], letters[3:4]),
  names_col = paste0("name-", letters[1:10])
)

ID 有 303 个,名字有 963 个。

我们可以用 dplyr 中的 quosure 来做到这一点。 enquo获取输入变量,转换为quosure,在group_bysummarise中,我们取消引用(UQ)求值的quosure

library(dplyr)
f1 <- function(source, id, name) {
         id <- enquo(id)
         name <- enquo(name)
         source %>%
                group_by(UQ(id)) %>%
                summarise(number_of_names = n_distinct(UQ(name)))
    }

f1(my_dataframe, ID_col, names_col)
# A tibble: 3 x 2 
#   ID_col number_of_names
#   <chr>           <int>
#1  FU181               2
#2  FU901               1
#3  FU992               1

注意:该解决方案基于 dplyr,它是 plyr 的更高级版本。该解决方案采用不带引号的输入参数,也可以对其进行修改以采用带引号的参数。该解决方案可以用管道扩展(%>%)并且非常灵活

注意 2:我们没有发现 tidyverse 变得更乱,但实际上与以前的 lazyeval

解决方案相比,它更干净、更一致

数据

my_dataframe <- structure(list(ID_col = c("FU901", "FU992", "FU181", "FU181"), 
names_col = c("take a breath", "use a tissue", "get up", 
"getting up")), .Names = c("ID_col", "names_col"), class = "data.frame", row.names = c("1", 
"2", "3", "4"))

R 一直具有使用双方括号按变量名的值选择列的功能。使用 tapply 你可以这样做:

function_name = function (source, id, name) {
    data.frame(
       N=tapply(
           source[[name]],
           my_dataframe[[id]],
           function(x){
             length(unique(x))
             }
          )
        )
  }

然后:

> function_name(my_dataframe,"ID_col","names_col")
      N
FU181 2
FU901 1
FU992 1

注意名称在返回的数据框的行名称中。

1) eval/substitute 将正文包裹在 eval.parent(substitute(...)) 中,使参数被替换。 ddply(...) 下面一行与问题中的相同。

library(plyr)

function_name = function (source, id, name) eval.parent(substitute(  
  ddply(source, ~id, summarise, number_of_names = length(unique(name))) 
))

function_name(my_dataframe, ID_col, names_col)

2) substitute 这也行,不依赖 eval:

function_name = function (source, id, name) {
  id <- substitute(id)
  name <- deparse(substitute(name))
  ddply(source, id, function(x) summarise(x, number_of_names = length(unique(x[[name]]))))
}

function_name(my_dataframe, ID_col, names_col)

2a) pass strings 如果你愿意传递字符串,它可以缩短为这个,这与 (2) 相同,只是我们省略了前两行body,我们在调用它的时候传递字符串:

function_name = function (source, id, name) {
  ddply(source, id, function(x) summarise(x, number_of_names = length(unique(x[[name]]))))
}

function_name(my_dataframe, "ID_col", "names_col")

3) defmacro 另一种方法是在 gtools 中使用 defmacro 创建一个宏。 ddply(...) 调用与问题中的调用相同。

library(gtools)

macro_name <- defmacro(source, id, name, expr = 
   ddply(source, ~id, summarise, number_of_names = length(unique(name)))
)

macro_name(my_dataframe, ID_col,names_col)