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]))]