Recherche de TOUTES les lignes en double, y compris les "éléments avec des indices plus petits"

111

R's duplicatedrenvoie un vecteur indiquant si chaque élément d'un vecteur ou d'un bloc de données est un double d'un élément avec un indice plus petit. Donc, si les lignes 3, 4 et 5 d'une trame de données de 5 lignes sont identiques, duplicatedme donnera le vecteur

FALSE, FALSE, FALSE, TRUE, TRUE

Mais dans ce cas, je veux vraiment avoir

FALSE, FALSE, TRUE, TRUE, TRUE

c'est-à-dire que je veux savoir si une ligne est dupliquée par une ligne avec un indice plus grand également.

Lauren Samuels
la source

Réponses:

128

duplicateda un fromLastargument. La section "Exemple" de ?duplicatedvous montre comment l'utiliser. Appelez simplement duplicateddeux fois, une fois avec fromLast=FALSEet une fois avec fromLast=TRUEet prenez les lignes où se trouvent les deux TRUE.


Quelques modifications tardives: vous n'avez pas fourni d'exemple reproductible, alors voici une illustration aimablement contribuée par @jbaums

vec <- c("a", "b", "c","c","c") 
vec[duplicated(vec) | duplicated(vec, fromLast=TRUE)]
## [1] "c" "c" "c"

Edit: Et un exemple pour le cas d'un bloc de données:

df <- data.frame(rbind(c("a","a"),c("b","b"),c("c","c"),c("c","c")))
df[duplicated(df) | duplicated(df, fromLast=TRUE), ]
##   X1 X2
## 3  c  c
## 4  c  c
Joshua Ulrich
la source
3
Attendez, j'ai juste fait un test et j'ai trouvé que j'avais tort: ​​j'ai x <- c(1:9, 7:10, 5:22); y <- c(letters, letters[1:5]); test <- data.frame(x, y); test[duplicated(test$x) | duplicated(test$x, fromLast=TRUE), ]renvoyé les trois exemplaires de 7, 8 et 9. Pourquoi cela fonctionne-t-il?
JoeM05
1
Parce que ceux du milieu sont capturés peu importe si vous commencez par la fin ou par l'avant. Par exemple, duplicated(c(1,1,1))vs duplicated(c(1,1,1,), fromLast = TRUE)donne c(FALSE,TRUE,TRUE)et c(TRUE,TRUE,FALSE). La valeur moyenne est TRUEdans les deux cas. La prise |des deux vecteurs donne c(TRUE,TRUE,TRUE).
Brandon
34

Vous devez assembler l'ensemble de duplicatedvaleurs, appliquer unique, puis tester avec %in%. Comme toujours, un exemple de problème rendra ce processus vivant.

> vec <- c("a", "b", "c","c","c")
> vec[ duplicated(vec)]
[1] "c" "c"
> unique(vec[ duplicated(vec)])
[1] "c"
>  vec %in% unique(vec[ duplicated(vec)]) 
[1] FALSE FALSE  TRUE  TRUE  TRUE
IRTFM
la source
Se mettre d'accord. Peut même ralentir le traitement, mais il est peu probable qu'il le ralentisse beaucoup.
IRTFM
Plutôt vrai. L'OP n'a pas proposé d'exemple de données pour tester les lignes «jamais dupliquées» dans une trame de données. Je pense que ma suggestion d'utiliser duplicated, uniqueet %in%pourrait facilement être généralisée à un dataframe si l'on devait d'abord pastechaque ligne avec un caractère de séparation inhabituel. (La réponse acceptée est meilleure.)
IRTFM
3

J'ai eu la même question , et si je ne me trompe pas, c'est aussi une réponse.

vec[col %in% vec[duplicated(vec$col),]$col]

Je ne sais pas lequel est le plus rapide, cependant, l'ensemble de données que j'utilise actuellement n'est pas assez grand pour faire des tests qui produisent des intervalles de temps importants.

François M.
la source
1
Cette réponse semble être utilisée à la vecfois comme vecteur atomique et comme trame de données. Je soupçonne qu'avec une trame de données réelle, cela échouerait.
IRTFM
3

Les lignes dupliquées dans une trame de données peuvent être obtenues dplyren faisant

df = bind_rows(iris, head(iris, 20)) # build some test data
df %>% group_by_all() %>% filter(n()>1) %>% ungroup()

Pour exclure certaines colonnes group_by_at(vars(-var1, -var2))pourrait être utilisé à la place pour regrouper les données.

Si les index de ligne et pas seulement les données sont réellement nécessaires, vous pouvez les ajouter d'abord comme dans:

df %>% add_rownames %>% group_by_at(vars(-rowname)) %>% filter(n()>1) %>% pull(rowname)
Holger Brandl
la source
1
Bonne utilisation de n(). N'oubliez pas de dissocier le dataframe résultant.
qwr
@qwr J'ai ajusté la réponse pour dissocier le résultat
Holger Brandl
2

Voici la solution de @Joshua Ulrich en fonction. Ce format vous permet d'utiliser ce code de la même manière que vous utiliseriez duplicated ():

allDuplicated <- function(vec){
  front <- duplicated(vec)
  back <- duplicated(vec, fromLast = TRUE)
  all_dup <- front + back > 0
  return(all_dup)
}

En utilisant le même exemple:

vec <- c("a", "b", "c","c","c") 
allDuplicated(vec) 
[1] FALSE FALSE  TRUE  TRUE  TRUE
canderson156
la source
0

Si vous êtes intéressé par les lignes dupliquées pour certaines colonnes, vous pouvez utiliser une approche plyr :

ddply(df, .(col1, col2), function(df) if(nrow(df) > 1) df else c())

Ajout d'une variable de comptage avec dplyr :

df %>% add_count(col1, col2) %>% filter(n > 1)  # data frame
df %>% add_count(col1, col2) %>% select(n) > 1  # logical vector

Pour les lignes en double (en tenant compte de toutes les colonnes):

df %>% group_by_all %>% add_tally %>% ungroup %>% filter(n > 1)
df %>% group_by_all %>% add_tally %>% ungroup %>% select(n) > 1

L'avantage de ces approches est que vous pouvez spécifier le nombre de doublons comme seuil.

qwr
la source
0

J'ai eu un problème similaire mais j'avais besoin d'identifier les lignes dupliquées par des valeurs dans des colonnes spécifiques. J'ai proposé la solution de dplyr suivante :

df <- df %>% 
  group_by(Column1, Column2, Column3) %>% 
  mutate(Duplicated = case_when(length(Column1)>1 ~ "Yes",
                            TRUE ~ "No")) %>%
  ungroup()

Le code regroupe les lignes par colonnes spécifiques. Si la longueur d'un groupe est supérieure à 1, le code marque toutes les lignes du groupe comme dupliquées. Une fois que cela est fait, vous pouvez utiliser la Duplicatedcolonne pour le filtrage, etc.

Adnan Hajizada
la source