Chapter 01 - 基础数据类型

1.1 Vector 向量

# 创建
x <- c("city", 2) # string要加双引号
x # c()创建的本身就是一个向量了
## [1] "city" "2"
length(x)
## [1] 2
length(c("city", 3, 5)) # 可以不用赋值给变量,直接算长度
## [1] 3
x1 <- c(x, c("Nanjing", 5)) # 可以直接把多个向量拼接起来
x1
## [1] "city"    "2"       "Nanjing" "5"
typeof(x1)
## [1] "character"
# 创建长度为n的向量
v <- vector("numeric") # 长度为0
# 或`vector("integer")`, `vector("character")`等
class(v)
## [1] "numeric"
v <- integer(0)
# 或 x <- integer()
# 或`character(0)`, `numeric(0)`等
class(v)
## [1] "integer"

Index

是元素在向量中的位置,用[]来写

x[1]
## [1] "city"
x1[c(1,2,4)] # 取1,2,4个元素
## [1] "city" "2"    "5"
x1[-c(2,3)] # 除了第2,3个元素
## [1] "city" "5"
x1[c(1,2)] <- c("city2",3) # 重新赋值(选取的元素)或者叫子集
x1
## [1] "city2"   "3"       "Nanjing" "5"
# 连续整数可以直接写出来
1:10
##  [1]  1  2  3  4  5  6  7  8  9 10
x1[1:3]
## [1] "city2"   "3"       "Nanjing"

复读机 rep

rep(6,8) # 把6重复8遍;或rep(6, times = 8)
## [1] 6 6 6 6 6 6 6 6
rep(c(0, 7, 6), 4) # 把(0, 7, 6)重复4遍
##  [1] 0 7 6 0 7 6 0 7 6 0 7 6
rep(c(0, 7, 6, 1), each = 4) # 把0, 7, 6, 1各重复4遍
##  [1] 0 0 0 0 7 7 7 7 6 6 6 6 1 1 1 1
rep(c(0, 7, 6, 1), c(1, 2, 3, 4)) # 把0, 7, 6, 1分别重复1, 2, 3, 4遍
##  [1] 0 7 7 6 6 6 1 1 1 1
rep(8:15, rep(1:5, rep(1:2, 2:3))) # 嵌套
##  [1]  8  9  9 10 10 10 11 11 11 12 12 12 12 13 13 13 13 14 14 14 14 14 15 15 15
## [26] 15 15

等差数列 seq

seq(0, 15, 2.5) # 其实是`seq(from = 0, to = 15, by = 2.5)`的简写
## [1]  0.0  2.5  5.0  7.5 10.0 12.5 15.0
seq(0, 50, length.out = 11) # 生成的向量长度确定时(有11个元素)
##  [1]  0  5 10 15 20 25 30 35 40 45 50

随机数

用r…生成,除了例子,还有rlnorm(), rpois(), rexp()等函数。 ?stats::distributions中介绍了R中自带的分布,其中大部分都有对应的随机数生成器。

# 连续型均匀分布随机数用runif(n, min, max), n是数量,min是最小值,max是最大值。默认min为0,max为1。
x_unif <- runif(100000, 40, 60) # 生成100000个40到60之间,连续均匀分布的的随机数
hist(x_unif) # 画直方图

# 正态分布随机数用rnorm(n, mean, sd), 三个参数分别为数量,平均值,标准差。默认mean为0,sd为1。
x_norm <- rnorm(100000, 250, 20) # 按照平均值为250,标准差为20的正态分布的概率密度函数生成100000个随机数
hist(x_norm) # 画直方图

简单随机抽样

balls <- letters[1:10]
balls
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
sample(balls, 20, replace = TRUE) # 随机取20个球,每取一次之后,把球放回
##  [1] "f" "j" "d" "g" "j" "f" "d" "f" "f" "a" "f" "j" "b" "g" "c" "g" "d" "d" "i"
## [20] "c"
# sample()函数第一个参数是样本空间,第二个参数是样本量。
sample(1:100, 10, replace = TRUE)
##  [1]  8 72 36 56 24 24 98 62 46 78

排序

x <- c(-10, 5, -89, 999, 84)
x
## [1] -10   5 -89 999  84
sort(x) # 原向量的元素从小到大重新排列。如果要从小到大:rev(sort(x))
## [1] -89 -10   5  84 999
rev(sort(x)) 
## [1] 999  84   5 -10 -89
rank(x) # rank()是原向量各个元素的(从小到大的)排名。(-10是第2名,5是第3名,-89是第1名,以此类推)
## [1] 2 3 1 5 4
order(x) # order()表示位置,依次对应的是向量的最小值、次小值、第三小值……最大值等在向量中的位置,比如最小值是-89,位置为3,所以3是第一个数
## [1] 3 1 2 5 4

元素的命名

scores <- c("ochem" = 79, "math" = 66, "mcb" = 64, "blc" = 75, "bpc" = 72)
scores # 名字放在前面
## ochem  math   mcb   blc   bpc 
##    79    66    64    75    72
# 可以用名字抓取元素
scores[c("math", "bpc")]
## math  bpc 
##   66   72
scores[c("math", "bpc")] == scores[c(2, 5)]
## math  bpc 
## TRUE TRUE

unique函数

unique()函数用来列举一个向量中所含的,不重复的元素

unique(c("a", "a", "x", "x", "x", "b", "h", "h"))
## [1] "a" "x" "b" "h"

R中的计算

R中的向量(矩阵和数列也是)的各种计算默认都是逐元素 (elementwise)的。

x <- c(4, 9, 25); y <- c(8, 6, 3)
x + y
## [1] 12 15 28
x * y
## [1] 32 54 75
sqrt(x)
## [1] 2 3 5

数据类型 5种

一个向量的所有元素必须属于同一种类型。如果尝试把不同类型的元素合并成一个向量,其中一些元素的类型会被强制转换 (coerced)。

类型 含义与说明 例子

numeric 浮点数向量 3, 0.5, sqrt(2), NaN, Inf

integer 整数向量 3L, 100L

character 字符向量 需被引号包围 “1”, “$”, “你好”

logical 逻辑向量 TRUE, FALSE, NA

complex 复数向量 3+5i, 1i, 1+0i

