为 sort.data.frame 创建通用/方法一致性的最佳方法?

Posted

技术标签:

【中文标题】为 sort.data.frame 创建通用/方法一致性的最佳方法?【英文标题】:Best way to create generic/method consistency for sort.data.frame? 【发布时间】:2011-10-13 19:08:39 【问题描述】:

我最终决定将互联网上流传的 sort.data.frame 方法放入 R 包中。它只是被要求太多,不能留给一种特殊的分发方法。

但是,它使用的参数使其与通用排序函数不兼容:

sort(x,decreasing,...)
sort.data.frame(form,dat)

如果我将 sort.data.frame 更改为像 sort.data.frame(form,decreasing,dat) 中那样将递减作为参数并丢弃递减,那么它就会失去其简单性,因为您总是必须指定 dat= 并且不能真正使用位置参数。如果我像sort.data.frame(form,dat,decreasing) 一样将其添加到末尾,则该顺序与通用函数不匹配。如果我希望递减在点`sort.data.frame(form,dat,...)中被捕获,那么当使用基于位置的匹配时,我相信通用函数会将第二个位置分配给递减并且它会得到丢弃。协调这两个功能的最佳方法是什么?

完整的功能是:

# Sort a data frame
sort.data.frame <- function(form,dat)
# Author: Kevin Wright
# http://tolstoy.newcastle.edu.au/R/help/04/09/4300.html
# Some ideas from Andy Liaw
# http://tolstoy.newcastle.edu.au/R/help/04/07/1076.html
# Use + for ascending, - for decending.
# Sorting is left to right in the formula
# Useage is either of the following:
# sort.data.frame(~Block-Variety,Oats)
# sort.data.frame(Oats,~-Variety+Block)

# If dat is the formula, then switch form and dat
  if(inherits(dat,"formula"))
    f=dat
    dat=form
    form=f
  
  if(form[[1]] != "~") 
    stop("Formula must be one-sided.")
  
# Make the formula into character and remove spaces
  formc <- as.character(form[2])
  formc <- gsub(" ","",formc)
# If the first character is not + or -, add +
  if(!is.element(substring(formc,1,1),c("+","-"))) 
    formc <- paste("+",formc,sep="")
  
# Extract the variables from the formula
  vars <- unlist(strsplit(formc, "[\\+\\-]"))
  vars <- vars[vars!=""] # Remove spurious "" terms
# Build a list of arguments to pass to "order" function
  calllist <- list()
  pos=1 # Position of + or -
  for(i in 1:length(vars))
    varsign <- substring(formc,pos,pos)
    pos <- pos+1+nchar(vars[i])
    if(is.factor(dat[,vars[i]]))
      if(varsign=="-")
        calllist[[i]] <- -rank(dat[,vars[i]])
      else
        calllist[[i]] <- rank(dat[,vars[i]])
    
    else 
      if(varsign=="-")
        calllist[[i]] <- -dat[,vars[i]]
      else
        calllist[[i]] <- dat[,vars[i]]
    
  
  dat[do.call("order",calllist),]
 

例子:

library(datasets)
sort.data.frame(~len+dose,ToothGrowth)

【问题讨论】:

plyr 包中的函数arrange 可能有点意思。 是的。不幸的是,它看起来不支持负(向后)排序,所以这个函数看起来仍然很有用。 我很确定 arrange 确实支持负排序:arrange(ToothGrowth,desc(dose),len) 用 plyr 写了一个完整的答案——感谢@joran 的例子! 【参考方案1】:

那里有一些问题。 sort.data.frame 需要与泛型具有相同的参数,因此至少需要

sort.data.frame(x, decreasing = FALSE, ...) 
....

要进行分派工作,第一个参数需要是分派对象。所以我会开始:

sort.data.frame(x, decreasing = FALSE, formula = ~ ., ...) 
....

其中x 是您的datformula 是您的form,我们为公式提供了一个默认值以包含所有内容。 (我还没有详细研究过你的代码,看看form到底代表什么。)

当然,你不需要在调用中指定decreasing,所以:

sort(ToothGrowth, formula = ~ len + dose)

将是如何使用上述规范调用函数。

否则,如果您不希望 sort.data.frame 成为 S3 泛型,请将其命名为其他名称,然后您可以随意使用任何您想要的参数。

【讨论】:

通过部分匹配,写sort(ToothGrowth, f = ~ len + dose) 也不错,所以这就是我这样做并保持它的 S3ness 的原因。感谢您的建议。 我们不应该定义一个将公式作为第一个参数的sort.data.frame.formula,如果它没有通过Use.method 中的公式测试,那么将分派到sort.data.frame,它需要第一个参数数据论据? (与aggregate.*的情况相同) @DWin 你的意思是sort.formula,是吗? 我在想我想让它回退到sort.data.frame.default 方法或sort.dataframe,它将接受第一个参数作为数据框。【参考方案2】:

我同意@Gavin 的观点,即x 必须排在第一位。不过,我会将decreasing 参数放在formula 之后——因为它可能没有被太多使用,而且几乎没有用作位置参数。

formula 参数将被使用得更多,因此应该是第二个参数。我也非常同意@Gavin 的观点,它应该被称为formula,而不是form

sort.data.frame(x, formula = ~ ., decreasing = FALSE, ...) 
  ...

您可能希望扩展 decreasing 参数以允许逻辑向量,其中每个 TRUE/FALSE 值对应于公式中的一列:

d <- data.frame(A=1:10, B=10:1)
sort(d, ~ A+B, decreasing=c(A=TRUE, B=FALSE)) # sort by decreasing A, increasing B

【讨论】:

like 将公式参数排在第二位,但我不确定我是否可以这样并且仍然让它成为一个 S3 类。我想根本没有decreasing,因为该公式采用否定参数,这意味着减少。 @gsk3, sort.int 仅将 decreasing=... 作为第四个参数,所以我猜你可以将 formula=... 作为第二个参数。我怀疑你也可以使用decreasing=NULL 并在你的代码中忽略这个参数(就像sort.intpartial=TRUE 时忽略decreasing 一样)。 PS。所有这些都可以在?sort 中找到。 @Andrie,即使你颠倒顺序,因为decreasing 在泛型函数中被命名为第二,它会获取位置参数。所以很遗憾,它没有帮助。 @Andrie sort.int 不是sort 的方法。没有课程int。您可以使用methods(sort) 看到已实现的方法。【参考方案3】:

plyr 中使用arrange 函数。它允许您单独选择哪些变量应该按升序和降序排列:

arrange(ToothGrowth, len, dose)
arrange(ToothGrowth, desc(len), dose)
arrange(ToothGrowth, len, desc(dose))
arrange(ToothGrowth, desc(len), desc(dose))

它还有一个优雅的实现:

arrange <- function (df, ...) 
  ord <- eval(substitute(order(...)), df, parent.frame())
  unrowname(df[ord, ])

desc只是一个普通的函数:

desc <- function (x) -xtfrm(x)

如果您正在编写此类函数,强烈建议您阅读xtfrm 的帮助。

【讨论】:

谢谢。这似乎准备成为我的替代品。但是我仍然很好奇如何使泛型及其方法保持一致,因为它经常出现在我身上。此外,从句法上讲,sort() 方法似乎使事情与其他数据类型保持一致。但这是一些漂亮的代码:-) ?arrange 表示:“# 注意:plyr 函数不保留 row.names”。如果想要保留row.names,这使得这个出色的功能不是最理想的。为什么不添加keep.row.names=FALSE 选项? @landroni 因为我认为它们不是一个好主意 - 最好将它们添加为显式变量。 我明白了。但是,这仍然是与data.frame 相关的标准功能,至少就大多数用户而言,给这些用户选择是有用的。

以上是关于为 sort.data.frame 创建通用/方法一致性的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

创建将 T 限制为枚举的通用方法

为通用 NSFetchRequest 方法传递参数

如何使用 LINQ to SQL 创建通用数据访问对象 (DAO) CRUD 方法

为 Hibernate 创建一个通用的 DAO 类

我可以读取输入数据并创建通用 C++ 数据结构吗?

是否可以解决“为可变参数参数创建 T 的通用数组”编译器警告?