如何在 R 语言中标准化数据框的列:从基础原理到代码实战

你是否曾经遇到过这样的情况:手里拿着一个大型数据集,里面的数据列千差万别?比如有一列记录的是“年龄”(范围大概在 10 到 60 之间),另一列记录的是“收入”(范围可能在 2000 到 100000 之间)?当你试图对这些数据应用某些机器学习算法(如 K-均值聚类或主成分分析 PCA)时,这些数量级的巨大差异往往会导致模型产生误导性的结果,因为算法会误以为数值较大的列(如收入)比数值较小的列(如年龄)更重要。

在进一步处理数据之前,解决这个问题的关键步骤通常就是数据标准化。在这篇文章中,我们将深入探讨如何在 R 语言中标准化数据框的列,不仅会告诉你“怎么做”,还会解释“为什么这么做”,并分享一些在实战中非常有用的技巧。

什么是标准化?

首先,让我们来理清一下概念。标准化是一种非常常用的特征缩放技术。简单来说,它是对数据进行重新缩放的过程,使得处理后的数据满足以下两个条件:

  • 均值为 0:数据的中心点被移到了原点。
  • 标准差为 1:数据的离散程度被归一化。

这也是我们通常所说的 Z-score 标准化。通过这种转换,我们可以把不同单位、不同量级的数据放到同一个“起跑线”上进行比较。

数学原理:Z-score 公式

标准化的核心公式非常直观。假设我们有一个观测值 $x_i$,标准化后的值 $Z$ 计算如下:

$$Z = \frac{x_i – \bar{x}}{\sigma}$$

在这里:

  • $x_i$ 是原始数据中的某个观测值。
  • $\bar{x}$ 是该列数据的均值
  • $\sigma$ 是该列数据的标准差

公式解读:我们实际上做的是,从每个观测值中减去数据的均值(这被称为中心化,Centering),然后再除以标准差(这被称为缩放,Scaling)。

实战示例:学生成绩数据

为了让你更好地理解,让我们通过一个具体的例子来看看标准化前后的变化。假设我们有以下一个包含学生信息的数据集:

Name

Age

CGPA

1.

A

15

5.0

2.

B

16

4.0

3.

C

20

5.0

4.

D

19

2.0

5.

E

19

1.0

6.

F

17

3.0在这个数据集中:

  • Age 的范围在 15 到 20 之间,均值为 17.67。
  • CGPA 的范围在 1.0 到 5.0 之间,均值为 3.33。

如果我们对这两列数据应用标准化公式,我们将得到以下结果:

Name

Age

CGPA

1.

A

-1.3561270

1.0206207

2.

B

-0.8475794

0.4082483

3.

C

1.1866111

1.0206207

4.

D

0.6780635

-0.8164966

5.

E

0.6780635

-1.4288690

6.

F

-0.3390318

-0.2041241请注意观察,现在的 Age 和 CGPA 列都变成了小数,正负值并存。这意味着数值大于 0 的学生(比如 C),其年龄或成绩高于平均水平;而数值小于 0 的学生(比如 E),其指标低于平均水平。这就是标准化的魅力所在。

现在,让我们进入正题,看看如何在 R 语言中通过代码实现这一过程。

方法 1:使用 R 内置的 scale() 函数(推荐)

R 语言提供了一个非常强大且简洁的内置函数 scale(),专门用于执行这种标准化操作。这是绝大多数 R 用户进行数据预处理的首选方法。

#### 语法解析

scale(x, center = TRUE, scale = TRUE)
  • x: 代表你想要应用标准化的数据列、矩阵或数据框。
  • center: 接受一个布尔值或数值向量。当设置为 TRUE(默认值)时,它会从观测值中减去均值。
  • scale: 接受一个布尔值或数值向量。当设置为 TRUE(默认值)时,它会将中心化后的数据除以标准差。

#### 代码实战

让我们来看看具体的操作步骤。我们将分三步走:创建数据集、应用函数、转换结果类型。

步骤 1:创建数据集

首先,我们构建一个模拟的学生数据框。

# 定义各列数据
X <- c('A', 'B', 'C', 'D', 'E', 'F')
Y <- c(15, 16, 20, 19, 19, 17)
Z <- c(5.0, 4.0, 5.0, 2.0, 1.0, 3.0)

