第7讲:数据读写与类型转换

数据分析与R语言

2026年09月20日

本讲导览

Data Import, Export & Type Conversion

  • 课前测 ——4道题,热热身
  • 一、复习导入 ——base R的read.csv()够用吗?
  • 二、CSV文件的读写 ——readr包
  • 三、Excel文件的读写 ——readxl/writexl包
  • 四、数据类型转换 ——给数据"验明正身"
  • 五、综合实战 ——读入→检查→转换→导出
  • 六、课堂总结与Exit Ticket ——数据准备的标准工作流

提示

今天是项目二"数据准备"的第一课。今天所有的演示数据都是现场用R代码生成的虚构公司数据——这也提醒大家:养成用代码而不是手工造数据做练习的习惯,任何同学都能一键复现今天的每一步。

课前测

温故知新,为今天的内容热热身

课前小测

请在正式上课前,凭记忆快速完成下面4道题(不查资料):

选择题1:第6课学过,把数据框写成CSV文件用的函数是(  )

A. read.csv() B. write.csv() C. save() D. export()

选择题2:第2课学过,查看一个向量数据类型(是字符型还是数值型)的函数是(  )

A. length() B. dim() C. class() D. names()

填空题1:把字符向量"A"转换成因子,第2课学过的写法是as.____("A")。

填空题2:read.csv()读入中文CSV文件如果出现乱码,很可能是因为文件是用Excel另存的,编码是____而不是UTF-8。

课前小测——参考答案

提示

这4道题复习了第6课的CSV读写和第2课的类型/因子知识——今天要学的readr包读写、以及类型转换,都是在这些基础上的"专业升级版"。

一、复习导入与新课导论

base R的read.csv()够用吗?

1.1 复习提问

第6课学过的read.csv()/write.csv()还记得吗?当时是怎么用的?

1.2 项目二开始,先给文件"安家"——用RStudio Project管理

从今天开始进入项目二"数据准备",我们会不停地读文件、写文件。养成一个好习惯:给每个项目建一个RStudio Project,而不是把代码和数据文件随手存在不同的位置,使用的时候到处找。

flowchart LR
    A["File 菜单"] --> B["New Project…"]
    B --> C["New Directory<br/>新建一个项目文件夹"]
    C --> D["填写项目名称<br/>选择保存位置"]
    D --> E["Create Project<br/>RStudio自动重新打开"]

    style A fill:#1a3a5c,color:#fff
    style B fill:#2e5984,color:#fff
    style C fill:#7a4419,color:#fff
    style D fill:#4a2f6b,color:#fff
    style E fill:#375623,color:#fff

提示

Project的核心作用:一个.Rproj文件绑定一个文件夹,RStudio打开这个项目时会自动把这个文件夹设为"工作目录"。这样一来,之后所有的read_csv()、write_csv()用的都可以是简单的相对路径(比如"company_data.csv"),不用再写一长串容易出错的绝对路径;把整个项目文件夹拷给同事或换一台电脑,代码原封不动就能跑。如果项目比较复杂,数据和文件比较多,建议在项目文件夹下再建两个子文件夹——data/存放原始数据、output/存放导出的结果,保持整洁。

1.3 数据导入:"傻瓜式"——RStudio交互式导入

第6课我们用代码读CSV。其实RStudio还藏着一个鼠标点选的导入工具——Import Dataset,完全不用写代码:

flowchart LR
    A["Environment 面板"] --> B["Import Dataset<br/>From Text (readr)"]
    B --> C["选文件 → 预览<br/>调分隔符 / 编码 / 列名"]
    C --> D["Import 导入"]

    style A fill:#1a3a5c,color:#fff
    style B fill:#2e5984,color:#fff
    style C fill:#7a4419,color:#fff
    style D fill:#375623,color:#fff

提示

交互式导入所见即所得,还自带预览界面,最适合第一次探索一份陌生的数据。但它的软肋同样明显:过程不留痕、换一份数据要重头再点一遍。而代码式导入,一行代码可以反复运行、逐行可查——代码本身就是最好的"操作说明书"。这也是为什么今天的主角是readr/readxl包,而不是鼠标。交互式导入更多扮演"入门的拐杖":用它快速摸清数据长什么样,然后复制它自动生成的代码,转入代码工作流。

