Est-il un moyen efficace pour muter uniquement sur les lignes qui répondent à une condition? Pensez à muter(when(condition))

0

La question

Je suis à la recherche d'appliquer une muter seulement lorsque certaines conditions sont remplies.

Je sais que je peux le faire...

data2 <- data1 %>%
   group_by(a, b) %>%
   mutate(
      var1 = case_when(
         condition ~ TRUE,
         TRUE ~ FALSE,
         NA
         ),
      var2 = case_when(
         condition ~ TRUE,
         max(var28),
         var2
         ),
      var3 = case_when(
         condition ~ TRUE,
         "happy",
         var3
         ),
...more vars here....
)

Ce que je voudrais, c'est quelque chose qui ressemble à ça...

data2 <- data1 %>%
   group_by(a, b) %>%
   mutate(
      when(condition),
      var1 = FALSE,
      var2 = max(var28),
      var3 = "happy",
...more vars here....
)

Malheureusement mutate(across(when(condition))) n'a pas fonctionné.

Tout suggesions?

conditional-statements dplyr r tidyverse
2021-11-23 22:06:02
2

La meilleure réponse

4

Il n'y a pas de fonctionnalité pour ce faire, dans muter, mais Romain François a partagé une fonction, vous pouvez définir vous-même qui fait ceci:

library(dplyr, warn.conflicts = F)

mutate_when <- function(.data, when, ...) {
  dots <- enquos(...)
  names <- names(dots)
  
  mutate(.data, {
    test <- {{ when }}
    
    changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
    out <- across(all_of(names))
    # assuming `changed` and `out` have the same data frame type

    out[test, ] <- changed[test, ]
    out
  })
  
}

tibble(x = 1:4, y = 1:4) %>% 
  mutate_when(x < 4, x = -x, y = -y)
#> # A tibble: 4 × 2
#>       x     y
#>   <int> <int>
#> 1    -1    -1
#> 2    -2    -2
#> 3    -3    -3
#> 4     4     4

Créé sur 2021-11-23 par le reprex paquet (v2.0.1)

2021-11-23 22:24:23

C'est exactement la simplification de la fonctionnalité que je cherchais. Merci!
k6adams
1

Une autre solution possible est d'utiliser sur les() et ifelse(), par exemple, si la valeur dans la colonne x est inférieur à quatre, effectuer la mutation:

library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>% 
  mutate(across(everything(), ~ ifelse(x < 4, -.x, .x)))
#> # A tibble: 4 × 2
#>       x     y
#>   <int> <int>
#> 1    -1    -1
#> 2    -2    -2
#> 3     4     3
#> 4     5     4

Créé sur 2021-11-24 par le reprex paquet (v2.0.1)

Vous pouvez également imbriquer des ifelse est, par exemple, si la valeur dans la colonne x est inférieur à 4, ou si une valeur dans une colonne est égal à 3, de muter:

library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>% 
  mutate(across(everything(), ~ ifelse(x < 4, -.x,
                                       ifelse(.x == 3, .x + 10, .x))))
#> # A tibble: 4 × 2
#>       x     y
#>   <int> <dbl>
#> 1    -1    -1
#> 2    -2    -2
#> 3     4    13
#> 4     5     4

Créé sur 2021-11-24 par le reprex paquet (v2.0.1)

Et, ainsi de suite:

library(tidyverse)
tibble(x = c(1:2, 4:5), y = 1:4) %>% 
  mutate(across(everything(), ~ ifelse(x < 4, -.x,
                                       ifelse(.x == 3, .x + 10,
                                              ifelse(.x >= 5, "outlier", .x)))))
#> # A tibble: 4 × 2
#>   x           y
#>   <chr>   <dbl>
#> 1 -1         -1
#> 2 -2         -2
#> 3 4          13
#> 4 outlier     4

Créé sur 2021-11-24 par le reprex paquet (v2.0.1)

--

Pour faire la mutation de manière plus efficace, ne pas utiliser de dplyr::muter. Le ifelse() la fonction est vectorisé (plus de détails: https://swcarpentry.github.io/r-novice-gapminder/09-vectorization/) et si vous avez un grand dataframe, ifelse seul sera très probablement être plus rapide que tidyverse des fonctions, par exemple

library(tidyverse)
df <- tibble(x = c(1:2, 4:5), y = 1:4)
df$y <- ifelse(df$x < 4, -df$y, df$y)
df
#> # A tibble: 4 × 2
#>       x     y
#>   <int> <int>
#> 1     1    -1
#> 2     2    -2
#> 3     4     3
#> 4     5     4

Modifier

Une autre option est de remplacer les valeurs par affectation: df$y[df$x < 4] <- -(df$y); df$x[df$x < 4] <- -(df$x) (rapide, mais il y a des limites).

Voici un rapide benchmark des méthodes suggérées avec 1 million de lignes:

library(tidyverse)
df <- tibble(x = sample(1:10, 1000000, replace = TRUE),
             y = sample(1:10, 1000000, replace = TRUE))

mutate_func <- function(df){
  df %>%
    mutate(across(everything(), ~ ifelse(x < 4, -.x, .x)))
}

ifelse_func <- function(df){
  df$y <- ifelse(df$x < 4, -df$y, df$y)
}

replacement_func <- function(df) {
  df$y[df$x < 4] <- -(df$y)
  df$x[df$x < 4] <- -(df$x)
}

mutate_when_func <- function(df) {
  mutate_when <- function(.data, when, ...) {
    dots <- enquos(...)
    names <- names(dots)
  
    mutate(.data, {
      test <- {{ when }}
    
      changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
      out <- across(all_of(names))
      # assuming `changed` and `out` have the same data frame type
    
      out[test, ] <- changed[test, ]
      out
    })
  }

df %>% 
  mutate_when(x < 4, x = -x, y = -y)
}

library(microbenchmark)
result <- microbenchmark(mutate_func(df), ifelse_func(df),
                         mutate_when_func(df), replacement_func(df),
                         times = 10)
autoplot(result)

example_1.png

2021-11-23 22:16:02

c'est vraiment une grande capacité d'analyse. Dans mon cas en particulier, j'ai de petites données, où la sortie est dérivée à partir des variables à l'aide d'un diagramme des flux, et le code peut être regardé par les moins techniques public. Ainsi, le mutate_when fonction correspond à la facture, cette fois, mais c'est bon à savoir au cas ou je ou quelqu'un d'autre est en train de faire quelque chose de plus de calcul intense.
k6adams

Dans d'autres langues

Cette page est dans d'autres langues

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................