R_AdvanceR

Advanced R学习笔记(三)Subsetting

2019-03-09  本文已影响54人  ihtguy

Introduction

R的取子集操作是很快并且有效的.使用它们可以使得简便地操作一个复杂的操作.需要了解的是

选取多个元素

使用[和多个数字可以从一个向量中选择多个元素.

原子向量


x <- c(2.1, 4.2, 3.3, 5.4)

这里小数点后代表数字原始的位置.

有六种方法去取一个向量的子集


x[c(3, 1)]

#> [1] 3.3 2.1

x[order(x)]

#> [1] 2.1 3.3 4.2 5.4

# Duplicate indices will duplicate values

x[c(1, 1)]

#> [1] 2.1 2.1

# Real numbers are silently truncated to integers

x[c(2.1, 2.9)]

#> [1] 4.2 4.2


x[-c(3, 1)]

#> [1] 4.2 5.4

不能混合使用正数和负数


x[c(TRUE, TRUE, FALSE, FALSE)]

#> [1] 2.1 4.2

x[x > 3]

#> [1] 4.2 3.3 5.4

当两者的长度不一样的时候,将会把短的那个循环.


x[c(TRUE, FALSE)]

#> [1] 2.1 3.3

# Equivalent to

x[c(TRUE, FALSE, TRUE, FALSE)]

#> [1] 2.1 3.3

索引中的丢失值将会在结果中产生一个丢失值


x[c(TRUE, TRUE, NA, FALSE)]

#> [1] 2.1 4.2 NA


x[]

#> [1] 2.1 4.2 3.3 5.4


x[0]

#> numeric(0)


(y <- setNames(x, letters[1:4]))

#> a b c d 

#> 2.1 4.2 3.3 5.4

y[c("d", "c", "a")]

#> d c a 

#> 5.4 3.3 2.1

# Like integer indices, you can repeat indices

y[c("a", "a", "a")]

#> a a a 

#> 2.1 2.1 2.1

# When subsetting with [, names are always matched exactly

z <- c(abc = 1, def = 2)

z[c("a", "d")]

#> <NA> <NA> 

#> NA NA

NOTE因子在取子集的时候没有被特殊处理,所以他们将会代表的是底层的整数向量,而不是字符串的水平.


y[factor("b")]

#> a 

#> 2.1

列表

在列表中取子集是和原子型向量一样的,使用[将会返回一个列表

矩阵和数组

可以在高维结构中取子集通过下面三种方式

大部分情况下都只是一个一维的应用拓展: 应用一维的索引在每一个维度上,通过一个逗号分隔.空白子集很有用,因为它保留了所有的列和行.


a <- matrix(1:9, nrow = 3)

colnames(a) <- c("A", "B", "C")

a[1:2, ]

#> A B C

#> [1,] 1 4 7

#> [2,] 2 5 8

a[c(TRUE, FALSE, TRUE), c("B", "A")]

#> B A

#> [1,] 4 1

#> [2,] 6 3

a[0, -2]

#> A C

默认情况下,[的结果会取尽可能少的维数.


a[1, ]

#> A B C 

#> 1 4 7

a[1, 1]

#> A 

#> 1

因为矩阵和数组都是一个带有特殊属性的向量,所以也可以使用单个向量来取子集,要注意的是数组是按列来存储的


vals <- outer(1:5, 1:5, FUN = "paste", sep = ",")

vals

#> [,1] [,2] [,3] [,4] [,5] 

#> [1,] "1,1" "1,2" "1,3" "1,4" "1,5"

#> [2,] "2,1" "2,2" "2,3" "2,4" "2,5"

#> [3,] "3,1" "3,2" "3,3" "3,4" "3,5"

#> [4,] "4,1" "4,2" "4,3" "4,4" "4,5"

#> [5,] "5,1" "5,2" "5,3" "5,4" "5,5"

vals[c(4, 15)]

#> [1] "4,1" "5,3"

还可以通过一个矩阵来取


select <- matrix(ncol = 2, byrow = TRUE, c(

  1, 1,

  3, 1,

  2, 4

))

vals[select]

#> [1] "1,1" "3,1" "2,4"

数据框和tibbles

数据框拥有列表和矩阵的特征


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

df[df$x == 2, ]

#> x y z

#> 2 2 2 b

df[c(1, 3), ]

#> x y z

#> 1 1 3 a

#> 3 3 1 c

# There are two ways to select columns from a data frame

# Like a list

df[c("x", "z")]

#> x z

#> 1 1 a

#> 2 2 b

#> 3 3 c

# Like a matrix

df[, c("x", "z")]

#> x z

#> 1 1 a

#> 2 2 b

#> 3 3 c

# There's an important difference if you select a single 

# column: matrix subsetting simplifies by default, list 

# subsetting does not.

str(df["x"])

#> 'data.frame': 3 obs. of 1 variable:

#> $ x: int 1 2 3

str(df[, "x"])

#> int [1:3] 1 2 3

对tibble用[取子集同样会返回一个tibble


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

str(df["x"])

#> Classes 'tbl_df', 'tbl' and 'data.frame': 3 obs. of 1 variable:

#> $ x: int 1 2 3

str(df[, "x"])

#> Classes 'tbl_df', 'tbl' and 'data.frame': 3 obs. of 1 variable:

#> $ x: int 1 2 3

保留维度

如果想要保留原始的维度,可以使用参数drop = FALSE


a <- matrix(1:4, nrow = 2)

str(a[1, ])

#> int [1:2] 1 3

str(a[1, , drop = FALSE])

#> int [1, 1:2] 1 3

df <- data.frame(a = 1:2, b = 1:2)

str(df[, "a"])

#> int [1:2] 1 2

str(df[, "a", drop = FALSE])

#> 'data.frame': 2 obs. of 1 variable:

#> $ a: int 1 2

在对2D的对象取子集的时候要养成设置drop=FALSE的习惯,而tibbles总是默认这个参数.

因子中也有这个参数,但是它是用来控制是否保留水平,而不是控制是否保留维度.


z <- factor(c("a", "b"))

z[1]

#> [1] a

#> Levels: a b

z[1, drop = TRUE]

#> [1] a

#> Levels: a

挑选单个元素

[[经常被用来挑选单个元素,$算是其简写

[[

[[对于列表来说很重要,因为[对于列表来说总是会返回一个更小的列表

image

因为只能返回单个对象,所以只能对单一的正整数和字符串使用.如果对于一个向量使用.它将会对自己进行递归操作. x[[c(1, 2)]] 等于 x[[1]][[2]] 这是很少有人知道的,所以最好避免这种使用方法.建议使用purrr::pluck()

在列表中只能使用[[,但是在对于原子型向量时建议也这么写,因为这可以强调想要得到单个的值.

$

是一个更短的操作符xy大致相当于x[["y"]].经常出现的一个错误就是讲列的名称储存在变量中来使用


var <- "cyl"# Doesn't work - mtcars$var translated to mtcars[["var"]]mtcars$var#> NULL# Instead use [[mtcars[[var]]#>  [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

和[[有一个重要的区别.是从左到右进行部分匹配


x <- list(abc = 1)

x$a

#> [1] 1

x[["a"]]

#> NULL

为了避免这一行为可以使用全局设置


options(warnPartialMatchDollar = TRUE)

x$a

#> Warning in x$a: partial match of 'a' to 'abc'

#> [1] 1

对于数据框来说,也可以使用tibble来避免这一行为

缺失/越界的索引

下面的表格汇总了通过一个out-of-bound 值,缺失值和长度为零的向量时的情况.

image

如果一个向量被命名了,那么使用名字的OOB和缺失值或者NULL时将会返回"<NA>"

上表的不一致,导致了purrr::pluck()的和purrr:chuck()的发展pluck()总是返回NULL(或是.default参数的值).chunck()则总是抛出一个错误

pluck()则允许混用整数和字符索引,且当对象不存在时提供一个可替换的默认值


x <- list(

  a = list(1, 2, 3),

  b = list(3, 4, 5)

)

purrr::pluck(x, "a", 1)

#> [1] 1

purrr::pluck(x, "c", 1)

#> NULL

purrr::pluck(x, "c", 1, .default = NA)

#> [1] NA

@和slot()

有两种额外的取子集操作符用于S4类 @相当于$ slot()相当于[[

@相对于$更严谨,当位置不存在时返回一个错误

取子集和赋值

所有的取子集操作符都能和复制挑选的值相关联.基础的形式是x[i] <- value


x <- 1:5

x[c(1, 2)] <- c(101, 102)

x

#> [1] 101 102 3 4 5

应该确保被选取的对象和值的长度相等,如果不等则会循环赋值,但是R的循环赋值很复杂(特别是在包含缺失值和重复值的时候)

对于列表可以通过x[[i]] <- NULL来移除一个组分,如果想要添加一个NULL,可以通过x[i] <- list(NULL)来达成


x <- list(a = 1, b = 2)

x[["b"]] <- NULL

str(x)

#> List of 1

#> $ a: num 1

y <- list(a = 1, b = 2)

y["b"] <- list(NULL)

str(y)

#> List of 2

#> $ a: num 1

#> $ b: NULL

取子集时什么都不填和赋值连用是很有用的,因为这样子它就会被设计成保留原始数据结构的对象.相对于下面两种表达,在第一种mtcars将会保留数据框结构,因为只是改变mtcars的内容而不是mtcars本身,在第二种方法里,mtcars会变成一个列表,因为改变了这个对象绑定的数据


mtcars[] <- lapply(mtcars, as.integer)

is.data.frame(mtcars)

#> [1] TRUE

mtcars <- lapply(mtcars, as.integer)

is.data.frame(mtcars)

#> [1] FALSE

应用

这产生了许多有用的应用,大部分重要的都被描述在下面.许多这些基本技术都包含在更简洁的函数中(例如,subset(),merge(),dplyr :: arrange()),但了解如何使用基本的子集实现它们是有用的.

搜索表(字符串子集)

字符串匹配提供了一个有效的方式去创建一个搜索表


x <- c("m", "f", "u", "f", "f", "m", "m")

lookup <- c(m = "Male", f = "Female", u = NA)

lookup[x]

#> m f u f f m m 

#> "Male" "Female" NA "Female" "Female" "Male" "Male"

如果不想名字显示在结果中,使用unname()去移除它


unname(lookup[x])

#> [1] "Male" "Female" NA "Female" "Female" "Male" "Male"

手动匹配并整合

您可能有一个更复杂的查找表,其中包含多列信息。 假设我们有一个整数等级的向量,以及一个描述其属性的表:


grades <- c(1, 2, 2, 3, 1)

info <- data.frame(

  grade = 3:1,

  desc = c("Excellent", "Good", "Poor"),

  fail = c(F, F, T)

)

我们想要复制信息表,以便我们的成绩中每个值都有一行。 一种优雅的方法是通过组合match()和整数子集(match(needle,haystack)返回位置)。


id <- match(grades, info$grade)

id

#> [1] 3 2 2 1 3

info[id, ]

#> grade desc fail

#> 3 1 Poor TRUE

#> 2 2 Good FALSE

#> 2.1 2 Good FALSE

#> 1 3 Excellent FALSE

#> 3.1 1 Poor TRUE

如果要匹配多个列,则需要先将它们折叠为单个列(例如,使用interaction()),但通常最好切换到专门为连接多个表(如merge())而设计的函数, 或者dplyr :: left_join().

随机样本

可以使用整数索引来执行向量或数据帧的随机采样或自举。 sample(n)生成1:n的随机排列,然后子集访问值:


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

# Randomly reorder

df[sample(nrow(df)), ]

#> x y z

#> 1 1 5 a

#> 4 1 2 d

#> 2 2 4 b

#> 5 2 1 e

#> 3 3 3 c

# Select 3 random rows

df[sample(nrow(df), 3), ]

#> x y z

#> 3 3 3 c

#> 2 2 4 b

#> 1 1 5 a

# Select 6 bootstrap replicates

df[sample(nrow(df), 6, replace = TRUE), ]

#> x y z

#> 4 1 2 d

#> 4.1 1 2 d

#> 5 2 1 e

#> 1 1 5 a

#> 1.1 1 5 a

#> 2 2 4 b

sample()的参数控制提取的样本数,以及是否将样品返回

排序

order()以一个向量为输入,返回一个横竖向量描述应该如何排序


x <- c("b", "c", "a")

order(x)

#> [1] 3 1 2

x[order(x)]

#> [1] "a" "b" "c"

可以有一些其他的变量提供,可以使用decrease = TRUE将升序变为降序.默认情况下任何的缺失值都会被放在向量的末尾,但是可以通过na.last=NA来删除它们.或合适用na.last=FALSE来放在前面.

对于二维或是更高纬度的对象来说,order()可以很方便地对列或行进行排序


# Randomly reorder df

df2 <- df[sample(nrow(df)), 3:1]

df2

#> z y x

#> 3 c 3 3

#> 1 a 5 1

#> 2 b 4 2

#> 4 d 2 1

#> 5 e 1 2

df2[order(df2$x), ]

#> z y x

#> 1 a 5 1

#> 4 d 2 1

#> 2 b 4 2

#> 5 e 1 2

#> 3 c 3 3

df2[, order(names(df2))]

#> x y z

#> 3 3 3 c

#> 1 1 5 a

#> 2 2 4 b

#> 4 1 2 d

#> 5 2 1 e

也可以直接对向量使用sort()来排序,或者是对数据框使用dplyr::arrange()

扩大汇总计数

有时您会获得一个数据框,其中相同的行已折叠为一个.并且已添加计数列. rep()和整数子集使得很容易解开,因为我们可以利用rep()的向量化:rep(x,y)重复x [i] y [i]次.


df <- data.frame(x = c(2, 4, 1), y = c(9, 11, 6), n = c(3, 5, 1))

rep(1:nrow(df), df$n)

#> [1] 1 1 1 2 2 2 2 2 3

df[rep(1:nrow(df), df$n), ]

#> x y n

#> 1 2 9 3

#> 1.1 2 9 3

#> 1.2 2 9 3

#> 2 4 11 5

#> 2.1 4 11 5

#> 2.2 4 11 5

#> 2.3 4 11 5

#> 2.4 4 11 5

#> 3 1 6 1

从数据框中移除列

有两种方法从数据框中移除列,可以设置特定的列为NULL


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

df$z <- NULL

或者是取自己想要的子集


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

df[c("x", "y")]

#> x y

#> 1 1 3

#> 2 2 2

#> 3 3 1

如果只知道不想要的列,可以这样子


df[setdiff(names(df), "z")]

#> x y

#> 1 1 3

#> 2 2 2

#> 3 3 1

基于一个条件挑选行

因为逻辑值可以值得很容易地组合各列的条件,所以这是最常见的方式


mtcars[mtcars$gear == 5, ]

#> mpg cyl disp hp drat wt qsec vs am gear carb

#> Porsche 914-2 26.0 4 120.3 91 4.43 2.14 16.7 0 1 5 2

#> Lotus Europa 30.4 4 95.1 113 3.77 1.51 16.9 1 1 5 2

#> Ford Pantera L 15.8 8 351.0 264 4.22 3.17 14.5 0 1 5 4

#> Ferrari Dino 19.7 6 145.0 175 3.62 2.77 15.5 0 1 5 6

#> Maserati Bora 15.0 8 301.0 335 3.54 3.57 14.6 0 1 5 8

mtcars[mtcars$gear == 5 & mtcars$cyl == 4, ]

#> mpg cyl disp hp drat wt qsec vs am gear carb

#> Porsche 914-2 26.0 4 120.3 91 4.43 2.14 16.7 0 1 5 2

#> Lotus Europa 30.4 4 95.1 113 3.77 1.51 16.9 1 1 5 2

应当使用&和|而不是&&和||提莫更适合在if语句中使用

布尔代数和集合

了解集合操作(整数子集)和布尔代数(逻辑子集)之间的自然等价是很有用的。使用集合操作时更有效

which()允许将布尔值转换为整数值,但还没有相反地操作符,可是可以很简单的创建一个


x <- sample(10) < 4

which(x)

#> [1] 2 5 8

unwhich <- function(x, n) {

  out <- rep_len(FALSE, n)

  out[x] <- TRUE

  out

}

unwhich(which(x), 10)

#> [1] FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE

让我们创建两个逻辑向量及其整数,然后研究布尔运算和集合运算之间的关系。


(x1 <- 1:10 %% 2 == 0)

#> [1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE

(x2 <- which(x1))

#> [1] 2 4 6 8 10

(y1 <- 1:10 %% 5 == 0)

#> [1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE

(y2 <- which(y1))

#> [1] 5 10

# X & Y <-> intersect(x, y)

x1 & y1

#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE

intersect(x2, y2)

#> [1] 10

# X | Y <-> union(x, y)

x1 | y1

#> [1] FALSE TRUE FALSE TRUE TRUE TRUE FALSE TRUE FALSE TRUE

union(x2, y2)

#> [1] 2 4 6 8 10 5

# X & !Y <-> setdiff(x, y)

x1 & !y1

#> [1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE FALSE

setdiff(x2, y2)

#> [1] 2 4 6 8

# xor(X, Y) <-> setdiff(union(x, y), intersect(x, y))

xor(x1, y1)

#> [1] FALSE TRUE FALSE TRUE TRUE TRUE FALSE TRUE FALSE FALSE

setdiff(union(x2, y2), intersect(x2, y2))

#> [1] 2 4 6 8 5

当刚开始学习时,经常犯的错误就是用x[which(y)}去代替x[y],这里which()什么都得不到,他转换逻辑值到整数值,但是这结果是完全一样的..在更通常的情况下,有两个重要的区别

通常来说 避免转换逻辑值到整数值.

上一篇 下一篇

猜你喜欢

热点阅读