2012-05-23 24 views
5

Para una data.table (o data.frame) en R, deseo encontrar todas las filas que contengan un valor en la columna 'value' que están a una distancia dada 'distance 'de otro que valora en fila con la misma clave. Por lo tanto, teniendo en cuenta lo siguiente:Encontrar filas con una diferencia dada entre valores en una columna

distance <- 22 
    key value 
    A  1 
    B  1 
    C  1 
    D  1 
    A  4 
    B  4 
    A 23 
    B 23 
    B 26 
    B 26 
    C 30 

me gustaría anotado la tabla original con una cuenta de cuántas filas existir con la misma clave y un valor que es 22 de ella:

key value count 
    A  1  1 
    B  1  1 
    C  1  0 
    D  1  0 
    A  4  0 
    B  4  2 
    A 23  0 
    B 23  0 
    B 26  0 
    B 26  0 
    C 30  0 

No sé realmente por dónde empezar con este enfoque autorreferencial para manipular datos en R. Mis intentos iniciales implicaron crear una segunda tabla e intentar hacer coincidir eso, pero eso parecía un enfoque extraño y pobre.

Nota: Estoy usando el paquete data.table, pero estoy contento de trabajar desde data.frame en este caso si eso hace las cosas más fáciles.

reproducible:

require(data.table) 
source <- data.table(data.frame(key=c("A","B","C","D","A","B","A","B","B","B", "C"),value=c(1,1,1,1,4,4,23,23,26,26,30))) 
result <- data.table(data.frame(key=c("A","B","C","D","A","B","A","B","B","B","C"),value=c(1,1,1,1,4,4,23,23,26,26,30),count=c(1,1,0,0,0,2,0,0,0,0,0))) 

Respuesta

5

Aquí es una solución basada data.table. Me interesará saber qué mejoras (si las hay) pueden hacerse.

# Your code 
library(data.table) 
source <- 
data.table(data.frame(key = c("A","B","C","D","A","B","A","B","B","B", "C"), 
         value = c(1,1,1,1,4,4,23,23,26,26,30))) 

Esa extraña data.table(data.frame(... es porque data.table() tiene un argumento llamado key, también. Esa es una forma de crear un data.table con una columna llamada "key". Capitalizando para evitar el nombre del argumento conflicto ilustra la sintaxis más estándar:

source <- data.table(Key = c("A","B","C","D","A","B","A","B","B","B","C"), 
        Value = c(1,1,1,1,4,4,23,23,26,26,30)) 

Junto a evitar la necesidad de as.integer() después, vamos a cambiar el tipo de la columna de Valuenumeric-integer ahora. Recuerde que 1 es numeric en R, es 1L que es integer. Por lo general, es mejor para la eficiencia almacenar los datos integer como integer, que integer como numeric. La siguiente línea es más fácil que escribir un montón de L s arriba.

source[,Value:=as.integer(Value)] # change type from `numeric` to `integer` 

Ahora proceder

distance <- 22L 
setkey(source, Key, Value) 

# Heart of the solution (following a few explanatory comments): 
# "J()" : shorthand for 'data.table()' 
# ".N" : returns the number of rows that matched a line (see ?data.table) 
# "[[3]]" : as with simple data.frames, extracts the vector in column 3 

source[,count:=source[J(Key,Value+distance),.N][[3]]] 
source 
     key value count 
[1,] A  1  1 
[2,] A  4  0 
[3,] A 23  0 
[4,] B  1  1 
[5,] B  4  2 
[6,] B 23  0 
[7,] B 26  0 
[8,] B 26  0 
[9,] C  1  0 
[10,] C 30  0 
[11,] D  1  0 

Tenga en cuenta que := cambió source por referencia directamente, por lo que es eso. Pero setkey() también cambió el orden de los datos originales. Si se requiere que conserva el orden original, entonces:

source <- data.table(Key = c("A","B","C","D","A","B","A","B","B","B","C"), 
        Value = c(1,1,1,1,4,4,23,23,26,26,30)) 
source[,Value:=as.integer(Value)] 
source[,count:=setkey(copy(source))[source[,list(Key,Value+distance)],.N][[3]]] 

     Key Value count 
[1,] A  1  1 
[2,] B  1  1 
[3,] C  1  0 
[4,] D  1  0 
[5,] A  4  0 
[6,] B  4  2 
[7,] A 23  0 
[8,] B 23  0 
[9,] B 26  0 
[10,] B 26  0 
[11,] C 30  0 
+0

Gracias, ¿puedes explicar la línea que asigna el resultado $ count? ¿Para qué es .N for y [[3]]? – Ina

+3

Claro. Acabo de agregar algunos comentarios en el código, que comienzan a descomprimir los datos.sintaxis compacta de la llamada de tabla. –

+1

¡Muy bien, gracias! – Ina

1

Usted podría utilizar mapply colocar a través de todas las combinaciones de valor clave y:

data.table(t(mapply(function(key,val) 
     c(key=key,value=val,count=length(source$value[source$key==key & source$value>(val+distance)])) 
    , as.character(source$key),source$value))) 
+0

Esto parece producir resultados incorrectos, aparece un recuento de 2 en la segunda fila y un recuento de 1 en la tercera fila con esta fórmula. Creo que entiendo lo que está haciendo, pero no puedo ver un error tipográfico obvio que haría de esto un simple error. Además, idealmente la respuesta no sería n^2 en complejidad, pero eso puede no ser posible. – Ina

Cuestiones relacionadas