引言:为什么 “对象改值” 是数据处理的核心?
在数据分析中,“拿到数据→修改数据→分析数据” 是常见流程 —— 比如根据不同游戏规则调整扑克牌点数(War 游戏中 A(14) 比 K(13) 大,Hearts 游戏中黑桃 Q 分值特殊)、清洗数据中的错误值、处理缺失信息等。《R 语言入门与实践》第五章 “对象改值”,正是围绕这些实际需求展开,通过 “扑克牌” 这一直观案例,教你如何精准、高效地修改 R 对象(尤其是数据框)中的值,为后续数据清洗和分析打下基础。
本章的核心目标是:掌握 “就地改值” 语法、用逻辑值取子集实现条件改值、处理缺失值(NA),所有知识点都围绕 “扑克牌数据框deck
” 展开,学完就能直接应用到真实数据处理场景。
一、就地改值:用索引精准修改数据
“就地改值” 指直接在原对象中修改指定位置的值,无需创建新对象,核心语法是 **“索引 + 赋值符<-
”**。这是 R 中最基础也最常用的数据修改方式,尤其适合已知目标位置的场景。
1.1 基础语法:定位→赋值
语法格式:对象[行索引, 列索引] <- 新值
- 行 / 列索引:可通过正整数、名称、逻辑值指定(第四章学过的索引方式都适用);
- 新值:单个值或与索引长度匹配的向量(R 会自动循环短向量)。
# 使用<-赋值符号更改这些值,R会在原始对象内部对这些值修改
vec <- c(0,0,0,0,0,0,0)
vec[1] <- 1000
vec
# [1] 1000 0 0 0 0 0 0
#再次对同一个索引处赋值,将覆盖原值
vec[c(1:3)] <- c(1,1,1)
vec
# [1] 1 1 1 0 0 0 0
vec[c(1,3,5)] <- c(1,1,1)
vec
# [1] 1 1 1 1 1 1 0
vec[4:6] <- vec[4:6] + 1
vec
# [1] 1 1 1 2 2 2 0
#也可以创建原先对象中不存在的新值,R会自动延伸长度以适应这个新值
vec[8] <- 8
vec
# [1] 1 1 1 2 2 2 0 8
1.2 实战:修改 War 游戏的 A 点数(从 1→14)
在 War 游戏中,A(ace)是最大牌,点数需从默认的 1 改为 14。观察deck
数据框,A 位于每 13 张牌的最后一行(行号 13、26、39、52),代码如下:
# 1. 复制原始deck(避免修改原数据)
deck_war <- deck
# 2. 定位A的位置,修改点数为14
# 行索引:13、26、39、52(4种花色的A);列索引:value列
deck_war$value[c(13, 26, 39, 52)] <- 14
# 3. 验证结果(查看黑桃A的点数)
deck_war[13, ] # 输出:face=ace, suit=spades, value=14
1.3 批量改值与新增列
- 批量改值:若需修改连续行,用
:
生成索引,如deck_war$value[1:12] <- 10
- 新增列:直接用
$
赋值新列名,如deck_war$new_col <- 1:52
(新增一列 1~52 的序号); - 删除列:将列赋值为
NULL
,如deck_war$new_col <- NULL
。
二、逻辑值取子集:按条件改值(数据分析核心技能)
当不知道目标数据的具体位置时(比如洗牌后 A 的位置变了),就需要用 “逻辑值取子集”—— 通过逻辑测试生成TRUE/FALSE
向量,自动定位目标数据。这是 R 中最灵活、最高效的数据筛选与修改方式。
2.1 第一步:逻辑测试(生成筛选条件)
逻辑测试通过 “逻辑运算符” 实现,返回长度与原向量一致的逻辑向量(TRUE
= 符合条件,FALSE
= 不符合)。常用运算符如下:
运算符 | 作用 | 示例(基于 deck 数据框) | 结果 |
---|---|---|---|
== |
等于 | deck$face == "ace" |
所有 A 返回 TRUE |
!= |
不等于 | deck$suit != "spades" |
非黑桃牌返回 TRUE |
> /< |
大于 / 小于 | deck$value > 10 |
人头牌(J/Q/K)返回 TRUE |
>= /<= |
大于等于 / 小于等于 | deck$value <= 5 |
2~5 点牌返回 TRUE |
%in% |
是否在指定集合中 | deck$face %in% c("king", "queen") |
K/Q 返回 TRUE |
示例:筛选洗牌后所有 A 的位置
# 1. 洗牌(打乱deck行顺序)
deck_shuffled <- shuffle(deck) # shuffle()是第四章写的洗牌函数
# 2. 逻辑测试:哪些行是A
is_ace <- deck_shuffled$face == "ace" # 返回长度52的逻辑向量
# 3. 取子集:提取所有A
deck_shuffled[is_ace, ] # 无论A在哪个位置,都能精准提取
2.2 第二步:布尔运算符(组合多条件)
当需要多个筛选条件时,用 “布尔运算符” 组合逻辑测试,常用运算符及函数如下:
运算符 / 函数 | 作用 | 核心参数 / 语法 | 示例(筛选黑桃 Q) | |||
---|---|---|---|---|---|---|
& |
逻辑与(所有条件为真) | 条件 1 & 条件 2 | deck$face == "queen" & deck$suit == "spades" |
|||
` | ` | 逻辑或(至少一个为真) | 条件 1 | 条件 2 | `deck$value == 10 | deck$face == "ace"`(10 点或 A) |
! |
逻辑非(反转结果) | ! 条件 | !is_ace (非 A 牌) |
|||
any() |
至少一个条件为真? | any(条件1, 条件2, ...) |
any(deck$value == 14) (是否有 14 点牌) |
|||
all() |
所有条件为真? | all(条件1, 条件2, ...) |
all(deck$value > 0) (所有牌点数 > 0?) |
新函数详解:any()
与all()
这两个函数是逻辑判断的核心工具,尤其适合验证数据完整性:
函数 | 核心参数 | 作用说明 | 示例与结果 |
---|---|---|---|
any() |
... :多个逻辑条件 |
检测是否至少一个条件为TRUE ,返回单个逻辑值 |
any(deck$face == "ace") → TRUE |
all() |
... :多个逻辑条件 |
检测是否所有条件为TRUE ,返回单个逻辑值 |
all(deck$value == 1) → FALSE |
实战:修改 Hearts 游戏点数
Hearts 游戏规则:红桃牌 1 点,黑桃 Q(queen of spades)13 点,其他 0 点。用逻辑值取子集实现:
# 1. 复制原始deck
deck_hearts <- deck
# 2. 初始化所有点数为0
deck_hearts$value <- 0
# 3. 条件1:红桃牌改1点(用%in%匹配花色)
is_hearts <- deck_hearts$suit %in% "hearts"
deck_hearts$value[is_hearts] <- 1
# 4. 条件2:黑桃Q改13点(组合两个条件)
is_queen_spades <- deck_hearts$face == "queen" & deck_hearts$suit == "spades"
deck_hearts$value[is_queen_spades] <- 13
# 5. 验证结果
deck_hearts[is_hearts | is_queen_spades, ] # 查看红桃和黑桃Q
三、缺失信息处理:NA 的正确用法
在数据中,“缺失值”(如 Blackjack 游戏中 A 的点数不确定,可能 1 也可能 11)用NA
表示。R 对NA
有特殊处理规则,需掌握is.na()
函数和na.rm
参数。
3.1 NA 的特性:“传染性”
NA 与任何值运算结果仍为 NA,避免因缺失值导致错误计算:
1 + NA # 输出:NA
mean(c(1, 2, NA)) # 输出:NA(未处理NA)
3.2 新函数:is.na()
—— 检测缺失值
is.na()
是识别 NA 的核心函数,返回与输入对象长度一致的逻辑向量(TRUE
=NA,FALSE
= 非 NA)。
函数 | 核心参数 | 作用说明 | 示例 |
---|---|---|---|
is.na() |
x :待检测对象 |
检测对象中哪些元素是 NA,返回逻辑向量 | is.na(deck$value) → 识别点数为 NA 的牌 |
示例:定位 Blackjack 游戏中的 A(设为 NA)
# 1. 复制原始deck
deck_blackjack <- deck
# 2. 人头牌(K/Q/J)改10点(用%in%匹配)
is_facecard <- deck_blackjack$face %in% c("king", "queen", "jack")
deck_blackjack$value[is_facecard] <- 10
# 3. A的点数设为NA(不确定是1还是11)
is_ace <- deck_blackjack$face == "ace"
deck_blackjack$value[is_ace] <- NA
# 4. 检测NA位置(A的位置)
which(is.na(deck_blackjack$value)) # 输出A的行号
3.3 关键参数:na.rm
—— 移除 NA 再计算
很多统计函数(如mean()
、sum()
、max()
)都有na.rm
参数,控制是否忽略 NA 后计算,默认na.rm = FALSE
(保留 NA,结果为 NA)。
函数 | na.rm 参数作用 |
示例(计算所有非 NA 点数的均值) | 结果 |
---|---|---|---|
mean() |
TRUE = 移除 NA;FALSE = 保留 |
mean(deck_blackjack$value, na.rm = TRUE) |
约 7.5(人头牌 10 点后的均值) |
sum() |
同上 | sum(deck_blackjack$value, na.rm = TRUE) |
所有非 A 牌的点数总和 |
示例:计算 Blackjack 牌堆的平均点数(忽略 A 的 NA)
mean(deck_blackjack$value) # 输出:NA(未移除NA)
mean(deck_blackjack$value, na.rm = TRUE) # 输出:~7.5(正确计算)
四、本章新函数 / 参数汇总(表格版)
为方便查阅,将本章核心新函数和参数整理如下:
类型 | 函数 / 参数 | 核心参数 | 作用说明 | 实战示例 |
---|---|---|---|---|
逻辑判断 | any(...) |
... :多个逻辑条件 |
至少一个条件为真?返回单个逻辑值 | any(deck$suit == "hearts") → TRUE |
逻辑判断 | all(...) |
... :多个逻辑条件 |
所有条件为真?返回单个逻辑值 | all(deck$value > 0) → TRUE |
缺失值检测 | is.na(x) |
x :待检测对象 |
检测 x 中 NA 的位置,返回逻辑向量 | is.na(deck_blackjack$value) |
统计函数参数 | na.rm |
TRUE /FALSE |
统计计算时是否移除 NA(默认 FALSE) | sum(deck$value, na.rm = TRUE) |
五、综合实战:打造多规则扑克牌系统
结合本章所有知识点,实现一个能切换三种游戏规则的扑克牌函数:
# 定义函数:根据游戏规则修改扑克牌点数
adjust_poker <- function(game = c("War", "Hearts", "Blackjack")) {
# 输入:game-游戏名称;输出:修改后的deck数据框
deck_adjust <- deck # 复制原始deck
if (game == "War") {
# War规则:A=14,其他不变
is_ace <- deck_adjust$face == "ace"
deck_adjust$value[is_ace] <- 14
} else if (game == "Hearts") {
# Hearts规则:红桃=1,黑桃Q=13,其他=0
deck_adjust$value <- 0
is_hearts <- deck_adjust$suit == "hearts"
is_queen_spades <- deck_adjust$face == "queen" & deck_adjust$suit == "spades"
deck_adjust$value[is_hearts] <- 1
deck_adjust$value[is_queen_spades] <- 13
} else if (game == "Blackjack") {
# Blackjack规则:人头牌=10,A=NA
is_facecard <- deck_adjust$face %in% c("king", "queen", "jack")
is_ace <- deck_adjust$face == "ace"
deck_adjust$value[is_facecard] <- 10
deck_adjust$value[is_ace] <- NA
}
return(deck_adjust)
}
# 测试:生成Hearts规则的牌堆
deck_hearts_final <- adjust_poker("Hearts")
head(deck_hearts_final[deck_hearts_final$value != 0, ]) # 查看有分值的牌
六、第五章核心小结
- 就地改值是基础:用 “索引 + 赋值” 精准修改数据,适合已知位置的场景,语法
对象[行, 列] <- 新值
; - 逻辑值取子集是核心:通过逻辑测试(
==
、%in%
)和布尔运算符(&
、|
)组合条件,实现 “按规则筛选 + 改值”,是数据分析中最常用的技能; - 缺失值处理要注意:用
is.na()
检测 NA,用na.rm
参数在统计计算时忽略 NA,避免 “NA 传染性” 导致错误; - 实战是关键:结合扑克牌案例理解改值逻辑,后续处理真实数据(如清洗错误值、调整指标口径)时可直接复用本章思路。
下一章(第六章)将学习 “R 的环境系统”,解决 “发牌后如何让牌堆记住已发的牌” 这类状态管理问题,进一步提升代码的实用性!