3
votes

Utilisation de mutate rowwise sur un sous-ensemble de colonnes

J'essaie de créer une nouvelle colonne qui contiendra un résultat de calculs effectués par ligne sur un sous-ensemble de colonnes d'un tibble, et ajouter cette nouvelle colonne au tibble existant. Comme ceci:

df_out <- tibble(
ID = c("one", "two", "three"),
A1 = c(1, 1, 1),
A2 = c(2, 2, 2),
A3 = c(3, 3, 3),
SumA = c(6, 6, 6)
)

Je veux effectivement faire un dplyr équivalent de ce code à partir de la base R:

df %>% 
mutate(SumA = rowSums(.[,grepl("^A", colnames(df))]))

Mon problème est que cela ne Ça marche pas:

df %>% 
select(starts_with("A")) %>% 
mutate(SumA = rowSums(.))
    # some code here

... parce que je me suis débarrassé de la colonne "ID" pour laisser muter exécuter les rowSums sur les autres colonnes (numériques). J'ai essayé de cbind ou bind_cols dans le tube après la mutation, mais cela ne fonctionne pas. Aucune des variantes de mutate ne fonctionne, car elles fonctionnent en place (dans chaque cellule du tibble, et pas à travers les colonnes, même avec rowwise).

Cela fonctionne, mais ne me frappe pas comme solution élégante:

df$SumA <- rowSums(df[,grepl("^A", colnames(df))])

Existe-t-il une solution basée sur tidyverse qui ne nécessite pas de grepl ou de crochets mais seulement plus de verbes et de paramètres dplyr standard?

Ma sortie attendue est la suivante:

df <- tibble(
ID = c("one", "two", "three"),
A1 = c(1, 1, 1),
A2 = c(2, 2, 2),
A3 = c(3, 3, 3)
)

Meilleur kJ


1 commentaires

Wow, merci à tous, il y a beaucoup de bonnes idées là-bas - il est vraiment difficile de choisir la réponse préférée. J'aime Callum You pour le pmap (bien que réduire me déroute toujours :-), j'aime celui de G. Grothendieck pour le tube imbriqué (je ne savais pas que vous pouviez faire ça) et utubun pour la version simplifiée de la solution de Callum You.


5 Réponses :


3
votes

Voici une façon d'aborder le calcul par ligne dans le tidyverse en utilisant purrr :: pmap . Ceci est mieux utilisé avec les fonctions qui doivent être exécutées ligne par ligne; un simple ajout pourrait probablement être fait plus rapidement. Fondamentalement, nous utilisons select pour fournir la liste d'entrée à pmap , ce qui nous permet d'utiliser les aides select telles que starts_with ou correspond si vous avez besoin de regex.

library(tidyverse)
df <- tibble(
  ID = c("one", "two", "three"),
  A1 = c(1, 1, 1),
  A2 = c(2, 2, 2),
  A3 = c(3, 3, 3)
)

df %>%
  mutate(
    SumA = pmap_dbl(
      .l = select(., starts_with("A")),
      .f = function(...) sum(...)
    )
  )
#> # A tibble: 3 x 5
#>   ID       A1    A2    A3  SumA
#>   <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 one       1     2     3     6
#> 2 two       1     2     3     6
#> 3 three     1     2     3     6

Créé le 30/01/2019 par le package reprex (v0.2.1)


0 commentaires

2
votes

Voici une approche différente qui ne bouge pas par ligne mais exploite à la place la nature vectorisée de l'addition et cette addition commute. Cela permet d'appliquer à plusieurs reprises + avec purrr::reduce

library(tidyverse)
df <- tibble(
  ID = c("one", "two", "three"),
  A1 = c(1, 1, 1),
  A2 = c(2, 2, 2),
  A3 = c(3, 3, 3)
)

df %>%
  mutate(
    SumA = reduce(
      .x = select(., starts_with("A")),
      .f = `+`
    )
  )
#> # A tibble: 3 x 5
#>   ID       A1    A2    A3  SumA
#>   <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 one       1     2     3     6
#> 2 two       1     2     3     6
#> 3 three     1     2     3     6

Créé le 2019-01-30 par le package reprex (v0.2.1)


0 commentaires

1
votes

1) Pour le faire avec rowSums , essayez d'imbriquer un deuxième pipeline dans le mutate comme ceci:

# A tibble: 3 x 5
  ID       A1    A2    A3   Sum
  <chr> <dbl> <dbl> <dbl> <dbl>
1 one       1     2     3     6
2 two       1     2     3     6
3 three     1     2     3     6

giving:

library(dplyr)
library(purrr)
library(tidyr)

df %>%
  mutate(Sum = gather(., key, value, -ID) %>% 
               group_by(., ID) %>%
               summarize(sum = sum(value)) %>%
               ungroup %>%
               pull(sum))

2) Une alternative consiste à le remodeler en forme longue, puis à le résumer:

# A tibble: 3 x 5
  ID       A1    A2    A3   Sum
  <chr> <dbl> <dbl> <dbl> <dbl>
1 one       1     2     3     6
2 two       1     2     3     6
3 three     1     2     3     6

giving:

library(dplyr)

df %>% mutate(Sum = select(., starts_with("A")) %>% rowSums)


0 commentaires

0
votes

[upd] Je n'ai pas remarqué que @Calum utilisait presque la même approche.

Une autre façon possible de faire cela:

# # A tibble: 3 x 5
#   ID       A1    A2    A3  SumA
#   <chr> <dbl> <dbl> <dbl> <dbl>
# 1 one       1     2     3     6
# 2 two       1     2     3     6
# 3 three     1     2     3     6

Données: p>

# dat <- tibble(
#   ID = c("one", "two", "three"),
#   A1 = c(1, 1, 1),
#   A2 = c(2, 2, 2),
#   A3 = c(3, 3, 3)
# )

Sortie:

library(dplyr)
library(purrr)

dat %>%
  mutate(SumA = pmap_dbl(select(., contains('A')), sum))   


0 commentaires

0
votes

Vous pouvez imbriquer et utiliser rowSums sur les colonnes imbriquées:

df$SumA <- rowSums(df[-1])

Ou cette variante sur l'approche pmap : p >

df %>% mutate(SumA = pmap_dbl(.[-1],sum))
# # A tibble: 3 x 5
#      ID    A1    A2    A3  SumA
#   <chr> <dbl> <dbl> <dbl> <dbl>
# 1   one     1     2     3     6
# 2   two     1     2     3     6
# 3 three     1     2     3     6

Et pour montrer que la base est parfois plus simple:

library(tidyverse)
df %>% nest(-ID) %>%
  mutate(SumA = map_dbl(data,rowSums)) %>%
  unnest

# # A tibble: 3 x 5
#      ID  SumA    A1    A2    A3
#   <chr> <dbl> <dbl> <dbl> <dbl>
# 1   one     6     1     2     3
# 2   two     6     1     2     3
# 3 three     6     1     2     3


0 commentaires