Menu

R 语言添加与转换列(mutate 与 base R)

如何添加、转换和删除数据框的列:df$new <- ...、用 ifelse() 创建条件列、transform(),以及 dplyr 的 mutate() 与 case_when()。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

添加一列:df$new <- value

在 base R 中添加一列,只需给一个还不存在的名称赋值。这个值通常是根据已有的列计算出来的:

这里发生了两件事。orders$total 得到了一个完整的向量:price * qty 会一次性为每一行计算,因为 R 中的算术运算是向量化的。orders$currency 得到的是单个值,R 把它循环补齐到了全部四行。其他任何长度都会报错(replacement has 3 rows, data has 4),这是 R 在防止你得到一个错位的列。

之所以能这样做,是因为数据框是由等长的列组成的列表,给它赋值一个新的命名元素,列表就会变长。数据框文档详细讲解了这个模型。

条件列:ifelse()

值取决于某个条件的列,要用 ifelse(),它是 if 的向量化版本。它接收一整个条件向量,并为每一行返回一个值:

为什么不用普通的 if?因为 if 只能求值恰好一个 TRUE/FALSE,交给它一整列就会报错(the condition has length > 1)。ifelse(test, yes, no) 会检查每个元素,并逐行做出选择。结果有三种或更多时,可以嵌套:

嵌套两层没问题;嵌套三层或更多就会变成金字塔,而这正是 dplyr 的 case_when()(见下文)要解决的问题。只针对单个值的 if/else 本身,请参阅 if-else 文档。

一次添加多列:transform()

base R 的 transform() 可以在一次调用中添加多列,并且直接使用列名:

注意 transform() 返回的是一个新的数据框,你必须把结果赋值回去;而 df$new <- ... 是直接原地修改。它有一个限制:同一次 transform() 调用中的表达式彼此看不见,所以在这里写 discount = ifelse(total > 50, ...) 会失败(total 还不存在)。分两步来做,或者改用允许这样写的 mutate()。

修改与创建

同样的赋值语法也会覆盖已有的列,唯一的区别在于这一列是否已经存在:

这一点值得停下来想想:覆盖一列时,R 不会给你任何警告。像 df$prise <- round(df$price, 2) 这样的拼写错误,会悄悄创建一个新列,而不是修正原来的列。完成转换后,用 print() 或 str() 看一下数据框,确认你改的就是你想改的。

删除一列

赋值 NULL 即可删除:

这一列就没了,不需要任何繁琐步骤。要删除多列,df[, c("a", "b")](保留你列出的列)通常比重复赋值 NULL 更清晰。

dplyr 的写法:mutate()

在 dplyr 中,完成以上所有操作的动词是 mutate()。下面的代码片段是静态的(沙盒只运行 base R),但同样的操作就是这样写的:

library(dplyr)

orders |>
    mutate(
        total    = price * qty,
        big      = total > 50        # can use total, defined one line up
    )

它比 base R 有两个优势。第一,在同一个 mutate() 中定义的列可以按顺序相互引用,这正是 transform() 不允许的。第二,它的条件辅助函数比嵌套的 ifelse() 更好用:

students |>
    mutate(
        grade = if_else(score >= 60, "pass", "fail"),
        band  = case_when(
            score >= 85 ~ "high",
            score >= 60 ~ "mid",
            .default    = "low"
        )
    )

if_else() 是更严格的 ifelse()(两个分支必须是同一种类型,能抓住真正的 bug)。case_when() 读起来就像一组扁平的 condition ~ value 配对,从上到下依次检查,.default 作为兜底,是 ifelse() 金字塔的易读替代品。和所有 dplyr 动词一样,mutate() 返回一个新的数据框;它如何与其他动词串联,见 dplyr 入门。

要点回顾

  • df$new <- expression 添加一列;表达式会一次性对每一行运行。
  • ifelse(test, yes, no) 用来构造条件列;普通的 if 做不到。
  • transform() 一次调用可以添加多列,但这些列彼此看不见;mutate() 则可以。
  • 同样的赋值会悄悄覆盖已有列:转换之后检查一下拼写。
  • df$col <- NULL 删除一列;case_when() 取代嵌套的 ifelse() 金字塔。

下一篇:重命名列,包括 names()、稳健的 base R 惯用法,以及 dplyr 的 rename()。

常见问题

R 语言怎么给数据框添加新列?

给一个还不存在的列名赋值:df$total <- df$price * df$qty。R 会当场创建这一列。值可以是单个值(循环补齐到每一行),也可以是由其他列计算出的向量,只要长度和行数一致即可。

R 语言怎么创建条件列?

使用向量化的 ifelse():df$grade <- ifelse(df$score >= 60, "pass", "fail") 会一次性检查每一行。不要用普通的 if,它只能处理单个条件,处理不了一整列。结果多于两种时,可以嵌套 ifelse() 调用,或者使用 dplyr 的 case_when()。

R 语言中 mutate() 有什么作用?

mutate() 是 dplyr 中用于添加或转换列的动词:df |> mutate(total = price * qty) 返回一个带有新列的数据框副本。它可以在一次调用中定义多个列,而且后面的列可以使用同一个 mutate 中前面定义的列。

R 语言怎么删除数据框中的列?

给它赋值 NULL:df$notes <- NULL 会直接删除这一列。在 dplyr 中,select(-notes) 返回一个不含该列的副本。

Coddy 编程语言插图

和 Coddy 一起学编程

开始