2011-07-26 17 views
8

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.¿La mejor manera de crear una coherencia genérica/de método para sort.data.frame?

Sin embargo, está escrito con argumentos que hacen que sea incompatible con la función de clasificación genérica:

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

si cambio sort.data.frame tomar disminuyendo como un argumento como en sort.data.frame(form,decreasing,dat) y desechar decreciente, entonces pierde su sencillez, porque siempre tendrá que especificar dat= y realmente no puede usar 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) 
+3

La función 'arrange' en el paquete' plyr' puede ser de su interés. – joran

+0

Lo es. Desafortunadamente, no parece que admita géneros negativos (hacia atrás), por lo que esta función aún parece ser útil. –

+0

Estoy bastante seguro de que 'arrange' no admite géneros negativos:' arrange (ToothGrowth, desc (dose), len) '. – joran

Respuesta

4

Hay algunos problemas allí. sort.data.frame tiene que tener los mismos argumentos que la genérica, por lo que como mínimo tiene que ser

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

Para que el trabajo de despacho, el primer argumento debe ser el objeto envió sucesivamente. Así que me gustaría empezar con:

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

donde x es su dat, 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 lo que representa form.)

Por supuesto, no es necesario especificar decreasing en la llamada, por lo que:

sort(ToothGrowth, formula = ~ len + dose) 

habría cómo llama a la función usando las especificaciones anteriores.

De lo contrario, si no desea que sort.data.frame sea un S3 genérico, llámelo de otra forma y podrá tener los argumentos que desee.

+0

Con la coincidencia parcial, no es tan malo escribir 'sort (ToothGrowth, f = ~ len + dose)' así que es por eso que lo hice y mantuve el S3ness de él. Gracias por la sugerencia. –

+1

¿No deberíamos definir un 'sort.data.frame.formula' que tomaría una fórmula como primer argumento, y si falla la prueba de fórmula en' Use.method' se enviaría luego a sort.data.frame que toma una primer argumento de datos? (Igual que la situación con 'aggregate. *') –

+0

@DWin Quiere decir 'sort.formula', ¿sí? –

0

Estoy de acuerdo con @Gavin en que x debe ser lo primero. Sin embargo, pondría el parámetro decreasing después del formula, ya que probablemente no se use tanto, y casi nunca como un argumento posicional.

El argumento 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 debe llamarse formula, y no form.

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

Es posible que desee ampliar el argumento decreasing para permitir un vector lógico que 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 
+1

Me gustaría * que * el argumento de la fórmula sea el segundo, pero no estoy seguro de poder tenerlo de esa manera y aún así ser una clase S3. Me gustaría no tener un 'decreciente' en absoluto, ya que la fórmula toma argumentos negativos que implican una disminución. –

+0

@ gsk3, 'sort.int' tiene' decreasing = ... 'solo como el cuarto parámetro, así que supongo que puedes tener' formula = ... 'como tu segundo. Sospecho que también puedes usar 'decreasing = NULL' e ignorar este parámetro en tu código (de la misma manera que' sort.int' ignora 'decreasing' cuando' partial = TRUE'). PD. Todo esto se puede encontrar en '? Sort'. – Andrie

+0

@Andrie, incluso si invierte el orden, porque 'decreasing' se nombra en segundo lugar en la función genérica, toma el argumento posicional. Por lo tanto, no ayuda, por desgracia. –

2

Puede usted acaba de enmascarar la definición de base 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) 
    } 
} 
+1

Estoy intrigado por este enfoque, pero ¿se lo considera "correcto"? Mi preocupación es que estaría sobreescribiendo 'sort' en un paquete diferente con mi propia alternativa funcionalmente similar. –

6

utilizar la función arrange en plyr. Se le permite elegir de forma individual 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 cuenta con una elegante aplicación:

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

Y desc es sólo 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.

+2

Gracias. Esto parece a punto de convertirse en mi reemplazo. Pero todavía tengo curiosidad de cómo se podría hacer para que un genérico y sus métodos sean consistentes, ya que aparece con bastante frecuencia para mí. Además, sintácticamente, un método de ordenación() parecería mantener las cosas consistentes con otros tipos de datos. Pero ese es un código bastante bonito :-) –

+1

'? Arrange' indica que:" # NOTA: las funciones plyr NO conservan row.names ". Esto hace que esta excelente función sea subóptima si se quiere mantener 'row.names'. ¿Por qué no agregar una opción 'keep.row.names = FALSE'? – landroni

+0

@landroni porque no creo que sean una buena idea, es mejor agregarlos como una variable explícita. – hadley

Cuestiones relacionadas