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

qoheleth
fuente

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!)

mnel
fuente
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
Gorka
fuente
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
Jaap
fuente
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
Matt Dancho
fuente
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))]
JoelKuiper
fuente
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.

thelatemail
fuente
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) ) )
IRTFM
fuente
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")
James King
fuente
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" " 
Boyce Byerly
fuente
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="")
Corey Levinson
fuente
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)
varun
fuente
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.

Tjebo
fuente
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
Dan
fuente
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,]
CafeínaConocido
fuente
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)
}
Zuti
fuente
1
esto parece ser lo mismo que la respuesta de JoelKuiper , pero luego reformulado como función .....
Jaap