identifier et marquer les lignes en double dans r

11

Je voudrais identifier et marquer les lignes en double sur la base de 2 colonnes. Je voudrais faire un identifiant unique pour chaque doublon, donc je sais non seulement que la ligne est un doublon, mais avec quelle ligne c'est un doublon. J'ai une trame de données qui ressemble à ci-dessous avec quelques paires d'éléments en double (sur ajustement et assis) et d'autres paires qui ne sont pas dupliquées. Alors que les paires d'articles sont dupliquées, les informations qu'elles contiennent sont uniques (par exemple, une ligne aura une valeur dans Value1 pour 1 ligne, mais pas Value2 et Value 3, la seconde ou la ligne 'dupliquée' aura des nombres pour Value2 et Value3 juste pas Value1)

trame de données actuelle

     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"

code pour générer un exemple de trame de données

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)

ce que je veux, c'est le convertir en une trame de données qui ressemble à ceci:

trame de données souhaitée

     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"

Je pensais faire les étapes suivantes: 1. créer de nouvelles variables en utilisant l'ajustement et s'asseoir avec l'élément le plus bas et les éléments les plus élevés pour identifier les paires en double 2. identifier les paires d'éléments en double 3. utiliser ifelse pour sélectionner et remplir des informations uniques.

Je sais comment faire les étapes 1 et 3, mais je suis coincé à l'étape 2. Je pense que ce que je dois faire n'est pas seulement d'identifier les doublons VRAI / FAUX, mais peut-être d'avoir une colonne avec un identifiant unique pour chaque paire d'articles comme ceci (il sont 2 lignes supplémentaires en raison de mon étape 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

Je ne sais pas comment procéder.

Ce que je demande, c'est soit de l'aide pour l'étape 2, soit il existe peut-être une meilleure façon de le résoudre que les étapes que j'ai décrites.

Heather Clark
la source

Réponses:

6

Une dplyroption pourrait être:

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

Et si vous avez également besoin des paires dans des colonnes individuelles, avec l'ajout de tidyrvous pouvez faire:

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
la source
Je vous remercie! Cela fonctionne bien. J'apprécie d'ajouter l'option de séparer les éléments.
Heather Clark
3

Utiliser !duplicated()après 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
la source
Merci pour votre réponse rapide. Cependant, cette solution supprime les informations que je dois conserver. Je veux combiner les informations des 3 colonnes de valeurs qui se trouvent sur les 2 lignes des mêmes paires d'articles. Faites-moi savoir si ce n'est pas clair
Heather Clark
2

Utilisation melt/dcastdedata.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

Les données

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

Une autre data.tableoption:

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]

production:

   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
la source
1

Voici ma tentative d'utilisation de data.table. Vos données sont appelées mydf. Tout d' abord, je triai fitet sitpour chaque ligne et créé une nouvelle variable, group. Ensuite, pour chaque groupe, j'ai trié les valeurs dans les trois colonnes de valeurs (c.-à-d. Valeur1, valeur2 et valeur3). Enfin, j'ai extrait la première ligne pour chaque groupe.

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

LES DONNÉES

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
la source
1

Cela peut également être fait en utilisant tidyr's pivot_longeravec values_drop_na = TRUEcombiné avec 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

Les données

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
la source