# 组合成数据框
dataframe <- data.frame(Name = X, Age = Y, CGPA = Z)

# 查看原始数据
print("原始数据:")
print(dataframe)

步骤 2:应用 scale 函数并转换类型

这里有一个新手常犯的错误:INLINECODE414305ea 函数返回的是一个矩阵,而不是数据框。如果你直接把它赋值回数据框的列,可能会报错或产生意想不到的结果。因此,我们需要使用 INLINECODEea1b05b2 将其转换回来。

# 对第 2 到第 3 列应用标准化
# 注意:scale() 返回的是矩阵,我们需要将其转回数据框
dataframe[2:3] <- as.data.frame(scale(dataframe[2:3]))

# 显示标准化后的结果
print("标准化后的数据:")
print(dataframe)

#### 深入理解:查看属性

scale() 函数返回的对象中实际上包含了很多有用的信息,它不仅仅是返回了数值,还“记住”了中心化参数和缩放参数。你可以这样查看它们:

# 假设我们只对 Age 列做操作
scaled_age <- scale(dataframe$Age)

# 查看计算过程中用到的均值
print("计算所用的均值:")
print(attr(scaled_age, "scaled:center"))

# 查看计算过程中用到的标准差
print("计算所用的标准差:")
print(attr(scaled_age, "scaled:scale"))

这个特性非常重要,因为当你有了新的测试数据时,你需要使用与训练数据相同的均值和标准差来进行标准化,而不是重新计算测试数据的均值。

方法 2:使用 Base R 手动编写函数(基础版)

如果你想深入理解算法背后的机制,或者你需要自定义标准化的逻辑(比如按中位数缩放),那么手动编写函数是一个非常好的练习。

#### 逻辑构建

我们将创建一个名为 standardize 的函数。它的逻辑就是直接实现我们刚才提到的 Z-score 公式:

  • 计算均值 mean(x)
  • 计算标准差 sd(x)
  • 应用公式 (x - mean) / sd

#### 代码实战

# 1. 创建数据集(同上)
X <- c('A', 'B', 'C', 'D', 'E', 'F')
Y <- c(15, 16, 20, 19, 19, 17)
Z <- c(5.0, 4.0, 5.0, 2.0, 1.0, 3.0)
dataframe <- data.frame(Name = X, Age = Y, CGPA = Z)

# 2. 定义标准化函数
standardize <- function(x){
  # 这里的 na.rm = TRUE 是为了防止缺失值导致计算失败
  z <- (x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE)
  return(z)
}

# 3. 将函数应用到数据集的特定列
# apply() 函数可以让我们对每一列循环操作
# dataframe[2:3] 表示选取第 2 到第 3 列
# 2 代表按列(MARGIN=2)应用函数
dataframe[2:3] <- apply(dataframe[2:3], 2, standardize)

# 4. 显示结果
print("使用自定义函数处理后的结果:")
print(dataframe)

#### 这种方法的优缺点

  • 优点:完全透明,你可以修改公式里的任何部分,比如将 INLINECODEc0d21760 换成 INLINECODE6038b28d(绝对中位差)来做抗噪标准化。
  • 缺点:代码量稍多,且通常不如直接调用 scale() 函数效率高,因为 R 的内置函数通常是用 C 语言优化的。

方法 3:使用 dplyr 包(现代 R 用户的最佳实践)

在现代 R 数据科学中,我们更倾向于使用 dplyr 包,因为它语法更流畅,且不会轻易因为数据类型(如矩阵和数据框的转换)而出错。这通常是我们最推荐你在生产环境中使用的方法。

#### 准备工作

你需要安装并加载 dplyr 包:

# install.packages("dplyr") # 如果未安装请运行此行
library(dplyr)

#### 代码实战:使用 INLINECODEa0b2fd4b 或 INLINECODE8c4eae15

我们可以非常优雅地一次性对所有数值列进行标准化,或者只针对特定的列。

# 重新创建数据集以进行演示
dataframe_new <- data.frame(
  Name = c('A', 'B', 'C', 'D', 'E', 'F'), 
  Age = c(15, 16, 20, 19, 19, 17), 
  CGPA = c(5.0, 4.0, 5.0, 2.0, 1.0, 3.0)
)

