R语言零基础详细教程 (V2 详细版)

基于您提供的11份资料的完整学习指南

1. R语言入门基础

欢迎来到R语言的世界!R是一种专为统计计算和图形展示而生的强大语言。本章将带您完成最基础的环境配置和操作。

1.1 什么是 R 和 RStudio?

把R想象成汽车的“引擎”,它负责所有的计算和分析。而 RStudio 则是“驾驶舱”,它提供了一个用户友好的界面(代码编辑器、控制台、图形窗口等),让您能更轻松地驾驶R这台“引擎”。

对于初学者,**请始终通过 RStudio 来使用 R**。

RStudio 通常有四个窗口:

  1. 代码编辑器 (左上): 您编写和保存R脚本 (.R 文件) 的地方。
  2. 控制台 (Console) (左下): 您与R引擎实时交互的地方,代码在这里被执行。
  3. 环境/历史 (右上): 显示您当前创建了哪些变量 (环境),以及您运行了哪些命令 (历史)。
  4. 文件/图形/包 (右下): 显示您的文件、您画的图、您已安装的包。

1.2 R的基本操作

打开 RStudio 后,您会看到一个控制台 (Console) 窗口,可以把R当作一个高级计算器:

# R会忽略以 # 开头的注释行
# 这是“注释”,它不会被执行,用于解释代码
1 + 1
# [1] 2

变量赋值

在R中,我们使用 <- (小于号加减号) 来“赋值”,即把一个值存入一个变量名中。这就像给一个盒子贴上标签。

x <- 10
y <- 5

# 查看变量的值
x
# [1] 10

# 变量间可以运算
x + y
# [1] 15

# 运算结果可以存入新变量
z <- x + y

R包:R的生态核心

R的强大之处在于其拥有海量的“包” (Packages)。每个包都是一个针对特定任务(如数据整理、绘图、空间分析)的工具箱。本教程的大部分内容都依赖于特定的包。

# 1. 安装一个包(例如 'dplyr',一个数据整理包)
# 这一步一生中每个包只需做一次
# 这会从R的官方仓库 (CRAN) 下载并安装
install.packages("dplyr")

# 2. 加载一个包(在每次R会话开始时)
# 告诉R:“我这次要用 'dplyr' 工具箱里的工具”
# 这就像从工具箱里拿出工具放到工作台上
library(dplyr)
# 如果您安装了 Tidyverse (一个包的集合),可以一次性加载所有核心包
# install.packages("tidyverse")
# library(tidyverse)

2. R的核心:数据结构

(源: 4数据结构.html) R中所有的数据都存储在特定的“容器”中,这些容器就是数据结构。理解它们是R编程的第一步。

2.1 向量 (Vector)

向量是R中最基本的数据结构。它是一个包含**相同类型**元素的一维序列。使用 c() (combine) 函数来创建向量。

# 1. 数值型向量 (Numeric)
a <- c(1, 2, 5.3, 6, -2, 4)

# 2. 字符型向量 (Character)
b <- c("one", "two", "three")

# 3. 逻辑型向量 (Logical)
c <- c(TRUE, TRUE, FALSE, TRUE)

# R会强制将所有元素变为同一类型
# "a" 会被强制转换为字符型
mixed_vec <- c(1, "a", TRUE)
# mixed_vec 现在是 c("1", "a", "TRUE")

向量化操作: R的一个核心特性是“向量化”。这意味着R会自动对向量中的**每个元素**执行操作,无需您编写循环。

# a 是 c(1, 2, 5.3, 6, -2, 4)
a * 2
# [1]   2.0   4.0  10.6  12.0  -4.0   8.0

a + 10
# [1] 11.0 12.0 15.3 16.0  8.0 14.0

子集 (Subsetting): 如何从向量中取出元素?使用方括号 []

# 索引从 1 开始
a[1]
# [1] 1

# 取第 2 和 第 4 个元素
a[c(2, 4)]
# [1] 2 6

# 取除第一个之外的所有元素
a[-1]

2.2 矩阵 (Matrix)

矩阵是二维的向量,同样,所有元素必须是同一类型。

# 创建一个 3 行 2 列的矩阵
m <- matrix(data = 1:6, nrow = 3, ncol = 2)
print(m)
#      [,1] [,2]
# [1,]    1    4
# [2,]    2    5
# [3,]    3    6

