Menu

R 语言读取 Excel 文件(readxl)

R 本身打不开 .xlsx 文件,readxl 包填补了这个空缺。如何读取工作表、单元格区域和表头,如何把数据写回 Excel,以及如何避开导入 Excel 时的经典陷阱。

R 需要一个包才能读取 Excel 文件

基础 R 开箱就能读取纯文本格式,但 .xlsx 不是文本,它是一个压缩打包的 XML,而在它之前的 .xls 是二进制格式。R 并没有一个内置的 read.xlsx() 等着你用。要打开 Excel 文件,你需要安装一个包,标准选择是 readxl:它能读取 .xlsx 和 .xls,没有任何外部依赖(不需要 Java,也不需要安装 Excel),专心把一件事做好。

install.packages("readxl")   # once per machine
library(readxl)              # once per session

本页的代码片段需要在本地安装 readxl,所以以静态代码而不是可运行代码块的形式展示,请在你自己的 R 会话中运行。如果你还不熟悉包,简单来说:install.packages() 下载一次,library() 在每次启动 R 时加载。

read_excel():基础用法

一次调用就能把工作簿的第一个工作表读入 R:

library(readxl)

sales <- read_excel("sales.xlsx")
head(sales)
str(sales)

和任何导入一样的习惯:用 str() 检查每一列的类型,用 head() 看一眼前几行。如果找不到文件,原因几乎总是工作目录,file.exists("sales.xlsx") 一秒钟就能告诉你,解决办法和 CSV 文件一样:使用完整路径,或者把工作目录设置为文件所在的文件夹。

返回的是一个 tibble,也就是 tidyverse 版的数据框。对于初学者会做的所有事情,它的行为和数据框完全一样(它就是数据框,只是多了些功能):$ 提取列,nrow() 统计行数,它也能直接交给 dplyr 动词处理。看得到的区别只是外观上的,而且更友好:它只打印前十行,并在列名下方显示列类型,而不是把所有内容一股脑地倒出来。如果某个老函数坚持要普通的数据框,用 as.data.frame(sales) 转换一下就行。

选择工作表、区域,跳过无用内容

真实的工作簿很少是一张从 A1 单元格开始的干净表格。readxl 的参数能处理常见的混乱情况。

有哪些工作表? 读之前先问:

excel_sheets("report.xlsx")
# [1] "Summary"  "Q1"  "Q2"  "Q3"  "Raw data"

sheet = 按名字或位置选择一个工作表:

q3 <- read_excel("report.xlsx", sheet = "Q3")
raw <- read_excel("report.xlsx", sheet = 5)

优先用名字:一旦有人调整了标签页的顺序,sheet = 5 就会悄悄地读错工作表。

range = 用 Excel 自己的表示法读取一个精确的矩形区域。要跳过表格周围的 logo 行、标题行和零散的备注,这是最干净的办法:

budget <- read_excel("budget.xlsx", range = "B4:E20")
budget <- read_excel("budget.xlsx", sheet = "Plan", range = "B4:E20")

skip = 和 col_names = 是更宽松的替代方案,适合你知道数据上方有几行无用内容、但不知道数据在哪里结束的情况:

# Data starts after 3 title rows, first real row is the header:
df <- read_excel("export.xlsx", skip = 3)

# No header row at all - supply names yourself:
df <- read_excel("export.xlsx", col_names = c("id", "region", "amount"))

col_names = TRUE(默认值)把第一行作为列名;FALSE 会生成 ...1、...2,并把第一行当作数据。

写出 Excel 需要另一个包

readxl 在设计上就是只读的。当同事想要你的结果以电子表格的形式交付时,最快的办法是 writexl,一行代码,没有任何依赖:

install.packages("writexl")
writexl::write_xlsx(results, "results.xlsx")

如果你需要的不只是原始值,比如加粗的表头、冻结窗格、单元格颜色、一个工作簿里有多个带格式的工作表,那就是 openxlsx 的领域了:

library(openxlsx)
write.xlsx(list(Summary = summary_df, Detail = detail_df), "report.xlsx")

一个具名列表中的每个元素会变成一个工作表。openxlsx 也能读取 Excel 文件,所以实际代码中你会看到读写两个方向都用它;不过只是读取的话,readxl 仍然是更简单的工具。

务实的替代方案:导出 CSV

