Перейменуйте кілька стовпців за іменами


84

Хтось мав це вже запитати, але я не міг знайти відповіді. Скажімо, що я маю:

x = data.frame(q=1,w=2,e=3, ...and many many columns...)  

який найелегантніший спосіб перейменувати довільну підмножину стовпців, позицію яких я не обов’язково знаю, в деякі інші довільні імена?

наприклад, скажіть, що я хочу перейменувати "q"та "e"в, "A"і "B"який найелегантніший код для цього?

Очевидно, я можу зробити цикл:

oldnames = c("q","e")
newnames = c("A","B")
for(i in 1:2) names(x)[names(x) == oldnames[i]] = newnames[i]

Але цікаво, чи є кращий спосіб? Можливо, використовуючи деякі пакети? ( plyr::renameтощо)

Відповіді:


106

setnamesз data.tableпакету буде працювати на data.frames або data.tables

library(data.table)
d <- data.frame(a=1:2,b=2:3,d=4:5)
setnames(d, old = c('a','d'), new = c('anew','dnew'))
d


 #   anew b dnew
 # 1    1 2    4
 # 2    2 3    5

Зверніть увагу, що зміни вносяться за допомогою посилання, тому копіювання не відбувається (навіть для data.frames!)


1
Для пізнього прибуття сюди - Також подивіться на відповідь Джоела нижче, яка охоплює перевірку наявних стовпців на випадок, якщо у вас є список змін імен, які можуть бути присутні не всі, наприкладold = c("a", "d", "e")
micstr

1
Цікаво, чи працює це, якщо ви хочете лише перейменувати підмножину / деякі стовпці замість усіх? Отже, якщо я мав фрейм даних із десяти стовпців і хотів перейменувати _id_firstname у firstname та _id_lastname у прізвище, але залишити решту вісім стовпців недоторканим, чи можу я це зробити чи мені потрібно перерахувати всі стовпці?
Mus

@MusTheDataGuy ви надаєте підмножину нових і старих імен, і це буде працювати.
mnel

@mnel Мені потрібно змінити імена змінних підмножини, як просив @Mus. Однак наведений вище код не працював для підмножини даних. @ Відповідь Горьки сrename_at() працювала для зміни імен змінних підмножини.
Mehmet Yildirim

95

За допомогою dplyr ви зробите:

library(dplyr)

df = data.frame(q = 1, w = 2, e = 3)
    
df %>% rename(A = q, B = e)

#  A w B
#1 1 2 3

Або якщо ви хочете використовувати вектори, як пропонує @ Jelena-bioinf:

library(dplyr)

df = data.frame(q = 1, w = 2, e = 3)

oldnames = c("q","e")
newnames = c("A","B")

df %>% rename_at(vars(oldnames), ~ newnames)

#  A w B
#1 1 2 3

Л. Д. Ніколас Мей запропонував внести зміниrename_at була замінена rename_with:

df %>% 
  rename_with(~ newnames[which(oldnames == .x)], .cols = oldnames)

#  A w B
#1 1 2 3

2
користувач запитав про передачу oldта newімена як вектори, я думаю
JelenaČuklina

4
Дякую @ Jelena-bioinf. Я змінив відповідь, включивши вашу пропозицію.
Горка

Не могли б ви пояснити значення ~ (тильда) та звідки походить ".x" у прикладі rename_with?
petzi

rename_withможе використовувати функцію або формулу для перейменування всіх стовпців, заданих як .colsаргумент. Наприклад rename_with(iris, toupper, starts_with("Petal")), еквівалентно rename_with(iris, ~ toupper(.x), starts_with("Petal")).
Paul Rougieux,

38

Іншим рішенням для не надто великих кадрів є (спираючись на відповідь @thelatemail):

x <- data.frame(q=1,w=2,e=3)

> x
  q w e
1 1 2 3

colnames(x) <- c("A","w","B")

> x
  A w B
1 1 2 3

Ви також можете використовувати:

names(x) <- c("C","w","D")

> x
  C w D
1 1 2 3

Крім того, ви також можете перейменувати підмножину назв стовпців:

names(x)[2:3] <- c("E","F")

> x
  C E F
1 1 2 3

24

Ось найефективніший спосіб перейменування кількох стовпців за допомогою комбінації purrr::set_names()та кількох stringrоперацій.

library(tidyverse)

# Make a tibble with bad names
data <- tibble(
    `Bad NameS 1` = letters[1:10],
    `bAd NameS 2` = rnorm(10)
)

data 
# A tibble: 10 x 2
   `Bad NameS 1` `bAd NameS 2`
   <chr>                 <dbl>
 1 a                    -0.840
 2 b                    -1.56 
 3 c                    -0.625
 4 d                     0.506
 5 e                    -1.52 
 6 f                    -0.212
 7 g                    -1.50 
 8 h                    -1.53 
 9 i                     0.420
 10 j                     0.957

