先看看列名:names() 和 colnames()
列名保存在一个属性中,你可以像读取任何向量一样读取它。对于数据框,names() 和 colnames() 都会返回它:
结果一样。唯一真正的区别是:colnames() 也能用于矩阵,而 names() 不行,所以通用的代码会用 colnames()。在日常的数据框上,两者可以互换。
理解下面所有内容的关键是:重命名列就是给这个向量赋值。 基础 R 没有专门的重命名函数;你用操作任何向量时都会用的索引方式来修改 names(df)。
按位置重命名(能用,但脆弱)
按位置索引列名向量,然后赋值:
第 2 列现在叫 price 了。在交互式环境里快速改一下没问题,但在脚本里就是个陷阱。位置是对列顺序的一种承诺,而列顺序恰恰是导入数据中最不稳定的东西:一旦你读取的 CSV 多出一列,names(df)[2] 就会悄悄地重命名错误的列。如果脚本按位置重命名,数据结构一变它就会出错,而且没有任何错误信息。
按名字重命名(稳妥的基础 R 写法)
匹配旧名字,而不是依赖位置:
先看内层部分:names(sales) == "prc" 会产生 FALSE TRUE FALSE,也就是列名向量上的一个逻辑掩码。用它做索引就能选出匹配的那一项,然后赋值替换它。如果 "prc" 不存在,就什么都不匹配,什么也不会改变(不会报错,所以要检查结果)。这种写法不怕列被重新排序、不怕新增列,复制粘贴到其他脚本里也照样能用。把它记住,这就是基础 R 对“重命名一列”的标准答案。
一次重命名多列
如果要给每一列都命名(导入数据后经常需要这样做),就替换整个向量;如果只重命名选中的部分列,就用 match():
match(old, names(df)) 返回旧名字所在的位置,所以新名字会准确地放到旧名字原来的位置上,和单个名字的写法一样,不受顺序影响。如果某个旧名字不存在,match() 会返回 NA,赋值就会报错,这正是你想要的失败方式:明确地报错。
管道中用 setNames()
上面所有的写法都要分两步修改一个变量。setNames(object, names) 在一个表达式中返回重命名后的副本,这正是管道需要的:
当某个函数返回的数据框没有列名或列名不好,而你想直接在行内修好它时,这就很方便,比如 read_something() |> setNames(c("id", "value")),不需要临时变量。
dplyr 的方式:rename() 和 rename_with()
dplyr 的 rename() 接受 new = old 这样的配对,新名字在前,每个人都会至少弄反一次(以下是静态代码,沙盒只运行基础 R):
library(dplyr)
sales |> rename(price = prc, qty = q)
调用中没有涉及的旧名字保持不变,而拼错的旧名字会直接报错,而不是悄悄地什么都不做,这是相对于基础 R 写法的实实在在的改进。如果要按规则而不是按配对来重命名,rename_with() 会把一个函数应用到列名上:
sales |> rename_with(toupper) # every column
sales |> rename_with(toupper, starts_with("p")) # just some
这些函数如何融入 dplyr 的动词家族,请参阅 dplyr 入门。
清理导入数据中杂乱的表头
真实的 CSV 表头往往是 "Order ID"、"unit.price ($)"、"2024 Total" 这样的,名字里有空格和符号,导致到处都得用反引号括起来。基础 R 的 make.names() 能把任何字符向量转换成合法的 R 名字:
合法是合法了,但也有它自己的难看之处(X2024.Total)。这就是为什么很多团队统一使用 janitor 包的 clean_names(),一次调用就能生成整齐的 snake_case(order_id、unit_price、x2024_total):df |> janitor::clean_names()。如果你每周都要导入杂乱的文件,它立刻就能回本。
要点回顾
- 列名只是一个向量:用
names()/colnames()读取,给它赋值就能重命名。 names(df)[2] <- "new"很脆弱;names(df)[names(df) == "old"] <- "new"才是要记住的写法。match()按名字重命名多列;setNames()在管道中直接重命名。- dplyr 的
rename(new = old)遇到不存在的名字会报错(这是好事),rename_with()按规则重命名。 - 处理杂乱的导入表头:基础 R 用
make.names(),想要漂亮的结果就用janitor::clean_names()。
下一篇:排序,向量用 sort(),数据框用 order() 技巧,还有 arrange()。
常见问题
R 中怎么重命名一列?
稳妥的基础 R 写法是按名字匹配:names(df)[names(df) == "old"] <- "new"。不管名为 old 的列在什么位置,它都能找到并重命名。在 dplyr 中是 rename(df, new = old),注意顺序:新名字在前。
names() 和 colnames() 有什么区别?
在数据框上,实际没有区别:两者都能读取和设置列名。colnames() 还能用于矩阵(names() 不行),所以需要同时处理两者的代码往往用 colnames()。在普通数据框上,用哪个都行。
R 中怎么按位置重命名一列?
给列名向量中对应的位置赋值:names(df)[2] <- "price" 会重命名第二列。这样能用,但很脆弱:一旦新增了一列,或者 CSV 改变了列的顺序,第 2 个位置就是另一列了。优先按名字重命名。
R 中怎么一次重命名多列?
赋值一个完整的向量:names(df) <- c("id", "name", "price") 会按顺序替换所有列名。要按名字只重命名选中的几列,用 match():names(df)[match(c("old1", "old2"), names(df))] <- c("new1", "new2")。在 dplyr 中:rename(df, new1 = old1, new2 = old2)。