关于NA, Inf, NaN和NULL NA为缺损值,意思是该元素所代表的数值丢失/不确定/不可用。与向量中其它元素是同一类型的。

而Inf(无限): 很大的数

NaN(非数): not a number,以及作为numeric的NA的数学计算,作为logical的NA的逻辑运算在后面讨论。

其它数据/对象类型 Dataframe/tibble 是R中存储复杂(多变量)数据的规范格式。

因子 (factor)有很多向量的特性,尤其是能在dataframe/tibble中作为变量,但是它并不是向量。

函数 (function)。我们刚才用c()来创建向量,它就是一个函数:class(c)。

list类似于向量,但是一个list可以包含不同类型的元素。性质和使用方法也和向量大相径庭。

矩阵 (matrix)和数组 (array)可以算作是二维和多维的向量,同样只能存储一种类型的数据.

class(6) # class()函数,可以查看数据/对象的类型。
## [1] "numeric"
is.numeric(6) # is.XXX()函数,可以得到一个逻辑值,指明此数据/对象是否属于某个类型
## [1] TRUE
is.character("6")
## [1] TRUE
as.integer(c(TRUE, FALSE)) # as.XXX()函数,可以把数据/对象强行转换成另一种类型
## [1] 1 0
as.character(c(23, 90))
## [1] "23" "90"

str 函数

random_list <- list(first = 1, 2, status = list(has_girlfriend = FALSE, had_girlfriend = FALSE), v = c(3, 4), list(a = "aspirin", z = "zymogen", date = c(y = 2019, m = 8, d = 1)))
str(random_list)
## List of 5
##  $ first : num 1
##  $       : num 2
##  $ status:List of 2
##   ..$ has_girlfriend: logi FALSE
##   ..$ had_girlfriend: logi FALSE
##  $ v     : num [1:2] 3 4
##  $       :List of 3
##   ..$ a   : chr "aspirin"
##   ..$ z   : chr "zymogen"
##   ..$ date: Named num [1:3] 2019 8 1
##   .. ..- attr(*, "names")= chr [1:3] "y" "m" "d"

数学表达和运算

# 创建整数
class(2); class(2L) 
## [1] "numeric"
## [1] "integer"
# 运算
6^2; 6**2 # 幂
## [1] 36
## [1] 36
7%%3 # 求整数商
## [1] 1
7%%3 # 求余数
## [1] 1
#常用数学函数
factorial(3) # 阶乘 factorial(n)
## [1] 6
choose(4, 2) # 二项式系数 choose(n, k)
## [1] 6
gamma(3) # gamma(z)
## [1] 2
lgamma(3) # 就是ln(gamma(z))
## [1] 0.6931472
abs(-2) # 取绝对值
## [1] 2
# exp()和log()
exp(1)
## [1] 2.718282
log(4, base=2) # log(x, base=y), 可以简写成log(x,y)
## [1] 2
log(exp(5)) # 默认的base是e
## [1] 5
log10(100)
## [1] 2
log2(128)
## [1] 7
# 近似数
round(12.3456789, digits = 3) # 保留3个小数位
## [1] 12.346
# 取整函数
floor(5.6) # = 5 # “地板”;比x小的最近的整数
## [1] 5
ceiling(5.4) # = 6 # “天花板”;比x大的最近的整数
## [1] 6
floor(-5.6) # = -6 # 不是-5,因为-6是比-5.6小的最近的整数
## [1] -6
ceiling(-5.4) # = -5 # 不是-6;因为-5是比x大的最近的整数
## [1] -5
trunc(-5.6) # = -5 # 你可能需要这个;它无视了小数点后面的位数
## [1] -5

统计学计算

# t分布
# P(t ≤ T) = 0.975时T的值
qt(0.975, df = 12) # 算之心区间为95%, df = 12的CI
## [1] 2.178813

t检验: 用t.test()

  1. one-sample
x <- c(2.23,2.24,2.34,2.31,2.35,2.27,2.29,2.26,2.25,2.21,2.29,2.34,2.32)
t.test(x, mu = 2.31) # two-tail
## 
##  One Sample t-test
## 
## data:  x
## t = -2.0083, df = 12, p-value = 0.06766
## alternative hypothesis: true mean is not equal to 2.31
## 95 percent confidence interval:
##  2.257076 2.312155
## sample estimates:
## mean of x 
##  2.284615
t.test(x, mu = 2.31, alternative = "less") # 单侧, 检验是否*less* than μ
## 
##  One Sample t-test
## 
## data:  x
## t = -2.0083, df = 12, p-value = 0.03383
## alternative hypothesis: true mean is less than 2.31
## 95 percent confidence interval:
##      -Inf 2.307143
## sample estimates:
## mean of x 
##  2.284615
  1. two-sample
x <- c(2.23,2.24,2.34,2.31,2.35,2.27,2.29,2.26,2.25,2.21,2.29,2.34,2.32)
y <- c(2.27,2.29,2.37,2.38,2.39,2.25,2.39,2.16,2.55,2.81,2.19,2.44,2.22)
t.test(x, y)
## 
##  Welch Two Sample t-test
## 
## data:  x and y
## t = -1.5624, df = 13.65, p-value = 0.1411
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -0.18460351  0.02921889
## sample estimates:
## mean of x mean of y 
##  2.284615  2.362308
# R的默认是non-paired, unequal variance,你可以通过增加paired = TRUE,var.equal = TRUE这两个参数来改变它
t.test(x, y, paired = TRUE)
## 
##  Paired t-test
## 
## data:  x and y
## t = -1.4739, df = 12, p-value = 0.1662
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -0.19253874  0.03715412
## sample estimates:
## mean of the differences 
##             -0.07769231

卡方检验: 用chisq.test()

χ2 检验有两种,goodness of fit test(适配度检验)和contigency table test/test of independence(列联表分析/独立性检验)。

  1. 适配度检验
# 假设我们制造了一个有问题的骰子,使1至6朝上的概率分别为:
expected_probs <- c(0.05, 0.1, 0.15, 0.2, 0.2, 0.3)
# 然后我们投掷了100次,实际1至6朝上的次数分别为:
observed_vals <- c(6, 9, 14, 24, 18, 29)
# 通过chisq.test(),检验实际的1至6朝上概率是否与预期有偏差:
chisq.test(observed_vals, p = expected_probs) # 参数p是指概率
## 
##  Chi-squared test for given probabilities
## 
## data:  observed_vals
## X-squared = 1.4, df = 5, p-value = 0.9243
# p值很大(远大于0.05),因此结论是骰子各面朝上的概率符合预期。

