Seleccione filas de un marco de datos que no estén presentes en un segundo marco de datos

Resuelto Tal Galili asked hace 55 años • 14 respuestas

Tengo dos marcos de datos:

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])

Quiero encontrar las filas que tiene a1 y que no tiene a2.

¿Existe una función integrada para este tipo de operación?

(PD: escribí una solución para ello, simplemente tengo curiosidad por saber si alguien ya creó un código más elaborado)

Aquí está mi solución:

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])

rows.in.a1.that.are.not.in.a2  <- function(a1,a2)
{
    a1.vec <- apply(a1, 1, paste, collapse = "")
    a2.vec <- apply(a2, 1, paste, collapse = "")
    a1.without.a2.rows <- a1[!a1.vec %in% a2.vec,]
    return(a1.without.a2.rows)
}
rows.in.a1.that.are.not.in.a2(a1,a2)
Tal Galili avatar Jan 01 '70 08:01 Tal Galili
Aceptado

sqldfproporciona una buena solución

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])

require(sqldf)

a1NotIna2 <- sqldf('SELECT * FROM a1 EXCEPT SELECT * FROM a2')

Y las filas que están en ambos marcos de datos:

a1Ina2 <- sqldf('SELECT * FROM a1 INTERSECT SELECT * FROM a2')

La nueva versión de dplyrtiene una función, anti_joinexactamente para este tipo de comparaciones.

require(dplyr) 
anti_join(a1,a2)

Y semi_joinpara filtrar filas a1que también están ena2

semi_join(a1,a2)
Rickard avatar Jan 29 '2013 08:01 Rickard

En dplyr :

setdiff(a1,a2)

Básicamente, setdiff(bigFrame, smallFrame)obtiene los registros adicionales en la primera tabla.

En SQLverse esto se llama

Diagrama de Venn de unión izquierda excluyendo

Para obtener buenas descripciones de todas las opciones de unión y temas establecidos, este es uno de los mejores resúmenes que he visto hasta la fecha: http://www.vertabelo.com/blog/technical-articles/sql-joins

Pero volvamos a esta pregunta: aquí están los resultados del setdiff()código cuando se utilizan los datos del OP:

> a1
  a b
1 1 a
2 2 b
3 3 c
4 4 d
5 5 e

> a2
  a b
1 1 a
2 2 b
3 3 c

> setdiff(a1,a2)
  a b
1 4 d
2 5 e

O incluso anti_join(a1,a2)obtendrá los mismos resultados.
Para más información: https://www.rstudio.com/wp-content/uploads/2015/02/data-wrangling-cheatsheet.pdf

leerssej avatar Nov 10 '2016 03:11 leerssej

Esto no responde directamente a su pregunta, pero le brindará los elementos que tienen en común. Esto se puede hacer con el paquete de Paul Murrell compare:

library(compare)
a1 <- data.frame(a = 1:5, b = letters[1:5])
a2 <- data.frame(a = 1:3, b = letters[1:3])
comparison <- compare(a1,a2,allowAll=TRUE)
comparison$tM
#  a b
#1 1 a
#2 2 b
#3 3 c

La función comparele brinda mucha flexibilidad en términos de qué tipo de comparaciones se permiten (por ejemplo, cambiar el orden de los elementos de cada vector, cambiar el orden y los nombres de las variables, acortar las variables, cambiar el caso de las cadenas). A partir de esto, deberías poder descubrir qué faltaba en uno u otro. Por ejemplo (esto no es muy elegante):

difference <-
   data.frame(lapply(1:ncol(a1),function(i)setdiff(a1[,i],comparison$tM[,i])))
colnames(difference) <- colnames(a1)
difference
#  a b
#1 4 d
#2 5 e
nullglob avatar Jul 03 '2010 18:07 nullglob

Ciertamente no es eficiente para este propósito particular, pero lo que hago a menudo en estas situaciones es insertar variables indicadoras en cada data.frame y luego fusionar:

a1$included_a1 <- TRUE
a2$included_a2 <- TRUE
res <- merge(a1, a2, all=TRUE)

Los valores faltantes en include_a1 indicarán qué filas faltan en a1. lo mismo para a2.

Un problema con su solución es que el orden de las columnas debe coincidir. Otro problema es que es fácil imaginar situaciones en las que las filas están codificadas como iguales cuando en realidad son diferentes. La ventaja de utilizar merge es que obtienes de forma gratuita todas las comprobaciones de errores necesarias para encontrar una buena solución.

Eduardo Leoni avatar Jul 03 '2010 17:07 Eduardo Leoni

Escribí un paquete ( https://github.com/alexsanjoseph/compareDF ) ya que tuve el mismo problema.

  > df1 <- data.frame(a = 1:5, b=letters[1:5], row = 1:5)
  > df2 <- data.frame(a = 1:3, b=letters[1:3], row = 1:3)
  > df_compare = compare_df(df1, df2, "row")

  > df_compare$comparison_df
    row chng_type a b
  1   4         + 4 d
  2   5         + 5 e

Un ejemplo más complicado:

library(compareDF)
df1 = data.frame(id1 = c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710",
                         "Hornet 4 Drive", "Duster 360", "Merc 240D"),
                 id2 = c("Maz", "Maz", "Dat", "Hor", "Dus", "Mer"),
                 hp = c(110, 110, 181, 110, 245, 62),
                 cyl = c(6, 6, 4, 6, 8, 4),
                 qsec = c(16.46, 17.02, 33.00, 19.44, 15.84, 20.00))

df2 = data.frame(id1 = c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710",
                         "Hornet 4 Drive", " Hornet Sportabout", "Valiant"),
                 id2 = c("Maz", "Maz", "Dat", "Hor", "Dus", "Val"),
                 hp = c(110, 110, 93, 110, 175, 105),
                 cyl = c(6, 6, 4, 6, 8, 6),
                 qsec = c(16.46, 17.02, 18.61, 19.44, 17.02, 20.22))

> df_compare$comparison_df
    grp chng_type                id1 id2  hp cyl  qsec
  1   1         -  Hornet Sportabout Dus 175   8 17.02
  2   2         +         Datsun 710 Dat 181   4 33.00
  3   2         -         Datsun 710 Dat  93   4 18.61
  4   3         +         Duster 360 Dus 245   8 15.84
  5   7         +          Merc 240D Mer  62   4 20.00
  6   8         -            Valiant Val 105   6 20.22

El paquete también tiene un comando html_output para una verificación rápida.

df_compare$html_output ingrese la descripción de la imagen aquí

Alex Joseph avatar Mar 08 '2016 16:03 Alex Joseph