有时候最不花哨的办法反而最好。如果这个工作簿只用一次,比如有人给你发了一份电子表格,你马上就要里面的数字,那就在 Excel 中打开它,用“另存为”把工作表导出为 CSV,再用你已经熟悉的工具读取:

df <- read.csv("sales.csv")

不需要包,不需要工作表名,也没有合并单元格。完整的流程见读取 CSV。代价是:你会丢掉其他工作表,依靠格式表达的信息也会被抹平(用单元格颜色来“表示”某种含义,本来就是数据设计上的坏味道),而且导出是一个手动步骤,源文件更新时总会有人忘记。对于反复运行的流程,直接读取 .xlsx;对于一次性的导入,用 CSV 完全没问题。

导入 Excel 的经典陷阱

Excel 文件会带来 CSV 没有的问题,因为电子表格允许人们做一些表格本不该做的事。

日期变成了数字。 Excel 把日期存储为一个按天计数的序列号,如果某一列混合了多种类型,或者格式设置得很奇怪,你期望得到日期的地方可能会出现 44688。readxl 通常能正确转换真正的日期单元格,但当你确实拿到原始数字时,要用 Excel 的起始日期来转换,它是 1899-12-30,而不是 1970 年:

as.Date(44688, origin = "1899-12-30")
# [1] "2022-05-07"

合并单元格拆开后变成空白。 一个横跨三列合并的表头,读进来是一个值加两个空单元格;一个向下合并了十行的类别标签,会变成一个值加九个缺失值。只存在于视觉上的意图,readxl 无法还原,导入后要准备好自己填补这些空缺。

一个多余的单元格就能把整列变成文本。 列的类型是根据数据推断的,所以只要有一个 "n/a"、一条夹在数字中的备注,或者一个“空”单元格里有个空格,整列都会被读成字符型。读完立刻运行 str() 就能发现;如果推断一直出错,用 col_types 参数(例如 col_types = c("text", "numeric", "date"))强制指定。

核心思想是:电子表格是一块供人随意涂画的画布,而不是一张表格。带着怀疑的态度去读它,运行 str(),并对照原文件核对几个值,然后再相信导入的结果。

要点回顾

  • 基础 R 读不了 .xlsx:安装 readxl,然后 read_excel("file.xlsx")。
  • excel_sheets() 列出工作簿中的内容;sheet =(优先用名字)选择工作表;range = "B4:E20" 精确截取你想要的表格。
  • 你得到的是一个 tibble,一个打印效果更好的数据框。
  • 写出要用另一个包:普通输出用 writexl::write_xlsx(),带格式的工作簿用 openxlsx。
  • 一次性的任务,从 Excel 导出 CSV 再用 read.csv(),是完全正当的捷径。
  • 留意三个经典问题:日期变成序列号(起始日期 1899-12-30),合并单元格变成空白,以及一个坏单元格把整列拖成文本。

下一篇:反方向的操作,把数据框写出到 CSV 和 RDS 文件。

常见问题

R 中怎么读取 Excel 文件?

先用 install.packages("readxl") 安装一次 readxl 包,用 library(readxl) 加载,然后调用 read_excel("file.xlsx")。它会根据第一个工作表返回一个 tibble(一种现代化的数据框)。要读取其他工作表,用 sheet = 参数按名字或位置指定。

R 不装包能读取 Excel 文件吗?

不能。基础 R 没有 .xlsx 或 .xls 的读取函数,它们是二进制或压缩格式,不是文本。你要么使用一个包(readxl 是标准选择,openxlsx 也可以),要么在 Excel 中把工作表导出为 CSV,再用完全不需要包的 read.csv() 读取。

R 中怎么读取 Excel 文件中的某个工作表?

给 read_excel 传入 sheet =:按名字是 read_excel("report.xlsx", sheet = "Q3"),按位置是 sheet = 3。如果不知道工作簿里有什么,excel_sheets("report.xlsx") 会以字符向量的形式返回所有工作表的名字。

R 中怎么写出 Excel 文件?

readxl 只能读。要写出文件,一行代码就够:writexl::write_xlsx(df, "out.xlsx")。如果需要格式,比如颜色、列宽、多个带样式的工作表,就改用 openxlsx 包,它可以通过代码构建带样式的工作簿。

Coddy 编程语言插图

和 Coddy 一起学编程

开始