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 .
3 Réponses :
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.
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 code> plusieurs fois, il est inefficace, utilisez { code> dans j, calcul code> 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.
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.
setkey sur group donnerait probablement de la vitesse si nous voulons regrouper trois fois par cette colonne
Une autre option est:
dat[x>1.5 | group!=shift(group, -1L), .SD[1L], .(group)]
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.