chisq.test(observed_vals) # 不指定p参数
## 
##  Chi-squared test for given probabilities
## 
## data:  observed_vals
## X-squared = 23.24, df = 5, p-value = 0.0003037
# 默认为检测是否所有值相等(即骰子的所有面朝上的概率相等)
# 这时p值小于0.05. 得出“骰子各面朝上的概率不等”的结论。
  1. 列联表分析/独立性检验
# 假设我们有一组不同年级的学生参加社团的人数数据:
社团参与 <- matrix(c(28,36,40,40,32,33,38,29,36), nrow = 3, dimnames = list(c("一年级", "二年级", "三年级"), c("棒球", "足球", "网球")))
社团参与
##        棒球 足球 网球
## 一年级   28   40   38
## 二年级   36   32   29
## 三年级   40   33   36
# 我们想知道社团的参与,与所在年级是否是独立事件:
chisq.test(社团参与)
## 
##  Pearson's Chi-squared test
## 
## data:  社团参与
## X-squared = 3.7587, df = 4, p-value = 0.4396
# p值不小于0.05,do not reject“社团的参与,与所在年级是独立事件”的H0

1.2 List 列表

lists是recursive vector. 这意味着一个list能存储多种类型的数据,且可以包含子列表。列表中的每个分量可以是任何R中的对象 (object):除了常用的 (atomic) vector和另外一个(子)列表以外,还可以有dataframe/tibble和函数:

y <- list(1, c("a","あ"), list(1+3i, c(FALSE, NA, TRUE)), 
          data.frame(x = c("阿拉木图", "什切青"), y = c(2, 3)),
          t.test)
y # 这个列表有5个分量,其中第3个是一个有2个分量的子列表。
## [[1]]
## [1] 1
## 
## [[2]]
## [1] "a"  "あ"
## 
## [[3]]
## [[3]][[1]]
## [1] 1+3i
## 
## [[3]][[2]]
## [1] FALSE    NA  TRUE
## 
## 
## [[4]]
##          x y
## 1 阿拉木图 2
## 2   什切青 3
## 
## [[5]]
## function (x, ...) 
## UseMethod("t.test")
## <bytecode: 0x1299a9498>
## <environment: namespace:stats>

列表的index和subset子集

y[2] # 使用单方括号,得到的是一个只有一个分量的列表
## [[1]]
## [1] "a"  "あ"
y[[2]] # 使用双方括号,得到的是一个向量
## [1] "a"  "あ"
y[[3]][[2]] # 得到的也是一个向量;父列表的索引在前,子列表的在后
## [1] FALSE    NA  TRUE
y[[3]] # 这个位置包含两个子列表,因此得到一个有两个分量的列表
## [[1]]
## [1] 1+3i
## 
## [[2]]
## [1] FALSE    NA  TRUE
y[[3]][[2]][1] # 得到向量时,直接在后面用单方括号
## [1] FALSE
# 列表里的分量可以有名字;被命名的元素可以通过$符号抓取:
z <- list(c(1, 3), z2 = c(4, 5, 6), c("a", "b"))
z # `[[2]]`被`$z2`所取代
## [[1]]
## [1] 1 3
## 
## $z2
## [1] 4 5 6
## 
## [[3]]
## [1] "a" "b"
z$z2
## [1] 4 5 6
z$z2 == z[[2]] # `z[[2]]`仍然是可用的,结果和`z$z2`一样
## [1] TRUE TRUE TRUE

合并与拆解

  1. 合并
c(list(1, 2), list(3, 4, list(5,6)))
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## [1] 3
## 
## [[4]]
## [1] 4
## 
## [[5]]
## [[5]][[1]]
## [1] 5
## 
## [[5]][[2]]
## [1] 6
# 将等同于list(1, 2, 3, 4, list(5,6))

# 也许你想把需要“合并”的列表作为子列表放在另一个列表里;这也很简单
list(list(1, 2), list(3, 4))
## [[1]]
## [[1]][[1]]
## [1] 1
## 
## [[1]][[2]]
## [1] 2
## 
## 
## [[2]]
## [[2]][[1]]
## [1] 3
## 
## [[2]][[2]]
## [1] 4
  1. 拆解
# 将等同于c(1, 2, 3, 4, 5, 6, 7, 8, 9) 
# 注意被化简成了向量
unlist(list(1, list(2, list(3, 4)), list(5, 6), 7, 8, 9))
## [1] 1 2 3 4 5 6 7 8 9
# 将等同于c("1", "2", "a", 4, 5, "TRUE", "7", 8, "9+0i")
# 化简成向量时,非字符元素被强制转换成字符了
unlist(list(1, list(2, list("a", 4)), list(5, TRUE), 7L, 8, 9+0i))
## [1] "1"    "2"    "a"    "4"    "5"    "TRUE" "7"    "8"    "9+0i"
# t.test无法存储于向量中,因此最简结果为一个list:
# list(1, 2, t.test, 4, 5, TRUE, 7L, x, 9+0i)
unlist(list(1, list(2, list(t.test, 4)), list(5, TRUE), 7L, x, 9+0i))
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## function (x, ...) 
## UseMethod("t.test")
## <bytecode: 0x1299a9498>
## <environment: namespace:stats>
## 
## [[4]]
## [1] 4
## 
## [[5]]
## [1] 5
## 
## [[6]]
## [1] TRUE
## 
## [[7]]
## [1] 7
## 
## [[8]]
## [1] 2.23
## 
## [[9]]
## [1] 2.24
## 
## [[10]]
## [1] 2.34
## 
## [[11]]
## [1] 2.31
## 
## [[12]]
## [1] 2.35
## 
## [[13]]
## [1] 2.27
## 
## [[14]]
## [1] 2.29
## 
## [[15]]
## [1] 2.26
## 
## [[16]]
## [1] 2.25
## 
## [[17]]
## [1] 2.21
## 
## [[18]]
## [1] 2.29
## 
## [[19]]
## [1] 2.34
## 
## [[20]]
## [1] 2.32
## 
## [[21]]
## [1] 9+0i
# 将等同于list(1, 2, 3, list(4, 5), 6, 7, 8, 9)
unlist(list(1, list(2, 3, list(4, 5)), list(6, 7), 8, 9), recursive = FALSE)
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 2
## 
## [[3]]
## [1] 3
## 
## [[4]]
## [[4]][[1]]
## [1] 4
## 
## [[4]][[2]]
## [1] 5
## 
## 
## [[5]]
## [1] 6
## 
## [[6]]
## [1] 7
## 
## [[7]]
## [1] 8
## 
## [[8]]
## [1] 9
# 若recursive = FALSE,最“靠外”的一级列表(可能是多个)将会被拆解。
# 因此,当A, B为列表,unlist(list(A, B), recursive = FALSE)等同于c(A, B).