# 使用 dplyr 进行标准化
# 方法 A:对数据框中的所有数值列进行标准化
result_all %
  mutate(across(where(is.numeric), ~ as.numeric(scale(.))))

print("使用 dplyr 标准化所有列:")
print(result_all)

# 方法 B:只对特定的列进行标准化(例如只标准化 Age)
result_specific %
  mutate(Age = scale(Age))

print("使用 dplyr 仅标准化 Age 列:")
print(result_specific)

这里 mutate(across(where(is.numeric), ...)) 的意思是:“找出所有是数值类型的列,然后对它们应用后面的标准化函数”。这种方法非常稳健,因为它会自动忽略字符类型的列(如 Name),而无需我们手动指定索引。

实战中的关键注意事项

虽然标准化听起来很简单,但在实际项目中,我们经常忽略一些细节,导致模型上线后出现问题。以下是我们总结的一些最佳实践

#### 1. 处理缺失值

如果你的数据列中包含 INLINECODE46c7fb51(缺失值),INLINECODE370dfdb0 函数默认会输出 NA。如果你需要在计算均值和标准差时忽略缺失值,仅对有效值进行标准化,你需要这样写:

# 假设 vector_x 包含 NA
# scale 默认无法直接处理 NA 聚合计算,通常需要先处理或使用自定义函数
# 最安全的方法是使用 dplyr 或手动计算时加上 na.rm
vector_x <- c(1, 2, 3, NA, 5)

# 正确的做法:先移除 NA 计算统计量,再应用
mean_val <- mean(vector_x, na.rm = TRUE)
sd_val <- sd(vector_x, na.rm = TRUE)

# 如果你想在 scale 中近似实现(需小心处理),通常建议先用 impute 填充 NA

#### 2. 训练集与测试集的一致性(非常重要!)

这是机器学习中最容易犯错的地方。假设你有一个训练集和一个测试集。

  • 错误做法:对训练集计算均值和标准差进行标准化;然后对测试集单独计算均值和标准差进行标准化。
  • 正确做法:先计算训练集的均值和标准差,将这些值保存下来;在处理测试集时,直接使用训练集的均值和标准差来标准化测试集数据。

为什么?因为测试集是用来模拟未来的真实数据的。如果你分别计算,标准化后的测试集均值可能不是 0,这会引入偏差。

#### 3. 标准化 vs 归一化

不要混淆标准化和归一化。

  • 标准化:均值变 0,标准差变 1。没有固定的范围(虽然大部分数据会在 -3 到 3 之间)。适用于大多数机器学习算法(如 SVM、逻辑回归、神经网络)。
  • 归一化:通常指将数据缩放到 [0, 1] 或 [-1, 1] 之间。公式是 $(x – min) / (max – min)$。适用于图像处理(像素值 0-255)或 K-近邻算法(KNN)。

性能优化建议

当处理超大规模数据(例如数百万行数据)时,使用 INLINECODE6c233bc2 或普通的 INLINECODEb59c3df6 可能会变慢。你可以考虑以下优化方案:

  • 向量化操作:尽量避免显式的 for 循环,使用 R 的内置向量化计算(也就是我们上面用到的公式)。
  • 使用 INLINECODE9eb56ff5:对于极大数据集,INLINECODEff96e365 包的运行速度通常比 INLINECODE7b6f5aa8 或 INLINECODE66c70b70 快得多。
  • 并行计算:如果数据列非常多且相互独立,可以使用 parallel 包对每一列并行标准化。

总结

在这篇文章中,我们全面探讨了如何在 R 语言中对数据框列进行标准化。我们从 Z-score 的数学原理出发,通过一个学生成绩的例子直观地展示了标准化的效果,并介绍了三种实现方法:

  • 基础方法:直接计算公式,适合理解原理。
  • 内置函数:最简单、最常用的方法。
  • Tidyverse 方法:适合现代数据分析工作流,代码更易读。

掌握数据标准化是你通往高级数据分析和机器学习的重要一步。下次当你拿到一个包含乱七八糟数值范围的数据集时,你就知道该如何从容应对了。建议你现在就打开 RStudio,导入你自己的数据,尝试运行一下这些代码,看看标准化后的结果是否符合你的预期。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/46908.html
点赞
0.00 平均评分 (0% 分数) - 0