提问人:qoheleth 提问时间:1/8/2014 最后编辑:Jaapqoheleth 更新时间:3/16/2023 访问量:281760
按名称重命名多个列
Rename multiple columns by names
问:
应该有人已经问过这个问题了,但我找不到答案。假设我有:
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
答:
这将更改所有名称中出现的所有字母:
names(x) <- gsub("q", "A", gsub("e", "B", names(x) ) )
评论
gsubfn
names(x)[names(x) %in% c("q","e")]<-c("A","B")
评论
oldnames
oldnames[i]
oldnames[j]
setnames
从包将在 S 或 S 上工作data.table
data.frame
data.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!
评论
old = c("a", "d", "e")
rename_at()
skip_absent=TRUE
基于 @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
评论
match
setnames
match
%in%
name
对于不太大的数据帧,另一个解决方案是(建立在@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
因此,如果您不确定这些列是否存在并且只想重命名那些存在列,那么我自己最近也遇到了这个问题:
existing <- match(oldNames,names(x))
names(x)[na.omit(existing)] <- newNames[which(!is.na(existing))]
如果数据的一行包含要更改所有列的名称,则可以执行
names(data) <- data[row,]
给定是数据帧,是包含新值的行号。data
row
然后,您可以删除包含名称的行
data <- data[-row,]
您可以设置名称,将其另存为列表,然后对字符串进行批量重命名。一个很好的例子是,当你在数据集上做一个从长到宽的转换时:
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" "
使用 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_at
rename_with
df %>%
rename_with(~ newnames[which(oldnames == .x)], .cols = oldnames)
# A w B
#1 1 2 3
评论
old
new
rename_with
可以使用函数或公式重命名作为参数给出的所有列。例如,等同于 ..cols
rename_with(iris, toupper, starts_with("Petal"))
rename_with(iris, ~ toupper(.x), starts_with("Petal"))
有很多答案,所以我只是编写了函数,以便您可以复制/粘贴。
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 重命名?
这是我发现的使用组合和一些操作重命名多个列的最有效方法。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
评论
~
.
set_names()
purrr::set_names()
purrr
~
.
data
~
...
set_names
rlang::set_names(head(iris), paste0, "_hi")
rlang::set_names(head(iris), ~ paste0(.x, "_hi"))
purrr::set_names()
今天得到了我。谢谢列维!
旁注,如果要将一个字符串连接到所有列名,则可以使用此简单代码。
colnames(df) <- paste("renamed_",colnames(df),sep="")
如果表包含两列具有相同名称,则代码如下所示:
rename(df,newname=oldname.x,newname=oldname.y)
这是您需要的功能: 然后只需在 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)
}
评论
您可以使用命名向量。下面两个选项(使用基本 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 对象列表。列表的元素是 就地拼接,这意味着它们各自成为一个单一的参数。
评论
!!!oldnames
c("A", "B")
c("A", "w", "B")
?`!!`
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))
有几个答案提到了这些功能,并且已经。因为它们是分开的。此答案说明了两者之间的区别以及使用函数和公式重命名列。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"))
重命名所有列时,也可以从 rlang 包中使用。为了做一个不同的例子,让我们用作重命名函数。 接受 2 个参数,因此有不同的方法可以传递第二个参数,具体取决于我们使用的是函数还是公式。set_names
paste0
pasteO
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"))
更新 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
评论
df %>% rename_with(~ newnames, all_of(oldnames))
newnames
oldnames
上面使用专门的软件包提供了许多很好的答案。这是一种仅使用基本 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
出于执行时间的目的,我建议使用数据表结构:
> 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
我最近根据 @agile Bean 的答案(以前使用 )构建了一个函数,该函数如果列名存在于数据框中,则该函数会更改列名,这样就可以在适用时使异构数据框的列名相互匹配。rename_with
rename_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)
一种基本方式,使用将采取第一场比赛的利用方式。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")
评论