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

Resuelto nofunsally asked hace 54 años • 4 respuestas

Tengo un data.frameproblema en el que 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, NAel 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 ddplymaniobra con los NAvalores no funcionará si la condición es distinta a " NA", o si el valor no es numérico.

Por ejemplo, si quisiera eliminar grupos que tienen una o más filas con un valor mundial de AF(como en el marco de datos a continuación), este ddplytruco 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 bucle for en el que para cada grupo se verifica el valor de cada miembro y, si se cumple la condición code, se podría completar una columna y luego se podría crear un subconjunto basado en ese código.

Pero, ¿quizás haya una forma vectorizada de hacer esto?

nofunsally avatar Jan 01 '70 08:01 nofunsally
Aceptado

Intentar

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
Steven Beaupré avatar Jul 27 '2015 19:07 Steven Beaupré

Solución alternativa de tabla de datos :

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 claves podemos ser un poco más rápidos:

setkey(df2,group) 
df2[!J((df2[world == "AF",group]))]
Chris avatar Jul 27 '2015 19:07 Chris

paquete básico :

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

Producción:

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

Producción:

  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
mpalanco avatar Jul 27 '2015 20:07 mpalanco