4. 数据导入与导出

做数据分析,第一步往往是跟数据本身打交道。你想想看,项目里最常遇到的场景就是:别人丢给你一个CSV、Excel,或者让你从数据库拉一张表,然后你才能开始清洗和建模。我个人习惯把这部分叫「数据管道的前置工程」——搞不好这个环节,后面再牛的分析模型也白搭。

嗯,先讲最基础的。

4.1 CSV 文件的读写

CSV 是通用格式。R 提供了 read.csv() 和 write.csv(),但性能差异很大。我以前做过一个几十万行、几百列的统计报表,直接用 read.csv() 跑了快一分钟,换成 data.table::fread() 三秒搞定。所以,我建议你从一开始就用 fread。

推荐方法:data.table::fread() 和 data.table::fwrite()。自动识别分隔符、引号、编码,速度是基础函数的 5~50 倍。

# 基础写法(不推荐大数据用)
df <- read.csv("data.csv", stringsAsFactors = FALSE)
write.csv(df, "output.csv", row.names = FALSE)

# 黄金组合
library(data.table)
df <- fread("data.csv", encoding = "UTF-8")
fwrite(df, "clean_data.csv", row.names = FALSE)

小技巧:用 fread 时加上 select 参数可以只读部分列,内存吃得消很多。例:fread("big.csv", select = c("id", "value"))。

4.2 Excel 文件的导入与导出

Excel 在业务部门里是「常青树」。但 R 读 Excel 曾经很痛苦——readxl 包只读不写,XLConnect 依赖 Java 容易出幺蛾子。我踩过最大的坑是:有一次用 XLConnect 跑生产环境,客户服务器没装 Java 8,整个脚本崩了。

现在我的标配是 openxlsx 包,不依赖 Java,读写都支持,还能保留格式。

library(openxlsx)

# 读 Excel
sales <- read.xlsx("sales_report.xlsx", sheet = 1)

# 写 Excel(可创建多个 sheet)
write.xlsx(list(Sheet1 = df1, Summary = df2), 
           file = "output.xlsx", 
           rowNames = FALSE)
包读写依赖我的评价
readxl✔✘无只读可用,写要搭配其他包
openxlsx✔✔无生产环境首选
XLConnect✔✔Java容易因 Java 环境出问题,慎用
writexl✘✔无纯写 Excel 可以,但功能少

注意:openxlsx 写大文件时内存占用较高。如果数据量超过 10 万行×100 列,建议先用 fwrite 导出 CSV,再用 Excel 打开转存。

4.3 数据库连接(MySQL / PostgreSQL / SQLite)

数据库是数据工程师的老本行。R 连接数据库主要靠 DBI + 具体驱动的组合。我个人经验:odbc 包在 Windows 上配置最省心,RMySQL 和 RPostgres 在 Linux 上更稳定。

你想想看,如果每次分析前都要全表拉数据,网络开销和内存压力都很大。所以我建议你在 SQL 层面做好过滤和聚合,只拉需要的数据到 R 里。

library(DBI)
library(RMySQL)

# 连接 MySQL
con <- dbConnect(
  RMySQL::MySQL(),
  host = "localhost",
  dbname = "sales_db",
  user = "analyst",
  password = "yourpass"
)

# 只拉本月数据,避免全表传输
query <- "SELECT date, product, amount 
           FROM transactions 
           WHERE sale_date >= '2025-03-01'"
df <- dbGetQuery(con, query)

# 别忘了关闭连接
dbDisconnect(con)

性能提示:使用 dbSendQuery + dbFetch 分块读取超大结果集,配合 n = 10000 参数。我遇到过一次 500 万行的订单表,分块读比一次性读少浪费一半内存。

4.4 R 原生格式:RDS 与 RData

如果你只是做个人项目或者团队内部共享中间结果,用 R 原生的 .rds 或 .RData 格式最好。它们保留数据类型、因子顺序、甚至 S3 对象结构。我项目里处理完的特征矩阵,从来都是存成 .rds,读出来直接能用,不用再转类型。

# 存单个对象
saveRDS(cleaned_data, "phase1_clean.rds")

# 读回来
cleaned_data <- readRDS("phase1_clean.rds")

# 存多个对象
save(df_model, model_fit, metrics, file = "results.RData")
load("results.RData")
格式特点适用场景
.rds只存一个对象,命名自由特征矩阵、模型对象
.RData可存多个对象,保留变量名工作空间快照
.parquet列式存储,压缩好,跨语言大数据、供 Python/Spark 使用

我个人的习惯:每次跑完特征工程,第一时间用 saveRDS 备份一份。万一后面模型调试崩了,不用重跑半天特征生成。

4.5 批量导入与导出流程

实际项目中数据往往不是单个文件,而是「数据湖」——几十个 CSV、每天更新。我设计过一套批量导入函数,核心逻辑就是自动识别目录下的文件模式。

library(data.table)

# 批量读同一目录下的所有 CSV
files <- list.files("raw_data/", pattern = "*.csv$", full.names = TRUE)
big_list <- lapply(files, fread)

# 合并成一个 data.table
all_data <- rbindlist(big_list, use.names = TRUE, fill = TRUE)

# 批量导出多个数据框到不同 sheet
library(openxlsx)
sheet_list <- list(Jan = jan_data, Feb = feb_data, Mar = mar_data)
write.xlsx(sheet_list, file = "quarter_report.xlsx")

曾经踩过的坑:不同 CSV 的列顺序不一致,直接 rbind 会错位。所以我用 rbindlist 时一定指定 use.names = TRUE 和 fill = TRUE,前者按名字匹配,后者遇到缺失列自动补 NA。

好了,梳理一下本章知识体系。我画了一张图,方便你直观理解数据导入导出的路径选择。

R 数据导入导出路径图 R 数据导入/导出 导入(读取) fread read.xlsx DBI + SQL readRDS 导出(写入) fwrite write.xlsx dbAppend saveRDS 按文件格式和性能需求选择合适的读写组合

图里左边是常用导入方式,右边是导出方式。核心就一条:选对工具,省时省内存。你如果在项目里遇到文件特别大、或者连接数据库特别慢的情况,多半是这一步没选对。

最后强调一点:数据导出前一定要检查编码。我碰到过好几次用 write.csv 导出中文后,Excel 打开乱码。解决方案很简单——fwrite 指定 encoding = "GBK" 或者用 openxlsx 写 Excel 文件就不会有编码问题。

终极建议:如果你的数据最终要给别人用(尤其是非技术人员),优先考虑 Excel 输出 + 友好的列名。把列名从下划线命名改成中文或者驼峰,再加一行注释 sheet,能省掉无数沟通成本。