J'ai ce bloc de données df

df <- data.frame(stringsAsFactors=FALSE,
          id = c(1L, 2L, 3L, 4L, 5L, 6L),
     Country = c("ESP", "ESP", "ESP", "ITA", "ITA", "ITA"),
        Year = c(1965L, 1965L, 1965L, 1965L, 1965L, 1965L),
   Time.step = c("Month", "Month", "Month", "Month", "Month", "Month"),
    GSA.numb = c("GSA 5", "GSA 5", "GSA 5", "GSA 17", "GSA 17", "GSA 17"),
     Species = c("Mullus", "Mullus", "Mullus", "Eledone", "Eledone", "Eledone"),
    Quantity = c(500L, 200L, 200L, 350L, 350L, 125L)
                )

df

   id  Country   Year    Time.step    GSA.numb  Species   Quantity
    1    ESP     1965     Month       GSA 5      Mullus     500   
    2    ESP     1965     Month       GSA 5      Mullus     200  
    3    ESP     1965     Month       GSA 5      Mullus     200 
    4    ITA     1965     Month       GSA 17     Eledone    350
    5    ITA     1965     Month       GSA 17     Eledone    350 
    6    ITA     1965     Month       GSA 17     Eledone    125

J'ai une ligne dupliquée, comme: 3 et 5. Je peux créer une colonne pour la valeur logique F ou T lorsque la ligne est dupliquée:

df$dup <- duplicated(df[,2:7]) #No id! 

Résultat:

id  Country   Year    Time.step    GSA.numb  Species   Quantity dup
 1    ESP     1965     Month       GSA 5      Mullus     500   FALSE
 2    ESP     1965     Month       GSA 5      Mullus     200   FALSE
 3    ESP     1965     Month       GSA 5      Mullus     200   TRUE
 4    ITA     1965     Month       GSA 17     Eledone    350   FALSE
 5    ITA     1965     Month       GSA 17     Eledone    350   TRUE
 6    ITA     1965     Month       GSA 17     Eledone    125   FALSE

Maintenant, je voudrais une nouvelle colonne (de manière dynamique, mon vrai df est très grand, avec de nombreuses lignes, colonnes et variables) où il est possible de voir le nombre de lignes dupliquées quand est TRUE, comme ceci:

aspected.df

id  Country Year  Time.step  GSA.numb  Species   Quantity dup  ref  
 1  ESP     1965  Month      GSA 5      Mullus     500   FALSE NA
 2  ESP     1965  Month      GSA 5      Mullus     200   FALSE NA
 3  ESP     1965  Month      GSA 5      Mullus     200   TRUE  =id2
 4  ITA     1965  Month      GSA 17     Eledone    350   FALSE NA
 5  ITA     1965  Month      GSA 17     Eledone    350   TRUE  =id4
 6  ITA     1965  Month      GSA 17     Eledone    125   FALSE NA

J'ai essayé avec:

with(df, ave(as.character(Species), df[,2:6], FUN = make.unique)) 

Mais le résultat est:

[1] "Mullus"    "Mullus.1"  "Mullus.2"  "Eledone"   "Eledone.1" "Eledone.2"

Je pense que j'ai besoin de plus de code. Quelles fonctions sont utiles? (duplicated,make.unit, row.names et ainsi de suite ...)

1
skylobo 20 nov. 2018 à 14:20

4 réponses

Meilleure réponse

Une approche data.table, à partir du fichier initial:

library(data.table)

setDT(df)[, `:=` (dup = seq_len(.N) > 1, ref = paste0("id", first(id))), 
          by = .(Country, Year, Time.step, GSA.numb, Species, Quantity)][dup == FALSE, ref := NA]

Production:

   id Country Year Time.step GSA.numb Species Quantity   dup  ref
1:  1     ESP 1965     Month     GSA5  Mullus      500 FALSE <NA>
2:  2     ESP 1965     Month     GSA5  Mullus      200 FALSE <NA>
3:  3     ESP 1965     Month     GSA5  Mullus      200  TRUE  id2
4:  4     ITA 1965     Month    GSA17 Eledone      350 FALSE <NA>
5:  5     ITA 1965     Month    GSA17 Eledone      350  TRUE  id4
6:  6     ITA 1965     Month    GSA17 Eledone      125 FALSE <NA>

Une approche tidyverse (avec dup déjà créée auparavant):

library(tidyverse)

df %>% 
  group_by_at(vars(2:7)) %>% 
  mutate(ref = ifelse(dup, paste0("id", first(id)), NA_character_))

Production:

     id Country  Year Time.step GSA.numb Species Quantity dup   ref  
  <int> <chr>   <int> <chr>     <chr>    <chr>      <int> <lgl> <chr>
1     1 ESP      1965 Month     GSA5     Mullus       500 FALSE NA   
2     2 ESP      1965 Month     GSA5     Mullus       200 FALSE NA   
3     3 ESP      1965 Month     GSA5     Mullus       200 TRUE  id2  
4     4 ITA      1965 Month     GSA17    Eledone      350 FALSE NA   
5     5 ITA      1965 Month     GSA17    Eledone      350 TRUE  id4  
6     6 ITA      1965 Month     GSA17    Eledone      125 FALSE NA

Si vous souhaitez créer la colonne dup dans l'instruction:

df %>% 
  group_by_at(vars(2:7)) %>% 
  mutate(
    dup = row_number() > 1,
    ref = ifelse(dup, paste0("id", first(id)), NA_character_))