# 矩阵的子集:[行, 列]
m[1, 2]  # 第 1 行, 第 2 列
# [1] 4

m[2, ]   # 第 2 行 (所有列)
# [1] 2 5

m[, 1]   # 第 1 列 (所有行)
# [1] 1 2 3

2.3 列表 (List)

列表是R中最灵活的数据结构。它像一个“袋子”,可以装**任何类型**的R对象,包括其他列表。

# 创建一个包含不同类型元素的列表
L <- list(
  name = "John Smith",
  age = 30,
  scores = c(95, 88, 100),
  is_student = TRUE,
  sub_list = list(a = 1, b = 2)
)

列表的子集: 列表的子集操作有两种方式,非常重要!

# 1. 使用 $ (推荐): 按名称访问元素
L$name
# [1] "John Smith"

# 2. 使用 [[]] (双括号): 提取元素本身
L[[1]]  # 按索引提取第 1 个元素
# [1] "John Smith"

L[["name"]] # 按名称提取元素
# [1] "John Smith"

# 3. 使用 [] (单括号): 提取的还是一个“列表”
L[1]
# $name
# [1] "John Smith"
# (注意:这返回的是一个包含 'name' 元素的子列表)

2.4 数据框 (Data Frame) 与 Tibble

数据框 (Data Frame) 是R中最重要的结构,是您在Excel或统计软件中看到的标准表格数据。它本质上是一个“列表”,但特殊之处在于它的每个元素(列)都是一个向量,且**所有列的长度必须相同**。

df <- data.frame(
  ID = 1:3,
  Name = c("Alice", "Bob", "Charlie"),
  Score = c(85, 92, 78)
)
print(df)
#   ID    Name Score
# 1  1   Alice    85
# 2  2     Bob    92
# 3  3 Charlie    78

# 数据框的子集
df$Name        # 访问 'Name' 列 (返回一个向量)
df[1, ]        # 访问第 1 行 (返回一个数据框)
df[, "Score"]  # 访问 'Score' 列 (返回一个向量)

Tibble 是数据框的现代“升级版”,来自 Tidyverse 生态 (源: 4数据结构.html)。它在打印和子集操作上更友好、更稳健。在现代R分析中,我们**强烈推荐使用 tibble**。dplyrggplot2 都更偏好使用 tibble。

Tibble vs. Data Frame 的主要区别:
  • 打印: Tibble 只会打印前 10 行和适合屏幕的列,不会淹没您的控制台。
  • 子集: tb$Name 总是有效, 但 tb[, "Name"] 返回的*仍然是一个 tibble*,而不是向量。这更可预测,不易出错。
  • 创建: 创建时不会自作主张地改变列的类型 (例如把字符串变为因子)。

3. R语言编程

掌握了数据结构,我们就可以开始学习如何用R“编程”——即控制代码的执行流程和创建可重用的工具。

3.1 控制结构 (源: 6.1控制结构.html)

控制结构允许您根据条件执行代码或重复执行代码。

条件判断 (if-else)

如果...那么...否则...。注意R中的逻辑操作符:& (和), | (或), ! (非)。

x <- 10
y <- 5

if (x > 5 & y < 10) {
  print("x 大于 5 且 y 小于 10")
} else if (x == 10 | y == 10) {
  print("x 或 y 等于 10")
} else {
  print("其他情况")
}

for 循环

当您知道需要循环的次数时,使用 for 循环。它会遍历一个序列中的每一个元素。

# 遍历 1 到 5
for (i in 1:5) {
  # i 在每次循环中会依次变为 1, 2, 3, 4, 5
  print(paste("这是第", i, "次循环"))
}

# 遍历一个字符向量
filenames <- c("file1.txt", "file2.txt", "file3.txt")
for (f in filenames) {
  print(paste("正在处理:", f))
}

while 循环

当您不知道要循环多少次,但知道何时停止时,使用 while 循环。只要条件为 TRUE,循环就会继续。

count <- 1
while (count <= 5) {
  print(paste("Count is:", count))
  count <- count + 1  # 关键:必须更新条件,否则会无限循环!
}

循环控制 (break / next)

  • break: 立刻跳出整个循环。
  • next: 跳过本次迭代,直接进入下一次循环。
for (i in 1:10) {
  if (i %% 2 == 0) {
    next  # 如果 i 是偶数 (%% 是取余),跳过本次循环
  }
  print(paste(i, "是奇数"))
  
  if (i == 7) {
    break # 当 i=7 时, 终止整个循环
  }
}
# 输出: 1 3 5 7

