Comment puis-je sous-définir le dataframe ci-dessous pour afficher uniquement les lignes où les colonnes AAA:CCC ont toutes la même valeur et conserver le champ IndID?

Dat <- data.frame(IndID = LETTERS[seq(1,10)],
                  AAA = c(1,5,3,2,3,1,5,4,6,2),
                  BBB = c(1,8,5,2,5,4,8,4,4,5),
                  CCC = c(1,5,3,2,3,5,5,4,6,5))

> Dat
   IndID AAA BBB CCC
1      A   1   1   1
2      B   5   8   5
3      C   3   5   3
4      D   2   2   2
5      E   3   5   3
6      F   1   4   5
7      G   5   8   5
8      H   4   4   4
9      I   6   4   6
10     J   2   5   5

Je voudrais retourner le résultat suivant.

Result <- data.frame(IndID = c("A", "D", "H"),
                  AAA = c(1,2,4),
                  BBB = c(1,2,4),
                  CCC = c(1,2,4))

> Result
  IndID AAA BBB CCC
1     A   1   1   1
2     D   2   2   2
3     H   4   4   4

J'ai trouvé un certain nombre d'articles connexes, notamment Rechercher les lignes dupliquées (basées sur 2 colonnes) dans le bloc de données dans R et Rechercher les doublons éléments avec dplyr, entre autres, mais n'ont pas pu reproduire le résultat souhaité avec trois colonnes. Par exemple, lors de la fermeture, le code ci-dessous montre toutes les distinct lignes, mais avec le résultat indésirable de l'inclusion de lignes, seules deux des valeurs sont égales.

Dat %>% distinct(AAA, BBB, CCC) 

Je soupçonne que la solution implique filter mais je ne sais pas comment obtenir le résultat souhaité à partir de l'exemple mentionné ci-dessus. Une solution dplyr est préférable.

Ajout Je me demande également si cela pourrait s'appliquer à plusieurs facteurs. Par exemple, avec les données ci-dessous, qui contiennent un IndID et trois dates stockées en tant que facteurs, un résultat similaire peut-il être produit en autorisant des facteurs ou en changeant les facteurs en valeurs numériques?

Dat <- structure(list(GenIndID = structure(c(1L, 2L, 6L, 7L, 3L, 4L, 
8L, 5L), .Label = c("BHS_601", "BHS_603", "BHS_604", "BHS_605", 
"BHS_631", "BHS_635", "BHS_636", "BHS_637"), class = "factor"), 
    CptrDate = structure(c(1L, 2L, 3L, 3L, 2L, 2L, 3L, 4L), .Label = c("2016-02-01", 
    "2016-02-02", "2016-02-04", "2016-12-11"), class = "factor"), 
    DtLastAlive = structure(c(2L, 2L, 1L, 1L, 2L, 2L, 1L, 3L), .Label = c("2016-02-04", 
    "2017-07-13", "2017-08-27"), class = "factor"), DtFnlFate = structure(c(2L, 
    2L, 1L, 1L, 2L, 2L, 1L, 3L), .Label = c("2016-02-04", "2017-07-13", 
    "2017-08-27"), class = "factor")), .Names = c("GenIndID", 
"CptrDate", "DtLastAlive", "DtFnlFate"), row.names = c(82L, 83L, 
224L, 225L, 84L, 85L, 226L, 360L), class = "data.frame")


> Dat
    GenIndID   CptrDate DtLastAlive  DtFnlFate
82   BHS_601 2016-02-01  2017-07-13 2017-07-13
83   BHS_603 2016-02-02  2017-07-13 2017-07-13
224  BHS_635 2016-02-04  2016-02-04 2016-02-04
225  BHS_636 2016-02-04  2016-02-04 2016-02-04
84   BHS_604 2016-02-02  2017-07-13 2017-07-13
85   BHS_605 2016-02-02  2017-07-13 2017-07-13
226  BHS_637 2016-02-04  2016-02-04 2016-02-04
360  BHS_631 2016-12-11  2017-08-27 2017-08-27

Le résultat souhaité étant

> Dat[c(3, 4, 7),]
    GenIndID   CptrDate DtLastAlive  DtFnlFate
