J'ai un long ensemble de données avec des colonnes représentant les heures de début et de fin, et je veux supprimer une ligne si elle chevauche une autre et a une priorité plus élevée (par exemple 1 est la priorité la plus élevée). Mes données d'exemple sont
library(tidyverse)
library(lubridate)
times_df <- tibble(start = as_datetime(c("2019-10-05 14:05:25",
"2019-10-05 17:30:20",
"2019-10-05 17:37:00",
"2019-10-06 04:43:55",
"2019-10-06 04:53:45")),
stop = as_datetime(c("2019-10-05 14:19:20",
"2019-10-05 17:45:15",
"2019-10-05 17:50:45",
"2019-10-06 04:59:00",
"2019-10-06 05:07:10")), priority = c(5,3,4,3,4))
La façon dont j'ai trouvé attaque le problème en arrière en trouvant les chevauchements avec une valeur de priorité plus élevée, puis en utilisant un anti_join
pour les supprimer de la trame de données d'origine. Ce code ne fonctionne pas s'il y a trois périodes chevauchant le même point de temps et je suis sûr qu'il existe un moyen plus efficace et fonctionnel de le faire.
dropOverlaps <- function(df) {
drops <- df %>%
filter(stop > lead(start) | lag(stop) > start) %>%
mutate(group = ({seq(1, nrow(.)/2)} %>%
rep(each=2))) %>%
group_by(group) %>%
filter(priority == max(priority))
anti_join(df, drops)
}
dropOverlaps(times_df)
#> Joining, by = c("start", "stop", "priority")
#> # A tibble: 3 x 3
#> start stop priority
#> <dttm> <dttm> <dbl>
#> 1 2019-10-05 14:05:25 2019-10-05 14:19:20 5
#> 2 2019-10-05 17:30:20 2019-10-05 17:45:15 3
#> 3 2019-10-06 04:43:55 2019-10-06 04:59:00 3
Quelqu'un peut-il m'aider à obtenir la même sortie mais avec une fonction plus propre? Bonus s'il peut gérer une entrée avec trois périodes de temps ou plus qui se chevauchent toutes.
combn
, mais cela peut coûter cher si vous avez beaucoup de lignes.times_df %>% mutate(interval = interval(start, stop)) %>% {combn(nrow(.), 2, function(x) if (int_overlaps(.$interval[x[1]], .$interval[x[2]])) x[which.min(.$priority[x])], simplify = FALSE)} %>% unlist() %>% {slice(times_df, -.)}
plyranges
qui adapte les IRanges / GRanges (utilisées pour trouver les chevauchements entre les génomes) pour l'inverse. Je pense que vous pourriez transformer vos heures en plages "génomiques" en convertissant vos jours + heures en un entier d'heures ("choromosome") et vos minutes + secondes en un entier de secondes ("nucléotides"). Si vous avez regardé la sortie depair_overlaps
(et utilisé une colonne ID pour supprimer les chevauchements auto-auto), vous pouvez conserver votre priorité et faire un joli filtre des résultats + inner_join avec votre table d'origine. C'est hacky mais devrait optimiser la facilité de codage + l'efficacité.Réponses:
Voici une
data.table
solution utilisantfoverlaps
pour détecter les enregistrements qui se chevauchent (comme déjà mentionné par @GenesRus). Les enregistrements qui se chevauchent sont affectés à des groupes pour filtrer l'enregistrement avec max. priorité dans le groupe. J'ai ajouté deux enregistrements supplémentaires à vos données d'exemple, pour montrer que cette procédure fonctionne également pour trois enregistrements qui se chevauchent ou plus:Edit: j'ai modifié et traduit la solution de @ pgcudahy
data.table
qui donne un code encore plus rapide:Pour plus de détails, veuillez consulter
?foverlaps
- Il existe des fonctionnalités plus utiles implémentées pour contrôler ce qui est considéré comme un chevauchement tel quemaxgap
,minoverlap
outype
(tout, dans, début, fin et égal).Mise à jour - nouvelle référence
Code de référence:
la source
J'ai une fonction d'aide qui regroupe les données qui se chevauchent / données de temps en utilisant le paquet igraph (il peut inclure un tampon de chevauchement, c'est-à-dire que le terminus est à moins d'une minute ...)
Je l'ai utilisé pour regrouper vos données en fonction des intervalles de lubrification, puis faire des manipulations de données pour obtenir uniquement l'entrée de priorité la plus élevée en cas de chevauchement.
Je ne sais pas dans quelle mesure il évoluera.
Qui donne:
la source
Je suis descendu dans un terrier de lapin en regardant des arbres d'intervalle (et des implémentations R comme IRanges / plyranges) mais je pense que ce problème n'a pas besoin d'une telle structure de données car les heures de début peuvent être facilement triées. J'ai également développé l'ensemble de test comme @ismirsehregal pour couvrir davantage de relations d'intervalle potentielles , comme un intervalle qui commence avant et se termine après son voisin, ou lorsque trois intervalles se chevauchent mais que le premier et le dernier ne se chevauchent pas, ou deux intervalles qui commencent et arrêtez exactement aux mêmes heures.
Je fais ensuite deux passes dans chaque intervalle pour voir s'il chevauche son prédécesseur ou son successeur
stop >= lead(start, default=FALSE)
etstart <= lag(stop, default=FALSE))
Lors de chaque passage, il y a une deuxième vérification pour voir si la priorité de l'intervalle a une valeur numérique supérieure à celle du prédécesseur ou du successeur
priority > lead(priority, default=(max(priority) + 1))
. Lors de chaque passage, si les deux conditions sont vraies, un indicateur "remove" est défini sur true dans une nouvelle colonne en utilisantmutate
. Toutes les lignes avec un indicateur de suppression sont ensuite filtrées.Cela évite de vérifier toutes les combinaisons potentielles d'intervalles comme la réponse de @ Paul (comparaisons 2n contre n!) Ainsi que mon ignorance de la théorie des graphes :)
De même, la réponse de @ ismirsehregal a une magie data.table qui dépasse ma compréhension.
La solution de @ MKa ne semble pas fonctionner avec> 2 périodes qui se chevauchent
Tester les solutions donne
De ce code
la source
tibble
structure et il semble quepull()
c'était à l'origine du problème. Cardataframe()
, cela devrait fonctionner tel quel. Je viens de mettre à jour la réponse.data.table
qui rend les choses encore plus rapides (veuillez vérifier mon nouveau benchmark).En utilisant également
igraph
pour identifier les groupes qui se chevauchent, vous pouvez essayer:la source