class - ¿La mejor forma de crear una coherencia genérica/de método para sort.data.frame?
generics methods (4)
¿Puedes enmascarar la definición básica de sort
, es decir, algo como esto?
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)
}
}
Finalmente he decidido poner el método sort.data.frame que está flotando en Internet en un paquete R. Simplemente se solicita demasiado como para dejarlo en un método ad hoc de distribución.
Sin embargo, está escrito con argumentos que lo hacen incompatible con la función de ordenación genérica:
sort(x,decreasing,...)
sort.data.frame(form,dat)
Si cambio sort.data.frame
para que tome la disminución como un argumento como en sort.data.frame(form,decreasing,dat)
y descarte disminuyendo, entonces pierde su simplicidad porque siempre tendrá que especificar dat=
y can '' Realmente uso argumentos posicionales. Si lo agrego al final como en sort.data.frame(form,dat,decreasing)
, entonces el orden no coincide con la función genérica. Si espero que la disminución quede atrapada en los puntos `sort.data.frame (form, dat, ...), entonces cuando use la coincidencia basada en posición, creo que la función genérica asignará la segunda posición a la disminución y se obtendrá descartado. ¿Cuál es la mejor manera de armonizar estas dos funciones?
La función completa es:
# 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),]
}
Ejemplo:
library(datasets)
sort.data.frame(~len+dose,ToothGrowth)
Estoy de acuerdo con @Gavin en que x
debe ser lo primero. Sin embargo, pondría el parámetro decreasing
después de la formula
, ya que probablemente no se use tanto y casi nunca como argumento posicional.
El argumento de formula
se usaría mucho más y, por lo tanto, debería ser el segundo argumento. También estoy totalmente de acuerdo con @Gavin en que debería llamarse formula
, y no form
.
sort.data.frame(x, formula = ~ ., decreasing = FALSE, ...) {
...
}
Es posible que desee extender el argumento decreasing
para permitir un vector lógico donde cada valor VERDADERO / FALSO corresponde a una columna en la fórmula:
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
Hay algunos problemas allí. sort.data.frame
necesita tener los mismos argumentos que el genérico, por lo que, como mínimo, debe ser
sort.data.frame(x, decreasing = FALSE, ...) {
....
}
Para que el despacho funcione, el primer argumento debe ser el objeto enviado. Entonces comenzaría con:
sort.data.frame(x, decreasing = FALSE, formula = ~ ., ...) {
....
}
donde x
es su dat
, la formula
es su form
, y proporcionamos un valor predeterminado para la fórmula para incluir todo. (No he estudiado su código en detalle para ver exactamente qué form
representa).
Por supuesto, no necesita especificar la decreasing
en la llamada, entonces:
sort(ToothGrowth, formula = ~ len + dose)
sería cómo llamar a la función utilizando las especificaciones anteriores.
De lo contrario, si no desea que sort.data.frame
sea un S3 genérico, sort.data.frame
de otra forma y podrá tener los argumentos que desee.
Use la función de arrange
en plyr
. Le permite elegir individualmente qué variables deben estar en orden ascendente y descendente:
arrange(ToothGrowth, len, dose)
arrange(ToothGrowth, desc(len), dose)
arrange(ToothGrowth, len, desc(dose))
arrange(ToothGrowth, desc(len), desc(dose))
También tiene una implementación elegante:
arrange <- function (df, ...) {
ord <- eval(substitute(order(...)), df, parent.frame())
unrowname(df[ord, ])
}
Y desc
es solo una función ordinaria:
desc <- function (x) -xtfrm(x)
Leer la ayuda para xtfrm
es muy recomendable si está escribiendo este tipo de función.