# Use purrr::set_names() with annonymous function of stringr operations
data %>%
    set_names(~ str_to_lower(.) %>%
                  str_replace_all(" ", "_") %>%
                  str_replace_all("bad", "good"))

# A tibble: 10 x 2
   good_names_1 good_names_2
   <chr>               <dbl>
 1 a                  -0.840
 2 b                  -1.56 
 3 c                  -0.625
 4 d                   0.506
 5 e                  -1.52 
 6 f                  -0.212
 7 g                  -1.50 
 8 h                  -1.53 
 9 i                   0.420
10 j                   0.957

6
Це має бути відповіддю, але чи могли б ви, мабуть, також розширити інформацію про те, що роблять аргументи ~та .в set_names()трубі.
DaveRGP

У деяких випадках потрібно чітко вводити текст purrr::set_names().
Levi Baguley

1
@DaveRGP при використанні purrrфункцій тильда ~означає "для кожного стовпця". .Є dplyr синтаксису для LHS = лівого боку труби, тобто посилання на об'єкт , який передається по конвеєру, в даному випадку data.
Agile Bean

Тильда ~- це формула. Ви також можете використовувати виклик функції та передати аргументи ...аргументу, set_namesнаприклад rlang::set_names(head(iris), paste0, "_hi"), еквівалентному rlang::set_names(head(iris), ~ paste0(.x, "_hi")).
Paul Rougieux

11

Отже, нещодавно я сам зіткнувся з цим, якщо ви не впевнені, чи існують стовпці, і хочете перейменувати лише ті, які це роблять:

existing <- match(oldNames,names(x))
names(x)[na.omit(existing)] <- newNames[which(!is.na(existing))]

6

Спираючись на відповідь @ user3114046:

x <- data.frame(q=1,w=2,e=3)
x
#  q w e
#1 1 2 3

names(x)[match(oldnames,names(x))] <- newnames

x
#  A w B
#1 1 2 3

Це не залежить від конкретного впорядкування стовпців у xнаборі даних.


1
Я підтримав вашу відповідь, але мені все ще цікаво, чи існує ще більш елегантний спосіб зробити це, зокрема методи, що перейменовують на ім'я, а не на посаду
qoheleth

@qoheleth - це перейменування по імені! Тут немає введення, яке є позиційним вектором, оскільки matchпро це дбає. Найкраще, що ви збираєтеся зробити, - це, мабуть, setnamesвідповідь @ mnel .
thelatemail

1
це все ще своєрідне перейменування за позицією, оскільки, як ви вже сказали, хоча мені не потрібно явно вказувати вектор позиції, matchце все одно команда, орієнтована на позицію. У цьому дусі я вважав позицію відповіді @ user3114046 також заснованою (навіть думаючи, що %in%команда дбає (або намагається) про речі). Звичайно, я припускаю, що ви можете стверджувати, що всі команди орієнтовані на позицію, коли ми переходимо до механізму низького рівня .... але це не те, що я маю на увазі ... відповідь data.table чудова, тому що немає багаторазового виклику nameкоманди.
qoheleth

4

Це змінить всі випадки входження цих букв у всі імена:

 names(x) <- gsub("q", "A", gsub("e", "B", names(x) ) )

2
Я не думаю, що це особливо елегантно, коли ви пройдете пару випадків перейменування.
thelatemail

Я просто недостатньо хороший, щоб підготувати gsubfnвідповідь. Можливо, заїде Г.Гротендік. Він - регулярний виразник.
IRTFM

4
names(x)[names(x) %in% c("q","e")]<-c("A","B")

2
Не зовсім, тому що, як я вже сказав, я не обов'язково знаю положення стовпців, ваше рішення працює, лише якщо oldnamesвідсортовано так, що oldnames[i]відбувається раніше oldnames[j]для i <j.
qoheleth

2

Ви можете отримати набір імен, зберегти його як список, а потім зробити групове перейменування на рядок. Хороший приклад цього, коли ви робите довгий до широкого переходу на наборі даних:

names(labWide)
      Lab1    Lab10    Lab11    Lab12    Lab13    Lab14    Lab15    Lab16
1 35.75366 22.79493 30.32075 34.25637 30.66477 32.04059 24.46663 22.53063

nameVec <- names(labWide)
nameVec <- gsub("Lab","LabLat",nameVec)

names(labWide) <- nameVec
"LabLat1"  "LabLat10" "LabLat11" "LabLat12" "LabLat13" "LabLat14""LabLat15"    "LabLat16" " 

2

Sidenote, якщо ви хочете об'єднати один рядок з усіма іменами стовпців, ви можете просто використати цей простий код.

colnames(df) <- paste("renamed_",colnames(df),sep="")

2

Якщо таблиця містить два стовпці з однаковим ім'ям, тоді код виглядає так,

rename(df,newname=oldname.x,newname=oldname.y)

2

Ви можете використовувати названий вектор.