Production:

     id Country  Year Time.step GSA.numb Species Quantity dup   ref  
  <int> <chr>   <int> <chr>     <chr>    <chr>      <int> <lgl> <chr>
1     1 ESP      1965 Month     GSA5     Mullus       500 FALSE NA   
2     2 ESP      1965 Month     GSA5     Mullus       200 FALSE NA   
3     3 ESP      1965 Month     GSA5     Mullus       200 TRUE  id2  
4     4 ITA      1965 Month     GSA17    Eledone      350 FALSE NA   
5     5 ITA      1965 Month     GSA17    Eledone      350 TRUE  id4  
6     6 ITA      1965 Month     GSA17    Eledone      125 FALSE NA 
4
arg0naut91 20 nov. 2018 à 12:34

Vous pouvez utiliser les fonctions tidyverse pour identifier rapidement les doublons

df$dup <- duplicated(df[,2:7]) #No id! 

library(tidyverse)

df %>% 
 group_by(dup) %>% 
 mutate(ref=ifelse(dup, paste0("id",1:n()), NA_character_))

#> # A tibble: 6 x 9
#> # Groups:   dup [2]
#>      id Country  Year Time.step GSA.numb Species Quantity dup   ref  
#>   <int> <chr>   <int> <chr>     <chr>    <chr>      <int> <lgl> <chr>
#> 1     1 ESP      1965 Month     GSA 5    Mullus       500 FALSE NA   
#> 2     2 ESP      1965 Month     GSA 5    Mullus       200 FALSE NA   
#> 3     3 ESP      1965 Month     GSA 5    Mullus       200 TRUE  id1  
#> 4     4 ITA      1965 Month     GSA 17   Eledone      350 FALSE NA   
#> 5     5 ITA      1965 Month     GSA 17   Eledone      350 TRUE  id2  
#> 6     6 ITA      1965 Month     GSA 17   Eledone      125 FALSE NA 
2
dmi3kno 20 nov. 2018 à 11:35

Utilisation de tidyverse:

df %>%
  group_by_at(vars(-id)) %>% #Group by all variables except of id
  mutate(n = n(), #Identifying the duplicate rows
         dup = ifelse(seq_along(n) > 1, TRUE, FALSE), #Coding the first unique row as TRUE and others as FALSE
         ref = ifelse(dup == TRUE, paste0("=id", first(id[dup == FALSE])), NA_character_)) %>% #Pasting the id of the first unique row
 select(-n)

     id Country  Year Time.step GSA.numb Species Quantity dup   ref  
  <int> <chr>   <int> <chr>     <chr>    <chr>      <int> <lgl> <chr>
1     1 ESP      1965 Month     GSA 5    Mullus       500 FALSE <NA> 
2     2 ESP      1965 Month     GSA 5    Mullus       200 FALSE <NA> 
3     3 ESP      1965 Month     GSA 5    Mullus       200 TRUE  =id2 
4     4 ITA      1965 Month     GSA 17   Eledone      350 FALSE <NA> 
5     5 ITA      1965 Month     GSA 17   Eledone      350 TRUE  =id4 
6     6 ITA      1965 Month     GSA 17   Eledone      125 FALSE <NA> 
0
tmfmnk 20 nov. 2018 à 12:17

Cet exemple utilise la base R et fait correspondre les doublons trouvés avec la valeur d'origine. C'est utile si vous avez également plusieurs doublons pour une seule ligne.

Exemple de données (utilisé dput(control = NULL) pour que les caractères / facteurs soient convertis en numérique)

df <- data.frame(id = c(1, 1, 1, 2, 2, 2), 
           Country = c(1965, 1965, 1965, 1965, 1965, 1965), 
           Year = c(1, 1, 1, 1, 1, 1), 
           Time.step = c(1, 1, 1, 1, 1, 1), 
           GSA.numb = c(5, 5, 5, 17, 17, 17), 
           Species = c(2, 2, 2, 1, 1, 1), Quantity = c(500, 200, 200, 350, 350, 125))

Le code est vectorisé donc, malgré la boucle externe, il devrait s'exécuter assez rapidement sur votre grande trame de données.

df$dup <- duplicated(df)
dupes <- df[df$dup,]
df$ref <- NA # initialize 
for(i in 1:nrow(dupes)){
  z=which(df[,1] == dupes[i,1]&
          df[,2] == dupes[i,2]&
          df[,3] == dupes[i,3]&
          df[,4] == dupes[i,4]&
          df[,5] == dupes[i,5]&
          df[,6] == dupes[i,6]&
          df[,7] == dupes[i,7]) # make sure not to include that $dup column!
  df$ref[z[-1]] <- paste0("=id",min(z))
}
df
#  id Country Year Time.step GSA.numb Species Quantity   dup  ref
#1  1    1965    1         1        5       2      500 FALSE <NA>
#2  1    1965    1         1        5       2      200 FALSE <NA>
#3  1    1965    1         1        5       2      200  TRUE =id2
#4  2    1965    1         1       17       1      350 FALSE <NA>
#5  2    1965    1         1       17       1      350  TRUE =id4
#6  2    1965    1         1       17       1      125 FALSE <NA>

Même si vous pouvez resserrer cela avec les fonctions Apply, cela fonctionnera plus rapidement.

0
Evan Friedland 20 nov. 2018 à 12:08