Zmień nazwę wielu kolumn według nazw

84

Ktoś powinien był już o to zapytać, ale nie mogłem znaleźć odpowiedzi. Powiedz, że mam:

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

jaki jest najbardziej elegancki sposób zmiany nazwy dowolnego podzbioru kolumn, których pozycji niekoniecznie znam, na inne dowolne nazwy?

np. powiedz, że chcę zmienić nazwę "q"i "e"na "A"i "B"jaki jest najbardziej elegancki kod do tego?

Oczywiście mogę zrobić pętlę:

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

Ale zastanawiam się, czy istnieje lepszy sposób? Może korzystając z niektórych pakietów? ( plyr::renameitp.)

qoheleth
źródło

Odpowiedzi:

106

setnamesz data.tablepakietu będzie działać na data.frames lub 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

Pamiętaj, że zmiany są wprowadzane przez odniesienie, więc nie ma kopiowania (nawet w przypadku data.frames!)

mnel
źródło
1
Dla spóźnialskich tutaj - spójrz również na odpowiedź Joela poniżej, która obejmuje sprawdzenie istniejących kolumn w przypadku, gdy masz listę zmian nazw, które mogą nie być obecne, np.old = c("a", "d", "e")
micstr
1
Zastanawiam się, czy to działa, jeśli chcesz zmienić nazwę tylko podzbioru / niektórych kolumn zamiast wszystkich? Więc gdybym miał ramkę danych zawierającą dziesięć kolumn i chciałbym zmienić nazwę _id_firstname na imię i _id_lastname na nazwisko, ale pozostawić pozostałe osiem kolumn nietkniętych, czy mogę to zrobić, czy muszę wyświetlić wszystkie kolumny?
Mus,
@MusTheDataGuy podajesz podzbiór nowych i starych nazw i to zadziała.
mnel
@mnel Muszę zmienić nazwy zmiennych podzbioru zgodnie z zapytaniem @Mus. Jednak powyższy kod nie działał dla podzbioru danych. Odpowiedź @ Gorka z rename_at()działała przy zmianie nazw zmiennych podzbioru.
Mehmet Yildirim
95

Z dplyrem zrobiłbyś:

library(dplyr)

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

#  A w B
#1 1 2 3

Lub jeśli chcesz użyć wektorów, zgodnie z sugestią @ 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 zasugerował, że podana zmiana rename_atjest zastępowana przez rename_with:

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

#  A w B
#1 1 2 3
Gorka
źródło
2
użytkownik zapytał o podanie oldi newnazwy jako wektory, myślę, że
JelenaČuklina
4
Dzięki @ Jelena-bioinf. Poprawiłem odpowiedź, aby uwzględnić twoją sugestię.
Gorka
Czy mógłbyś wyjaśnić znaczenie znaku ~ (tylda) i skąd pochodzi „.x” w przykładzie rename_with?
petzi
rename_withmoże użyć funkcji lub formuły, aby zmienić nazwy wszystkich kolumn podanych jako .colsargument. Na przykład rename_with(iris, toupper, starts_with("Petal"))jest równoważne rename_with(iris, ~ toupper(.x), starts_with("Petal")).
Paul Rougieux
38