1.4 情境导入

如果数据不是CSV,而是财务人员最常用的Excel文件(.xlsx),base R的read.csv()还能读吗?

如果CSV文件里的"营收"这一列因为带了千分位逗号(如"1,500.00")被读成了字符串而不是数字,要怎么办?

引出:今天学习专业的数据读写工具(readr、readxl包)和规范的类型转换方法——这是项目二"数据准备"的第一课,也是后续所有数据分析工作的起点。

二、CSV文件的读写——readr包

readr包

2.1 今天要用到的新朋友——R包(package)

R语言的很多功能不是"自带"的,而是靠包(package)扩展出来的。今天要用到3个包:readr(读写CSV)、readxl(读Excel)、writexl(写Excel)。

▶️ 查看代码
library(readr)
library(readxl)
library(writexl)

提示

打个比方:install.packages("readr")是"买书",电脑上只要买过一次就行(今天机房已经装好,不用再装);而library(readr)是"把书从书架上取出来打开"——每次新开一个R/RStudio会话,想用这个包里的函数,都要先运行一次library(),不然R会提示"找不到这个函数"。接下来的read_csv()、read_excel()等函数,都来自刚刚加载的这几个包。

2.2 先造一份数据,再学怎么读写

▶️ 查看代码
company_data <- data.frame(
  company = c("锦城科技","蜀汉制造","天府物流","青羊金融","锦江生物","浣花实业"),
  revenue  = c(1200.5, 980.0, 1560.3, 760.2, 2100.0, 890.75),
  cost     = c(800, 650, 1020, 500, 1400, 600),
  rating   = c("A", "B", "A", "C", "A", "B")
)
company_data
   company revenue cost rating
1 锦城科技    1200  800      A
2 蜀汉制造     980  650      B
3 天府物流    1560 1020      A
4 青羊金融     760  500      C
5 锦江生物    2100 1400      A
6 浣花实业     891  600      B

今天全程用的都是这样"现场生成"的虚构数据——好处是每个人都能一键复现,不需要提前准备任何外部文件

2.3 read_csv() vs read.csv()

▶️ 查看代码
write_csv(company_data, "company_data.csv")   # 先写出成CSV文件
company_data2 <- read_csv("company_data.csv") # 再用readr读回来
company_data2
# A tibble: 6 × 4
  company  revenue  cost rating
  <chr>      <dbl> <dbl> <chr> 
1 锦城科技   1200.   800 A     
2 蜀汉制造    980    650 B     
3 天府物流   1560.  1020 A     
4 青羊金融    760.   500 C     
5 锦江生物   2100   1400 A     
6 浣花实业    891.   600 B     

read_csv()是readr包(tidyverse核心成员之一)提供的读取函数,相较base R的read.csv():速度更快、自动识别列类型更准确、返回的是tibble(打印更整洁)。这也正是1.3所说的"代码式导入"的落地形态——同一份文件,之后每次想重新读入,运行这一行代码就够了,不必再点选一遍。

2.4 读取时的常见参数——跳过说明性表头

▶️ 查看代码
writeLines(c("本文件由财务部导出", "导出时间:2026-10-20"), "report_with_header.csv")
write_csv(company_data, "report_with_header.csv", append = TRUE, col_names = TRUE)
read_csv("report_with_header.csv")
# A tibble: 8 × 1
  本文件由财务部导出         
  <chr>                      
1 导出时间:2026-10-20       
2 company,revenue,cost,rating
3 锦城科技,1200.5,800,A      
4 蜀汉制造,980,650,B         
5 天府物流,1560.3,1020,A     
6 青羊金融,760.2,500,C       
7 锦江生物,2100,1400,A       
8 浣花实业,890.75,600,B      
▶️ 查看代码
read_csv("report_with_header.csv", skip = 2)
# A tibble: 6 × 4
  company  revenue  cost rating
  <chr>      <dbl> <dbl> <chr> 
1 锦城科技   1200.   800 A     
2 蜀汉制造    980    650 B     
3 天府物流   1560.  1020 A     
4 青羊金融    760.   500 C     
5 锦江生物   2100   1400 A     
6 浣花实业    891.   600 B     