1.3 数组 array 和矩阵 matrix

向量是一维的数据。数组是多维的数据。

矩阵是二维的数据,因此矩阵是数组的一种特殊情况。

Dataframe不是矩阵(虽然都是方的,且取子集方法和矩阵有近似之处。

矩阵是二维的,仅包含一种数据类型的数组。

Dataframe是一个二维的列表,不同列(即列表的分量)可以存储不同的数据类型。

本质上,矩阵和数组都是以向量的形式存储的。它们只是额外地拥有dim(即“dimensions”,维度)属性。我们可以用dim()函数从向量创建数组/矩阵:

A <- 1:48 
dim(A) <- c(6,8)  # 将向量分为几行几列,竖着从左到右
A # 注意48个数字排列的方式。第一个维度是行,所以先把6行排满,随后再使用下一个维度(列),使用第2列继续排6行
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]    1    7   13   19   25   31   37   43
## [2,]    2    8   14   20   26   32   38   44
## [3,]    3    9   15   21   27   33   39   45
## [4,]    4   10   16   22   28   34   40   46
## [5,]    5   11   17   23   29   35   41   47
## [6,]    6   12   18   24   30   36   42   48
# 同时注意最左边和最上边的[1,], [,3]之类的标记。这些是索引. 
A[5,3] # 假设你要抓取第五行第三列的数值
## [1] 17
A[3,] # 第三行的全部数值
## [1]  3  9 15 21 27 33 39 45
A[,4] # 第四列的全部数值
## [1] 19 20 21 22 23 24
# 三维的例子
B <- 1:48 
dim(B) <- c(2,8,3)
B # 它生成了三个二维的矩阵。在每个2*8的矩阵存储满16个元素后,第三个维度就要加一了。每个矩阵开头的, , x正是第三个维度的值。
## , , 1
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]    1    3    5    7    9   11   13   15
## [2,]    2    4    6    8   10   12   14   16
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]   17   19   21   23   25   27   29   31
## [2,]   18   20   22   24   26   28   30   32
## 
## , , 3
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]   33   35   37   39   41   43   45   47
## [2,]   34   36   38   40   42   44   46   48
B[,,3]
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]   33   35   37   39   41   43   45   47
## [2,]   34   36   38   40   42   44   46   48
B[1,1,3]
## [1] 33

给矩阵和列表的维度命名

假设我们记录了3种药物(chloroquine, artemisinin, doxycycline) 对5种疟原虫(P. falciparum, P. malariae, P. ovale, P. vivax, P. knowlesi)的疗效. 其中每个药物对每种疟原虫做6次实验(这里用的是随机生成的数字)。为了记录数据,我们可以做3个6 x 5的矩阵,即一个6 x 5*3的数组。

C <- runif(90, 0, 1) #从均匀分布中取90个0到1之间的数
dim(C) <- c(6, 5, 3) #注意顺序
# 然后我们用dimnames()来命名
dimnames(C) <- list(paste("trial.", 1:6), # 第一个维度
                    c('P. falciparum', 'P. malariae', 'P. ovale', 'P. vivax', 'P. knowlesi'), # 第二个维度
                    c('chloroquine', 'artemisinin', 'doxycycline')) # 第三个维度
C
## , , chloroquine
## 
##          P. falciparum P. malariae    P. ovale   P. vivax P. knowlesi
## trial. 1     0.2374765   0.6256667 0.815533534 0.09741447   0.5502874
## trial. 2     0.8893720   0.5567689 0.370829786 0.26856062   0.2999085
## trial. 3     0.4051371   0.1501036 0.196540787 0.52176052   0.6218495
## trial. 4     0.2756672   0.6979271 0.004782117 0.26475245   0.9355366
## trial. 5     0.5300503   0.7887955 0.498699893 0.43022984   0.1437302
## trial. 6     0.1668426   0.4624978 0.665145014 0.74132877   0.5165605
## 
## , , artemisinin
## 
##          P. falciparum P. malariae  P. ovale  P. vivax P. knowlesi
## trial. 1    0.18429746   0.3776177 0.9686632 0.5550014  0.80532923
## trial. 2    0.09146852   0.7839543 0.5285481 0.7194910  0.39244568
## trial. 3    0.29753747   0.5422041 0.9774971 0.4473598  0.08081377
## trial. 4    0.40463763   0.8547827 0.5222402 0.8345819  0.09384141
## trial. 5    0.16538329   0.8982010 0.1102004 0.2448218  0.38840597
## trial. 6    0.03063786   0.9484275 0.4868039 0.0439384  0.33652129
## 
## , , doxycycline
## 
##          P. falciparum P. malariae  P. ovale   P. vivax P. knowlesi
## trial. 1    0.38830498   0.3276467 0.8097366 0.91163532  0.86383957
## trial. 2    0.94967540   0.8260454 0.5215516 0.22524927  0.07661164
## trial. 3    0.07075996   0.3554906 0.9605473 0.06647341  0.12128992
## trial. 4    0.18858086   0.6417300 0.4193811 0.37501585  0.19853707
## trial. 5    0.34478070   0.3440303 0.2261023 0.24901803  0.94799296
## trial. 6    0.67610089   0.4065688 0.4779508 0.84821899  0.79172570

1.4 逻辑

逻辑值有三个。TRUE, FALSE和NA.

关系运算符

符号 描述 == equal to(等于) != equal to(不等于) < less than(小于) > more than(大于) <= less than or equal to(小于等于) >= more than or equal to(大于等于)