3.2 自定义函数 (源: !1DY函数.html)

函数是R编程的核心。它将一系列复杂操作打包成一个可重用的工具。一个好的函数应该像一个“黑匣子”:您给它输入(参数),它给您输出(返回值)。

函数定义与作用域

# 定义一个函数,名为 add_numbers
# 它有两个参数:x 和 y
add_numbers <- function(x, y) {
  # 'result' 是一个局部变量 (local variable)
  # 它只存在于函数内部
  result <- x + y
  
  # 使用 return() 显式返回结果, 这是好习惯
  return(result)
}

# 使用我们定义的函数
sum_val <- add_numbers(x = 5, y = 3)
print(sum_val)
# [1] 8

# 尝试访问函数内部的 'result' 变量
print(result)
# 错误: 找不到对象 'result'
# (因为它在函数执行完毕后就消失了)

默认参数

您可以为参数设置默认值,使其变为可选参数。

my_function <- function(arg1, arg2 = "default", arg3 = TRUE) {
  print(paste("arg1 is:", arg1))
  print(paste("arg2 is:", arg2))
  if (arg3) {
    print("arg3 is TRUE")
  }
}

my_function(arg1 = "Hello")
# [1] "arg1 is: Hello"
# [1] "arg2 is: default"
# [1] "arg3 is TRUE"

异常处理 (stop / warning)

健壮的函数应该能在用户输入错误时给出提示。

  • warning(): 给出警告,但函数继续执行。
  • stop(): 抛出错误,函数立即终止。
check_age <- function(age) {
  if (age < 0) {
    stop("年龄不能为负数!")
  }
  if (age < 18) {
    warning("用户未成年")
  }
  print("年龄检查通过")
}

check_age(15)
# [1] "年龄检查通过"
# 警告信息: 在 check_age(15) 中: 用户未成年

check_age(-5)
# 错误: 在 check_age(-5) 中: 年龄不能为负数!
# (函数停止执行)

3.3 R的“类循环”:Apply函数族 (源: 6.2类循环apply系列函数.html)

在R中,直接使用 for 循环(尤其是在大数据上)通常效率不高。R提供了更高效、更简洁的“向量化”操作和 apply 函数族,它们是R的“类循环”操作,更符合R的函数式编程思想。

lapply(X, FUN) (List Apply)

用途: 将一个函数 FUN 应用于列表 X 的**每一个元素**,并**始终返回一个列表**。

my_list <- list(a = 1:5, b = 6:10)
# my_list 是一个列表,包含两个向量

# 计算 my_list 中每个元素的平均值
mean_list <- lapply(my_list, mean)
print(mean_list)
# $a
# [1] 3
# $b
# [1] 8

sapply(X, FUN) (Simplify Apply)

用途: lapply 的“简化版”。它会尝试将 lapply 返回的列表**简化**为一个更友好的格式,例如向量或矩阵。

# 同样的操作,使用 sapply
mean_vector <- sapply(my_list, mean)
print(mean_vector)
# a b 
# 3 8 
# (结果是一个简洁的命名向量)

apply(X, MARGIN, FUN)

用途: 专门用于对矩阵 (Matrix) 或数组 (Array) 的行或列进行操作。

  • MARGIN = 1: 表示按**行**应用函数。
  • MARGIN = 2: 表示按**列**应用函数。
m <- matrix(1:9, nrow = 3, ncol = 3)
#      [,1] [,2] [,3]
# [1,]    1    4    7
# [2,]    2    5    8
# [3,]    3    6    9

# 1. 计算每一列的和 (MARGIN = 2)
col_sums <- apply(m, MARGIN = 2, FUN = sum)
# [1]  6 15 24

# 2. 计算每一行的平均值 (MARGIN = 1)
row_means <- apply(m, MARGIN = 1, FUN = mean)
# [1] 4 5 6

mapply(FUN, ...) (Multiple Apply)

用途: apply 的多参数版本,可将函数 FUN 应用于多个列表或向量的**对应元素**。

x_vals <- c(1, 2, 3)
y_vals <- c(10, 20, 30)

# 计算 x_vals[1]+y_vals[1], x_vals[2]+y_vals[2], ...
mapply(sum, x_vals, y_vals)
# [1] 11 22 33