财务人员导出的报表经常在正式表头之前多加几行说明文字,skip参数可以跳过这些干扰行,从真正的表头开始读取

警告

环境避坑提醒:中文CSV文件如果是用Excel另存的,编码常常是GBK而不是UTF-8,读取乱码时第一反应就是加上locale = locale(encoding = "GBK")参数。

2.5 write_csv()——导出

▶️ 查看代码
write_csv(company_data, "company_data_cleaned.csv")

对比第6课的write.csv():write_csv()默认就不会写入多余的行号列,也不需要额外指定row.names=FALSE,更省心

三、Excel文件的读写——readxl/writexl包

readxl/writexl包

3.1 readxl自带的示例文件

▶️ 查看代码
readxl_example()   # readxl包自带了几个示例Excel文件,方便练习
 [1] "clippy.xls"    "clippy.xlsx"   "datasets.xls"  "datasets.xlsx"
 [5] "deaths.xls"    "deaths.xlsx"   "geometry.xls"  "geometry.xlsx"
 [9] "type-me.xls"   "type-me.xlsx" 

readxl包自带的示例文件已经装在你电脑上了,不需要自己准备Excel文件也能练习读取

3.2 read_excel()——读取多工作表文件

▶️ 查看代码
sample_xlsx <- readxl_example("datasets.xlsx")
excel_sheets(sample_xlsx)              # 先看看这个Excel文件里有哪些工作表
[1] "mtcars"   "chickwts" "quakes"  
▶️ 查看代码
read_excel(sample_xlsx, sheet = "mtcars")  # 按名称指定工作表
# A tibble: 32 × 11
     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1  21       6  160    110  3.9   2.62  16.5     0     1     4     4
 2  21       6  160    110  3.9   2.88  17.0     0     1     4     4
 3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
 4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
 5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
 6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
 7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
 8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
 9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
# ℹ 22 more rows

3.3 财务场景:自己生成一份多工作表报表

▶️ 查看代码
q1_data <- data.frame(company = company_data$company, revenue = c(300,240,390,190,520,220))
q2_data <- data.frame(company = company_data$company, revenue = c(310,250,400,200,530,230))
write_xlsx(list("2026Q1" = q1_data, "2026Q2" = q2_data), "quarterly_report.xlsx")

excel_sheets("quarterly_report.xlsx")
[1] "2026Q1" "2026Q2"
▶️ 查看代码
read_excel("quarterly_report.xlsx", sheet = "2026Q1")
# A tibble: 6 × 2
  company  revenue
  <chr>      <dbl>
1 锦城科技     300
2 蜀汉制造     240
3 天府物流     390
4 青羊金融     190
5 锦江生物     520
6 浣花实业     220

3.4 Excel文件的常见"坑"

read_excel("quarterly_report.xlsx", skip = 3)    # 表头不在第一行时,跳过前面的标题/说明行
read_excel("quarterly_report.xlsx", n_max = 100) # 只读取前100行(大文件先取样看看结构)
read_excel("quarterly_report.xlsx", range = "B2:D50") # 数据不在左上角时,用range指定读取范围(对应交互式导入预览界面里的Range选项)

财务人员做的Excel报表常常有合并单元格、多级表头、汇总行穿插在数据中间——这些情况read_excel()不能自动处理,需要先在Excel里整理成"规范表格",或者读入后用项目二后续课程学的工具做清理

3.5 导出格式选择指南——数据要"送"到哪里去?

数据读进来、处理好之后,导出的格式怎么选?

场景 推荐格式 函数
交给财务同事/老师审阅 Excel write_xlsx()
数据交换(任何软件都能读) CSV write_csv()
只在R内部传递、保留完整类型信息 RDS saveRDS()

提示

经验法则:不确定用哪个?就用CSV——它是数据世界的"普通话",任何软件都能打开。RDS是R的"私有语言",好处是能完整保留因子、日期等类型信息,适合在R项目内部传递中间数据。

四、数据类型转换——给数据"验明正身"

给数据"验明正身"

4.1 拿到数据先"体检"——str()与glimpse()