# 使用关系运算符进行计算,会产生逻辑值作为结果。
x <- 5
x != 3 # x等于5,所以“x不等于3”为真
## [1] TRUE
# 有一些其他的运算符或函数也会返回逻辑值,比如
7 %in% c(1,4,5,6,7) # 这个运算符是用来检测一个元素是否在另一个向量中。
## [1] TRUE
# 关系运算符具有的向量化的性质。也就是说,用于长度大于1的向量时,会返回一个同等长度的逻辑向量,且这种运算速度极快。
x <- c(2, 1, 6, 5, 4, 9, 7, 3, 10, 8)
x <= 5
##  [1]  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE FALSE
x[x <= 5] # 直接用关系运算化为Index
## [1] 2 1 5 4 3
# 有时候,你可能不需要向量化的计算,只需要一个TRUE或FALSE. all()和any()或许能帮到你。顾名思义,all()测试的是“是否全部为TRUE”,any()测试的是“是否至少有一个TRUE”:
all(x <= 5); any(x <= 5)
## [1] FALSE
## [1] TRUE

关于NA

有很多种运算会以NA作为计算结果,在此不一一列举。最重要的一个是: NA == NA 这看起来像是一个bug,然而仔细想想才发现这个设计很巧妙。假设你问我是否知道我的一些朋友写完了暑假作业。我说我不知道张三是否写完了,也不知道李四是否写完了。你再问我“张三和李四的作业完成情况是一样的吗”?鬼才知道咧!

# 这意味着不能直接使用x == NA来判断x是否是NA,而要用is.na()函数:
x <- NA
is.na(x)
## [1] TRUE

逻辑运算符

最常用的三个逻辑运算符:

符号 描述 & AND(且) # &判断是否两边运算结果都为TRUE。如果是,才会得到TRUE(即一真和一假得到假)。 | OR(或)# 判断两边运算结果是否至少有一个 TRUE,如果是,就会得到TRUE。 ! 反义符号 # 使TRUE FALSE颠倒

1 == 1 & 1 == 2 & 3 == 3 # 即:“1等于1 且 1等于2 且 3等于3”,是真还是假?
## [1] FALSE
FALSE | FALSE | TRUE # FALSE/TRUE等价于一个运算结果
## [1] TRUE
!(FALSE | TRUE) & TRUE # 注意反义符号
## [1] FALSE
!(3 < 4) 
## [1] FALSE

Chapter 02 - 基本语句和函数

2.1 if语句

# EX 1. 以下代码翻译成英语就是:If 1 + 1 = 2, print "hi". Else, print "bye".
if (1 + 1 == 2) {
  print("hi")
} else {
  print("bye")
}
## [1] "hi"
# EX 2. 代码第一行中的FALSE可以替换成任何计算结果为FALSE的运算,
# 比如1 + 1 == 3;小括号内的计算过程不重要,
# 但运算结果必须为TRUE或FALSE(不可以是NA)
if (FALSE) { 
  print("hi")
} else if (FALSE) { # 检查下一个`else if`,也是FALSE
  print("yoo")
} else if (TRUE) { # 再检查下一个`else if`,这次是TRUE
  print("hey") # 所以执行`print("hey")`
} else {
  print("bye") # 而轮不到else
}
## [1] "hey"

2.2 for循环

以下是R中for循环的伪代码:

for(i in ) {

on every i

}

for(i in x)中i的意思是x中的元素 x中有三个元素,每个元素都是一个i. 因此大括号里写的print(i * y)便是各个元素* y的意思。这个i可以替换成其他的名字(大括号内相应的名字也要变),比如num

# EX 1. 以下代码翻译成英文就是: for every element i in c(2, 4, 6, 8): 
# assign i^2 to n, then print n
for (i in c(2, 4, 6, 8)) { # i可以是任何你想要的名字,比如num
  n <- i^2 # 如果上一行是 for (num in ..., 这一行就要写成 n <- num^2
  print(n)
}
## [1] 4
## [1] 16
## [1] 36
## [1] 64
# EX 2.
x <- vector("numeric") # 创建一个空的numeric vector
for (m in 1:10) {
  if (m %% 2 == 0) {
    x <- append(x, m)
  }
}
x
## [1]  2  4  6  8 10
# EX 3. 嵌套循环, 应当尽量避免
M <- c(1, 2, 3 ,4 ,5)
N <- c(10, 100, 1000)

x <- vector("numeric")
for (m in M) {
  for (n in N) { # 在一个for循环中嵌入另一个for循环
    x <- append(x, m*n)
  }
}
x 
##  [1]   10  100 1000   20  200 2000   30  300 3000   40  400 4000   50  500 5000

append 函数

# append(x, values, after = length(x))
append(1:5, 0:2, after = 3) # 在第三个数后面插入0:2
## [1] 1 2 3 0 1 2 4 5

while循环

以下是R中while循环的伪代码:

while() {

repeat doing something

}

小括号里的内容必须是一个计算结果为TRUE或FALSE的表达式(和if/else语句类似)。

当这个条件为TRUE时,大括号内的语句将会被执行,直到小括号里的判别结果为FALSE. 需要注意的是,不要让小括号里的运算结果一直为TRUE, 否则会造成无限循环。

记得设定条件, 不要进入无限循环

x <- 1
while (x < 10) { # 当x<10的时候,执行大括号内的语句
  print(x)
  x <- x + 3 # 一定要让x的值增加,否则会进入无限循环
}
## [1] 1
## [1] 4
## [1] 7
# 一个错误的例子是:

i <- 1
# while (i < 5) {
#   print(i * 10)
# }
# i永远小于5,所以是一个无限循环。我们只需每次执行大括号里的计算时给i增加一定的值,即可解决这个问题:

i <- 1
while (i < 5) {
  print(i * 10)
  i <- i + 1 #  当i被加到5时候,i不再小于5,因此大括号内的语句不再执行。
}
## [1] 10
## [1] 20
## [1] 30
## [1] 40

break 和 next

# EX. 1
for (i in 1:10) {
  if (i == 3) {
    next # 当i == 3时,跳过它,继续(最近的)for循环的下一个回合
  } else if (i == 6) {
    break # 当i == 6时,结束(最近的)for循环
  } 
  print(i) # 只有当if和else if里的检验都为FALSE时,`print(i)`才会执行。
}
## [1] 1
## [1] 2
## [1] 4
## [1] 5
# EX. 2
M <- c(1, 2, 3, 4, 5)

