2011-10-05 11 views
9

¿Me falta algo obvio aquí? Parece que la función inversa de which falta en la base R (Google e incluso una búsqueda en SO para "R inverso que" devuelve una gran cantidad de enlaces no relacionados)?Inverso del cual

Bueno, no es que no pueda escribir uno, sino simplemente para aliviar mi frustración por su falta y como un desafío de flexión de los músculos R: ¿cómo harías para escribir uno?

Lo que necesitamos es una función como:

invwhich<-function(indices, totlength) 

que devuelve un vector lógica de longitud totlength que cada elemento de indices es TRUE y el resto es FALSE.

Es probable que haya muchas formas de lograr esto (algunas de las cuales son realmente bajas), por lo tanto, argumente por qué su solución es la "mejor". Oneliner alguien?

Si se toma en cuenta algunos de los otros parámetros de which (arr.ind ??), eso es, obviamente, mucho mejor ...

+0

No estoy seguro de que esta es la inversa de ' que' Es como decir que hacer un pastel es lo contrario a comer uno. No puede tener una inversa porque 'which' solo le proporciona índices de un vector y no puede conocer la longitud del vector del resultado. Por lo tanto, no hay función 'invwhich' en R. – John

Respuesta

7

solución de una sola línea:

invwhich <- function(indices, totlength) is.element(seq_len(totlength), indices) 

invwhich(c(2,5), 10) 
[1] FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE 
+0

+1 para el delineador.Para mi sorpresa, el rendimiento no es tan bueno (aunque solo se nota en los vectores grandes y en muchas repeticiones). –

+2

Demuestra de manera más efectiva que los one-liners no siempre son la "mejor" solución. – Andrie

+1

'is.element' es idéntico a'% in% ', por lo que la alternativa es' seq_len (totlength)% en% indices'. – Marek

5

Mi propia solución (por ahora): EDITAR según la sugerencia de @ Marek.

invwhich<-function(indices, outlength, useNames = TRUE) 
{ 
    rv<-logical(outlength) 
    #rv<-rep(FALSE, outlength) #see Marek's comment 
    if(length(indices) > 0) 
    { 
     rv[indices]<-TRUE 
     if(useNames) names(rv)[indices]<-names(indices) 
    } 
    return(rv) 
} 

Se comporta muy bien (aparentemente mejor que @ oneliner de Andrie) y, en la medida de lo posible, las cuentas de useNames. ¿Pero es posible hacer esto en un oneliner?

rendimiento WRT, simplemente use:

someindices<-sample(1000000, 500000, replace=FALSE) 
system.time(replicate(100, tmp<-invwhich(someindices, 1000000))) 

como una medida del rendimiento muy baja fidelidad.

+1

Puede acelerarlo 5 veces reemplazando 'rep (FALSE, outlength)' por 'logical (outlength)'. – Marek

+0

O reemplace 'rep' por' rep.int'. – Marek

+0

Este sería el delineador: 'invwhich <- function (i, len) '[<-' (logical (len), i, T)'. O con useNames (pero ¿quién va a necesitar eso?): 'Invwhich <- function (i, len, useNames = TRUE) setNames ('[<-' (lógico (len), i, T), if (useNames) nombres (i) else NULL) ' –

1

Otra variante, para hacer una oneliner:

lWhich <- function(indices, totlength, vec = vector(length = totlength)){vec[indices] <- TRUE; return(vec)} 

preferiría diferentes nombres, por razones de brevedad:

lWhich <- function(ix, len, vec = vector(length = len)){vec[ix] <- TRUE; return(vec)} 

O, utilizando el paquete de bit:

lWhichBit <- function(ix, len){return(as.logical(bitwhich(len, x = ix, poslength = length(ix))))} 

Sorprendentemente, eso parece lento. Resulta que el código usa rep en algunos lugares. :(

Este es un trabajo para RCPP o compile :)

+0

Usando"; " equivale a no tener un oneliner ... Aún así, te reembolsaré un +1 por el esfuerzo (y si tengo tiempo, haré algunos perfiles y te dejaré saber cómo fue) –

+0

Ah sí, eso es verdad ; no te vuela :) Era tarde, ¿qué puedo decir? Intenté hacer una tarea dentro de la devolución, pero se volvió desordenado. – Iterator

0

versión Overkill trabajar con todo tipo de índices:!

#' Logical which 
#' 
#' Inverse of \link[base]{which}. 
#' Converts an array of any indices to a logical index array. 
#' 
#' Either \code{nms} or \code{len} has to be specified. 
#' 
#' @param idx  Numeric or character indices. 
#' @param nms  Array of names or a sequence. 
#'     Required if \code{idx} is a character array 
#' @param len  Length of output array. 
#'     Alternative to \code{nms} if \code{idx} is numeric 
#' @param useNames Use the names of nms or idx 
#' 
#' @examples 
#' all(lWhich(2, len = 3) == c(F, T, F)) 
#' all(lWhich(c('a', 'c'), letters[1:3]) == c(T, F, T)) 
#' 
#' @export 
lWhich <- function(idx, nms = seq_len(len), len = length(nms), useNames = TRUE) { 
    rv <- logical(len) 
    if (is.character(nms)) # we need names here so that rv[idx] works 
     names(rv) <- nms 

    if (useNames && !is.null(names(idx))) 
     names(rv)[idx] <- names(idx) 

    rv[idx] <- TRUE 

    if (!useNames) # if we don’t want names, we’ll remove them again 
     names(rv) <- NULL 
    rv 
} 
0
setdiff(1:total_length, indices) 
+2

Ese no es el significado de "inverso" utilizado aquí. La pregunta es sobre pasar de un vector de números a un vector de valores verdadero/falso, es decir, qué hace 'que' pero en la dirección opuesta. –