函数无法嵌入另一个函数

时间:2017-06-29 11:40:13

标签: r unique plyr distinct-values

我的数据集中的

是只有一个不同名称的ID。为了检测它们,我建立了这个功能:

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

这很好用,所以我在第一个col中得到一个ID,在第二个col中有一些不同的名字。

因为我需要对几个ID /名称对执行此操作,所以我决定将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"或"名称"但是德语单词,所以现有的其他功能应该没有问题。我也已经尝试将变量放在引号中。

感谢您的帮助!

这就是DF有点像:

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

有303个ID,但有963个名字。

3 个答案:

答案 0 :(得分:6)

R始终具有通过使用双方括号按变量名的值选择列的功能。使用import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.core.AnyOf.anyOf; ... Date expectedMin = new Date() // Execute the method being tested Date resultDate = getDate(); // Validate assertThat(resultDate, anyOf(greaterThan(expectedMin), equalTo(expectedMin))) ,你可以这样做:

tapply

然后:

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

请注意,名称位于返回数据框的行名称中。

答案 1 :(得分:2)

1)eval / substitute 将身体包裹在eval.parent(替换(...))中以使参数被替换。下面的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)替换这也有效,不依赖于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)传递字符串如果你愿意传递字符串,可以将它缩短为与(2)相同,除非我们省略了正文的前两行并且我们传递了字符调用它时的字符串:

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)

答案 2 :(得分:0)

我们可以使用quosure中的dplyr来执行此操作。 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

注意:解决方案基于dplyrplyr%>%的更高级版本。该解决方案将输入参数视为未引用的,并且还可以修改它以使用带引号的参数。解决方案可以使用管道(tidyverse)进行扩展,并且非常灵活

注意2:我们没有发现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"))

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

数据

<svg height="250" width="500">
  <defs>
    <radialGradient id="grad1" cx="50%" cy="50%" r="50%" fx="60%" fy="20%">
      <stop offset="0%" style="stop-color:rgb(0,0,0);stop-opacity:0" />
      <stop offset="100%" style="stop-color:rgb(1,1,1);stop-opacity:1" />
    </radialGradient>
  </defs>
  <circle cx="180" cy="100" r="100"  fill="url(#grad1)" />

</svg>