0%

R语言中的子集问题深入(一)

在语言编程的过程当中,选取数据的子集是一个常规操作,通常的情况下我们会使用到索引,来帮助我们灵活地选取自己,或者是使用subset函数。但无可避免的是这些方法,通常情况下都会给我们造成一定的困扰。因为在不同的数据结构中,索引的使用方法,往往是不同的,并且subset函数有的时候并不那么的简洁和灵活。这里就R语言中的subset函数和索引进行一定的深入展开。

常规索引取子集操作

这一部分将会稍微顺带的提一下常规的索引取子集操作。由于内容比较基础,所以基本上就举几个简单的例子一笔带过。

使用iris数据集和数据框数据类型为例

上述着四种结果无疑是不同的,其中涉及到两个方面,一个方面是维度,另外一个方面是操作符号: “[”、"[["、“$”。

“[”操作符

向量是一维的,但是数据框、矩阵的维度更高,所以当你直接使用“[”操作符号的时候需要考虑到这个问题。

拿数据框为例,数据框下表分为[row,colum],如果你直接输入[x],则会返回一个第x列的数据框,如果你输入[x,]则返回第x行的,[,x]同理。如果想要体会其中的不同,不妨使用str来查看一下。

“:”、“c()”、“-”以及字符串形式

介于上面有个实时操作的嵌入iframe,简易可以直接拿来直接练手。此处直接以行为例。

1
2
3
4
iris[1:5,]    #输出1到5行,“:”的含义基本等同于from left to right
iris[c(1,5),] #输出1和5行的值
iris[c(-1,-5),] #输出除了1和5行的其他行的值
iris[,"Sepal.Length"] #输出名称为"Sepal.Length"的列的值

"[["操作符和“$”操作符

"[["操作符和“$”操作符直接提取相应下标、字符串的

1
2
3
iris[[1]] #提取第1列的值
iris[[1,1]] #提取第1行第1列的值
iris$“Sepal.Length” #提取名称为"Sepal.Length"的列的值

下面给出《Advanced R》里面的插图帮助理解。

Wickham, H. (2020). 4 Subsetting | Advanced R. Retrieved 21 April 2020, from https://adv-r.hadley.nz/subsetting.html#subset-single

另外,这是本神书,在怎么推崇也不为过,但是建立在你有基础的情况下。刷过几次《R语言实战》还是觉得自己的代码写的稀烂。还自行总结了很多技巧。但人家早给你系统化的整理好了,真的是茅塞顿开。

排序操作

排序

在R语言中的排序操作主要包含三类,即order、rank、和sort,在提取子集的时候往往需要用到这些排序,但很多初学者无法有一个明确的区分。这里梳理下。

我们以一个名称为ppd的向量为例(调戏pdd大佬😈)。

1
pdd <- setNames(c(11,43,5,20),letters[1:4])

order

下面看一下order的结果:

1
2
3
4
5
6
7
8
pdd
# a b c d
# 11 43 5 20
order(pdd)
# [1] 3 1 4 2
pdd[order(pdd)]
# c a d b
# 5 11 20 43

看不懂。。。没关系,让我们来画个示意图:

注:上述示意图为博主自行手绘,随意引用但不要去水印。也不要介意小黄人都叫什么…

实时上order就是干了这么一件事情,它告诉你排序完之后目前对应位置的元素现在在哪里

这样就很清晰了。

那么pdd(order(pdd))这个指令事实上做的操作就是按照排序完以后的顺序输出结果。

rank

rank相对于order来说就简单很多了,rank输出的事实上就是当前位置上的值在总体排序的位次。

1
2
3
rank(pdd)
# a b c d
# 2 4 1 3

图中的例子虽然得出的结果和order一样(巧合),但是意义完全不同

sort

sort(pdd) == pdd[order(pdd)]

1
2
identical(sort(pdd),pdd[order(pdd)])
# TRUE

order在多维数据中使用更加灵活,所以理解以后应用还是很方便的。

排序在取子集中的应用

例如我就想看看iris数据集中Sepal.Length排名前20的,那么可以这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
iris[order(iris$Sepal.Length)[1:20],]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#14 4.3 3.0 1.1 0.1 setosa
#9 4.4 2.9 1.4 0.2 setosa
#39 4.4 3.0 1.3 0.2 setosa
#43 4.4 3.2 1.3 0.2 setosa
#42 4.5 2.3 1.3 0.3 setosa
#4 4.6 3.1 1.5 0.2 setosa
#7 4.6 3.4 1.4 0.3 setosa
#23 4.6 3.6 1.0 0.2 setosa
#48 4.6 3.2 1.4 0.2 setosa
#3 4.7 3.2 1.3 0.2 setosa
#30 4.7 3.2 1.6 0.2 setosa
#12 4.8 3.4 1.6 0.2 setosa
#13 4.8 3.0 1.4 0.1 setosa
#25 4.8 3.4 1.9 0.2 setosa
#31 4.8 3.1 1.6 0.2 setosa
#46 4.8 3.0 1.4 0.3 setosa
#2 4.9 3.0 1.4 0.2 setosa
#10 4.9 3.1 1.5 0.1 setosa
#35 4.9 3.1 1.5 0.2 setosa
#38 4.9 3.6 1.4 0.1 setosa

这里可能会有小伙伴作出这样鬼畜的事情:

1
2
iris[order(Sepal.Length)[1:20],]
#Error in order(Sepal.Length) : object 'Sepal.Length' not found

如果你了解R的enviroment策略,那么就不会出现这样的问题,Sepal.Length是对象iris中的列名,并不包含在environment里,所以会提示上述错误。但是如果你改不了这个习惯,那么也有办法。

1
2
3
attach(iris)
iris[order(Sepal.Length)[1:20],]
# 输出结果正常

attach函数事实上达到的效果就是将iris的列名模拟做对象名称。

我个人觉得这不失为一个好方法,但是更喜欢直接使用$进行补全,两种方法我都用,但是分场景。

  • 当你指处理少量数据的时候,可以用attach,无疑使得代码很简洁。

  • 但是当你导入大量数据,且不同表格数据中有大量相同的列名或标签名时不建议这么做。还是老老实实以object$colname的形式比较好,自己容易记清楚。

当你学会排序以后,对于子集的截取会相对高效很多。

结语

本来想要毕其功于一役的,但是这部分内容其实能深入的还是很多的。所以分成2到3部分进行讲述。下面是一些注意事项。

注意:

  1. R语言是为了数据科学家进创造的“编程”语言,所以,它的第1位并不是像常规编程语言从0开始,而是从1开始
  2. 习惯py的小伙伴千万不要把取后20位写成iris[,-20:-1]…,R里面不是这么玩的。

欢迎关注我的其它发布渠道