未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适用于学习 R 多格式读写、列类型与编码控制、数据库分批查询及基础 API 请求,不是可直接部署的生产数据接入方案。本次仅作结构审阅,未进行外部事实核验或代码运行验证;性能表、提速数字及按文件大小给出的选型建议只能作为评估起点,不能作为实测排名或容量保证。示例文件、数据库表和 API 地址需自行准备,远程 dplyr 查询需 dbplyr 支持,命令读取示例依赖相应系统工具。API 重试仅展示基础机制,实际接入仍需补充认证、状态分类、限流响应与结果结构检查;其中“非2xx 自动抛出错误”的注释不应作为严格成功状态校验的依据。write_excel_csv 的 BOM 有助于 Excel 识别编码,但效果仍受版本和导入方式影响。

R 数据导入导出

数据分析的起点是把数据读进来,终点是把结果输出去。R 生态为各种格式提供了成熟的读写工具。本文覆盖 CSV/文本、Excel、大文件(data.table)、数据库(DBI)、API(httr + jsonlite)以及 R 原生格式,并给出各方案的性能对比和选型建议。

目录


CSV 与文本文件

基础包 read.csv / read.table

R 内置的读取函数,无需安装额外包,适合小型文件的快速读取。

# read.csv 是 read.table 的特化版(sep=",",header=TRUE)
df <- read.csv("data.csv")

# read.table 参数全集(最常用的几个)
df <- read.table(
  file        = "data.txt",
  header      = TRUE,            # 第一行是否为列名,默认FALSE
  sep         = "\t",            # 分隔符:"\t"=制表符,","=逗号," "=空格
  fileEncoding = "UTF-8",        # 输入文件编码,用于读取时转码
  na.strings  = c("", "NA", "NULL", "N/A", "缺失"),  # 哪些字符串视为NA
  colClasses  = c("character", "numeric", "Date"),    # 预设列类型(加速+防猜错)
  nrows       = 10000,           # 只读前N行(调试或抽样时有用)
  skip        = 2,               # 跳过开头N行
  quote       = '"',             # 引号字符
  comment.char = "#",            # 注释行标记
  stringsAsFactors = FALSE       # R3.x 必须显式设置
)

tip `colClasses` 参数是加速 `read.table` 的关键技巧。预设列类型可减少类型推断工作;若以“100 万行 CSV 提速 30% 以上”为测试假设,应在实际数据和运行环境中测量,本文未提供支持该数值的基准。

readr 包:更快更智能的现代替代

readr 是 tidyverse 的文件读写组件,相比基础包有三个核心优势:更快(C++ 实现)、类型推断更准确、进度条可视化。

library(readr)

# read_csv 返回 tibble(而非 data.frame)
df <- read_csv("data.csv")

# 关键参数详解
df <- read_csv(
  "data.csv",
  col_names = TRUE,              # 第一行为列名(默认TRUE)
  col_types = cols(              # 精确指定列类型
    id     = col_character(),
    age    = col_integer(),
    salary = col_double(),
    date   = col_date(format = "%Y-%m-%d"),
    active = col_logical()
  ),
  na        = c("", "NA", "null"),
  skip      = 0,
  n_max     = Inf,               # 最大读取行数
  locale    = locale(
    encoding  = "UTF-8",         # 文件编码
    decimal_mark  = ".",         # 小数点符号(欧洲数据常用",")
    grouping_mark = ","          # 千分位符号
  )
)

# 快捷函数
read_tsv("data.tsv")             # 制表符分隔
read_delim("data.txt", delim="|") # 自定义分隔符

# 输出
write_csv(df, "output.csv")
write_csv(df, "output.csv", na = "")  # NA 输出为空字符串而非"NA"

col_types 规范速查:

函数 类型 示例格式
col_character() 字符串 -
col_integer() 整数 123
col_double() 浮点数 3.14
col_logical() 逻辑值 TRUE/FALSE/T/F/1/0
col_date(format) 日期 "%Y-%m-%d"
col_datetime(format) 日期时间 "%Y-%m-%d %H:%M:%S"
col_skip() 跳过此列 -
col_guess() 自动推断(默认) -

Excel 文件

readxl 读取

library(readxl)

# 基础读取(默认第一个工作表)
df <- read_excel("data.xlsx")

