按名称重命名多个列

Rename multiple columns by names

提问人:qoheleth 提问时间:1/8/2014 最后编辑:Jaapqoheleth 更新时间:3/16/2023 访问量:281760

问:

应该有人已经问过这个问题了,但我找不到答案。假设我有:

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

将我不一定知道其位置的任意列子集重命名为其他任意名称的最优雅方法是什么?

例如,假设我想重命名 and into 和 ,最优雅的代码是什么?"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

DataFrame 重命名 R-FAQ

评论


答:

4赞 IRTFM 1/8/2014 #1

这将更改所有名称中出现的所有字母:

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

评论

2赞 thelatemail 1/8/2014
我不认为一旦你通过了几个重命名实例,这就不是特别优雅了。
0赞 IRTFM 1/8/2014
我只是不够好,无法给出答案。也许 G.Grothendieck 会过来。他是正则表达式大师。gsubfn
5赞 James King 1/8/2014 #2
names(x)[names(x) %in% c("q","e")]<-c("A","B")

评论

4赞 qoheleth 1/8/2014
不完全是,因为正如我所说,我不一定知道列的位置,您的解决方案只有在对 i<j 进行排序时才有效。oldnamesoldnames[i]oldnames[j]
129赞 mnel 1/8/2014 #3

setnames从包将在 S 或 S 上工作data.tabledata.framedata.table

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!

评论

2赞 micstr 11/7/2016
对于迟到的人 - 还可以看看下面的 Joel 的回答,其中包括检查现有列,以防您有一个名称更改列表,这些更改可能并非全部存在,例如old = c("a", "d", "e")
1赞 Mus 7/9/2018
我想知道,如果您只想重命名一个子集/一些列而不是所有列,这是否有效?因此,如果我有一个包含十列的数据框,并希望将_id_firstname重命名为 firstname,将 _id_lastname重命名为 lastname,但保留其余 8 列不变,我是否可以这样做,或者我必须列出所有列?
0赞 mnel 7/16/2018
@MusTheDataGuy提供新名称和旧名称的子集,它就会起作用。
1赞 Mehmet Yildirim 2/1/2020
@mnel,我需要按照@Mus的要求更改子集的变量名称。但是,上面的代码不适用于数据子集。@Gorka的答案适用于更改子集的变量名称。rename_at()
2赞 bers 10/25/2021
@micstr :)skip_absent=TRUE
10赞 thelatemail 1/8/2014 #4

基于 @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 1/8/2014
我已经对你的答案投了赞成票,但我仍然想知道是否有一种更优雅的方法可以做到这一点,特别是按名称而不是按位置重命名的方法
0赞 thelatemail 1/8/2014
@qoheleth - 它正在按名称重命名!这里没有作为位置向量的输入来处理它。你要做的最好的事情可能是@mnel的答案。matchsetnames
1赞 qoheleth 1/8/2014
它仍然是按位置重命名的,因为正如您所说,即使我不必明确指定位置向量,它仍然是一个面向位置的命令。本着这种精神,我认为@user3114046的回答位置也是基于的(甚至认为命令会照顾(或试图)事情)。当然,我想你可以说,当我们深入研究低级机制时,所有命令都是面向位置的。但这不是我的意思......data.table 的答案很好,因为没有多次调用命令。match%in%name
45赞 Jaap 1/8/2014 #5