З базою R (можливо, трохи незграбний):

x = data.frame(q = 1, w = 2, e = 3) 

rename_vec <- c(q = "A", e = "B")

names(x) <- ifelse(is.na(rename_vec[names(x)]), names(x), rename_vec[names(x)])

x
#>   A w B
#> 1 1 2 3

Або dplyrваріант із !!!:

library(dplyr)

rename_vec <- c(A = "q", B = "e") # the names are just the other way round than in the base R way!

x %>% rename(!!!rename_vec)
#>   A w B
#> 1 1 2 3

Останнє працює, оскільки оператор "великого вибуху"!!! змушує оцінювати список або вектор.

?`!!`

!!! сили-сплайсинг списку об’єктів. Елементи списку поєднані на місцях, що означає, що кожен з них стає єдиним аргументом.


не розумію, як це працює - !!!oldnamesповертається, c("A", "B")але яка логіка перетворює це на c("A", "w", "B")??
Agile Bean

@AgileBean Я не знаю, де ти це знайшов !!! старі імена повернуть вектор. Він використовується для примусового нестандартного обчислення кількох аргументів у dplyr. див ?`!!` Use `!!!` to add multiple arguments to a function. Its argument should evaluate to a list or vector: args <- list(1:3, na.rm = TRUE) ; quo(mean(!!!args)). Думаю, додам це пояснення до відповіді. Вітання за підняття
тібо

1

Багато різновидів відповідей, тому я щойно написав функцію, щоб ви могли копіювати / вставляти.

rename <- function(x, old_names, new_names) {
    stopifnot(length(old_names) == length(new_names))
    # pull out the names that are actually in x
    old_nms <- old_names[old_names %in% names(x)]
    new_nms <- new_names[old_names %in% names(x)]

    # call out the column names that don't exist
    not_nms <- setdiff(old_names, old_nms)
    if(length(not_nms) > 0) {
        msg <- paste(paste(not_nms, collapse = ", "), 
            "are not columns in the dataframe, so won't be renamed.")
        warning(msg)
    }

    # rename
    names(x)[names(x) %in% old_nms] <- new_nms
    x
}

 x = data.frame(q = 1, w = 2, e = 3)
 rename(x, c("q", "e"), c("Q", "E"))

   Q w E
 1 1 2 3

rename(x, c("q", "e"), c("Q", "E"))здається, більше не працює в dplyr rename?
sindri_baldur

0

Якщо один рядок даних містить імена, ви хочете змінити всі стовпці

names(data) <- data[row,]

Дано dataваш фрейм даних таrow номер рядка, що містить нові значення.

Потім ви можете видалити рядок, що містить імена з

data <- data[-row,]

0

Це функція, яка вам потрібна: Тоді просто передайте x у перейменуванні (X), і воно перейменує всі значення, що з'являються, і якщо його там немає, то помилка не буде

rename <-function(x){
  oldNames = c("a","b","c")
  newNames = c("d","e","f")
  existing <- match(oldNames,names(x))
  names(x)[na.omit(existing)] <- newNames[which(!is.na(existing))]
  return(x)
}

1
здається, це те саме , що відповідь ДжоельКайпера , але потім перероблено як функція .....
Яап,

0

Є кілька відповідей, що згадують про функції dplyr::rename_withта rlang::set_namesвже. Вони окремі. ця відповідь ілюструє різницю між ними та використання функцій та формул для перейменування стовпців.

rename_withз dplyrпакета може використовувати функцію або формулу для перейменування виділених стовпців, заданих як .colsаргумент. Наприклад, передача імені функції toupper:

library(dplyr)
rename_with(head(iris), toupper, starts_with("Petal"))

Еквівалентно передачі формули ~ toupper(.x):

rename_with(head(iris), ~ toupper(.x), starts_with("Petal"))

Під час перейменування всіх стовпців ви також можете використовувати set_namesпакет rlang. Щоб зробити інший приклад, давайте використаємо paste0як функцію перейменування. pasteOприймає 2 аргументи, в результаті існують різні способи передачі другого аргументу, залежно від того, використовуємо ми функцію чи формулу.

rlang::set_names(head(iris), paste0, "_hi")
rlang::set_names(head(iris), ~ paste0(.x, "_hi"))

Те саме можна досягти rename_with, передавши фрейм даних як перший аргумент .data, функцію як другий аргумент .fn, усі стовпці як третій аргумент .cols=everything()і параметри функції як четвертий аргумент .... В якості альтернативи ви можете розмістити другий, третій та четвертий аргументи у формулі, наведеній як другий аргумент.

rename_with(head(iris), paste0, everything(), "_hi")
rename_with(head(iris), ~ paste0(.x, "_hi"))

rename_withпрацює лише з фреймами даних. set_namesє більш загальним і може також виконувати перейменування векторів

rlang::set_names(1:4, c("a", "b", "c", "d"))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.