Innym rozwiązaniem dla ramek danych, które nie są zbyt duże, jest (na podstawie odpowiedzi @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

Alternatywnie możesz również użyć:

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

> x
  C w D
1 1 2 3

Ponadto możesz również zmienić nazwę podzbioru nazw kolumn:

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

> x
  C E F
1 1 2 3
Jaap
źródło
24

Oto najbardziej efektywny sposób, jaki znalazłem, aby zmienić nazwę wielu kolumn za pomocą kombinacji purrr::set_names()i kilku stringroperacji.

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
źródło
6
To powinna być odpowiedź, ale czy mógłbyś również prawdopodobnie rozwinąć działanie argumentów ~i .w set_names()potoku.
DaveRGP
W niektórych przypadkach musisz jawnie wpisać purrr::set_names().
Levi Baguley,
1
@DaveRGP podczas korzystania z purrrfunkcji tylda ~oznacza „dla każdej kolumny”. .Jest dplyr składni LHS = lewej stronie rury, to znaczy w odniesieniu do przedmiotu, który jest wyprowadzony w tym przypadku data.
Agile Bean
Tylda ~to formuła. Możesz również użyć wywołania funkcji i przekazać argumenty do ...argumentu set_namesna przykład rlang::set_names(head(iris), paste0, "_hi")jest równoważne rlang::set_names(head(iris), ~ paste0(.x, "_hi")).
Paul Rougieux
11

Niedawno natknąłem się na to sam, jeśli nie jesteś pewien, czy kolumny istnieją i chcesz zmienić tylko te, które to robią:

existing <- match(oldNames,names(x))
names(x)[na.omit(existing)] <- newNames[which(!is.na(existing))]
JoelKuiper
źródło
6

Opierając się na odpowiedzi @ 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

Nie będzie to zależne od określonej kolejności kolumn w zestawie xdanych.

thelatemail
źródło
1
Głosowałem za twoją odpowiedzią, ale nadal zastanawiam się, czy istnieje jeszcze bardziej elegancki sposób, aby to zrobić, szczególnie metody, które zmieniają nazwę według nazwy zamiast pozycji
qoheleth
@qoheleth - to zmiana nazwy według nazwy! Nie ma tu danych wejściowych, które są wektorem pozycyjnym, które o matchto dbają. Najlepsze, co zamierzasz zrobić, to prawdopodobnie odpowiedź @ mnel setnames.
thelatemail
1
nadal jest to zmiana nazwy według pozycji, ponieważ, jak powiedziałeś, nawet jeśli nie muszę jawnie określać wektora pozycji, matchnadal jest to polecenie zorientowane na pozycję. W tym duchu uznałem, że pozycja odpowiedzi @ user3114046 jest również oparta na (nawet myślałem, że %in%polecenie zajmuje się (lub próbuje)). Oczywiście, przypuszczam, że można argumentować, że wszystkie polecenia są zorientowane na pozycję, kiedy przechodzimy do mechanizmu niskiego poziomu ... ale nie o to mi chodzi ... odpowiedź w tabeli danych jest świetna, ponieważ nie ma wielokrotnego wywoływania funkcji namepolecenia.
qoheleth
4

Zmieniłoby to wszystkie wystąpienia tych liter we wszystkich nazwach:

 names(x) <- gsub("q", "A", gsub("e", "B", names(x) ) )
IRTFM
źródło
2
Nie sądzę, żeby było to szczególnie eleganckie, gdy minie się kilka przypadków zmiany nazwy.
thelatemail
Po prostu nie jestem wystarczająco dobry, by znaleźć gsubfnodpowiedź. Być może przyjdzie G.Grothendieck. On jest regex-meisterem.
IRTFM
4
names(x)[names(x) %in% c("q","e")]<-c("A","B")
James King
źródło
2
Niezupełnie, ponieważ tak jak powiedziałem, niekoniecznie znam położenie kolumn, twoje rozwiązanie działa tylko wtedy, gdy oldnamesjest posortowane tak, że oldnames[i]występuje wcześniej oldnames[j]dla i <j.
qoheleth
2

Możesz pobrać zestaw nazw, zapisać go jako listę, a następnie wykonać zbiorczą zmianę nazwy ciągu. Dobrym przykładem tego jest sytuacja, gdy dokonujesz przejścia od długiego do szerokiego w zbiorze danych:

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
źródło
2

Uwaga, jeśli chcesz połączyć jeden ciąg ze wszystkimi nazwami kolumn, możesz po prostu użyć tego prostego kodu.

colnames(df) <- paste("renamed_",colnames(df),sep="")
Corey Levinson
źródło
2

Jeśli tabela zawiera dwie kolumny o tej samej nazwie, kod wygląda następująco:

rename(df,newname=oldname.x,newname=oldname.y)
varun
źródło
2

Możesz użyć nazwanego wektora.

Z podstawą R (może być nieco niezgrabna):

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

Lub dplyropcja z !!!:

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

Ten ostatni działa, ponieważ operator „big bang”!!! wymusza ocenę listy lub wektora.

?`!!`

!!! force-splice listę obiektów. Elementy listy są splatane na miejscu, co oznacza, że ​​każdy z nich staje się pojedynczym argumentem.

tjebo
źródło
nie rozumiem, jak to działa - !!!oldnameszwraca, c("A", "B")ale która logika przekształca to w c("A", "w", "B")?
Agile Bean
@AgileBean Nie wiem, gdzie znalazłeś to !!! stare nazwy zwróciłyby wektor. Służy do wymuszenia niestandardowej oceny wielu argumentów w dplyr. zobacz ?`!!` 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)). Myślę, że dodam to wyjaśnienie do odpowiedzi. Pozdrawiam za wychowanie
tjebo
1

Wiele odpowiedzi, więc właśnie napisałem funkcję, abyś mógł kopiować / wklejać.

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
źródło
rename(x, c("q", "e"), c("Q", "E"))nie wydaje się już działać w dplyr rename?
sindri_baldur
0

Jeśli jeden wiersz danych zawiera nazwy, na które chcesz zmienić wszystkie kolumny, możesz to zrobić

names(data) <- data[row,]

Podano datatwoją ramkę danych irow numer wiersza zawierającego nowe wartości.

Następnie możesz usunąć wiersz zawierający nazwy za pomocą

data <- data[-row,]
CaffeineConnoisseur
źródło
0

To jest funkcja, której potrzebujesz: Następnie po prostu podaj x w zmianie nazwy (X), a zmieni nazwę wszystkich pojawiających się wartości, a jeśli go tam nie ma, nie wystąpi błąd

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
źródło
1
to wydaje się być taka sama jak odpowiedź JoelKuiper za , ale potem przeformułować jako funkcja .....
Jaap
0

Istnieje kilka odpowiedzi dotyczących funkcji dplyr::rename_withirlang::set_names już. Są oddzielne. ta odpowiedź ilustruje różnice między nimi oraz użycie funkcji i formuł do zmiany nazw kolumn.

rename_withz dplyrpakietu można użyć funkcji lub formuły do ​​zmiany nazwy wybranych kolumn podanych jako .colsargument. Na przykład przekazanie nazwy funkcji toupper:

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

Jest równoznaczne z przekazaniem wzoru ~ toupper(.x):

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

Zmieniając nazwy wszystkich kolumn, możesz także użyć set_namesz pakietu rlang. Aby zrobić inny przykład, użyjmy paste0jako funkcji zmiany nazwy. pasteOprzyjmuje 2 argumenty, w rezultacie istnieją różne sposoby przekazania drugiego argumentu w zależności od tego, czy używamy funkcji, czy formuły.

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

To samo można osiągnąć rename_with, przekazując ramkę danych jako pierwszy argument .data, funkcję jako drugi argument .fn, wszystkie kolumny jako trzeci argument .cols=everything()i parametry funkcji jako czwarty argument .... Alternatywnie możesz umieścić drugi, trzeci i czwarty argument we wzorze podanym jako drugi argument.

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

rename_withdziała tylko z ramkami danych. set_namesjest bardziej ogólny i może również zmieniać nazwy wektorów

rlang::set_names(1:4, c("a", "b", "c", "d"))
Paul Rougieux
źródło