4. 高效的数据整理 (Tidyverse)

(源: !DY5数据整理.html) 现代R数据分析的核心是 Tidyverse,这是一个包含 dplyrggplot2 等一系列包的“生态系统”。dplyr 是数据整理的核心工具,它提供了一套易读、高效的“动词”来操作数据框。

4.1 管道操作符 %>%

dplyr 的灵魂是管道操作符 %>% (读作 "then")。它允许你将代码“串联”起来,从左到右阅读,逻辑非常清晰。

x %>% f(y) 等价于 f(x, y)

想象一个汽车组装流水线:数据 %>% 第一道工序 %>% 第二道工序 %>% 成品

# 使用管道的代码 (从上到下阅读,清晰)
data %>%
  filter(col1 > 100) %>%    # 第一步:筛选数据
  group_by(col2) %>%       # 第二步:按 col2 分组
  summarize(mean_val = mean(col3)) # 第三步:计算均值

4.2 dplyr 的核心函数

我们将使用R内置的 mtcars 数据集来演示。

library(dplyr)
cars_data <- as_tibble(mtcars) # 转换为 Tibble

filter(): 按条件筛选行

选择满足特定条件的行。, 逗号等同于 & (和)。

# 筛选所有6缸 (cyl == 6) 且马力 (hp > 110) 的车
cars_data %>% 
  filter(cyl == 6, hp > 110)

# 筛选6缸 或 马力大于 200 的车
cars_data %>% 
  filter(cyl == 6 | hp > 200)

select(): 按名称选择列

选择你感兴趣的列。select() 提供了很多“助手函数”。

# 只选择 'mpg', 'cyl', 'hp' 这三列
cars_data %>% 
  select(mpg, cyl, hp)

# 选择从 'mpg' 到 'hp' 之间的所有列
cars_data %>% 
  select(mpg:hp)

# 选择所有以 'c' 开头的列 (cyl, carb)
cars_data %>% 
  select(starts_with("c"))

# 选择所有列,但把 'cyl' 和 'hp' 移到最前面
cars_data %>% 
  select(cyl, hp, everything())

arrange(): 对行进行排序

默认升序。使用 desc() 来降序排列。可以按多个列排序。

# 按 'cyl' 升序排列, 如果 'cyl' 相同, 则按 'mpg' (油耗) 降序排列
cars_data %>% 
  arrange(cyl, desc(mpg))

mutate(): 创建或修改列

mutate() 是最强大的函数之一,用于添加新列或修改现有列。

# 增加一列 'hp_per_wt' (马力/重量比)
# 并且将 'mpg' 转换为 'kpl' (公里/升)
cars_data %>% 
  mutate(
    hp_per_wt = hp / wt,
    kpl = mpg * 0.425
  ) %>%
  select(hp_per_wt, kpl, everything()) # 把新列放到最前面

group_by()summarize(): 分组汇总

这两个函数通常一起使用。group_by() 本身不做任何事,只是给数据“打上标签”,告诉R接下来的操作要按组进行。summarize() 则对这些组进行汇总统计,汇总后,数据会“折叠”成每个组一行

# 目标:按气缸数 (cyl) 分组,计算每组的平均马力 (hp) 和平均油耗 (mpg)
cars_data %>%
  group_by(cyl) %>  # 按 cyl (4, 6, 8) 分组
  summarize(
    avg_hp = mean(hp),    # 计算每组的平均 hp
    avg_mpg = mean(mpg),  # 计算每组的平均 mpg
    count = n()           # n() 是一个特殊函数,计算每组的行数
  )
  
# # A tibble: 3 × 4
#     cyl avg_hp avg_mpg count
#   <dbl>  <dbl>   <dbl> <int>
# 1     4   82.6    26.7    11
# 2     6  122.     19.7     7
# 3     8  209.     15.1    14

left_join(): 连接数据框

数据分析中,数据常常分散在多个表格中。left_join 是最常用的连接函数。

# 假设有两个数据框
df1 <- data.frame(id = 1:3, name = c("A", "B", "C"))
df2 <- data.frame(id = c(1, 2, 4), score = c(100, 90, 80))

# 以 df1 为基础 (左),将 df2 的信息匹配过来
# 匹配的“钥匙”是 'id' 列
left_join(df1, df2, by = "id")
#   id name score
# 1  1    A   100
# 2  2    B    90
# 3  3    C    NA  (因为 df2 中没有 id=3)

