2017-10-12 4 views
1

Je dois faire quelque chose de similaire à ci-dessous sur un très grand ensemble de données (avec beaucoup de groupes), et lire quelque part que l'utilisation de .SD est lente. Y a-t-il un moyen plus rapide d'effectuer l'opération suivante? Pour être plus précis, j'ai besoin de créer une nouvelle colonne qui contient la valeur min pour chaque groupe après avoir exclu un sous-ensemble d'observations dans ce groupe (quelque chose de similaire à minif dans Excel).Façon rapide de trouver min dans les groupes après exclusion des observations en utilisant R

library(data.table) 
dt <- data.table(valid = c(0,1,1,0,1), 
        a = c(1,1,2,3,4), 
        groups = c("A", "A", "A", "B", "B")) 

dt[, valid_min := .SD[valid == 1, min(a, na.rm = TRUE)], by = groups] 

Avec la sortie:

> test 
valid a k valid_min 
1:  0 1 A   1 
2:  1 1 A   1 
3:  1 2 A   1 
4:  0 3 B   4 
5:  1 4 B   4 

Pour les groupes pourraient avoir encore plus compliquée faire, pas d'entrées valides ou ils pourraient avoir plusieurs entrées valides mais manquantes. Mon code actuel est semblable à ceci:

dt <- data.table(valid = c(0,1,1,0,1,0,1,1), 
       a = c(1,1,2,3,4,3,NA,NA), 
       k = c("A", "A", "A", "B", "B", "C", "D", "D")) 

dt[, valid_min := .SD[valid == 1, 
         ifelse(all(is.na(a)), NA_real_, min(a, na.rm = TRUE))], by = k] 

Sortie:

> dt 
valid a k valid_min 
1:  0 1 A   1 
2:  1 1 A   1 
3:  1 2 A   1 
4:  0 3 B   4 
5:  1 4 B   4 
6:  0 3 C  NA 
7:  1 NA D  NA 
8:  1 NA D  NA 

Répondre

2

... Il y a

dt[dt[valid == 1 & !is.na(a), min(a), by=k], on=.(k), the_min := i.V1] 

Cela devrait être rapide puisque l'appel interne à min est optimisé pour les groupes. (Voir ?GForce.)

+1

Merci! Ceci est exactement ce que je cherchais :) – adamski

1

Nous pouvons faire la même chose en utilisant dplyr

dt %>% 
    group_by(groups) %>% 
    mutate(valid_min = min(ifelse(valid == 1, 
           a, NA), 
         na.rm = TRUE)) 

Ce qui donne:

valid  a groups valid_min 
    <dbl> <dbl> <chr>  <dbl> 
1  0  1  A   1 
2  1  1  A   1 
3  1  2  A   1 
4  0  3  B   4 
5  1  4  B   4 

Alternativement, si vous n'êtes pas intéressé par k eeping les lignes « non valides », nous pouvons faire ce qui suit:

dt %>% 
    filter(valid == 1) %>% 
    group_by(groups) %>% 
    mutate(valid_min = min(a)) 

On dirait que j'ai fourni la plus lente approche. En comparant chaque approche (en utilisant une plus grande, trame de données répliquées appelé df) avec un test de microbenchmark:

library(microbenchmark) 
library(ggplot2) 
mbm <- microbenchmark(
    dplyr.test = suppressWarnings(df %>% 
            group_by(k) %>% 
            mutate(valid_min = min(ifelse(valid == 1, 
                   a, NA), 
                 na.rm = TRUE), 
             valid_min = ifelse(valid_min == Inf, 
                  NA, 
                  valid_min))), 


    data.table.test = df[, valid_min := .SD[valid == 1, 
              ifelse(all(is.na(a)), NA_real_, min(a, na.rm = TRUE))], by = k], 
    GForce.test = df[df[valid == 1 & !is.na(a), min(a), by=k], on=.(k), the_min := i.V1] 
) 

autoplot(mbm) 

enter image description here

... eh bien, je l'ai essayé ...

+1

Merci! Je vais l'essayer. Mais la table de données n'est-elle pas toujours plus rapide? – adamski