▶️ 查看代码
str(company_data)       # base R的结构查看函数,第2课学过
'data.frame':   6 obs. of  4 variables:
 $ company: chr  "锦城科技" "蜀汉制造" "天府物流" "青羊金融" ...
 $ revenue: num  1200 980 1560 760 2100 ...
 $ cost   : num  800 650 1020 500 1400 600
 $ rating : chr  "A" "B" "A" "C" ...
▶️ 查看代码
glimpse(company_data)   # dplyr提供的"升级版" str(),横向展示更适合列数多的表
Rows: 6
Columns: 4
$ company <chr> "锦城科技", "蜀汉制造", "天府物流", "青羊金融", "锦江生物", "浣花实业"…
$ revenue <dbl> 1200, 980, 1560, 760, 2100, 891
$ cost    <dbl> 800, 650, 1020, 500, 1400, 600
$ rating  <chr> "A", "B", "A", "C", "A", "B"

养成习惯:任何新导入的数据,第一步永远是看一眼每一列的类型,而不是直接开始计算

4.2 as.numeric()——文本变数字

▶️ 查看代码
revenue_text <- c("1200", "1350", "980")        # 看起来像数字,实际是字符串
class(revenue_text)
[1] "character"
▶️ 查看代码
as.numeric(revenue_text)                         # 转换成真正的数值型
[1] 1200 1350  980
▶️ 查看代码
revenue_with_comma <- c("1,200", "1,350")         # 财务场景更常见:带千分位逗号
as.numeric(gsub(",", "", revenue_with_comma))     # 先用gsub()去掉逗号,再转数值
[1] 1200 1350

财务场景:从Excel读进来的"营收"列如果本该是数值却显示为字符串,十有八九是因为原始数据里带了千分位逗号或货币符号,需要先处理符号再转换

4.3 as.character()、as.factor()——数字变文本、文本变类别

▶️ 查看代码
stock_code <- c(300750, 2594)     # 股票代码:数值型,但其实不该参与数学运算
as.character(stock_code)          # 转成字符串,避免被误当成数量参与计算
[1] "300750" "2594"  
▶️ 查看代码
rating <- c("A", "B", "A", "C")
as.factor(rating)                 # 转成因子,回顾第2课的知识
[1] A B A C
Levels: A B C

财务场景:股票代码、身份证号、员工编号这类"看起来是数字但本质是标识符"的字段,通常应该存为字符串,避免被误参与加减乘除,也避免开头的0被自动去掉

4.4 日期处理——as.Date()与更省心的lubridate包

▶️ 查看代码
as.Date("2026-01-01")                          # 标准格式,直接转换
[1] "2026-01-01"
▶️ 查看代码
as.Date("2026/1/1", format = "%Y/%m/%d")        # 格式不标准时,需要指定format参数
[1] "2026-01-01"

base R的as.Date()每次都要自己写对format参数,稍不注意就会出错。lubridate包(tidyverse成员之一)提供了更省心的写法:

▶️ 查看代码
library(lubridate)
ymd("2026-01-01")   # y-m-d顺序,分隔符是"-"
[1] "2026-01-01"
▶️ 查看代码
ymd("2026/1/1")      # 分隔符换成"/",依然能识别
[1] "2026-01-01"
▶️ 查看代码
ymd("20260101")      # 完全没有分隔符,也能识别
[1] "2026-01-01"

财务场景:报表日期、交易日期是财务数据中最常见也最容易格式混乱的字段。只要日期都是"年-月-日"这个顺序,不管分隔符是-、/还是没有分隔符,ymd()都能正确识别,比每次都要纠结format参数该写"%Y/%m/%d"还是"%Y%m%d"省心很多。

警告

ymd()只能处理"年、月、日顺序一致"的情况——如果同一份数据里有的日期是"年-月-日",有的是"日/月/年"这种顺序不同的情况(不只是分隔符不同),仍然需要先分别处理、统一顺序,再用ymd()转换。

4.5 课堂练习

给定一个字符串向量c("5,000", "6,200", "4,800"),把它转换成正确的数值型向量并计算总和

五、综合实战——读入→检查→转换→导出

