web-dev-qa-db-fra.com

Comment dplyr renommer une colonne, par index de colonne?

Le code suivant renomme la première colonne de l'ensemble de données:

require(dplyr)    
mtcars %>%
        setNames(c("RenamedColumn", names(.)[2:length(names(.))]))

Résultats souhaités:

                    RenamedColumn cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4                    21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag                21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Datsun 710                   22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1

Serait-il possible d'arriver au même résultat en utilisant rename et index de colonne?

Cette:

mtcars %>%
    rename(1 = "ChangedNameAgain")

échouera:

Error in source("~/.active-rstudio-document", echo = TRUE) : 
  ~/.active-rstudio-document:7:14: unexpected '='
6: mtcars %>%
7:     rename(1 =
                ^

De même, essayez d'utiliser rename_ ou .[[1]] comme référence de colonne renverra une erreur.

17
Konrad

À partir de dplyr0.7.5, rlang0.2.1, tidyselect0.2.4, Cela fonctionne simplement:

library(dplyr)

rename(mtcars, ChangedNameAgain = 1)

#                     ChangedNameAgain cyl  disp  hp drat    wt  qsec vs am gear carb
# Mazda RX4                       21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
# Mazda RX4 Wag                   21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
# Datsun 710                      22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
# Hornet 4 Drive                  21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
# Hornet Sportabout               18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
# ...

La réponse d'origine et les modifications sont désormais obsolètes:

La logique de rename() est new_name = old_name, Donc ChangedNameAgain = 1 Aurait plus de sens que 1 = ChangedNameAgain.

Je voudrais suggerer:

mtcars %>% rename_(ChangedNameAgain = names(.)[1])
#                     ChangedNameAgain cyl  disp  hp drat    wt  qsec vs am gear carb
# Mazda RX4                       21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
# Mazda RX4 Wag                   21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
# Datsun 710                      22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
# Hornet 4 Drive                  21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
# Hornet Sportabout               18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
# Valiant                         18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1

Modifier

Je n'ai pas encore fait le tour du nouveau système de programmation dplyr basé sur rlang, depuis les versions 0.6/0.7 de dplyr.

La version avec suffixe de soulignement de rename utilisée dans ma réponse initiale est maintenant obsolète, et selon le commentaire de @ jzadra, elle ne fonctionnait pas de toute façon avec des noms syntaxiquement problématiques comme "foo bar".

Voici ma tentative avec le nouveau système d'évaluation non standard basé sur rlang. N'hésitez pas à me dire ce que j'ai fait de mal, dans les commentaires:

df <- tibble("foo" = 1:2, "bar baz" = letters[1:2])

# # A tibble: 2 x 2
#     foo `bar baz`
#   <int>     <chr>
# 1     1         a
# 2     2         b

J'essaie d'abord directement avec rename() mais malheureusement j'ai une erreur. Il semble être un FIXME (ou est-ce que ce FIXME n'est pas lié?) Dans le code source (j'utilise dplyr 0.7.4), donc cela pourrait fonctionner A l'avenir:

df %>% rename(qux = !! quo(names(.)[[2]]))

# Error: Expressions are currently not supported in `rename()`

(Edit: le message d'erreur maintenant (dplyr 0.7.5) lit Error in UseMethod("rename_") : no applicable method for 'rename_' applied to an object of class "function")

(Mise à jour 2018-06-14: df %>% rename(qux = !! quo(names(.)[[2]])) semble maintenant fonctionner, toujours avec dplyr 0.7.5, je ne sais pas si un paquet sous-jacent a changé).

Voici une solution de contournement avec select qui fonctionne. Cependant, il ne conserve pas l'ordre des colonnes comme rename:

df %>% select(qux = !! quo(names(.)[[2]]), everything())

# # A tibble: 2 x 2
#     qux   foo
#   <chr> <int>
# 1     a     1
# 2     b     2

Et si nous voulons le mettre dans une fonction, nous devons le modifier légèrement avec := Pour permettre le retrait des guillemets sur le côté gauche. Si nous voulons être robustes aux entrées comme les chaînes et les noms de variables nues, nous devons utiliser la "magie noire" (ou c'est ce que dit la vignette ) de la fonction enquo() et quo_name() (honnêtement, je ne comprends pas bien ce que ça fait):

rename_col_by_position <- function(df, position, new_name) {
  new_name <- enquo(new_name)
  new_name <- quo_name(new_name)
  select(df, !! new_name := !! quo(names(df)[[position]]), everything())
}

Cela fonctionne avec un nouveau nom sous forme de chaîne:

rename_col_by_position(df, 2, "qux")

# # A tibble: 2 x 2
#     qux   foo
#   <chr> <int>
# 1     a     1
# 2     b     2

Cela fonctionne avec un nouveau nom comme une suspension:

rename_col_by_position(df, 2, quo(qux))

# # A tibble: 2 x 2
#     qux   foo
#   <chr> <int>
# 1     a     1
# 2     b     2

Cela fonctionne avec un nouveau nom comme nom nu:

rename_col_by_position(df, 2, qux)

# # A tibble: 2 x 2
#     qux   foo
#   <chr> <int>
# 1     a     1
# 2     b     2

Et même cela fonctionne:

rename_col_by_position(df, 2, `qux quux`)

# # A tibble: 2 x 2
#   `qux quux`   foo
#        <chr> <int>
# 1          a     1
# 2          b     2
31
Aurèle

Voici quelques solutions alternatives qui sont sans doute plus faciles à lire car elles ne se concentrent pas sur la référence .. select comprend les indices de colonne, donc si vous renommez la première colonne, vous pouvez simplement faire

mtcars %>% select( RenamedColumn = 1, everything() )

Cependant, le problème avec l'utilisation de select est qu'il réorganisera les colonnes si vous renommez une colonne au milieu. Pour contourner le problème, vous devez présélectionner les colonnes à gauche de celle que vous renommez:

## This will rename the 7th column without changing column order
mtcars %>% select( 1:6, RenamedColumn = 7, everything() )

Une autre option consiste à utiliser le nouveau rename_at, Qui comprend également les indices de colonne:

## This will also rename the 7th column without changing the order
## Credit for simplifying the second argument: Moody_Mudskipper
mtcars %>% rename_at( 7, ~"RenamedColumn" )

~ Est nécessaire car rename_at Est assez flexible et peut accepter des fonctions comme deuxième argument. Par exemple, mtcars %>% rename_at( c(2,4), toupper ) mettra les noms des deuxième et quatrième colonnes en majuscules.

7
Artem Sokolov

Imho rlang comme suggéré par @Aurele est trop ici.

Solution 1: utilisez un contexte de tuyau de tuyau à accolades:

bcMatrix %>% {colnames(.)[1] = "foo"; .}

Solution 2: Ou (ab) utilisez l'opérateur tee %>% à partir du package magrittr (installé de toute façon si dplyr est utilisé) pour effectuer le changement de nom en tant qu'effet secondaire:

bcMatrix %T>% {colnames(.)[1] = "foo"}

Solution 3: à l'aide d'une fonction d'assistance simple:

rename_by_pos = function(df, index, new_name){ 
    colnames(df)[index] = new_name 
    df 
}
iris %>% rename_by_pos(2,"foo")
1
Holger Brandl