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

10

我最终决定将流传于互联网上的 sort.data.frame 方法放入一个 R 包中。这个方法被要求的次数太多,不能再以临时发布的方式存在。

然而,它使用的参数与通用的 sort 函数不兼容:

sort(x,decreasing,...)
sort.data.frame(form,dat)
如果我将sort.data.frame更改为接受一个参数decreasing,并在sort.data.frame(form, decreasing, dat)中丢弃decreasing,那么它就失去了简单性,因为您总是必须指定dat=,而且不能真正使用位置参数。如果我像sort.data.frame(form,dat,decreasing)一样将其添加到末尾,那么顺序就与通用函数不匹配。如果我希望decreasing被包含在sort.data.frame(form,dat,...)中,那么当使用基于位置的匹配时,我认为通用函数将把第二个位置分配给decreasing并将其丢弃。如何协调这两个函数最好的方法是什么?
完整的功能如下:
# 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)

3
plyr包中的arrange函数可能会引起一些兴趣。 - joran
很遗憾,它似乎不支持负向(反向)排序,因此这个函数仍然很有用。 - Ari B. Friedman
我相当确定 arrange 支持负排序:arrange(ToothGrowth,desc(dose),len) - joran
使用plyr撰写了完整的答案 - 感谢@joran提供的示例! - hadley
3个回答

6

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的帮助文档。


2
谢谢。这看起来准备好取代我的方法了。但我仍然很好奇如何使通用类及其方法保持一致,因为这对我来说经常会出现。此外,在语法上,sort()方法似乎可以使其他数据类型的代码保持一致。 但这是非常漂亮的代码 :-) - Ari B. Friedman
1
"?arrange" 表示:"#注意:plyr函数不会保留行名称"。如果想要保留 row.names,这将使得这个优秀的函数变得次优。为什么不添加一个 keep.row.names=FALSE 选项呢? - landroni
@landroni 因为我认为它们不是一个好主意——最好将它们作为显式变量添加。 - hadley
1
我明白了。但是对于大多数用户来说,这仍然是与data.frame相关的标准功能,给那些用户提供选择将会很有用。 - landroni

5
那里有一些问题。sort.data.frame需要与通用函数具有相同的参数,因此最少应该是这样的:
sort.data.frame(x, decreasing = FALSE, ...) {
....
}

为了使调度工作正常进行,第一个参数需要是被调度的对象。因此,我建议从以下代码开始:
```javascript ```
sort.data.frame(x, decreasing = FALSE, formula = ~ ., ...) {
....
}

其中 x 是您的数据,formula 是您的公式,我们提供了一个默认值,包括所有内容。 (我没有详细研究您的代码,以确定form表示什么。)

当然,在调用中您不需要指定decreasing参数,如下所示:

sort(ToothGrowth, formula = ~ len + dose)

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

否则,如果您不希望sort.data.frame成为S3通用函数,请将其命名为其他内容,然后您可以拥有任何参数。


使用部分匹配,写sort(ToothGrowth, f = ~ len + dose)并不会那么糟糕,这就是我这样做并保持其S3性质的原因。感谢您的建议。 - Ari B. Friedman
1
我们是否应该定义一个 sort.data.frame.formula,它将以公式作为第一个参数,如果在 Use.method 中未通过公式测试,则会分派到以第一个数据参数为参数的 sort.data.frame?(与 aggregate.* 的情况相同) - IRTFM
@DWin 您是指 sort.formula,对吗? - Gavin Simpson
我在考虑让它回退到一个sort.data.frame.default方法或sort.dataframe,该方法将接受一个第一个参数作为数据框。 - IRTFM

0

我同意 @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

1
我希望公式参数在第二个位置,但我不确定是否可以这样做并仍然使其成为S3类。我希望根本没有“decreasing”,因为公式接受负参数,这意味着是递减的。 - Ari B. Friedman
@gsk3,“sort.int”仅将“decreasing = ...”作为第四个参数,因此我猜您可以将“formula = ...”作为第二个参数。我怀疑您也可以使用“decreasing = NULL”,并在代码中忽略此参数(就像“partial = TRUE”时“sort.int”忽略“decreasing”一样)。PS。所有这些都可以在“?sort”中找到。 - Andrie
@Andrie,即使您翻转顺序,因为decreasing在通用函数中命名第二,它仍会获取位置参数。所以很遗憾,这并没有帮助。 - Ari B. Friedman
@Andrie,sort.int不是sort的方法。也没有int类。您可以使用methods(sort)查看已实现的方法。 - Marek

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