学习 R 语言里的 subset
Apr 2, 2018
2 minute read

主要内容:

  • 三种 subset 操作
  • 六类 subset
  • 不同对象的的重要区别

众所周知,R 语言里面有几种不同的 subset 方法:[][[]]$,但是有时候会产生混淆。所以这篇笔记主要分析理清 R 语言里面的 subset 操作。

来让我们用 [] 来 subset!

对 atomic vector 进行 subset 操作时,通常使用 [],总共有 6 种形式:

  • 正数:简单,就是返回这个位置上的数(注意 R 语言从 1 开始计数)

  • 负数:忽略这个位置上的数

  • 逻辑值:只选择那些值为 TRUE 的数,所以基于此可以在 [] 中放入某些逻辑判断条件

    x[c(TRUE, FALSE, TRUE)]
    x[x > 3]
    
  • 什么也没有:返回原始的 vector

  • 0:返回一个长度为 0 的 vector

  • 字符:如果 vector 设置好了 name,就可以使用字符型进行 subset

    y <- setNames(x, letters[1:4])
    y[c("d", "c", "a")]
    

对于 list 来说,subset 可以有两种:使用 [] 往往会返回一个 list,使用 [[]]$ 则会得到 list 的组成部分。

对于 data frame 来说,它兼具两种数据结构的特点:使用一个 vector 对其 subset,它就可以当做 list;使用两个 vector 对其 subset,它就可以当做 matrix。

df <- data.frame(
  x = 1:3,
  y = 3:1,
  z = letters[1:3]
)

df[1:2]
#   x y
# 1 1 3
# 2 2 2
# 3 3 1
df[1:2, ]
#   x y z
# 1 1 3 a
# 2 2 2 b

还有两种操作:[[]]$

前面提到 [[]] 这种方式对于 list 来说,会从里面取出它的元素。这里可以引申出简化操作和保留操作的概念。

顾名思义,保留操作会保证输出和输入的数据结构类型是一样的,而简化操作会对输入进行不同程度的简化:

  • Atomic vector:移除变量名字
  • List:返回的是 list 里的元素,而不是 list
  • Factor: 舍弃没有出现的 level
  • Matrix:如果某个维度长度是 1,舍弃这个维度
  • Data frame:如果输出一个单变量,把它变成一个 vector
Simplifying Preserving
Vector x[[1]] x[1]
List x[[1]] x[1]
Factor x[1:4, drop=T] x[1:4]
Array x[1, ] x[, 1] x[1, , drop=F] x[, 1, drop=F]
Data frame x[, 1] x[[1]] x[, 1, drop=F] x[1]

$ 在某种方面上和 [[]] 一样,它经常被用来从一个 data frame 种提取变量,如 mtcars$cyl 等。

$[[]] 有一些区别:

  • 不能这样使用 $(但 [[]] 可以):

    var <- cyl
    mtcars$var
    
  • $ 可以进行名字检索:

    mod <- lm(mpg ~ wt, data = mtcars)
    mod$df.r # 返回 mod$df.residual
    

应用

对字符缩写的转换(本质上是对分类变量设置了标签):

x <- c("m", "f", "m", "m", "f", "u")
lookup <- c(m="Male", f="Female", u="Unknown")
lookup[x]
#         m         f         m         m         f         u
#    "Male"  "Female"    "Male"    "Male"  "Female" "Unknown"

info 是关于成绩的相关信息,我们可以使用 integer subset 进行信息的匹配和合并:

grades <- c(1, 2, 3, 3, 1)
info = data.frame(
  grade = 1:3,
  desc = c("Excellent", "Good", "Poor"),
  fail = c(F, F, T)
)

// 使用 match
id <- match(grades, info$grade)
info[id, ]

// 使用 rownames
rownames(info) <- info$grade
info[grades, ]

Data frame 对行进行随机抽样:

df <- data.frame(
  x = rep(1:3, each=2),
  y = 6:1,
  z = letters[1:6]
)

df[sample(nrow(df)), ]
df[sample(nrow(df), 8, rep = T), ]

n 列表示了每个观测重复几次,这里相当于把频数表转换为原始数据:

df <- data.frame(x = c(2, 4, 1), y = c(9, 11, 6), n = c(3, 5, 1))
df[rep(1:nrow(df), df$n), ]

使用布尔值的 subset 可以让我们在 data frame 中进行条件筛选:

  • 注意和 if 中使用的 &&|| 不同,这里使用的是 &|
  • 如果有很多条件,不想重复写 data frame 的名字,可以使用 subset
mtcars[mtcars$gear == 5 & mtcars$cyl == 4, ]
subset(mtcars, gear == 5 & cyl == 4)