5. R语言数据可视化

R语言拥有无与伦比的绘图能力。主要分为两大系统:基础绘图系统和 ggplot2

5.1 基础绘图 (源: !常见图形2025.html)

R自带的绘图功能,简单快捷,适合快速探索数据。它们像是在一张“画布”上作画,第一个命令 (如 plot) 会创建画布,后续命令 (如 lines, points) 会在上面**叠加**。

# 1. plot(): 创建画布并画散点图
plot(x = cars_data$wt, y = cars_data$mpg, 
     main = "车重 vs 油耗", xlab = "车重", ylab = "油耗",
     pch = 19, col = "blue") # pch=19 是实心圆, col 是颜色

# 2. lines(): 在已有的图上添加线
# (这里添加一条简单的线性回归线)
model <- lm(mpg ~ wt, data = cars_data) # 建立线性模型
abline(model, col = "red", lwd = 2) # abline 画直线, lwd 是线宽

# 3. legend(): 添加图例
legend("topright", legend = "回归线", col = "red", lty = 1)

5.2 ggplot2 绘图 (源: !!ggplot2_2025.html)

ggplot2 是 Tidyverse 的核心绘图包,是R中最流行、最强大的绘图工具。它基于“图形语法”(Grammar of Graphics),逻辑非常严谨,允许你“图层式”地构建精美图形。

ggplot2 的核心三要素:

  1. Data (数据): 你要绘制的数据框 (data frame 或 tibble)。
  2. Aesthetics (aes(), 审美映射): 数据中的变量如何**映射**到图形属性(如 x 轴, y 轴, color, size, fill 等)。
  3. Geometries (geom_, 几何对象): 你实际看到的图形(如点 geom_point(), 线 geom_line(), 柱 geom_bar())。

映射 (aes) vs. 设置 (setting)

这是 ggplot2 最关键的概念!

  • 映射 (Mapping): 放在 aes() 内部。意思是“让**数据变量**来控制图形属性”。(例如: aes(color = cyl), 颜色**取决于** cyl 列的值)。
  • 设置 (Setting): 放在 aes() 外部。意思是“把图形属性**写死**为一个固定值”。(例如: geom_point(color = "blue"), 所有的点都是蓝色)。
