Sélectionnez uniquement les premières lignes pour chaque valeur unique d’une colonne dans R

A partir d’un tel dataframe

test <- data.frame('id'= rep(1:5,2), 'string'= LETTERS[1:10]) test <- test[order(test$id), ] rownames(test)  test id ssortingng 1 1 A 2 1 F 3 2 B 4 2 G 5 3 C 6 3 H 7 4 D 8 4 I 9 5 E 10 5 J 

Je veux en créer un nouveau avec la première apparition de chaque paire identifiant / chaîne. Si sqldf acceptait le code R, la requête pourrait ressembler à ceci:

 res  res id ssortingng 1 1 A 3 2 B 5 3 C 7 4 D 9 5 E 

Existe-t-il une solution sans créer une nouvelle colonne comme

 test$row <- rownames(test) 

et exécuter la même requête sqldf avec min (row)?

Vous pouvez utiliser la duplicated pour le faire très rapidement.

 test[!duplicated(test$id),] 

Repères, pour les amateurs de vitesse:

 ju <- function() test[!duplicated(test$id),] gs1 <- function() do.call(rbind, lapply(split(test, test$id), head, 1)) gs2 <- function() do.call(rbind, lapply(split(test, test$id), `[`, 1, )) jply <- function() ddply(test,.(id),function(x) head(x,1)) jdt <- function() { testd <- as.data.table(test) setkey(testd,id) # Initial solution (slow) # testd[,lapply(.SD,function(x) head(x,1)),by = key(testd)] # Faster options : testd[!duplicated(id)] # (1) # testd[, .SD[1L], by=key(testd)] # (2) # testd[J(unique(id)),mult="first"] # (3) # testd[ testd[,.I[1L],by=id] ] # (4) needs v1.8.3. Allows 2nd, 3rd etc } library(plyr) library(data.table) library(rbenchmark) # sample data set.seed(21) test <- data.frame(id=sample(1e3, 1e5, TRUE), string=sample(LETTERS, 1e5, TRUE)) test <- test[order(test$id), ] benchmark(ju(), gs1(), gs2(), jply(), jdt(), replications=5, order="relative")[,1:6] # test replications elapsed relative user.self sys.self # 1 ju() 5 0.03 1.000 0.03 0.00 # 5 jdt() 5 0.03 1.000 0.03 0.00 # 3 gs2() 5 3.49 116.333 2.87 0.58 # 2 gs1() 5 3.58 119.333 3.00 0.58 # 4 jply() 5 3.69 123.000 3.11 0.51 

Essayons à nouveau, mais avec seulement les concurrents de la première manche et avec plus de données et plus de répétitions.

 set.seed(21) test <- data.frame(id=sample(1e4, 1e6, TRUE), string=sample(LETTERS, 1e6, TRUE)) test <- test[order(test$id), ] benchmark(ju(), jdt(), order="relative")[,1:6] # test replications elapsed relative user.self sys.self # 1 ju() 100 5.48 1.000 4.44 1.00 # 2 jdt() 100 6.92 1.263 5.70 1.15 

Qu’en est-il de

 DT <- data.table(test) setkey(DT, id) DT[J(unique(id)), mult = "first"] 

modifier

Il existe également une méthode unique pour data.tables qui renverra la première ligne par clé

 jdtu <- function() unique(DT) 

Je pense que si vous commandez un test dehors du benchmark, vous pouvez également supprimer la conversion setkey et data.table du benchmark (car la setkey sortinge essentiellement par id, le même que order ).

 set.seed(21) test <- data.frame(id=sample(1e3, 1e5, TRUE), string=sample(LETTERS, 1e5, TRUE)) test <- test[order(test$id), ] DT <- data.table(DT, key = 'id') ju <- function() test[!duplicated(test$id),] jdt <- function() DT[J(unique(id)),mult = 'first'] library(rbenchmark) benchmark(ju(), jdt(), replications = 5) ## test replications elapsed relative user.self sys.self ## 2 jdt() 5 0.01 1 0.02 0 ## 1 ju() 5 0.05 5 0.05 0 

et avec plus de données

** Modifier avec une méthode unique **

 set.seed(21) test <- data.frame(id=sample(1e4, 1e6, TRUE), string=sample(LETTERS, 1e6, TRUE)) test <- test[order(test$id), ] DT <- data.table(test, key = 'id') test replications elapsed relative user.self sys.self 2 jdt() 5 0.09 2.25 0.09 0.00 3 jdtu() 5 0.04 1.00 0.05 0.00 1 ju() 5 0.22 5.50 0.19 0.03 

La méthode unique est la plus rapide ici.

Une simple option ddply :

 ddply(test,.(id),function(x) head(x,1)) 

Si la vitesse pose problème, une approche similaire pourrait être adoptée avec data.table :

 testd <- data.table(test) setkey(testd,id) testd[,lapply(.SD,function(x) head(x,1)),by = key(testd)] 

(1) SQLite a une pseudo-colonne rowid , donc ça marche:

 sqldf("select min(rowid) rowid, id, ssortingng from test group by id") 

donnant:

  rowid id ssortingng 1 1 1 A 2 3 2 B 3 5 3 C 4 7 4 D 5 9 5 E 

(2) Aussi sqldf lui-même a un argument row.names= :

 sqldf("select min(cast(row_names as real)) row_names, id, ssortingng from test group by id", row.names = TRUE) 

donnant:

  id ssortingng 1 1 A 3 2 B 5 3 C 7 4 D 9 5 E 

(3) Une troisième alternative qui mélange les éléments des deux précédents pourrait être encore meilleure:

 sqldf("select min(rowid) row_names, id, ssortingng from test group by id", row.names = TRUE) 

donnant:

  id ssortingng 1 1 A 3 2 B 5 3 C 7 4 D 9 5 E 

Notez que ces trois éléments reposent sur une extension SQLite de SQL où l’utilisation de min ou max garantit que les autres colonnes seront choisies dans la même ligne. (Dans d’autres bases de données SQL qui peuvent ne pas être garanties.)

maintenant, pour dplyr , append un compteur distinct.

 df %>% group_by(aa, bb) %>% summarise(first=head(value,1), count=n_distinct(value)) 

Vous créez des groupes, les résument au sein des groupes.

Si les données sont numériques, vous pouvez utiliser:
first(value) [il y a aussi last(value) ] à la place de head(value, 1)

voir: http://cran.rstudio.com/web/packages/dplyr/vignettes/introduction.html

Plein:

 > df Source: local data frame [16 x 3] aa bb value 1 1 1 GUT 2 1 1 PER 3 1 2 SUT 4 1 2 GUT 5 1 3 SUT 6 1 3 GUT 7 1 3 PER 8 2 1 221 9 2 1 224 10 2 1 239 11 2 2 217 12 2 2 221 13 2 2 224 14 3 1 GUT 15 3 1 HUL 16 3 1 GUT > library(dplyr) > df %>% > group_by(aa, bb) %>% > summarise(first=head(value,1), count=n_distinct(value)) Source: local data frame [6 x 4] Groups: aa aa bb first count 1 1 1 GUT 2 2 1 2 SUT 2 3 1 3 SUT 3 4 2 1 221 3 5 2 2 217 3 6 3 1 GUT 2 

Une option de base R est l’idiome split()lapply()do.call() :

 > do.call(rbind, lapply(split(test, test$id), head, 1)) id ssortingng 1 1 A 2 2 B 3 3 C 4 4 D 5 5 E 

Une option plus directe consiste à lapply() la [ fonction:

 > do.call(rbind, lapply(split(test, test$id), `[`, 1, )) id ssortingng 1 1 A 2 2 B 3 3 C 4 4 D 5 5 E 

L’espace virgule 1, ) à la fin de l’appel lapply() est essentiel car cela équivaut à appeler [1, ] pour sélectionner la première ligne et toutes les colonnes.

Je suis en faveur de l’approche dplyr.

 library(dplyr) test %>% group_by(id) %>% filter(row_number()==1) # A tibble: 5 x 2 # Groups: id [5] id ssortingng   1 1 A 2 2 B 3 3 C 4 4 D 5 5 E 

Regrouper par identifiant et filtre pour ne renvoyer que la première ligne. Dans certains cas, organiser les identifiants après le group_by peut être nécessaire.

 test_subset <- test[unique(test$id),] 

Juste cette ligne générera le sous-ensemble que vous voulez.