Identificar y marcar filas duplicadas en r

11

Me gustaría identificar y marcar filas duplicadas basadas en 2 columnas. Me gustaría hacer un identificador único para cada duplicado, así que sé no solo que la fila es un duplicado, sino con qué fila es un duplicado. Tengo un marco de datos que se ve a continuación con algunos pares de elementos duplicados (en forma y sentado) y otros pares que no están duplicados. Si bien los pares de elementos están duplicados, la información que contienen es única (por ejemplo, una fila tendrá un valor en Value1 para 1 fila, pero no Value2 y Value 3, la segunda fila o 'duplicado' tendrá números para Value2 y Value3 solo no valor1)

marco de datos actual

     value1 value2 value3 fit   sit  
[1,] "1"    NA     NA     "it1" "it2"
[2,] NA     "3"    "2"    "it2" "it1"
[3,] "2"    "3"    "4"    "it3" "it4"
[4,] NA     NA     NA     "it4" "it3"
[5,] "5"    NA     NA     "it5" "it6"
[6,] NA     NA     "2"    "it6" "it5"
[7,] NA     "4"    NA     "it7" "it9"

código para generar un marco de datos de ejemplo

value1<-c(1,NA,2,NA,5,NA,NA)
value2<-c(NA,3,3,NA,NA,NA, 4)
value3<-c(NA,2,4,NA,NA,2, NA)
fit<-c("it1","it2","it3","it4", "it5", "it6","it7")
sit<-c("it2","it1","it4","it3", "it6", "it5", "it9")
df.now<-cbind(value1,value2,value3, fit, sit)

lo que quiero es convertirlo a un marco de datos que se vea así:

marco de datos deseado

     val1 val2 val3 it1   it2  
[1,] "1"  "3"  "2"  "it1" "it2"
[2,] "2"  "3"  "4"  "it3" "it4"
[3,] "5"  NA   "2"  "it5" "it6"
[4,] NA   "4"  NA   "it7" "it9"

Estaba pensando en seguir los siguientes pasos: 1. crear nuevas variables usando fit y sentarse con el elemento más bajo y los elementos más altos para identificar pares duplicados 2. identificar pares de elementos duplicados 3. usar ifelse para seleccionar y completar información única.

Sé cómo hacer los pasos 1 y 3, pero estoy atascado en el paso 2. Creo que lo que tengo que hacer no es solo identificar el duplicado VERDADERO / FALSO, sino quizás tener una columna con un identificador único para cada par de elementos como este (hay son 2 filas adicionales debido a mi paso 1):

     value1 value2 value3 fit   sit   lit   hit    dup
[1,] "1"    NA     NA     "it1" "it2" "it1" "it2"   1
[2,] NA     "3"    "2"    "it2" "it1" "it1" "it2"   1
[3,] "2"    "3"    "4"    "it3" "it4" "it3" "it4"   2
[4,] NA     NA     NA     "it4" "it3" "it3" "it4"   2
[5,] "5"    NA     NA     "it5" "it6" "it5" "it6"   3
[6,] NA     NA     "2"    "it6" "it5" "it5" "it6"   3
[7,] NA     "4"    NA     "it7" "it9" "it7" "it9"   NA

No estoy seguro de cómo hacer esto.

Lo que estoy pidiendo es ayuda con el paso 2 o tal vez hay una mejor manera de resolverlo que los pasos que describí.

Heather Clark
fuente

Respuestas:

6

Una dplyropción podría ser:

df.now %>%
 group_by(pair = paste(pmax(fit, sit), pmin(fit, sit), sep = "_")) %>%
 summarise_at(vars(starts_with("value")), ~ ifelse(all(is.na(.)), 
                                                   NA,
                                                   first(na.omit(.))))

  pair    value1 value2 value3
  <chr>    <dbl>  <dbl>  <dbl>
1 it2_it1      1      3      2
2 it4_it3      2      3      4
3 it6_it5      5     NA      2
4 it9_it7     NA      4     NA

Y si también necesita los pares en columnas individuales, con la adición de tidyrpuede hacer:

df.now %>%
 group_by(pair = paste(pmax(fit, sit), pmin(fit, sit), sep = "_")) %>%
 summarise_at(vars(starts_with("value")), ~ ifelse(all(is.na(.)), 
                                                   NA,
                                                   first(na.omit(.)))) %>%
 separate(pair, into = c("fit", "hit"), sep = "_", remove = FALSE)

  pair    fit   hit   value1 value2 value3
  <chr>   <chr> <chr>  <dbl>  <dbl>  <dbl>
1 it2_it1 it2   it1        1      3      2
2 it4_it3 it4   it3        2      3      4
3 it6_it5 it6   it5        5     NA      2
4 it9_it7 it9   it7       NA      4     NA
tmfmnk
fuente
¡Gracias! Esto funciona bien Agradezco agregar la opción de separar los elementos.
Heather Clark
3

