0
votes

sous-ensemble dans data.table lorsque les groupes sont vides

Pour ces données

> tmp <- dat[x>1.5, .SD[1], by=group]
> rbind(tmp,dat[!group%in%tmp$group,.SD[.N], by=group])
   group id         x
1:     2  7 1.5115220
2:     3  9 2.0184237
3:     1  4 0.6328626

Mon objectif est d'obtenir, de chaque groupe, le premier identifiant pour lequel x est supérieur à un certain seuil, disons x> 1.5 .

> dat[x>1.5, .SD[1], by=group]
   group id        x
1:     2  7 1.511522
2:     3  9 2.018424

est en effet correct, mais je ne suis pas satisfait du fait qu'il ne donne silencieusement aucun résultat pour le groupe 1. Au lieu de cela, je voudrais qu'il donne le dernier identifiant de chaque groupe pour lequel aucun identifiant ne remplit la condition. Je vois que je pourrais y parvenir en deux étapes

library(data.table)
set.seed(42)
dat <- data.table(id=1:12, group=rep(1:3, each=4), x=rnorm(12))

> dat
    id group           x
 1:  1     1  1.37095845
 2:  2     1 -0.56469817
 3:  3     1  0.36312841
 4:  4     1  0.63286260
 5:  5     2  0.40426832
 6:  6     2 -0.10612452
 7:  7     2  1.51152200
 8:  8     2 -0.09465904
 9:  9     3  2.01842371
10: 10     3 -0.06271410
11: 11     3  1.30486965
12: 12     3  2.28664539

mais je suis sûr que je n'utilise pas pleinement les capacités data.table ici, ce qui doit permettre une solution plus élégante .


1 commentaires

Pour information, la raison pour laquelle il ne donne pas de résultat pour 1 est que la première partie ( x> 1.5 ) filtre le data.table, donc le groupe 1 n'existe pas dans data.table plus.


3 Réponses :


2
votes

En utilisant data.table , nous pouvons vérifier une condition et un sous-ensemble ligne par groupe.

dat[, .SD[{temp = x > 1.5; if (any(temp)) which.max(temp) else .N}], by = group]

Le dplyr , traduction de ce serait

library(dplyr)
dat %>%
  group_by(group) %>%
  slice(if(any(x > 1.5)) which.max(x > 1.5) else n())

Ou plus efficacement

library(data.table)
dat[dat[, if(any(x>1.5)) .I[which.max(x > 1.5)] else .I[.N], by=group]$V1]

#   id group         x
#1:  4     1 0.6328626
#2:  7     2 1.5115220
#3:  9     3 2.0184237

Merci à @IceCreamTouCan, @sindri_baldur et @jangorecki pour leurs précieuses suggestions pour améliorer cette réponse.


4 commentaires

Ou simplifiez-vous en: dat [ .SD [if (any (x> 1.5)) which.max (x> 1.5) else .N], by = group]


N'utilisez pas x> 1.5 plusieurs fois, il est inefficace, utilisez { dans j, calcul x> 1.5` une fois, attribuer à temp var et réutiliser


Je conviens que l'approche sans rejoindre semble plus intuitive. Pouvez-vous l'ajouter à la solution, y compris la suggestion de Jan (que je ne reçois pas)?


@bumblebee okay..J'ai mis à jour la réponse en fonction des suggestions.



1
votes

Vous pouvez sous-définir les deux méthodes (qui sont optimisées par GForce), puis les combiner:

D1 = dat[x>1.5, lapply(.SD, first), by=group]
D2 = dat[, lapply(.SD, last), by=group]
rbind(D1, D2[!D1, on=.(group)])

   group id         x
1:     2  7 1.5115220
2:     3  9 2.0184237
3:     1  4 0.6328626

Il y a une certaine inefficacité ici puisque nous regroupons par groupe trois fois. Je ne sais pas si cela sera compensé par des calculs plus efficaces dans j grâce à GForce ou non. @jangorecki souligne que l'inefficacité du regroupement trois fois pourrait être atténuée en définissant la clé en premier.

Commentaire : J'ai utilisé le dernier (.SD) depuis .SD [.N] n'est pas encore optimisé et le dernier (.SD) génère une erreur. J'ai changé le code de l'OP pour utiliser lapply d'abord par souci de symétrie.


1 commentaires

setkey sur group donnerait probablement de la vitesse si nous voulons regrouper trois fois par cette colonne



1
votes

Une autre option est:

dat[x>1.5 | group!=shift(group, -1L), .SD[1L], .(group)]


0 commentaires