# 完整参数
df <- read_excel(
  path      = "data.xlsx",
  sheet     = "销售数据",        # 工作表名(或1,2等序号)
  range     = "A1:F100",        # 精确读取范围(Cell Range)
  col_names = TRUE,
  col_types = c("text", "numeric", "date", "guess", "guess", "guess"),  # 与 A1:F100 的六列对应,类型需按实际数据调整;可用 "skip" 跳过列
  na        = c("", "NA"),
  skip      = 3,                 # 未指定 range 时跳过前3行;本例指定 range 后,skip 和 n_max 不控制读取边界
  n_max     = Inf,
  trim_ws   = TRUE               # 自动去除字符串两端空格
)

# 列出所有工作表
excel_sheets("data.xlsx")

# 批量读取所有工作表
sheets <- excel_sheets("data.xlsx")
all_data <- lapply(sheets, function(s) read_excel("data.xlsx", sheet = s))
names(all_data) <- sheets

# 写入 Excel
library(writexl)
write_xlsx(df, "output.xlsx")

# 写入多个 Sheet
write_xlsx(list("Sheet1" = df1, "Sheet2" = df2), "multi_sheet.xlsx")

note readxl 不依赖 Java(不像 xlsx 包),安装更简单,跨平台兼容性更好。如果需要设置单元格格式、公式、样式,改用 `openxlsx` 包。


大文件高速读取:data.table::fread

当文件超过 500MB 或行数超过 500 万时,可优先评估 fread,同时检查可用内存和数据类型。它支持自动检测分隔符和多线程读取;“比 read.csv 快 10–50 倍”仅可作为待测试的假设,本文未提供支持这一范围的基准。

library(data.table)

# 最简用法(自动检测分隔符)
dt <- fread("bigdata.csv")

# 关键参数
dt <- fread(
  input     = "bigdata.csv",
  sep       = "auto",            # 自动检测,也可指定","或"\t"
  header    = TRUE,
  nThread   = 8,                 # 并行线程数(建议设为 CPU 核心数)
  nrows     = -1L,               # -1L 表示全部行
  skip      = 0,
  select    = c("id","name","salary"),  # 只读指定列(节省内存)
  # drop = c("col_to_drop"),    # 如需排除列,先移除上面的 select 参数,再启用 drop
  colClasses = list(
    character = "id",
    integer   = "age"
  ),
  na.strings = c("", "NA"),
  encoding  = "UTF-8",           # 或 "Latin-1"
  verbose   = FALSE              # 是否打印读取信息
)

# fread 返回 data.table,可无缝转成 data.frame
df <- as.data.frame(dt)

# 写入(fwrite 同样多线程,速度极快)
fwrite(dt, "output.csv", nThread = 8)

fread 的特殊能力:

# 直接读取命令输出(管道输入)
dt <- fread(cmd = "grep '2024' large_log.txt")

# 直接读取 URL
dt <- fread("https://example.com/data.csv")

# 读取压缩文件(需要系统有 zcat/gzip)
dt <- fread(cmd = "zcat data.csv.gz")

数据库连接

R 通过 DBI(DataBase Interface)包提供统一的数据库操作接口,底层驱动按数据库类型选择。

连接 - 查询 - 断开的标准模式

library(DBI)
library(RSQLite)   # SQLite 驱动

# 1. 建立连接
con <- dbConnect(
  drv  = SQLite(),
  dbname = "mydb.sqlite"
)

# MySQL 连接(需要 RMySQL 或 RMariaDB 包)
# con <- dbConnect(RMySQL::MySQL(),
#   host     = "192.168.1.100",
#   port     = 3306,
#   dbname   = "sales_db",
#   user     = "analyst",
#   password = Sys.getenv("DB_PASSWORD")  # 密码从环境变量读取,不硬编码
# )

# PostgreSQL 连接(需要 RPostgres 包)
# con <- dbConnect(RPostgres::Postgres(),
#   host = "localhost", dbname = "mydb",
#   user = "postgres", password = "xxx"
# )

# 2. 探查数据库结构
dbListTables(con)          # 列出所有表
dbListFields(con, "users") # 列出 users 表的所有字段
dbExistsTable(con, "sales") # 检查表是否存在

# 3. 查询数据
# 小结果集:一次性读取
df <- dbGetQuery(con, "SELECT * FROM users WHERE age > 30 LIMIT 100")

# 大结果集:分批读取
res <- dbSendQuery(con, "SELECT * FROM transactions WHERE year = 2024")
while (!dbHasCompleted(res)) {
  chunk <- dbFetch(res, n = 10000)  # 每次读1万行
  # 处理 chunk...
}
dbClearResult(res)

# 读取整张表
df <- dbReadTable(con, "products")

# 4. 写入数据
dbWriteTable(con, "cleaned_data", df,
  overwrite = TRUE,     # 覆盖已有表
  append    = FALSE     # 追加到已有表
)

