4 votes

Extraire des lignes pour la première occurrence d'une variable dans un groupe

Je dispose d'un énorme ensemble de données (plus de 2 millions de lignes de plus de 100 variables ; voici un petit échantillon). Pour chaque subj_trial je veux trouver la première occurrence de chaque variable unique contenant ".wav" dans le groupe message . Il doit s'agir d'un simple contenu, et non d'une fin (c'est-à-dire *.wav), car certaines lignes contiennent un grand nombre d'informations dans le fichier message (non illustrés dans l'exemple, désolé).

Il serait possible de produire un data.frame ne contenant que ces trois colonnes, mais ce n'est pas nécessaire. Plus tard, j'aurai besoin d'utiliser la fonction timestamp pour les analyses.

J'ai trouvé cette question : Extraire des lignes pour la première occurrence d'une variable dans un cadre de données Mais je n'arrive pas à adapter cet exemple au mien.

Voici quelques exemples de données :

   subj_trial     message timestamp
1         1_1 message 459    755616
2         1_1           .    755618
3         1_1   test1.wav    755662
4         1_1           .    765712
5         1_1   test1.wav    767918
6         1_2           .    769342
7         1_2   test2.wav    775662
8         1_2           .    786412
9         1_2   test2.wav    797460
10        1_2           .    807626
11        1_3   test3.wav    817794
12        1_3  warning 11    827960
13        2_1 message 481    817313
14        2_1   test1.wav    817347
15        2_1           .    834959
16        2_1   test1.wav    855007
17        2_1           .    880107
18        2_2           .    895723
19        2_2   test2.wav    922671
20        2_2           .    958003
21        2_2   test2.wav    994385
22        2_3           .   1016217
23        2_3   test3.wav   1036899
24        2_3           .   1047331
25        2_3   test3.wav   1142527

Il s'agit là d'un tout petit exemple de ce que j'ai à faire ici. Pour chaque subj_trial il y a probablement 3000 lignes, et il y a plus de 700 groupes.

Voici un exemple de ce que j'aimerais avoir.

  subj_trial   message timestamp
1        1_1 test1.wav    755662
2        1_2 test2.wav    775662
3        1_3 test3.wav    817794
4        2_1 test1.wav    817347
5        2_2 test2.wav    922671
6        2_3 test3.wav   1036899

J'ai trouvé le moyen d'obtenir les valeurs uniques en message sur l'ensemble de la base de données :

unique_message <- df[match(unique(df$message), df$message),]

Mais je n'arrive pas à trouver comment le faire par groupe. J'ai également essayé d'utiliser group_by dans le dplyr mais cela ne fonctionne pas non plus. Ayez pitié et montrez-moi le chemin, mes amis. Merci de votre compréhension.

2voto

Serban Tanasa Points 2618

Nous utilisons également data.table, mais avec une formulation plus concise :

setDT(DT)
DT[,.SD[grep("\\.wav",message)[1]],by=subj_trial]

Edita: Comme le suggère un commentaire ci-dessous,

DT[grepl("\\.wav", message), .SD[1], by=subj_trial]

pourrait être encore plus rapide, puisqu'il utilise la logique booléenne et le système optimisé de I le sous-ensemble.

.SD est un tableau de données contenant le sous-ensemble des données de DT pour chaque groupe, à l'exclusion de toute colonne utilisée dans by (ou keyby).

by est un peu comme le group by en SQL. Il désigne la ou les colonnes de regroupement.

grep(pattern, x) renvoie l'index de toutes les correspondances pour l'élément pattern en x donde x est un vecteur. Le vecteur \\ antes de .wav empêche grep de traiter . comme un caractère spécial (dans l'analyse de grep, un caractère non encapsulé . signifie "tout").

vector_name[1] renvoie le premier élément d'un vecteur appelé nom_du_vecteur. il peut être appelé sur les résultats d'une fonction, comme grep ci-dessus.

les data.table La formule est DT[I,J,by] -- I est le sous-ensemble ou la jointure, J est l'opération à effectuer, by est l'élément de regroupement. Dans notre cas, I est ignorée (d'où la présence d'un , ) puisque nous voulons travailler sur l'ensemble des données. J est l'opération sur toutes les colonnes .SD. par est la colonne par laquelle vous souhaitez que les résultats soient regroupés.

1voto

agstudy Points 55104

Utilisation data.table :

library(data.table)
setDT(DT)
DT[,{
  id=head(grep("\\.wav",message),1)
  list(message=message[id],timestamp=timestamp[id])
},subj_trial]

#    subj_trial   message timestamp
# 1:        1_1 test1.wav    755662
# 2:        1_2 test2.wav    775662
# 3:        1_3 test3.wav    817794
# 4:        2_1 test1.wav    817347
# 5:        2_2 test2.wav    922671
# 6:        2_3 test3.wav   1036899

1voto

Cotton.Rockwood Points 1049

Voici un dplyr si vous êtes intéressé :

dat %>%
  filter(grepl("\\.wav", message)) %>%
  group_by(subj_trial) %>%
  top_n(n=1, wt=desc(timestamp))

Tout d'abord, filtrez les données pour ne retenir que celles qui contiennent *.wav dans la colonne message. Ensuite, regroupez les données par essai de sujet et renvoyez le premier résultat avec l'horodatage le plus petit. Cela suppose que vous souhaitiez obtenir le le plus petit pas nécessairement le premier de l'ensemble de données (c'est-à-dire que si un enregistrement avec un horodatage plus important est arrivé en premier, il ne sera PAS renvoyé). Je n'ai pas compris clairement ce que vous recherchiez, mais il n'y a peut-être pas de différence dans votre cas.

Et comme je suis toujours curieux de connaître les différences d'efficacité entre les data.table et dplyr approches, j'ai fait une microbenchmark test. Il semble que dans ce cas, data.table a un léger avantage en termes de vitesse :

library(microbenchmark)
library(data.table)

set.seed(1)
dat <- data.frame(subj_trial=paste0(sample(1:20,1e6,replace=TRUE),"_",sample(1:20,1e6,replace=TRUE)),
                  message=sample(c(".wav","others"), 1e6, replace=TRUE),
                  timestamp=round(seq(from=1000, to=9142527, length.out = 1e6))) 

dat2 <- dat
setDT(dat2)

microbenchmark({dat %>%
  filter(grepl("\\.wav", message)) %>%
  group_by(subj_trial) %>%
  top_n(1, wt=desc(timestamp))},
  {dat2[grepl("\\.wav", message), .SD[1], by=subj_trial]})

Résultats :

Unit: milliseconds

expr

dat %>% filter(grepl("\\\\.wav", message)) %>% group_by(subj_trial) %>% top_n(1, wt = desc(timestamp))
dat2[grepl("\\\\.wav", message), .SD[1], by = subj_trial] 
      min       lq     mean   median       uq      max neval cld
 332.9693 357.7426 387.2245 367.6443 380.9935 637.9223   100   b
 263.0292 272.8627 293.4976 281.4568 285.7699 582.9954   100  a

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X