x <- vector("numeric")
for (m in M) {
  while (TRUE) { # 原本while(TRUE){}将会是一个无限循环(判定条件永远TRUE)
    x <- append(x, 2*m)
    break # break打破了最近的这个while循环,而不影响for循环。
  }
}
x
## [1]  2  4  6  8 10

ifelse

ifelse()是if/else语句的向量化版本。假设我有一组长度:

l <- c(1.21, 1.34, -1.45, 1.56, 1.22, 1.10, 1.78, -1.33, 1.71)
# 我们发现有两个值是负数。长度不可能是负数,因此这些测量结果是错误的,我们需要把它们替换成NA. 这时可以用ifelse()函数:

l_1 <- ifelse(l < 0, NA, l)
l_1
## [1] 1.21 1.34   NA 1.56 1.22 1.10 1.78   NA 1.71

2.3 apply家族

用到apply()家族的一系列函数:apply(), sapply(), lapply(), mapply(), tapply(), vapply(), rapply(), eapply();此外,像Map(), rep(), seq()等函数也会执行向量化的计算。

lapply

lapply() (list, function)至少需要两个参数,第一个是对象(可以是vector或者list),第二个是函数。它的作用是把函数作用于对象中的每一个元素,并返回一个list. 无论对象是vector还是list, 返回的都是一个list.

# 有两类使用lapply()的方法。
# 第一种是使用匿名函数,这个很直观:
lapply(c(1, 2, 3), function(i) i^2*10)
## [[1]]
## [1] 10
## 
## [[2]]
## [1] 40
## 
## [[3]]
## [1] 90
# 另一种是使用有命名的函数。此时,第二个参数是函数名;随后,如果有需要,还可以加上这个函数需要的其它参数:
lapply(list(5, 6, 7), rnorm, 3, .1)
## [[1]]
## [1] 3.221980 3.093027 2.970841 2.987707 2.970066
## 
## [[2]]
## [1] 3.006255 3.088478 2.973625 2.958407 2.930638 3.034126
## 
## [[3]]
## [1] 3.105135 3.018310 3.051436 3.029396 2.905762 3.199359 2.960570
# 默认 lapply() 的对象的各元素作为函数的第一个参数。上面这个例子等同于:
rnorm(5, 3, .1) # 即 `rnorm(n = 5, mean = 3, sd = .1)`
## [1] 3.042739 2.946853 3.132229 2.935976 2.878190
# 当第一个参数在后面被指定时,lapply()的对象的各元素所代表的参数按照排序顺延,比如:
lapply(list(5, 6, 7), rnorm, n = 3, .1)
## [[1]]
## [1] 4.999437 5.108181 5.155853
## 
## [[2]]
## [1] 6.125958 5.971444 5.887496
## 
## [[3]]
## [1] 6.855051 6.661926 6.792022
# 等同于:
rnorm(n = 3, 5, .1)
## [1] 4.992940 4.937412 5.189133
# 但是这么做会降低易读性。当对象不是被作为函数的第一个参数时,最好使用匿名函数,使之更易读:
lapply(list(5, 6, 7), function(x) rnorm(3, x, .1))
## [[1]]
## [1] 5.060967 5.011685 4.963928
## 
## [[2]]
## [1] 5.849774 6.156677 6.043128
## 
## [[3]]
## [1] 6.998548 6.956731 6.915347

sapply

sapply() (list, function) 功能本质上和lapply()一样。sapply()额外的一个特点是尽可能地化简结果:

当结果只有一个分量时,sapply()返回一个vector

当结果有多个分量,但每个分量只包含一个vector且长度相等时,sapply()会返回一个matrix

# EX. 1
lapply(c(1, 2, 3), function(i) i*10)
## [[1]]
## [1] 10
## 
## [[2]]
## [1] 20
## 
## [[3]]
## [1] 30
sapply(c(1, 2, 3), function(i) i*10)
## [1] 10 20 30
# EX. 2
lapply(list(c(1, 2), c(4, 6), c(7, 9)), function(i) i*10)
## [[1]]
## [1] 10 20
## 
## [[2]]
## [1] 40 60
## 
## [[3]]
## [1] 70 90
sapply(list(c(1, 2), c(4, 6), c(7, 9)), function(i) i*10)
##      [,1] [,2] [,3]
## [1,]   10   40   70
## [2,]   20   60   90
# EX. 3
lapply(list(c(1, 2), c(4, 6), c(7, 9)), function(i) i*10)
## [[1]]
## [1] 10 20
## 
## [[2]]
## [1] 40 60
## 
## [[3]]
## [1] 70 90
sapply(list(c(1, 2, 3), c(4, 6), c(7, 9)), function(i) i*10)
## [[1]]
## [1] 10 20 30
## 
## [[2]]
## [1] 40 60
## 
## [[3]]
## [1] 70 90

rapply

rapply(list, function) 因为lapply()无法使用于含有子列表的列表。rapply()是lapply()的recursive版本,它可以使用于含有子列表的列表,并且有三种使用模式,其中两种比较常用。第一种是unlist, 它是默认的模式。它会在计算之后拆解列表至单个向量:

# unlist
rapply(list(c(1, 2, 3), list(c(4, 5, 6), list(7, 8, 9))), function(x) x * 10, how = "unlist") # 是以一个子列表为一个分量的
## [1] 10 20 30 40 50 60 70 80 90
# list, 保留了原列表的结构:
rapply(list(c(1, 2, 3), list(c(4, 5, 6), list(c("a", "b", "c")))), function(x) c(x, 1), how = "list") 
## [[1]]
## [1] 1 2 3 1
## 
## [[2]]
## [[2]][[1]]
## [1] 4 5 6 1
## 
## [[2]][[2]]
## [[2]][[2]][[1]]
## [1] "a" "b" "c" "1"

mapply

mapply()是Map()的自动化简版本: mapply(function, …)

  1. lapply()和它的衍生产物sapply()和rapply()本质上是把一个函数应用在一个向量/列表上,即这个向量/列表作为函数唯一的“自变量”。

  2. Map()则可以使用多组自变量。这意味着,lapply()能做到的,Map都能做到;Map能做到的,lapply()不一定做得到。

