Tengo un conjunto de datos que contiene variables como nombre, nacionalidad y sexo. Quiero verificar la exactitud de la variable de sexo de este conjunto de datos en función de un conjunto de datos de referencia que contiene valores de sexo correctos para cada combinación de nombre-nacionalidad.

Considere los dos conjuntos de datos de ejemplo a continuación:

# Real data set that should be validated
df_real <- data.frame(name = c("Kevin", "Marie", "Rute", NA, "Charles", "Bruno"),
                      nationality = c("USA", "DE", "PT", "FR", NA, "PT"),
                      sex = c(1, 2, 1, 2, 2, NA)) # 1 = Male; 2 = Female

# Correct data set as basement for validation
df_check <- data.frame(name = c("Alfons", "Kevin", "Kevin", "Kevin", "Rute", "Charles", "Bruno", "Anne"),
                       nationality = c("FR", "USA", "DE", "PT", "PT", "FR", "PT", "LU"),
                       sex = c(1, 1, 1, 1, 2, 1, 1, 2))

Se debe verificar que el sexo de la columna de df_real sea correcto según df_check. En el ejemplo, todos los valores sexuales serían correctos al lado de la tercera fila (es decir, Rute debería ser una mujer en df_real).

Es necesario considerar varias condiciones de datos adicionales:

  • df_real tiene varios NA. Si cualquier valor de una fila en df_real es NA, se debe omitir la verificación de esta fila.
  • Los nombres que aparecen en df_real no siempre aparecen en df_check. En este caso, también se debe omitir la verificación de esta fila.

El resultado final debe ser un vector ficticio con una longitud de nrow(df_real) que contenga 0 (es decir, el valor es correcto o se omitió la verificación) y 1 (es decir, el valor es incorrecto).

Salida esperada:

output_check <- c(0, 0, 1, 0, 0, 0)

Traté de resolver esto con complicados bucles for y condiciones if. Sin embargo, dado que mis datos son muy grandes, esto requiere mucho tiempo de cálculo. Estoy seguro de que debe haber una solución más sencilla basada en las funciones de apply() que, lamentablemente, no puedo entender.

2
Joachim Schork 4 feb. 2019 a las 17:22

2 respuestas

La mejor respuesta

Podríamos left_join ambas tablas en name y nationality luego verificar ambas columnas sex y asignar el valor 1 en caso de que sean valores diferentes y replace el no -coincidencia de NA s con 0.

library(tidyverse)

df_real %>% 
  left_join(df_check, by = c("name" = "name","nationality" = "nationality")) %>%
  mutate(check = +(sex.x != sex.y)) %>%
  replace_na(list(check = 0)) #%>%
  #select(-sex.x, -sex.y) #if you don't need sex columns


#     name nationality sex.x sex.y check
#1   Kevin         USA     1     1     0
#2   Marie          DE     2    NA     0
#3    Rute          PT     1     2     1
#4    <NA>          FR     2    NA     0
#5 Charles        <NA>     2    NA     0
#6   Bruno          PT    NA     1     0

Usando la misma lógica con la base R merge

df1 <- merge(df_real, df_check, by = c("name", "nationality"), all.x = TRUE)
df1$check <- +(df1$sex.x != df1$sex.y)
df1$check[is.na(df1$check)] <- 0

df1
#     name nationality sex.x sex.y check
#1   Bruno          PT    NA     1     0
#2 Charles        <NA>     2    NA     0
#3   Kevin         USA     1     1     0
#4   Marie          DE     2    NA     0
#5    Rute          PT     1     2     1
#6    <NA>          FR     2    NA     0

El orden en el que se muestra la salida difiere en base R.

1
Ronak Shah 4 feb. 2019 a las 14:50

Podríamos usar data.table para unirnos en 'nombre', 'nacionalidad' para crear la columna de 'verificación'

library(data.table)
setDT(df_real)[df_check, check :=  +(sex !=  i.sex), on = .(name, nationality)]
df_real[is.na(check), check := 0]
df_real
#      name nationality sex check
#1:   Kevin         USA   1     0
#2:   Marie          DE   2     0 
#3:    Rute          PT   1     1
#4:    <NA>          FR   2     0
#5: Charles        <NA>   2     0
#6:   Bruno          PT  NA     0
2
akrun 4 feb. 2019 a las 14:38