Лучший способ создать общую/согласованность методов для sort.data.frame?
Наконец-то я решил поместить метод 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)
Ответы
Ответ 1
Есть несколько проблем. sort.data.frame
должен иметь те же аргументы, что и общий, поэтому, как минимум, он должен быть
sort.data.frame(x, decreasing = FALSE, ...) {
....
}
Чтобы иметь диспетчерскую работу, первым аргументом должен быть отправлен объект. Поэтому я бы начал с:
sort.data.frame(x, decreasing = FALSE, formula = ~ ., ...) {
....
}
где x
- ваш dat
, formula
- ваш form
, и мы предоставляем по умолчанию формулу для включения всего. (Я не изучил ваш код в деталях, чтобы увидеть, что именно представляет form
.)
Конечно, вам не нужно указывать decreasing
в вызове, поэтому:
sort(ToothGrowth, formula = ~ len + dose)
будет показано, как вызвать функцию, используя приведенные выше спецификации.
В противном случае, если вы не хотите, чтобы sort.data.frame
был общим для S3, назовите его чем-то другим, а затем вы можете свободно выбирать любые аргументы.
Ответ 2
Используйте функцию arrange
в plyr
. Он позволяет индивидуально выбирать, какие переменные должны быть в порядке возрастания и убывания:
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
настоятельно рекомендуется, если вы пишете такую функцию.
Ответ 3
Можете ли вы просто скрыть базовое определение sort
, то есть что-то вроде этого?
sort <- function(x,...) {
if (inherits(x,"data.frame")) {
sort.data.frame(x,...)
} else {
L <- list(...)
if (!is.null(names(L))) {
if ("decreasing" %in% names(L)) {
decreasing <- L[["decreasing"]]
L <- L[names(L)!="decreasing"]
}
} else {
if (any(names(L)=="")) {
dpos <- which.min(names(L)=="")
decreasing <- L[[dpos]]
L <- L[-dpos]
} else decreasing <- FALSE
}
arglist <- c(list(x=x,decreasing=decreasing),L)
do.call(base::sort,arglist)
}
}
Ответ 4
Я согласен с @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