# Compare
# 之前lapply()的例子
lapply(c(5, 6, 7), rnorm, n = 3, .1)
## [[1]]
## [1] 5.064915 4.970829 4.936729
## 
## [[2]]
## [1] 6.051411 5.882479 5.960502
## 
## [[3]]
## [1] 7.089589 6.994848 6.979543
# Map()版本是这样的:
Map(rnorm, c(5, 6, 7), 3, .1)
## [[1]]
## [1] 2.904346 3.029560 2.940057 3.042969 3.107728
## 
## [[2]]
## [1] 2.897278 2.908505 3.064077 3.121059 3.094183 3.009256
## 
## [[3]]
## [1] 2.925081 2.837984 2.913400 2.925940 2.978479 3.049882 2.967461
# 多个自变量的计算也很自然:
# rnorm中的n是c(2,3,4), mean = c(1, 10, 100), sd是最后一个
# 对应了rnorm(2, 1, 0.1), rnorm(3, 10, 0.5), rnorm(4, 100, 0.1)
Map(rnorm, c(2, 3, 4), c(1, 10, 100), c(.1, .5, 1)) 
## [[1]]
## [1] 0.8933350 0.9779402
## 
## [[2]]
## [1] 10.502766 11.099003  9.699878
## 
## [[3]]
## [1] 101.45427  99.45937  99.84053 100.82598
mapply(rnorm, 3, c(1, 10, 100), c(.1, .5, 1)) # n都是3个
##           [,1]      [,2]     [,3]
## [1,] 1.0303440  9.722340 99.62198
## [2,] 0.9000869  9.150392 99.70705
## [3,] 1.0228908 10.566287 99.23305
# example
Map(rep, list(c(1,2), list(2,3)), 3)
## [[1]]
## [1] 1 2 1 2 1 2
## 
## [[2]]
## [[2]][[1]]
## [1] 2
## 
## [[2]][[2]]
## [1] 3
## 
## [[2]][[3]]
## [1] 2
## 
## [[2]][[4]]
## [1] 3
## 
## [[2]][[5]]
## [1] 2
## 
## [[2]][[6]]
## [1] 3

2.4 purrr

这一节需要使用purrr, 它是tidyverse的一部分。purrr 包中的apply家族函数替代进化

map(), map_dbl(), map_chr(), …

map()的使用方法和lapply()几乎一样。lapply(list(5, 6, 7), rnorm, 3, .1)用map()转写就是map(list(5, 6, 7), rnorm, 3, .1)。

map()(和下面介绍的其他函数)有一个绝招就是简写匿名函数。之前讲过,lapply()的对象默认会被作为函数的第一个参数(map()也是如此)。当不想让它作为第一个参数的时候,要使用匿名函数以保证易读性:

library(tidyverse) # 或library(purrr)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.6     ✓ dplyr   1.0.7
## ✓ tidyr   1.1.4     ✓ stringr 1.4.0
## ✓ readr   2.1.1     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
lapply(list(5, 6, 7), function(x) rnorm(3, x, .1))
## [[1]]
## [1] 5.017130 5.008887 5.142494
## 
## [[2]]
## [1] 6.260008 6.070846 5.782771
## 
## [[3]]
## [1] 6.992822 6.936216 6.719654
# 用map()的简写版本则是:
map(list(5, 6, 7), ~ rnorm(3, ., .1))
## [[1]]
## [1] 5.092694 4.996733 4.942220
## 
## [[2]]
## [1] 5.958756 5.968516 6.038103
## 
## [[3]]
## [1] 6.895229 7.101214 7.037566
# map_dbl(), map_chr()函数可以把结果化简为一个向量,前提是每次的计算结果的长度都为1(即一个标量),比如这里,mean(x), mean(y), mean(z)的结果都是一个标量,所以map()的结果可以化简为一个浮点数向量。
x = c(1, 2, 3); y = c(10, 20, 30); z = c(5, 60, 115)
map_dbl(list(x, y, z), mean)
## [1]  2 20 60