读入→检查→转换→导出

5.1 先制造一份"脏数据"

▶️ 查看代码
raw_data <- data.frame(
  company = c("锦城科技", "蜀汉制造", "天府物流"),
  revenue = c("1,200.50", "980.00", "1,560.30"),
  report_date = c("2026-03-31", "2026-06-30", "2026-09-30")
)
write_csv(raw_data, "raw_report.csv")

真实工作中拿到的数据往往就是这样"不规范"的——现在我们模拟一次完整的清理流程

5.2 动手实战

  • 用read_csv()读入raw_report.csv
  • 用glimpse()检查每一列的类型是否合理(revenue和report_date都应该是字符串,需要转换)
  • 用as.numeric()+gsub()处理revenue列
  • 用lubridate的ymd()处理report_date列
  • 用write_csv()把处理好的数据导出为一个新文件

5.3 课堂展示

现场邀请1-2名学生展示自己的处理流程与报告

六、课堂总结与Exit Ticket

数据准备的标准工作流

6.1 本讲知识回顾

flowchart LR
    A["CSV读写<br/>readr"] --> B["Excel读写<br/>readxl/writexl"]
    B --> C["类型转换<br/>as.numeric/ymd()等"]
    C --> D["综合实战<br/>读入→检查→转换→导出"]

    style A fill:#1a3a5c,color:#fff
    style B fill:#2e5984,color:#fff
    style C fill:#7a4419,color:#fff
    style D fill:#375623,color:#fff

CSV读写(readr) → Excel读写(readxl/writexl) → 类型转换(as.numeric/as.character/as.factor/lubridate::ymd()) → 综合实战——这是数据准备工作最基础也最重要的一条流水线

6.2 学习成果自查

对照一下,今天这五件事你都做到了吗?

  • ✓ 能够用library()加载今天用到的R包(readr、readxl、writexl、lubridate)
  • ✓ 能够用read_csv()/read_excel()读取CSV和Excel数据
  • ✓ 能够用str()/glimpse()检查数据类型
  • ✓ 能够用as.numeric()/as.character()/as.factor()完成类型转换,能够用lubridate的ymd()处理日期
  • ✓ 能够处理金额字段中的千分位逗号问题
  • ✓ 能够用write_csv()/write_xlsx()导出数据

提示

如果有没打勾的,下课后一定要找同学或老师补上,不要留到下一讲。

6.3 Exit Ticket —— 写给自己的三个问题

请花1分钟,在纸条或在线问卷上简要回答:

  1. read_csv()和base R的read.csv()相比,你觉得最大的便利是什么?
  2. 遇到"看起来是数字但读进来是字符串"的情况,你会先检查什么?
  3. lubridate的ymd()相比as.Date(),好在哪里?

课后作业

数据读写与类型转换综合练习

  1. 用R代码自己生成一份包含至少5家虚构公司的数据框(自定列,比如公司名、营收、评级),用write_csv()导出,再用read_csv()读回来,用glimpse()查看结构
  2. 构造一个包含以下"脏数据"的字符向量,并完成对应的类型转换:
    • 带千分位逗号的金额(如"3,285.94"),转换为数值型
    • 一组统一格式的日期字符串(如"2026-05-01"、"2026-06-15"),用lubridate的ymd()转换为日期型
    • 一组股票代码(数值型),转换为字符型
  3. 将练习1的数据用write_csv()导出为一个新的CSV文件,并用write_xlsx()导出为一个Excel文件
  4. 【选做】用readxl_example()查看readxl包还自带了哪些示例文件,任选一个用read_excel()读取并简单探索其结构

下讲预告

第8讲:dplyr筛选与排序——filter()、select()、arrange()与管道符

  • 管道符%>%:把多个处理步骤串起来
  • filter()筛选行、select()选列、arrange()排序
  • 把base R的"手动挡"升级成dplyr的"自动挡"

提示

课前准备:保留好今天处理好的数据文件,下节课会继续使用类似的数据。

谢谢!

第7讲:数据读写与类型转换

"In God we trust; all others must bring data." — W. Edwards Deming,质量管理与统计学家

朱 奇 | 锦城大学 · 财会学院