224  BHS_635 2016-02-04  2016-02-04 2016-02-04
225  BHS_636 2016-02-04  2016-02-04 2016-02-04
226  BHS_637 2016-02-04  2016-02-04 2016-02-04
2
B. Davis 15 nov. 2017 à 19:14

4 réponses

Meilleure réponse

Voici une autre solution avec apply et all:

Dat[apply(Dat[,-1], 1, function(x) all(x==x[1])),]

Ou avec filter_at de dplyr:

library(dplyr)
Dat %>% 
  filter_at(vars(AAA:CCC), all_vars(. == .data$AAA))

Résultat:

  IndID AAA BBB CCC
1     A   1   1   1
4     D   2   2   2
8     H   4   4   4

Modifier:

En réponse à l'exemple supplémentaire d'OP, l'exemple apply fonctionnerait quel que soit le type de variable. Ainsi, ce qui suit fonctionnerait pour le nouvel exemple:

Dat[apply(Dat[,-1], 1, function(x) all(x==x[1])),]

Résultat:

    GenIndID   CptrDate DtLastAlive  DtFnlFate
224  BHS_635 2016-02-04  2016-02-04 2016-02-04
225  BHS_636 2016-02-04  2016-02-04 2016-02-04
226  BHS_637 2016-02-04  2016-02-04 2016-02-04

Pour filter_at, cependant, si les colonnes à comparer sont celles de factor, il faudrait d'abord convertir en character:

Dat %>% 
  filter_at(vars(-1), all_vars(as.character(.) == .data$CptrDate))

Notez que vous devez seulement convertir . en caractère, mais pas .data$CptrDate car les facteurs peuvent être comparés avec des caractères, mais pas avec un autre facteur avec des niveaux différents.

Une alternative serait:

Dat %>%
  mutate_at(vars(-1), as.character) %>%
  filter_at(vars(-1), all_vars(. == .data$CptrDate))

Résultat:

  GenIndID   CptrDate DtLastAlive  DtFnlFate
1  BHS_635 2016-02-04  2016-02-04 2016-02-04
2  BHS_636 2016-02-04  2016-02-04 2016-02-04
3  BHS_637 2016-02-04  2016-02-04 2016-02-04
6
avid_useR 15 nov. 2017 à 17:37

Si vous n'avez besoin que d'un sous-ensemble de 3 colonnes, vous pouvez utiliser l'opérateur DF [,].

# DF[where rows have a value, select columns]
# [where rows where AAA==BBB==CCC, select all columns with ""]
temp <- Dat[Dat$AAA == Dat$BBB & Dat$BBB == Dat$CCC,] 
2
Adam Sampson 15 nov. 2017 à 16:53

Vous pouvez avoir une solution vectorisée:

Dat[do.call(function(...) pmax(...) - pmin(...), Dat[, -1]) == 0,]
#  IndID AAA BBB CCC
#1     A   1   1   1
#4     D   2   2   2
#8     H   4   4   4
1
eddi 15 nov. 2017 à 23:50

Vous pouvez utiliser une combinaison de range() et diff().

Dat[apply(Dat[ ,-1], 1, function(x) diff(range(x)))==0, ] 
#   IndID AAA BBB CCC
# 1     A   1   1   1
# 4     D   2   2   2
# 8     H   4   4   4

range() vous donne le minimum et le maximum d'un vecteur. diff() vous donne la différence entre les valeurs d'un vecteur. Si la différence entre min-value et max-value est nulle, vous savez que toutes les valeurs sont égales. Si nous appliquons cette ligne par ligne en utilisant apply, nous obtenons un vecteur TRUE / FALSE que nous pouvons utiliser pour indexer Dat.


Petits exemples pour illustrer la logique:

test <- c(1, 5, 3)
minmax <- range(test)    # gives c(1,5)
diff(minmax)             # gives 4
diff(range(c(1, 1, 1)))  # gives 0 

Si nous vérifions pour chaque ligne, si diff(range(your_row)) est égal à zéro ou non, nous pouvons utiliser la sortie de celle-ci pour indexer Dat, comme nous l'avons fait ci-dessus.

1
KenHBS 15 nov. 2017 à 16:31
47312275