Cambiar el nombre de varias columnas por nombres


82

Alguien debería haber preguntado esto ya, pero no pude encontrar una respuesta. Di que tengo:

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

¿Cuál es la forma más elegante de cambiar el nombre de un subconjunto arbitrario de columnas, cuya posición no necesariamente conozco, a otros nombres arbitrarios?

Por ejemplo, digamos que quiero cambiar el nombre "q"y "e"en "A"y "B", ¿cuál es el código más elegante para hacer esto?

Obviamente, puedo hacer un bucle:

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

Pero me pregunto si hay una mejor manera. ¿Quizás usando algunos de los paquetes? ( plyr::renameetc.)

Respuestas:


102

setnamesDel data.tablepaquete trabajará en data.frames o 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

Tenga en cuenta que los cambios se realizan por referencia, por lo que no se permite copiar (¡incluso para data.frames!)


1
Para llegadas tardías aquí: también eche un vistazo a la respuesta de Joel a continuación, que cubre la verificación de las columnas existentes en caso de que tenga una lista de cambios de nombre que pueden no estar todos presentes, por ejemploold = c("a", "d", "e")
micstr

1
Me pregunto, ¿funciona esto si solo desea cambiar el nombre de un subconjunto / algunas de las columnas en lugar de todas? Entonces, si tuviera un marco de datos de diez columnas y quisiera cambiar el nombre de _id_firstname a firstname y _id_lastname a lastname pero dejo las ocho columnas restantes sin tocar, ¿puedo hacer esto o tengo que enumerar todas las columnas?
Mus

@MusTheDataGuy proporcionas el subconjunto de nombres nuevos y antiguos, y funcionará.
mnel

@mnel Necesito cambiar los nombres de las variables de un subconjunto como preguntó @Mus. Sin embargo, el código anterior no funcionó para un subconjunto de datos. La respuesta de @ Gorka con rename_at()funcionó para cambiar los nombres de las variables de un subconjunto.
Mehmet Yildirim

91

Con dplyr harías:

library(dplyr)

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

#  A w B
#1 1 2 3

O si desea usar vectores, como lo sugiere @ 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

LD Nicolas May sugirió que un cambio dado rename_atestá siendo reemplazado por rename_with:

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

#  A w B
#1 1 2 3

2
el usuario preguntó sobre pases oldy newnombres como vectores, creo
JelenaČuklina

4
Gracias @ Jelena-bioinf. Modifiqué la respuesta para incluir su sugerencia.
Gorka

¿Podría explicar el significado de ~ (tilde) y de dónde viene ".x" en el ejemplo de rename_with?
petzi

38

Otra solución para marcos de datos que no son demasiado grandes es (basada en la respuesta de @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

Alternativamente, también puede utilizar:

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

> x
  C w D
1 1 2 3

Además, también puede cambiar el nombre de un subconjunto de los nombres de las columnas:

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

> x
  C E F
1 1 2 3

23

Esta es la forma más eficiente que he encontrado para cambiar el nombre de varias columnas usando una combinación de purrr::set_names()y algunas stringroperaciones.

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
Esta debería ser la respuesta, pero probablemente también debería ampliar lo que hacen los argumentos ~y .en la set_names()tubería.
DaveRGP

En algunos casos, debe escribir explícitamente purrr::set_names().
Levi Baguley

1
@DaveRGP cuando se usan purrrfunciones, la tilde ~significa "para cada columna". La .sintaxis is dplyr para LHS = lado izquierdo de la tubería, es decir, la referencia al objeto que se canaliza, en este caso data.
Agile Bean

11

Así que recientemente me encontré con esto yo mismo, si no está seguro de si las columnas existen y solo desea cambiar el nombre de las que sí lo hacen:

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

6

Sobre la base de la respuesta de @ 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

Esto no dependerá de un orden específico de columnas en el xconjunto de datos.


1
He votado a favor de su respuesta, pero todavía me pregunto si hay una forma aún más elegante de hacer esto, en particular los métodos que
cambian el

@qoheleth - ¡está cambiando el nombre por el nombre! No hay entrada aquí que sea un vector posicional ya que matchse encarga de eso. Lo mejor que vas a hacer es probablemente la setnamesrespuesta de @ mnel .
thelatemail

1
todavía es una especie de cambio de nombre por posición porque, como dijiste, aunque no tengo que especificar explícitamente un vector de posición, matchsigue siendo un comando orientado a la posición. Con este espíritu, consideré que la posición de respuesta de @ user3114046 también se basaba (incluso aunque el %in%comando se encarga (o intenta) de las cosas). Por supuesto, supongo que puede argumentar que todos los comandos están orientados a la posición cuando profundizamos en el mecanismo de bajo nivel ... pero eso no es lo que quiero decir ... la respuesta de data.table es excelente porque no hay llamadas múltiples del namecomandos.
qoheleth

4

Esto cambiaría todas las apariciones de esas letras en todos los nombres:

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

2
No creo que esto sea particularmente elegante una vez que pasa un par de instancias de cambio de nombre.
thelatemail

No soy lo suficientemente bueno para preparar una gsubfnrespuesta. Quizás venga G. Grothendieck. Él es el regex-meister.
IRTFM

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

2
No del todo, porque como dije, no necesariamente sé la posición de las columnas, su solución solo funciona si oldnamesestá ordenada para que oldnames[i]ocurra antes oldnames[j]para i <j.
qoheleth

2

Puede configurar el nombre, guardarlo como una lista y luego hacer su cambio de nombre masivo en la cadena. Un buen ejemplo de esto es cuando está haciendo una transición larga a amplia en un conjunto de datos:

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

Nota al margen, si desea concatenar una cadena a todos los nombres de columna, puede usar este simple código.

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

2

Si la tabla contiene dos columnas con el mismo nombre, el código es el siguiente:

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

2

Puede utilizar un vector con nombre.

Con base R (quizás algo torpe):

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

O una dplyropción con !!!:

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

Este último funciona porque el operador 'big-bang'!!! está forzando la evaluación de una lista o un vector.

?`!!`

!!! fuerzas-empalme una lista de objetos. Los elementos de la lista están empalmados en su lugar, lo que significa que cada uno se convierte en un solo argumento.


No entiendo cómo funciona esto, !!!oldnamesdevuelve, c("A", "B")pero ¿en qué lógica se transforma esto c("A", "w", "B")?
Agile Bean

@AgileBean No sé dónde encontraste eso !!! nombres antiguos devolverían un vector. Se utiliza para forzar la evaluación no estándar de varios argumentos en dplyr. ver ?`!!` 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)). Creo que agregaré esta explicación a la respuesta. Saludos por mencionarlo
Tjebo

1

Muchas respuestas, así que escribí la función para que puedas copiar / pegar.

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"))ya no parece funcionar en dplyr renombrar?
sindri_baldur

0

Si una fila de los datos contiene los nombres a los que desea cambiar todas las columnas, puede hacerlo

names(data) <- data[row,]

Dado dataes su marco de datos y rowes el número de fila que contiene los nuevos valores.

Luego puede eliminar la fila que contiene los nombres con

data <- data[-row,]

0

Esta es la función que necesita: luego simplemente pase la x en un cambio de nombre (X) y cambiará el nombre de todos los valores que aparecen y si no está allí, no se producirá un error

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
esto parece ser lo mismo que la respuesta de JoelKuiper , pero luego reformulado como función .....
Jaap
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.