Как dplyr переименовать столбец по индексу столбца?

Следующий код переименовывает первый столбец в наборе данных:

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

Желаемые результаты:

                    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

Можно ли получить тот же результат, используя rename и индекс столбца?

Этот:

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

не удастся:

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

Точно так же попытка использовать rename_ или .[[1]] в качестве ссылки на столбец вернет ошибку.


person Konrad    schedule 13.03.2017    source источник


Ответы (4)


Начиная с dplyr 0.7.5, rlang 0.2.1, tidyselect 0.2.4 это просто работает:

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
# ...

Исходный ответ и изменения устарели:

Логика rename() - это new_name = old_name, поэтому ChangedNameAgain = 1 будет иметь больше смысла, чем 1 = ChangedNameAgain.

Я бы предложил:

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

Изменить

Мне еще предстоит обдумать новую систему программирования dplyr, основанную на rlang, начиная с версий 0.6/0.7 dplyr.

Версия rename с суффиксом подчеркивания, использованная в моем первоначальном ответе, теперь устарела, и, согласно комментарию @jzadra, она все равно не работала с синтаксически проблемными именами, такими как "foo bar".

Вот моя попытка с новой системой нестандартной оценки на основе rlang. Не стесняйтесь сказать мне, что я сделал неправильно, в комментариях:

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

Сначала я пытаюсь напрямую с rename(), но, к сожалению, получаю ошибку. Кажется, это FIXME (или этот FIXME не имеет отношения?) в исходном коде (я использую dplyr 0.7.4), чтобы он мог работать в будущем:

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

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

(Редактировать: сообщение об ошибке теперь (dplyr 0.7.5) читается как Error in UseMethod("rename_") : no applicable method for 'rename_' applied to an object of class "function")

(Обновление 2018-06-14: df %>% rename(qux = !! quo(names(.)[[2]])) теперь работает, по-прежнему с dplyr 0.7.5, не уверен, изменился ли базовый пакет).

Вот обходной путь с select, который работает. Однако он не сохраняет порядок столбцов, например rename:

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

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

И если мы хотим поместить его в функцию, нам придется немного изменить его с помощью :=, чтобы разрешить раскаты с левой стороны. Если мы хотим быть устойчивыми к входным данным, таким как строки и простые имена переменных, мы должны использовать «темную магию» (по крайней мере, так говорит vignette) из enquo() и quo_name() (честно говоря, я не совсем понимаю, что он делает):

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())
}

Это работает с новым именем в виде строки:

rename_col_by_position(df, 2, "qux")

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

Это работает с новым именем как quosure:

rename_col_by_position(df, 2, quo(qux))

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

Это работает с новым именем как с голым именем:

rename_col_by_position(df, 2, qux)

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

И даже это работает:

rename_col_by_position(df, 2, `qux quux`)

# # A tibble: 2 x 2
#   `qux quux`   foo
#        <chr> <int>
# 1          a     1
# 2          b     2
person Aurèle    schedule 13.03.2017
comment
Это не работает, если существующее имя требует обратных кавычек. - person jzadra; 20.10.2017
comment
Спасибо. Должен согласиться с тем, что новый rlang отодвинул очистку от понятной грамматики, что кажется мне огромным шагом назад. - person jzadra; 23.10.2017

Вот несколько альтернативных решений, которые, возможно, легче читать, потому что они не сосредоточены вокруг ссылки .. select понимает индексы столбцов, поэтому, если вы переименовываете первый столбец, вы можете просто сделать

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

Однако проблема с использованием select заключается в том, что он изменит порядок столбцов, если вы переименовываете столбец в середине. Чтобы обойти эту проблему, вам нужно предварительно выбрать столбцы слева от того, который вы переименовываете:

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

Другой вариант — использовать новый rename_at, который также понимает индексы столбцов:

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

~ необходим, потому что rename_at достаточно гибок и может принимать функции в качестве второго аргумента. Например, mtcars %>% rename_at( c(2,4), toupper ) сделает имена второго и четвертого столбцов прописными.

person Artem Sokolov    schedule 05.03.2018
comment
rename_at кажется лучшим и наиболее актуальным решением из всех. Спасибо, @artem-solokov! - person shreyasgm; 07.03.2018
comment
@Артем можешь написать просто mtcars %>% rename_at(1,~"RenamedColumn") - person Moody_Mudskipper; 25.03.2018
comment
Отличное предложение, @Moody_Mudskipper. Я отредактировал свой ответ, чтобы включить его. - person Artem Sokolov; 26.03.2018

dplyr заменил rename_at() на rename_with(). Вы можете переименовать столбец по индексу следующим образом:

library(tidyverse)

mtcars %>% 
  rename_with(.cols = 1, ~"renamed_column")

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

Не забудьте включить тильду (~)* перед именем нового столбца.

Также обратите внимание, что если вы вводите пакет glue, вы можете изменить существующие имена столбцов следующим образом:

library(glue)

mtcars %>% 
  rename_with(.cols = 1, ~glue::glue("renamed_{.}"))
#>                     renamed_mpg 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
#> ...

Применение описанного выше подхода к нескольким столбцам — это просто передача диапазона номеров индексов столбцов с использованием двоеточия (:) или нескольких индексов в векторе с использованием c(); вот комбинация обоих:

mtcars %>% 
  rename_with(.cols = c(1:3, 5), ~glue::glue("renamed_{.}"))
#>                     renamed_mpg renamed_cyl renamed_disp  hp renamed_drat    wt
#> Mazda RX4                  21.0           6        160.0 110         3.90 2.620
#> Mazda RX4 Wag              21.0           6        160.0 110         3.90 2.875
#> Datsun 710                 22.8           4        108.0  93         3.85 2.320
#> Hornet 4 Drive             21.4           6        258.0 110         3.08 3.215
#> Hornet Sportabout          18.7           8        360.0 175         3.15 3.440
#> ...

И имейте в виду, что, поскольку . представляет имя текущего столбца, вы можете применить к нему функции модификации строки следующим образом:

mtcars %>% 
  rename_with(.cols = c(1:3), 
              ~glue::glue("renamed_{str_replace(.,'mpg','miles_per_gallon')}"))
#>                     renamed_miles_per_gallon renamed_cyl renamed_disp  hp
#> Mazda RX4                               21.0           6        160.0 110
#> Mazda RX4 Wag                           21.0           6        160.0 110
#> Datsun 710                              22.8           4        108.0  93
#> Hornet 4 Drive                          21.4           6        258.0 110
#> Hornet Sportabout                       18.7           8        360.0 175
#> ...

*Вы можете узнать больше о сокращении функции ~ и . NSE здесь .

person phi    schedule 13.03.2021

Имхо rlang, как предложил @Aurele, здесь слишком много.

Решение 1. Используйте контекст трубы с фигурными скобками:

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

Решение 2: Или (ab) используйте оператор тройника %>% из пакета magrittr (установленный в любом случае, если используется dplyr) для выполнения переименования в качестве побочного эффекта:

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

Решение 3: использование простой вспомогательной функции:

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