# 执行 DDL/DML
dbExecute(con, "CREATE INDEX idx_user_id ON orders(user_id)")
dbExecute(con, "UPDATE users SET status='active' WHERE last_login > '2024-01-01'")

# 5. 断开连接(必须执行,否则连接池泄漏)
dbDisconnect(con)

warning 永远不要在代码中硬编码数据库密码。使用 `Sys.getenv("DB_PASSWORD")` 从环境变量读取,或用 `keyring` 包管理凭证。

结合 dplyr 的懒查询

library(dplyr)

con <- dbConnect(SQLite(), "mydb.sqlite")
users_tbl <- tbl(con, "users")    # 创建远程表引用(不立即执行)

result <- users_tbl %>%
  filter(age > 25) %>%
  select(name, salary) %>%
  arrange(desc(salary)) %>%
  head(100)

# 查看 dplyr 生成的 SQL
show_query(result)

# 真正执行并拉到内存
df <- collect(result)
dbDisconnect(con)

API 数据获取

httr + jsonlite 的标准模式

library(httr)
library(jsonlite)

# GET 请求
resp <- GET(
  url   = "https://api.example.com/v1/data",
  query = list(city = "Beijing", limit = 100),  # URL 查询参数
  add_headers(
    Authorization = paste("Bearer", Sys.getenv("API_TOKEN")),
    Accept        = "application/json"
  ),
  timeout(30)  # 30秒超时
)

# 检查状态码
stop_for_status(resp)   # 非2xx 自动抛出错误

# 解析响应
raw_text <- content(resp, "text", encoding = "UTF-8")
data     <- fromJSON(raw_text)           # 解析 JSON
df       <- as.data.frame(data$results) # 取出数据字段

# POST 请求(提交 JSON 体)
resp_post <- POST(
  url  = "https://api.example.com/v1/query",
  body = list(start_date = "2024-01-01", metrics = c("sales", "orders")),
  encode = "json",        # 自动序列化为 JSON
  add_headers(Authorization = paste("Bearer", Sys.getenv("API_TOKEN")))
)

基础 API 重试封装(示例,不构成生产可用性保证)

# 带重试逻辑的安全请求函数
safe_api_get <- function(url, params = list(), max_retries = 3) {
  for (attempt in 1:max_retries) {
    tryCatch({
      resp <- GET(url, query = params, timeout(30))
      stop_for_status(resp)
      return(fromJSON(content(resp, "text", encoding = "UTF-8")))
    }, error = function(e) {
      message(sprintf("第%d次尝试失败:%s", attempt, e$message))
      if (attempt < max_retries) Sys.sleep(2 ^ attempt)  # 指数退避
      else stop(e)
    })
  }
}

R 原生格式:RDS 与 RData

CSV 和 Excel 无法保留 R 对象的完整状态(因子水平、属性、模型对象等)。R 原生格式解决了这个问题。

saveRDS / readRDS(推荐)

一次保存一个对象,加载时可以赋给任意变量名:

# 保存(自动 gzip 压缩)
saveRDS(model_object, "model_v1.rds")
saveRDS(large_df,     "data_cleaned.rds", compress = "xz")  # 更高压缩率

# 读取(赋给新变量名,不污染全局环境)
my_model <- readRDS("model_v1.rds")
df       <- readRDS("data_cleaned.rds")

# 验证完整性保留
x <- factor(c("低","中","高"), levels = c("低","中","高"), ordered = TRUE)
saveRDS(x, "factor_test.rds")
y <- readRDS("factor_test.rds")
identical(x, y)         # TRUE:水平、顺序、类型全部保留

save / load(多对象打包)

同时保存多个对象到一个 .RData 文件,加载时变量名自动恢复:

# 保存多个对象
save(df_train, df_test, model, params, file = "experiment_01.RData")

# 加载(自动在当前环境创建 df_train, df_test, model, params)
load("experiment_01.RData")

# 保存整个工作区(不推荐,边界模糊)
save.image("workspace.RData")

warning `load()` 会直接向当前环境注入变量,可能覆盖同名对象,且你无法提前知道里面有什么。优先使用 `readRDS()`,它的行为更可预测。

RDS vs RData 对比:

特性 saveRDS / readRDS save / load
保存对象数 单个 多个
加载时变量名 自由指定 固定为保存时的名字
推荐场景 模型、数据框的持久化 打包整个实验状态
文件扩展名 .rds .RData / .rda

编码问题处理

中文数据是 R 用户绕不开的问题,核心是 UTF-8 与 GBK 的互转。