map2()和`pmap()系列

map2()使用两个因变量。

map2(.x = c(1, 100, 10000), .y = c(.1, 1, 10), ~ rnorm(5, .x, .y))
## [[1]]
## [1] 1.055072 0.903390 1.149917 1.044458 1.040040
## 
## [[2]]
## [1]  99.95073  99.48441 100.67432 100.29485 101.50550
## 
## [[3]]
## [1]  9998.562 10009.981  9993.177  9982.330 10004.651

pmap()使用多个因变量。 与Map()不同,pmap()的第一个参数是对象,第二个才是函数。你可以使用命名列表来指定使用的函数的参数:

pmap(list(mean = c(1, 100, 10000), sd = c(.1, 1, 10)), rnorm, n = 3)
## [[1]]
## [1] 0.859757 1.111731 1.157411
## 
## [[2]]
## [1]  98.90177 100.64730 100.28505
## 
## [[3]]
## [1] 10004.87 10003.22 10005.31

2.5 函数

R中所有函数的通用格式是这样的:

function(argument1 = value1, argument2 = value2, …)

# EX. 1
sample <- c(5.1, 5.2, 4.5, 5.3, 4.3, 5.5, 5.7)
# 根据传统,赋值变量时用`<-`号,赋值函数参数时才用`=`
t.test(x = sample, mu = 4.5)
## 
##  One Sample t-test
## 
## data:  sample
## t = 3.0308, df = 6, p-value = 0.02307
## alternative hypothesis: true mean is not equal to 4.5
## 95 percent confidence interval:
##  4.612840 5.558589
## sample estimates:
## mean of x 
##  5.085714
# 二元运算符和[(取子集符号)看起来一点都不像函数,而实际上它们也是函数,因此也可以用通用的格式使用他们,只是需要加上引号或转义符号:
# 可自定义的二元运算符形式为%x%, 其中x为任何字符。
"+"(2, 3) # 这样就是函数形式了
## [1] 5
`+`(2, 3) # 这样就是函数形式了
## [1] 5
"["(c("四川担担面", "武汉热干面", "兰州牛肉面", "北京炸酱面"), 2) # 取子集
## [1] "武汉热干面"

普通函数

根据通用格式(function(argument1 = value1, argument2 = value2, …))调用函数。对于二元运算符,a %x% b等价于”x”(a, b).

函数名 <- function(参数1, 参数2, …) {

对参数1和参数2

进行一系列一行或者多行计算

return(计算结果)

}

SE <- function(x) {
  s <- sd(x)
  n <- length(x)
  result <- s/sqrt(n)
  return(result)
}
# 随后,你就可以使用自定义的函数了
SE(c(5,6,5,5,4,5,6,6,5,4,5,3,8)) 
## [1] 0.3367673

匿名函数

函数不需要名字也可以执行。一般,会与apply族函数联用

sapply(1:5, function(x) x^2)
## [1]  1  4  9 16 25

二元运算符

定义二元运算符的方式和定义普通函数的方法极其类似,只是参数必须要有且仅有两个(否则作为“二元”运算符就无意义了),且运算符名称需要用引号包围。

# 比如我们可以定义一个计算椭圆面积的函数
'%el%' <- function(x, y) pi*x*y # 记得加引号! 转义

2 %el% 5
## [1] 31.41593

闭包 Closure

函数里可以包含着另一个函数,这就形成了一个闭包:

myfunc <- function(){
  a = 5
  function(){
    b = 10
    return(a*b)
  }
}
# 执行myfunc()的时候,默认结果为最后一句/一行,在这里应为内函数:
myfunc()() # 既然`myfunc()`的结果是一个函数,那么在后面再加上一个括号就是执行内函数了;内函数可以使用外函数中所定义的变量(比如这里使用了外函数的`a = 5`)
## [1] 50

关于…

有时候,你想写的函数可能有数量不定的参数,或是有需要传递给另一个函数的“其他参数”(即本函数不需要的参数),这时候可以在函数定义时加入一个名为…的参数,然后用list()来读取它们。

# arg1指定了是"foo"(通过简写),因此第一行印出"foo";
# arg2未指定,因此使用默认值100,印在第二行。
# cities和nums在形式参数中没有匹配,因此归为“…”,作为list印在第三行及之后。
my_func <- function(arg1, arg2 = 100, ...){
  other_args <- list(...) # 这就读取了...哦
  print(arg1)
  print(arg2)
  print(other_args)
}

my_func("foo", cities = c("崇阳", "Αθήνα", "つがる"), nums = c(3,4,6))
## [1] "foo"
## [1] 100
## $cities
## [1] "崇阳"   "Αθήνα"  "つがる"
## 
## $nums
## [1] 3 4 6
# Example
# function 1
calc_v <- function(v, pow = 1, times = 1, add = 0) {
  v ^ pow * times + add
}
# function 2
calc_l <- function(L, pow = 1, times = 1, add = 0) {
  rapply(L, function(l) l ^ pow * times + add, how = "list")
}
# function 3
calc <- function(data, ...) {
  if(is.list(data)) {
    calc_l(data, ...) # 即 calc_l(L = data, ...)
  } else if(is.vector(data)) {
    calc_v(data, ...) # 即 calc_v(v = data, ...)
  }
}

# pow, times和add不是calc的参数,它们以...的形式被传递给calc_l()和calc_v()
calc(c(1, 2, 3), pow = 2, add = 1)
## [1]  2  5 10
calc(list(1, 2, list(10, 20)), pow = 2, times = 2)
## [[1]]
## [1] 2
## 
## [[2]]
## [1] 8
## 
## [[3]]
## [[3]][[1]]
## [1] 200
## 
## [[3]][[2]]
## [1] 800

赋值函数外的对象

# 函数内的赋值一般只在函数内有效,比如:
x <- 5
fun1 <- function() {
  x <- 100
}
fun1()
x # 结果还是开始的5,说明函数内的赋值在函数外无效
## [1] 5
# 使用assign()函数可以在函数内赋值任意environment中的对象,其中最常见的是Global environment里的(即等价于在console中直接赋值)。
x1 <- 5
fun1 <- function() {
  assign("x1", 100, envir = .GlobalEnv)
}
fun1()
x1
## [1] 100
# 在下面的例子中,fun2()赋值了fun1()里的n, 但.GlobalEnv里的n不受影响。
n <- 1 # `GlobalEnv`里的`n` = 1
fun1 <- function() {
  n <- 10 # `fun1()`里的`n` = 10
  fun2 <- function() {
    n <- 50 # 赋值`fun2()`里的`n`
    n <<- 100 # 重赋值`fun1()`里的`n`为100
  }
  fun2() # 运行`fun2()`
  return(n) # 返回`fun1()`里的`n`
}

fun1() # 10是否变为100?
## [1] 100
n # 是否仍然是1?
## [1] 1
# 利用这个性质,我们可以使apply()族函数进行递归计算,比如求累加和:
cum = 0
sapply(1:10, FUN = function(x) {
  cum <<- cum + x
  cum
})
##  [1]  1  3  6 10 15 21 28 36 45 55

paste

paste()函数把多个字符串拼接成一个,其中参数sep指定连接符号,默认为空格:

x <- "world"
paste("Hello", x, "Bye", x, sep = "--")
## [1] "Hello--world--Bye--world"

Test 基础测试

  1. 向量取子集和逻辑运算。
x <- c(3, 4, 6, 1, NA, 8, 2, 5, NA, 9, 7)
x[-c(1, 3)] # 4, 1, NA, 8, 2, 5, NA, 9, 7
## [1]  4  1 NA  8  2  5 NA  9  7
x[(length(x)-3):length(x)] # 5, NA, 9, 7
## [1]  5 NA  9  7
x[x < 5] # 3, 4, 1, NA, 2, NA
## [1]  3  4  1 NA  2 NA
x[!(x < 5)] # 6, NA, 8, 5, NA, 9, 7
## [1]  6 NA  8  5 NA  9  7
# 如何得到(不包含NA的)所有小于5的值的向量?
x[x < 5 & is.na(x) == FALSE]
## [1] 3 4 1 2
  1. 转换年份到世纪。

写一个名为as.century()的函数,把存储着年份的向量,比如years <- c(2014, 1990, 1398, 1290, 1880, 2001),转换成对应的世纪(注意,19XX年是20世纪),像这样:

as.century <- function(x) {
  a <- (x + 100) %/% 100
  return(a)
}
as.century(c(2014, 1990, 1398, 1290, 1880, 2001))
## [1] 21 20 14 13 19 21

Chapter 03 - Dataframe 和 Tibble