Usar !duplicated()después de sorting.

df.now[!duplicated(t(apply(df.now[, c("fit", "sit")], 1, sort))), ]
#       value1 value2 value3 fit   sit  
# [1,] "1"    NA     NA     "it1" "it2"
# [2,] "2"    "3"    "4"    "it3" "it4"
# [3,] "5"    NA     NA     "it5" "it6"
# [4,] NA     "4"    NA     "it7" "it9"
jay.sf
fuente
Gracias por la rápida respuesta. Sin embargo, esta solución elimina la información que necesito mantener. Quiero combinar la información de las 3 columnas de valor que se encuentran en las 2 filas de los mismos pares de elementos. Avíseme si esto no está claro
Heather Clark
2

Usando melt/dcastdesdedata.table

library(data.table)
dcast(melt(setDT(df.now)[, c('fit1', 'sit1') := .(pmin(fit, sit), 
    pmax(fit, sit))], measure = patterns("^value"), na.rm = TRUE),
     fit1 + sit1 ~ variable, value.var = 'value')
#   fit1 sit1 value1 value2 value3
#1:  it1  it2      1      3      2
#2:  it3  it4      2      3      4
#3:  it5  it6      5     NA      2
#4:  it7  it9     NA      4     NA

datos

df.now <- data.frame(value1,value2,value3, fit, sit, stringsAsFactors = FALSE)
akrun
fuente
2

Otra data.tableopcion:

library(data.table)
as.data.table(df.now)[, lapply(.SD, function(x) first(x[!is.na(x)])), 
    .(it1=pmin(fit, sit), it2=pmax(fit, sit)), 
    .SDcols=value1:value3]

salida:

   it1 it2 value1 value2 value3
1: it1 it2      1      3      2
2: it3 it4      2      3      4
3: it5 it6      5   <NA>      2
4: it7 it9   <NA>      4   <NA>
chinsoon12
fuente
1

Aquí está mi intento de usar data.table. Se llaman sus datos mydf. En primer lugar, me lo solucionaron fity sitpara cada fila y ha creado una nueva variable, group. Luego, para cada grupo, clasifiqué los valores en las tres columnas de valores (es decir, valor1, valor2 y valor3). Finalmente, extraje la primera fila para cada grupo.

library(data.table)

mydt <- setDT(mydf)[, group := paste(sort(.SD), collapse = "_"),
                    .SD = c("fit", "sit"), by = 1:nrow(mydf)][,
                        c("value1", "value2", "value3") := lapply(.SD, sort),
                        .SDcols = value1:value3, by = group][, .SD[1], by = group]

mydt[]

#     group value1 value2 value3 fit sit
#1: it1_it2      1      3      2 it1 it2
#2: it3_it4      2      3      4 it3 it4
#3: it5_it6      5     NA      2 it5 it6
#4: it7_it9     NA      4     NA it7 it9

DATOS

mydf <- structure(list(value1 = c(1L, NA, 2L, NA, 5L, NA, NA), value2 = c(NA, 
3L, 3L, NA, NA, NA, 4L), value3 = c(NA, 2L, 4L, NA, NA, 2L, NA
), fit = c("it1", "it2", "it3", "it4", "it5", "it6", "it7"), 
sit = c("it2", "it1", "it4", "it3", "it6", "it5", "it9")), class = "data.frame", row.names = c(NA, 
-7L))
jazzurro
fuente
1

Esto también se puede hacer usando tidyr's pivot_longercon values_drop_na = TRUEcombinado con pivot_wider:

library(tidyverse)

mydf %>%
   mutate(it1 = pmin(fit, sit), it2 = pmax(fit, sit)) %>%
   pivot_longer(cols = starts_with("value"), values_drop_na = TRUE) %>%
   pivot_wider(id_cols = c("it1", "it2"))

#> # A tibble: 4 x 5
#>   it1   it2   value1 value2 value3
#>   <chr> <chr>  <int>  <int>  <int>
#> 1 it1   it2        1      3      2
#> 2 it3   it4        2      3      4
#> 3 it5   it6        5     NA      2
#> 4 it7   it9       NA      4     NA

Datos

mydf <- structure(list(value1 = c(1L, NA, 2L, NA, 5L, NA, NA), value2 = c(NA, 
3L, 3L, NA, NA, NA, 4L), value3 = c(NA, 2L, 4L, NA, NA, 2L, NA
), fit = c("it1", "it2", "it3", "it4", "it5", "it6", "it7"), 
sit = c("it2", "it1", "it4", "it3", "it6", "it5", "it9")), class = "data.frame", row.names = c(NA, 
-7L))
Joris Chau
fuente