对于不太大的数据帧,另一个解决方案是(建立在@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
14赞 JoelKuiper 3/15/2016 #6

因此,如果您不确定这些列是否存在并且只想重命名那些存在列,那么我自己最近也遇到了这个问题:

existing <- match(oldNames,names(x))
names(x)[na.omit(existing)] <- newNames[which(!is.na(existing))]
0赞 CaffeineConnoisseur 8/17/2016 #7

如果数据的一行包含要更改所有列的名称,则可以执行

names(data) <- data[row,]

给定是数据帧,是包含新值的行号。datarow

然后,您可以删除包含名称的行

data <- data[-row,]
3赞 Boyce Byerly 10/6/2016 #8

您可以设置名称,将其另存为列表,然后对字符串进行批量重命名。一个很好的例子是,当你在数据集上做一个从长到宽的转换时:

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" " 
138赞 Gorka 12/27/2016 #9

使用 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

L. D. Nicolas May建议,所给出的更改将被以下命令所取代:rename_atrename_with

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

#  A w B
#1 1 2 3

评论

3赞 JelenaČuklina 3/23/2018
我认为,用户询问了将传递和名称作为向量的问题oldnew
4赞 Gorka 3/25/2018
谢谢@Jelena-bioinf。我修改了答案以包含您的建议。
1赞 petzi 10/26/2020
您能否解释一下~(波浪号)的含义以及rename_with示例中“.x”的来源?
2赞 Paul Rougieux 12/17/2020
rename_with可以使用函数或公式重命名作为参数给出的所有列。例如,等同于 ..colsrename_with(iris, toupper, starts_with("Petal"))rename_with(iris, ~ toupper(.x), starts_with("Petal"))
1赞 Matteo Bulgarelli 4/3/2023
不清楚,可怕的语法,这个解决方案太糟糕了,假设我必须在“2012”中重命名一个名为“2012 (%)”的列:试图根据这个例子猜测你的解决方案在现实生活中的含义是不可能的。rename() 总的来说很糟糕。
1赞 Dan 1/10/2018 #10

有很多答案,所以我只是编写了函数,以便您可以复制/粘贴。

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

评论

0赞 s_baldur 3/8/2018
rename(x, c("q", "e"), c("Q", "E"))似乎不再适用于 dplyr 重命名?
33赞 Matt Dancho 4/10/2018 #11

这是我发现的使用组合和一些操作重命名多个列的最有效方法。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赞 DaveRGP 5/24/2018
这应该是答案,但您还应该扩展管道中的 and 参数的作用。~.set_names()
1赞 Levi Baguley 2/12/2020
在某些情况下,您需要显式键入 .purrr::set_names()
2赞 Agile Bean 5/19/2020
@DaveRGP使用函数时,波浪号的意思是“对于每一列”。LHS = 管道左侧的 is dplyr 语法,即对管道对象的引用,在本例中为 。purrr~.data
0赞 Paul Rougieux 12/17/2020
波浪号是一个公式。您还可以使用函数调用并将参数传递给 的参数,例如等效于 。~...set_namesrlang::set_names(head(iris), paste0, "_hi")rlang::set_names(head(iris), ~ paste0(.x, "_hi"))
0赞 taiyodayo 2/25/2022
purrr::set_names()今天得到了我。谢谢列维!
2赞 Corey Levinson 6/1/2018 #12

旁注,如果要将一个字符串连接到所有列名,则可以使用此简单代码。

colnames(df) <- paste("renamed_",colnames(df),sep="")
3赞 varun 10/1/2018 #13

如果表包含两列具有相同名称,则代码如下所示:

rename(df,newname=oldname.x,newname=oldname.y)
0赞 Zuti 1/9/2019 #14

这是您需要的功能: 然后只需在 rename(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)
}

评论

3赞 Jaap 4/9/2019
这似乎与 JoelKuiper 的答案相同,但后来被重新构建为函数......
8赞 tjebo 1/3/2020 #15

您可以使用命名向量。下面两个选项(使用基本 R 和 dplyr)。

基数 R,通过子集:

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

rename_vec <- c(q = "A", e = "B")
## vector of same length as names(x) which returns NA if there is no match to names(x)
which_rename <- rename_vec[names(x)]
## simple ifelse where names(x) will be renamed for every non-NA 
names(x) <- ifelse(is.na(which_rename), names(x), which_rename)

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

后者之所以有效,是因为“大爆炸”算子正在强制对列表或向量进行求值。!!!

?`!!`

!!!forces-splice 对象列表。列表的元素是 就地拼接,这意味着它们各自成为一个单一的参数。

评论

1赞 Agile Bean 5/19/2020
不明白这是如何工作的 - 返回,但哪个逻辑将其转换为??!!!oldnamesc("A", "B")c("A", "w", "B")
1赞 tjebo 5/19/2020
@AgileBean我不知道你在哪里找到的!!oldnames 将返回一个向量。它用于强制对 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))
5赞 Paul Rougieux 12/17/2020 #16

有几个答案提到了这些功能,并且已经。因为它们是分开的。此答案说明了两者之间的区别以及使用函数和公式重命名列。dplyr::rename_withrlang::set_names

rename_with从包中可以使用函数或公式 重命名作为参数给出的选定列。例如,传递函数名称:dplyr.colstoupper

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

相当于传递公式:~ toupper(.x)

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

重命名所有列时,也可以从 rlang 包中使用。为了做一个不同的例子,让我们用作重命名函数。 接受 2 个参数,因此有不同的方法可以传递第二个参数,具体取决于我们使用的是函数还是公式。set_namespaste0pasteO

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"))
32赞 Agile Bean 2/27/2021 #17

更新 dplyr 1.0.0

最新的 dplyr 版本通过添加 where 引用函数作为输入变得更加灵活。诀窍是将字符向量重新表述为公式 (by ),因此它等价于 。rename_with()_with newnames~function(x) return (newnames)

在我的主观意见中,这是最优雅的 dplyr 表达。 更新:多亏了@desval,oldnames 向量必须被包装以包含其所有元素:all_of

# shortest & most elegant expression
df %>% rename_with(~ newnames, all_of(oldnames))

A w B
1 1 2 3

旁注:

如果颠倒顺序,则必须将任一参数 .fn 指定为在 .cols 参数之前指定 .fn:

df %>% rename_with(oldnames, .fn = ~ newnames)

A w B
1 1 2 3

或指定参数 .col:

 df %>% rename_with(.col = oldnames, ~ newnames)

