R函数源码解析(一)-- fivenum函数

本文是R语言中的函数源代码解析系列的第一篇文章。

本文将逐句解释fivenum函数的源码。之所以选取fivenum函数,是因为这个函数比较简单,结构并不复杂。太复杂我都要看晕了,^_^。以后会解析一些比较复杂的函数源代码,当然以 本人目前的水平,只能解释纯粹的R代码,更深层次的C或者其他的语言的代码,还是够呛。

好了,言归正传。直接进入主题吧…

首先看看fivenum函数是干嘛的。以下内容来自帮助文档。

fivenum: Returns Tukey’s five number summary (minimum, lower-hinge, median, upper-hinge, maximum) for the input data.

上面写的很清楚了,返回Tukey五数统计量。

下面看看fivenum的用法:

1
2
# na.rm默认为TRUE
fivenum(x, na.rm = TRUE)
  • x: 数值型,可以包含NA+Inf-Inf
  • na.rm: 逻辑型,若为真, 在计算之前删去所有的缺失值(NA)和NaN(Not a number)

举个例子

1
2
set.seed(1234)
fivenum(c(rnorm(100), -1:1/0))
1
#> [1] -Inf -0.9111954 -0.3846280 0.5060559 Inf
1
fivenum(c(rnorm(20), NA, 10, NA, NaN))
1
#> [1] -1.24428785 -0.50247778 0.02236253 0.35496826 10.00000000

通过这两个例子,很容弄清楚fivenum的用法。下面正式进入本文的核心部分,逐句解释fivenum函数的源码。

1
fivenum
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#> function (x, na.rm = TRUE)
#> {
#> xna <- is.na(x)
#> if (any(xna)) {
#> if (na.rm)
#> x <- x[!xna]
#> else return(rep.int(NA, 5))
#> }
#> x <- sort(x)
#> n <- length(x)
#> if (n == 0)
#> rep.int(NA, 5)
#> else {
#> n4 <- floor((n + 3)/2)/2
#> d <- c(1, n4, (n + 1)/2, n + 1 - n4, n)
#> 0.5 * (x[floor(d)] + x[ceiling(d)])
#> }
#> }
#> <bytecode: 0x0000000017225fc8>
#> <environment: namespace:stats>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
five_nums <- function(x, na.rm = FALSE) {
# 判断x中是否存在缺失值,xna为一个布尔值向量
xna <- is.na(x)
# 判断x中是否存在缺失值,any(xna)为一个长度为1的布尔值向量,即TRUE或者FALSE
if (any(xna)) {
# 如果na.rm = TRUE,执行x <- x[!xna]
if (na.rm)
# 删去向量x中的缺失值
x <- x[!xna]
else
# 如果na.rm = FALSE,返回NA, NA, NA, NA, NA
return(rep.int(NA, 5))
}
# 上面的代码中包含return(),表明:
# 如果na.rm = FALSE, 则返回rep.int(NA, 5),
# 下面的代码将不会被执行,这与if ... else ...结构有些类似
# 我猜测,如果代码中提前出现了return()函数,那么如果执行
# 这一语句的条件为真,那么将返回return()返回的值,后面的
# 代码将不会被执行
# 如果前面的条件为假,那么将执行后面的代码
# 对x进行排序,返回排序后的x
x <- sort(x)
# 求x的长度
n <- length(x)
# 如果x的长度为0,返回NA, NA, NA, NA, NA
if (n == 0)
rep.int(NA, 5)
else {
# 如果x的长度不为0,那么执行下面的代码
# 下面的代码就是用来计算Tukey五数统计量
n4 <- floor((n + 3) / 2) / 2
d <- c(1, n4, (n + 1) / 2, n + 1 - n4, n)
0.5 * (x[floor(d)] + x[ceiling(d)])
}
}

上面出现了floorceiling两个函数,这里解释一下。这两个函数都是取整函数,floor函数是向下取整,ceiling函数则是向上取整。给个例子吧…

1
floor(c(1.2, 3.8, -2.3, -0.7))
1
#> [1] 1 3 -3 -1
1
ceiling(c(1.2, 3.8, -2.3, -0.7))
1
#> [1] 2 4 -2 0

之前很少看函数的源代码,现在发现要把R语言学好,学深入,学习R语言里面函数的写法是非常有必要的。很多时候自己不知道该怎么写,如何写出更有效率的函数,这时候看看R中(包括可怕的CRAN库)有没有实现类似功能的函数,这时候写起来就更加顺畅了。当然,推荐新手直接用别人造的轮子,稳定性,效率基本都有保证。至于老手,可以在别人基础上加以改进,或者自己造轮子。哈哈,我就是个新手,在这里大放厥词的谈老手,不要被笑话啊…