# 1. 读取时指定编码
df_utf8 <- read.csv("data_utf8.csv", fileEncoding = "UTF-8")
df_gbk  <- read.csv("data_gbk.csv",  fileEncoding = "GBK")   # Windows 中文常见

# readr 版(推荐)
df <- read_csv("data.csv", locale = locale(encoding = "GBK"))

# 2. iconv() 转换字符向量编码
x_gbk <- "\xd5\xc5\xc8\xfd"           # GBK 编码的"张三"
x_utf8 <- iconv(x_gbk, from = "GBK", to = "UTF-8")

# 转换整列
df$name <- iconv(df$name, from = "GBK", to = "UTF-8")

# 3. 写出时指定编码
write.csv(df, "output.csv", fileEncoding = "UTF-8", row.names = FALSE)
# row.names=FALSE 是写 CSV 必须加的参数,否则会多出一列行号

# 4. 检测文件编码(依赖 readr 的探测)
guess_encoding("unknown_encoding.csv")

# 5. Windows 打开 UTF-8 CSV 乱码的解决方法
# 用 write_excel_csv(自动在文件头加 BOM 标记)
write_excel_csv(df, "for_excel.csv")  # 添加 UTF-8 BOM,有助于 Excel 识别编码;效果仍取决于版本及导入方式

tip 默认编码取决于操作系统、区域设置和 R 运行环境;Windows 中文环境中的旧文件可能使用 GBK,现代环境也可能使用 UTF-8,不能仅凭操作系统判断文件编码。在团队协作中,统一约定用 UTF-8,并在读写时显式指定,可避免大多数乱码问题。


读取方式性能对比

以下数值仅作为读取 100 万行 × 10 列 CSV 文件(假设约 80MB)的性能比较示意,不是已提供测试依据的典型耗时;列内容会影响文件大小,实际耗时和内存峰值需自行测量:

函数 包 耗时(参考值) 内存峰值 返回类型
read.csv() base ~25s 高 data.frame
read_csv() readr ~3s 中 tibble
fread() data.table ~0.5s 低 data.table
fread(nThread=8) data.table ~0.2s 低 data.table

note 上表数值是待测试的示意假设,不代表实测量级或工具排名。具体耗时和内存峰值取决于硬件、数据内容、操作系统及读取参数;可在实际文件上比较 `read_csv` 与 `fread`,结合类型控制和使用便利性选择工具,不能仅凭文件超过 100MB 判断速度优势。

选型建议:

  • 文件 < 50MB:read_csv(),易用、类型智能
  • 文件 50MB - 1GB:fread(),速度优先
  • 文件 > 1GB:fread(nThread=n) 或直接用数据库/数据湖
  • 需要精确列类型控制:read_csv(col_types=...) 或 fread(colClasses=...)

格式选型指南

格式 写入函数 读取函数 适用场景 缺点
CSV write_csv() read_csv() / fread() 数据交换、跨语言 类型信息丢失、大文件慢
Excel write_xlsx() read_excel() 与业务人员协作 不适合大数据、商业软件
RDS saveRDS() readRDS() R 对象持久化 仅 R 可读
RData save() load() 多对象打包 加载时自动注入变量
SQLite dbWriteTable() dbGetQuery() 本地结构化存储 并发写入有限
JSON write_json() fromJSON() API 数据、嵌套结构 大数据效率低

掌握检查

  • [ ] read.csv 的 colClasses 参数有什么作用?什么情况下必须使用?
  • [ ] read_csv 和 read.csv 的主要区别有哪些?
  • [ ] fread(nThread=8) 比单线程快的原理是什么?
  • [ ] dbGetQuery 和 dbSendQuery + dbFetch 各适合什么场景?
  • [ ] saveRDS 和 save 在加载时的行为有何本质区别?

练习题

题一(基础):从网络读取一个公开 CSV 数据集(如 UCI 机器学习库的任意数据集),用 read_csv 读入,检查列类型推断是否正确,对类型有误的列用 col_types 修正,最后用 write_csv 保存,用 fread 重新读入,对比两种读法的耗时(用 system.time())。

题二(进阶):在 R 中创建一个 SQLite 数据库,将两张有关联的数据表写入(如用户表和订单表),然后用 SQL JOIN 查询每个用户的订单总金额,结果用 collect() 拉到内存,最后 saveRDS 保存为 .rds 文件。


关联文档:R数据结构 | dplyr数据操作 | 数据清洗技巧

创建时间:2024-12-11 | 更新时间:2026-03-21 预计学习时长:60-75分钟

#R语言 #数据导入 #数据导出 #CSV #Excel #数据库 #API #readr #data.table #DBI #编码