A w B
1 1 2 3

评论

3赞 desval 1/24/2022
看起来这个答案目前返回警告,将来会返回错误,因为在 select tidyselect.r-lib.org/reference/faq-external-vector.html 中使用外部向量时存在歧义。这应该可以解决它df %>% rename_with(~ newnames, all_of(oldnames))
0赞 FLonLon 6/23/2022
您能举一个具体的例子吗?我找不到任何替代品或工作。newnamesoldnames
0赞 Soldalma 3/25/2021 #18

上面使用专门的软件包提供了许多很好的答案。这是一种仅使用基本 R 执行此操作的简单方法。

df.rename.cols <- function(df, col2.list) {
  tlist <- transpose(col2.list)
    
  names(df)[which(names(df) %in% tlist[[1]])] <- tlist[[2]]

  df
} 

下面是一个示例:

df1 <- data.frame(A = c(1, 2), B = c(3, 4), C = c(5, 6), D = c(7, 8))
col.list <- list(c("A", "NewA"), c("C", "NewC"))
df.rename.cols(df1, col.list)

  NewA B NewC D
1    1 3    5 7
2    2 4    6 8
0赞 A. chahid 10/25/2021 #19

出于执行时间的目的,我建议使用数据表结构:

> df = data.table(x = 1:10, y = 3:12, z = 4:13)
> oldnames = c("x","y","z")
> newnames = c("X","Y","Z")
> library(microbenchmark)
> library(data.table)
> library(dplyr)
> microbenchmark(dplyr_1 = df %>% rename_at(vars(oldnames), ~ newnames) ,
+                dplyr_2 = df %>% rename(X=x,Y=y,Z=z) ,
+                data_tabl1= setnames(copy(df), old = c("x","y","z") , new = c("X","Y","Z")),
+                times = 100) 
Unit: microseconds
       expr    min      lq     mean  median      uq     max neval
    dplyr_1 5760.3 6523.00 7092.538 6864.35 7210.45 17935.9   100
    dplyr_2 2536.4 2788.40 3078.609 3010.65 3282.05  4689.8   100
 data_tabl1  170.0  218.45  368.261  243.85  274.40 12351.7   100

0赞 Jessica Burnett 12/10/2021 #20

我最近根据 @agile Bean 的答案(以前使用 )构建了一个函数,该函数如果列名存在于数据框中,则该函数会更改列名,这样就可以在适用时使异构数据框的列名相互匹配。rename_withrename_at

循环肯定可以改进,但我想我会为后代分享。

创建示例数据框:
x= structure(list(observation_date = structure(c(18526L, 18784L, 
17601L), class = c("IDate", "Date")), year = c(2020L, 2021L, 
2018L)), sf_column = "geometry", agr = structure(c(id = NA_integer_, 
common_name = NA_integer_, scientific_name = NA_integer_, observation_count = NA_integer_, 
country = NA_integer_, country_code = NA_integer_, state = NA_integer_, 
state_code = NA_integer_, county = NA_integer_, county_code = NA_integer_, 
observation_date = NA_integer_, time_observations_started = NA_integer_, 
observer_id = NA_integer_, sampling_event_identifier = NA_integer_, 
protocol_type = NA_integer_, protocol_code = NA_integer_, duration_minutes = NA_integer_, 
effort_distance_km = NA_integer_, effort_area_ha = NA_integer_, 
number_observers = NA_integer_, all_species_reported = NA_integer_, 
group_identifier = NA_integer_, year = NA_integer_, checklist_id = NA_integer_, 
yday = NA_integer_), class = "factor", .Label = c("constant", 
"aggregate", "identity")), row.names = c("3", "3.1", "3.2"), class = "data.frame")
功能
match_col_names <- function(x){

  col_names <- list(date = c("observation_date", "date"),
                    C =    c("observation_count", "count","routetotal"),
                    yday  = c("dayofyear"),
                    latitude  = c("lat"),
                    longitude = c("lon","long")
                    )

  for(i in seq_along(col_names)){
    newname=names(col_names)[i]
    oldnames=col_names[[i]]

  toreplace = names(x)[which(names(x) %in% oldnames)]
  x <- x %>%
    rename_with(~newname, toreplace)
}

return(x)

}

apply 函数
x <- match_col_names(x)
0赞 GKi 3/15/2023 #21

一种基本方式,使用将采取第一场比赛的利用方式。setNames[]

names(x) <- setNames(c(newnames, names(x)), c(oldnames, names(x)))[names(x)]

names(x) <- (\(.) setNames(c(newnames, .), c(oldnames, .))[.])(names(x)) #Variant

x
#  A w B
#1 1 2 3

用。transform

names(x) <- do.call(transform, c(list(as.list(setNames(names(x), names(x)))),
                                 as.list(setNames(newnames, oldnames))))

数据

x = data.frame(q=1,w=2,e=3)
oldnames = c("q","e")
newnames = c("A","B")