r subset plyr

Elimine el grupo de data.frame si al menos un miembro del grupo cumple con la condición



subset plyr (4)

Tengo un data.frame donde me gustaría eliminar grupos completos si alguno de sus miembros cumple una condición.

En este primer ejemplo, si los valores son números y la condición es NA el siguiente código funciona.

df <- structure(list(world = c(1, 2, 3, 3, 2, NA, 1, 2, 3, 2), place = c(1, 1, 2, 2, 3, 3, 1, 2, 3, 1), group = c(1, 1, 1, 2, 2, 2, 3, 3, 3, 3)), .Names = c("world", "place", "group"), row.names = c(NA, -10L), class = "data.frame") ans <- ddply(df, . (group), summarize, code=mean(world)) ans$code[is.na(ans$code)] <- 0 ans2 <- merge(df,ans) final.ans <- ans2[ans2$code !=0,]

Sin embargo, esta maniobra de ddply con los valores de NA no funcionará si la condición no es " NA " o si el valor no es numérico.

Por ejemplo, si quisiera eliminar cualquier grupo que tuviera un miembro con un valor mundial de AF (como en el cuadro de datos a continuación), este truco ddply no funcionaría.

df2 <-structure(list(world = structure(c(1L, 2L, 3L, 3L, 3L, 5L, 1L, 4L, 2L, 4L), .Label = c("AB", "AC", "AD", "AE", "AF"), class = "factor"), place = c(1, 1, 2, 2, 3, 3, 1, 2, 3, 1), group = c(1, 1, 1, 2, 2, 2, 3, 3, 3, 3)), .Names = c("world", "place", "group"), row.names = c(NA, -10L), class = "data.frame")

Puedo imaginar un ciclo for donde donde para cada grupo se verifica el valor de cada miembro, y si se cumple la condición, se podría completar una columna de code , y luego se podría hacer un subconjunto basado en ese código.

Pero, ¿tal vez hay una forma vectorizada de hacer esto?


Opción Base R usando ave

df2[with(df2, ave(world != "AF", group, FUN = all)),] # world place group #1 AB 1 1 #2 AC 1 1 #3 AD 2 1 #7 AB 1 3 #8 AE 2 3 #9 AC 3 3 #10 AE 1 3

O también podemos usar el subset

subset(df2, ave(world != "AF", group, FUN = all))

Lo anterior también se puede escribir como

df2[with(df2, !ave(world == "AF", group, FUN = any)),]

y

subset(df2, !ave(world == "AF", group, FUN = any))


Paquete base:

df2[df2$group != df2[df2$world==''AF'', 3],]

Salida:

world place group 1 AB 1 1 2 AC 1 1 3 AD 2 1 7 AB 1 3 8 AE 2 3 9 AC 3 3 10 AE 1 3

Usando sqldf :

library(sqldf) sqldf("SELECT df2.world, df2.place, [group] FROM df2 LEFT JOIN (SELECT * FROM df2 WHERE world LIKE ''AF'') AS t USING([group]) WHERE t.world IS NULL")

Salida:

world place group 1 AB 1 1 2 AC 1 1 3 AD 2 1 4 AB 1 3 5 AE 2 3 6 AC 3 3 7 AE 1 3


Tratar

library(dplyr) df2 %>% group_by(group) %>% filter(!any(world == "AF"))

O según lo mencionado por @akrun:

setDT(df2)[, if(!any(world == "AF")) .SD, group]

O

setDT(df2)[, if(all(world != "AF")) .SD, group]

Lo que da:

#Source: local data frame [7 x 3] #Groups: group # # world place group #1 AB 1 1 #2 AC 1 1 #3 AD 2 1 #4 AB 1 3 #5 AE 2 3 #6 AC 3 3 #7 AE 1 3


solución alternativa de data.table:

setDT(df2) df2[!(group %in% df2[world == "AF",group])]

da:

world place group 1: AB 1 1 2: AC 1 1 3: AD 2 1 4: AB 1 3 5: AE 2 3 6: AC 3 3 7: AE 1 3

Usando teclas podemos ser un poco más rápidos:

setkey(df2,group) df2[!J((df2[world == "AF",group]))]