# 1. 映射 (Mapping): 颜色由 'cyl' 变量决定
ggplot(cars_data, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point()

# 2. 设置 (Setting): 所有的点都是蓝色
ggplot(cars_data, aes(x = wt, y = mpg)) +
  geom_point(color = "blue")

# 3. 错误的做法 (ggplot会懵掉)
ggplot(cars_data, aes(x = wt, y = mpg, color = "blue")) +
  geom_point()
# (这会创建一个名为 "blue" 的图例, 而不是把点设为蓝色)

图层 (Layers)

ggplot2 使用 + 号来添加图层。你可以随意组合它们。

ggplot(data = cars_data, mapping = aes(x = wt, y = mpg)) +
  geom_point(aes(color = factor(cyl))) + # 第1层: 带颜色的点
  geom_smooth(method = "lm") +           # 第2层: 添加线性回归平滑线
  labs(                                  # 第3层: 修改标签
    title = "车重 vs 油耗 (按气缸数分组)",
    x = "车重 (1000 lbs)",
    y = "油耗 (MPG)",
    color = "气缸数"
  ) +
  theme_minimal()                        # 第4层: 应用一个简洁的主题

分面 (Faceting)

ggplot2 的另一个强大功能是分面,即“小图表”。

# 目标:按 'cyl' (气缸数) 绘制 3 个独立的子图
ggplot(cars_data, aes(x = wt, y = mpg)) +
  geom_point() +
  facet_wrap(~ cyl) # 按 'cyl' 变量分面
  
# (这会为 4缸、6缸、8缸的车分别创建一个散点图)

6. 专题:文本处理

(源: !R语言文本处理.html) R 语言也提供了强大的文本处理能力。虽然现代分析更推荐使用 stringr 包(Tidyverse的一部分),但R的基础函数是理解文本处理的基石。

nchar(): 计算字符数

nchar("Hello")
# [1] 5

paste() / paste0(): 字符串拼接

paste0()paste(..., sep = "") 的简写,即将所有元素无缝拼接。

paste("Hello", "world", "!")
# [1] "Hello world !" (默认用空格分隔)

paste0("Hello", "world", "!")
# [1] "Helloworld!" (无分隔符)

strsplit(): 字符串拆分

根据分隔符将一个字符串拆分为一个列表。

text <- "a,b,c;d,e"
strsplit(text, split = ";")
# [[1]]
# [1] "a,b,c" "d,e"
# (返回的是一个列表,因为一个字符串可能被拆成多块)

模式匹配 (正则表达式)

正则表达式是匹配文本模式的强大语言。例如 \\d 匹配数字, . 匹配任意字符。

grep() / grepl(): 查找匹配项

  • grep(): 返回匹配项的**索引**或**值**。
  • grepl(): 返回一个**逻辑值 (TRUE/FALSE)**,非常适合用于 filter()
filenames <- c("report_2023.csv", "data_final.txt", "report_2024.csv", "image.png")

# 1. 检查哪些文件名包含 "report"
grepl("report", filenames)
# [1]  TRUE FALSE  TRUE FALSE

# 2. 查找所有以 ".csv" 结尾的文件名
# $ 符号在正则表达式中表示“结尾”
grep("\\.csv$", filenames, value = TRUE)
# [1] "report_2023.csv" "report_2024.csv"

sub() / gsub(): 替换匹配项

  • sub(): 替换**第一个**匹配项。
  • gsub(): (Global Sub) 替换**所有**匹配项。
text <- "Hello_world_this_is_R"
# 2. 将所有 "_" 替换为空格
gsub(pattern = "_", replacement = " ", x = text)
# [1] "Hello world this is R"

7. 专题:空间数据分析

(源: 空间数据.html, !!terra处理矢量数据.html, !!!13-矢量数据读取与处理2025-1.html)

R在地理信息系统 (GIS) 领域也非常强大。R中有两个处理空间数据的现代包:

  • sf (Simple Features): 处理矢量数据(点、线、面)的王者。它将空间数据存储为一种特殊的数据框,与 Tidyverse 完美兼容。
  • terra: 处理栅格数据(如遥感影像、DEM)的王者。它也具有强大的矢量数据处理能力,是 rastersp 包的继任者。

您提供的资料 `!!!13...` 详细介绍了 sf 包的函数,我们将重点讲解它,因为它在矢量分析中更为主流和易用。

7.1 `sf` 包入门

library(sf)

sf 对象的核心就是一个 data.frame,它增加了一个特殊的 geometry 列。这意味着您在第 4 节学到的**所有 dplyr 动词** (filter, mutate, select, left_join...) 都可以**直接**用在 sf 对象上!

数据读取与坐标系 (CRS)

sf 可以使用 st_read() 读取几乎所有的矢量数据格式 (如 Shapefile, GeoPackage)。

# 使用 sf 自带的北卡罗来纳州数据
nc_path <- system.file("gpkg/nc.gpkg", package="sf")
nc <- st_read(nc_path)

print(nc)
# Simple feature collection with 100 features and 14 fields
# Geometry type: MULTIPOLYGON
# ...
# CRS:           NAD27 (EPSG:4267)

# 查看坐标系 (CRS)
st_crs(nc)
# NAD27 (EPSG:4267) - 这是一个地理坐标系,单位是“度”。
警告:坐标系 (CRS) 101

地理坐标系 (Geographic CRS), 如 EPSG:4267 (NAD27) 或 EPSG:4326 (WGS84),使用“度”(经纬度)作为单位。它们在球体上定义位置,但**不适合**计算面积或距离。因为 1 度的经度在赤道和在北极代表的实际距离是天差地别的。

投影坐标系 (Projected CRS), 如 EPSG:32617 (UTM 17N),使用“米”作为单位。它们将地球“压平”到二维平面上,非常适合计算准确的面积和距离。

结论:在进行任何空间计算 (st_area, st_length, st_buffer) 之前,必须使用 st_transform() 将数据转换为投影坐标系!

# 转换 CRS 到一个以“米”为单位的投影坐标系 (例如 UTM)
nc_utm <- st_transform(nc, crs = 32617) # 32617 是 UTM 17N 的 EPSG 代码
st_crs(nc_utm)
# ... (现在单位是米)

7.2 提取对象几何信息 (源: !!!13...)

以下是 sf 包中用于提取几何属性的核心函数,这些操作都应在投影坐标系下进行

st_area() / st_length(): 计算面积/周长

# 计算面积 (单位:平方米)
nc_utm$area_sqm <- st_area(nc_utm)

# 计算周长 (单位:米)
nc_utm$perimeter_m <- st_length(nc_utm)

# 我们可以用 dplyr::mutate 来做同样的事
nc_utm <- nc_utm %>%
  mutate(
    area_sqkm = as.numeric(st_area(.)) / 1000000, # 转换为平方公里
    perimeter_km = as.numeric(st_length(.)) / 1000 # 转换为公里
  )

st_centroid(): 提取质心

计算每个几何对象的质心,返回点 (POINT) 对象。这对于为多边形创建标签位置非常有用。

county_centers <- st_centroid(nc_utm)
# 几何类型现在是 POINT

st_geometry() / st_drop_geometry()

  • st_geometry(): 仅提取几何列 (sfc)。
  • st_drop_geometry(): 移除几何列,将其变回一个普通的 data.frame

st_coordinates(): 提取坐标

对于点,返回 X, Y 坐标。对于多边形/线,返回构成它们的所有顶点的坐标。

st_boundary(): 提取边界

将多边形 (POLYGON) 转换为其边界线 (LINESTRING)。

st_voronoi(): 生成泰森多边形

泰森多边形(Voronoi 图)是一种空间剖分。对于一组点,它会生成一系列多边形,使得每个多边形内的区域到其对应的点最近。

# 使用上面的质心点来创建泰森多边形
voronoi_polygons <- st_voronoi(st_geometry(county_centers))

7.3 空间操作与叠置分析 (Overlay) (源: !!!13...)

这是 GIS 的核心,用于组合两个空间图层 (xy)。

st_buffer(x, dist): 缓冲区分析

创建一个围绕 x 的缓冲区,距离为 dist (单位与CRS一致,这里是米)。

# 1. 取第一个县的质心
center_point <- st_centroid(st_geometry(nc_utm)[1])
# 2. 创建一个半径 100 公里 (100000米) 的缓冲区
buffer_circle <- st_buffer(center_point, dist = 100000)

plot(st_geometry(buffer_circle))
plot(st_geometry(center_point), add=TRUE, col="red")

st_intersection(x, y): 空间交集

这是最常用的叠置操作。它返回两个图层中**重叠**的部分。想象一下,用 y 图层作为“饼干切割器”去切割 x 图层。输出的几何是重叠部分,属性是两个图层属性的合并。

# 3. 用这个圆去“切割” nc_utm
intersected_counties <- st_intersection(nc_utm, buffer_circle)

# 绘制结果
plot(st_geometry(nc_utm), border = 'grey')
plot(st_geometry(intersected_counties), col = 'red', add = TRUE)
plot(st_geometry(buffer_circle), border = 'blue', add = TRUE)

st_difference(x, y): 空间差集

x 中减去 y 的部分,即 x 中**不**与 y 重叠的部分。

# 减去缓冲区
difference_counties <- st_difference(nc_utm, buffer_circle)

st_crop(x, y): 裁剪

用一个矩形范围 (y 可以是一个 st_bbox 对象) 来快速裁剪 x。比 st_intersection 更快,但只适用于矩形。

# 创建一个裁剪框
crop_box <- st_bbox(c(xmin = 0, xmax = 200000, ymin = 100000, ymax = 200000), crs = st_crs(nc_utm))
cropped_data <- st_crop(nc_utm, crop_box)

7.4 terra 包简介 (源: !!terra...)

虽然 sf 擅长矢量,但 terra 在处理栅格数据时是必须的。terra 也可以处理矢量数据,其函数名通常是动词(如 intersect, buffer, crop),而 sf 的函数名是名词(如 st_intersection, st_buffer, st_crop)。

library(terra)
# 将 sf 对象转换为 terra 的 SpatVector 对象
nc_spatvector <- vect(nc_utm)

# terra 的函数
# intersected_terra <- terra::intersect(nc_spatvector, vect(buffer_circle))
# buffer_terra <- terra::buffer(nc_spatvector, width = 10000)
sf vs. terra (矢量处理):
  • sf (首选): Tidyverse 原生集成。使用 dplyr 操作 sf 对象非常流畅。可读性强。
  • terra: 性能怪兽。在处理超大规模矢量数据 (百万级别多边形) 时,terra 的 C++ 底层通常比 sf 更快。但它的语法与 Tidyverse 不同。