library(magrittr)
第2章 数据结构
2.1 向量
两种向量:原子向量,列表,三个共同属性:
- typeof()
- length()
- attributes() 附加任意元数据
a <- 1:3
typeof(a)
## [1] "integer"
length(a)
## [1] 3
attributes(a)
## NULL
l <- list(a)
typeof(l)
## [1] "list"
length(l)
## [1] 1
attributes(l)
## NULL
2.1.1 原子向量
四种常见类型:
- 逻辑型
- 整型
- 双精度型
- 字符型
原子向量总是被展开
c(1,c(2,c(3,4)))
## [1] 1 2 3 4
给定一个向量,使用typeof()来确定类型,或使用is.character()查看是否为指定类型。
is.numeric判断向量的数值型
强制转换
当把不同类型的数据结合成一个向量时,强制转换为最具灵活性的数据类型。
灵活性由低到高:逻辑,整型,双精度,字符型
2.1.2 列表
列表中的元素可以是任意的,使用列表来构造更复杂的数据结构。
2.2 属性
所有对象可以通过任意附加的属性来存储对象的元数据
- 可以通过attr()单独访问对象的每一个属性;
- 也可以通过attributes()同时访问所有的属性;
a <- 1:10
attr(a, "my_attri") <- "This is a vector"
attr(a, "my_attri")
## [1] "This is a vector"
3个最重要的属性:
- name,使用names()访问
- dimension,使用dim()访问
- class,使用class()访问
元素的命名:唯一性命名是非常有用的。
names(a) <- NULL
来去掉元素的名称
2.1.1 因子
因子构建在整型向量的基础上,levels()定义因子中所有可能的取值
使用因子而不是字符向量可以知道没有观测的可能取值
特殊方式编码缺失值
x <- read.csv("···",na.strings=".")
可以通过stringsAsFactors=FALSE设置读取过程不转换为因子,但不建议修改全局设置(会引起依赖包产生不可预测行为)。
因子型本质上是整型而不是字符串
2.3 矩阵和数组
可以通过将原子向量添加dim()属性将其变为矩阵
a <- 1:20
dim(a) <- c(4,5)
a
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 5 9 13 17
## [2,] 2 6 10 14 18
## [3,] 3 7 11 15 19
## [4,] 4 8 12 16 20
rownames(a) <- LETTERS[1:4]
colnames(a) <- letters[1:5]
a
## a b c d e
## A 1 5 9 13 17
## B 2 6 10 14 18
## C 3 7 11 15 19
## D 4 8 12 16 20
attributes(a)
## $dim
## [1] 4 5
##
## $dimnames
## $dimnames[[1]]
## [1] "A" "B" "C" "D"
##
## $dimnames[[2]]
## [1] "a" "b" "c" "d" "e"
可以使用t()对矩阵转置
列表矩阵
l <- list(1:3,"a",T,1.0)
l
## [[1]]
## [1] 1 2 3
##
## [[2]]
## [1] "a"
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] 1
dim(l) <- c(2,2)
l
## [,1] [,2]
## [1,] Integer,3 TRUE
## [2,] "a" 1
列表矩阵用于按网状结构存储数据。
2.4 数据框(矩阵+列表)
2.4.1 构建
使用data.frame()构建,默认将字符串转换成因子,使用stringAsFactors=F来禁止。
2.4.2 类型判断
x <- 1:6
y <- letters[1:6]
df <- data.frame(x,y)
typeof(df)
## [1] "list"
class(df)
## [1] "data.frame"
is.data.frame(df)
## [1] TRUE
2.4.3 合并
cbind,rbind
列向合并时,两数据框行数保持一致,行名可忽略;
行向合并时,列名和列数都必须一致。
特殊列
对列表使用data.frame,会尝试将每个元素放入自己的列中。
df <- data.frame(x = 1:3)
df$y <- list(1:2,1:3,1:4)
df
## x y
## 1 1 1, 2
## 2 2 1, 2, 3
## 3 3 1, 2, 3, 4
第3章 子集选取
3.1 数据类型
3.1.1 原子向量
空索引返回原始向量。
a <- 1:10
a[]
## [1] 1 2 3 4 5 6 7 8 9 10
0索引返回长度为0的向量,在创建测试数据时有用
a[0]
## integer(0)
3.1.2 列表
- []总是返回列表
- [[]]和$将列表中的元素取出
3.1.3 矩阵和数组
dim(a) <- c(2,5)
a
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 3 5 7 9
## [2,] 2 4 6 8 10
a[2,3]
## [1] 6
3.1.4 数据框
如果仅仅使用一个单一的向量,数据框的行为像列表;如果使用两个向量,数据框的行为像矩阵。列表不回退化,矩阵在一维时退化成原子向量。
df <- data.frame(x=1:3,y=3:1,z=letters[1:3])
str(df["x"])
## 'data.frame': 3 obs. of 1 variable:
## $ x: int 1 2 3
str(df[,"x"])
## int [1:3] 1 2 3
3.2 子集选取运算符
3.2.1 简化与保留
最后的子集是不是保持着原来的数据结构。
- 简化:有利于交互式分析;
- 保持:有利于编程;drop=F保持。输入与输出相同
简化的类型:
- 原子向量:去除名字;
- 列表:返回列表元素,而不是列表;
- 因子:扔掉所有不用的水平;
- 矩阵或数组:抛弃所有长度为1的维度;
- 数据框:如果输出只有一列,返回一个向量而不是数据框
df <- data.frame(a=1:2,b=1:2)
str(df[1])
## 'data.frame': 2 obs. of 1 variable:
## $ a: int 1 2
str(df[[1]])
## int [1:2] 1 2
str(df[,"a",drop=F])
## 'data.frame': 2 obs. of 1 variable:
## $ a: int 1 2
str(df[,"a"])
## int [1:2] 1 2
3.2.2 $运算符
$与[[]]的两个区别:
- $后不能跟存储列名的变量;
- $部分匹配,[[]]全部匹配;
3.3 子集选取与赋值
子集选取时使用空索引会保持原有对象类和数据结构
mtcars[] <- lapply(mtcars, as.integer)
str(mtcars)
## 'data.frame': 32 obs. of 11 variables:
## $ mpg : int 21 21 22 21 18 18 14 24 22 19 ...
## $ cyl : int 6 6 4 6 8 6 8 4 4 6 ...
## $ disp: int 160 160 108 258 360 225 360 146 140 167 ...
## $ hp : int 110 110 93 110 175 105 245 62 95 123 ...
## $ drat: int 3 3 3 3 3 2 3 3 3 3 ...
## $ wt : int 2 2 2 3 3 3 3 3 3 3 ...
## $ qsec: int 16 17 18 19 17 20 15 20 22 18 ...
## $ vs : int 0 0 1 1 0 1 0 1 1 1 ...
## $ am : int 1 1 1 0 0 0 0 0 0 0 ...
## $ gear: int 4 4 4 3 3 3 3 4 4 4 ...
## $ carb: int 4 4 1 1 2 1 4 2 2 4 ...
mtcars <- lapply(mtcars, as.integer)
str(mtcars)
## List of 11
## $ mpg : int [1:32] 21 21 22 21 18 18 14 24 22 19 ...
## $ cyl : int [1:32] 6 6 4 6 8 6 8 4 4 6 ...
## $ disp: int [1:32] 160 160 108 258 360 225 360 146 140 167 ...
## $ hp : int [1:32] 110 110 93 110 175 105 245 62 95 123 ...
## $ drat: int [1:32] 3 3 3 3 3 2 3 3 3 3 ...
## $ wt : int [1:32] 2 2 2 3 3 3 3 3 3 3 ...
## $ qsec: int [1:32] 16 17 18 19 17 20 15 20 22 18 ...
## $ vs : int [1:32] 0 0 1 1 0 1 0 1 1 1 ...
## $ am : int [1:32] 1 1 1 0 0 0 0 0 0 0 ...
## $ gear: int [1:32] 4 4 4 3 3 3 3 4 4 4 ...
## $ carb: int [1:32] 4 4 1 1 2 1 4 2 2 4 ...
3.4 应用
3.4.1 查询表
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(lookup[x])
## [1] "Male" "Female" NA "Female" "Female" "Male" "Male"
3.4.2 人工比对与合并
grades <- c(1,2,2,3,1)
info <- data.frame(
grade = 3:1,
desc = c("Excellent","Good","Poor"),
fail = c(F,F,T)
)
info
## grade desc fail
## 1 3 Excellent FALSE
## 2 2 Good FALSE
## 3 1 Poor TRUE
grades
## [1] 1 2 2 3 1
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
rownames(info) <- info$grade
info[as.character(grades),]
## grade desc fail
## 1 1 Poor TRUE
## 2 2 Good FALSE
## 2.1 2 Good FALSE
## 3 3 Excellent FALSE
## 1.1 1 Poor TRUE
3.4.3 随机样本(自助法)
df <- data.frame(x=rep(1:3,each=2),y=6:1,z=letters[1:6])
df
## x y z
## 1 1 6 a
## 2 1 5 b
## 3 2 4 c
## 4 2 3 d
## 5 3 2 e
## 6 3 1 f
df[sample(nrow(df)),]
## x y z
## 2 1 5 b
## 5 3 2 e
## 1 1 6 a
## 4 2 3 d
## 3 2 4 c
## 6 3 1 f
df[sample(nrow(df),3),]
## x y z
## 3 2 4 c
## 6 3 1 f
## 5 3 2 e
df[sample(nrow(df),6,rep=T),]
## x y z
## 1 1 6 a
## 3 2 4 c
## 4 2 3 d
## 5 3 2 e
## 3.1 2 4 c
## 2 1 5 b
3.4.4 排序
df2 <- df[sample(nrow(df)),3:1]
df2
## z y x
## 3 c 4 2
## 5 e 2 3
## 4 d 3 2
## 2 b 5 1
## 1 a 6 1
## 6 f 1 3
df2[order(df2$x),]
## z y x
## 2 b 5 1
## 1 a 6 1
## 3 c 4 2
## 4 d 3 2
## 5 e 2 3
## 6 f 1 3
df2[,order(names(df2))]
## x y z
## 3 2 4 c
## 5 3 2 e
## 4 2 3 d
## 2 1 5 b
## 1 1 6 a
## 6 3 1 f
3.4.5 展开重复记录
df <- data.frame(x=c(2,4,1),y=c(9,11,6),n=c(3,5,1))
df
## x y n
## 1 2 9 3
## 2 4 11 5
## 3 1 6 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
3.4.6 剔除数据框中的某些列
把列设为NULL
3.4.7 根据条件选取行
mtcars <- data.frame(mtcars)
mtcars[mtcars$gear==5,]
## mpg cyl disp hp drat wt qsec vs am gear carb
## 27 26 4 120 91 4 2 16 0 1 5 2
## 28 30 4 95 113 3 1 16 1 1 5 2
## 29 15 8 351 264 4 3 14 0 1 5 4
## 30 19 6 145 175 3 2 15 0 1 5 6
## 31 15 8 301 335 3 3 14 0 1 5 8
mtcars[mtcars$gear==5 & mtcars$cyl==4,]
## mpg cyl disp hp drat wt qsec vs am gear carb
## 27 26 4 120 91 4 2 16 0 1 5 2
## 28 30 4 95 113 3 1 16 1 1 5 2
3.4.8 布尔代数与集合
which将布尔转化为整数
x <- sample(10)<4
which(x)
## [1] 3 4 10
除非选取第一个TRUE值,否则不要用which将逻辑转为整数再选取
第6章 函数
函数本身就是对象
6.1 函数的组成部分
所有的R函数包含3个部分:
- body() 函数的内部代码;
- formals() 控制如何调用函数的参数列表;
- environment() 函数变量位置的“地图”;
6.1.1 原函数
.Primitive()直接调用C代码,不包含R代码
class(sum)
## [1] "function"
is.primitive(sum)
## [1] TRUE
objs <- mget(ls("package:base"),inherits=T)
funs <- Filter(is.function,objs)
哪个函数有最多的参数
funcformalslen <- lapply(funs, formals) %>% sapply(length)
which(funcformalslen==max(funcformalslen))
## scan
## 941
多少个基础函数没有参数
sum(funcformalslen==0)
## [1] 226
找到所有的原函数
lapply(funs, is.primitive) %>% as.logical() %>% sum()
## [1] 183
6.2 词法作用域
- 名字屏蔽:先在最内层寻找,逐层向外寻找;
- 函数与变量:避免函数与变量名字相同;
- 重新开始:函数在每次调用时创建新环境,运行结束后,内部变量释放;
- 动态查找:当函数运行时,R才开始查找,创建时不查找。
注意:全局环境的变量对函数运行结果产生影响。
6.3 每个运算都是一次函数调用
- Everything that exists is an object.
- Everything that happens is a function all.
重音符可以引用预留的函数,将中辍运算符变为标准的函数调用格式。
`+`(1,2)
## [1] 3
`for`(i,1:2,print(i))
## [1] 1
## [1] 2
x <- 5:10
`[`(x,3)
## [1] 7
`{`(print(1),print(2),print(3))
## [1] 1
## [1] 2
## [1] 3
match.fun()查找给定名字(字符串)的函数对象
match.fun("sum")
## function (..., na.rm = FALSE) .Primitive("sum")
sapply(1:5, `+`,3)
## [1] 4 5 6 7 8
sapply(1:5, "+",3)
## [1] 4 5 6 7 8
x <- list(1:3,4:9,10:12)
sapply(x,"[",2)
## [1] 2 5 11
R中发生的一切都是函数调用
6.4 函数参数
6.4.1 函数调用
实参映射到形参
通常只对第一个或前两个参数使用位置匹配
6.4.2 使用参数列表调用函数
args <- list(1:10,na.rm=T)
args
## [[1]]
## [1] 1 2 3 4 5 6 7 8 9 10
##
## $na.rm
## [1] TRUE
do.call(mean,list(1:10,na.rm=T))
## [1] 5.5
mean(1:10,na.rm=T)
## [1] 5.5
6.4.3 默认参数和缺失参数
所有参数的默认值可以通过其他参数来定义,甚至可以用函数内部创建的变量来定义。
可以使用missing()函数来确定一个参数是否被设置了
将默认值设置为NULL,然后使用函数is.null()来检查这个参数是否被设置了。
6.4.4 惰性求值
参数只在实际被用到时求值
6.4.5 …参数
这个参数将与所有没有匹配的参数进行匹配,并很容易地传递给其他函数。
如果想手机参数来调用其他函数,又不想提前设定参数名时,这个参数很有用。
经常与S3泛型函数联合使用以便使单个方法更加灵活。
可使用list(,,,)来捕获
f <- function(...){
names(list(...))
}
f(a=1,b=2)
## [1] "a" "b"
使用…的代价是任何拼写错误不会产生错误警告
6.5 特殊调用
6.5.1 中辍运算符
所有用户创建的中辍函数必须以%开头,以%结尾;
R里面预定义的中辍函数: - %% - %*% - %/% - %in% - %o% - %x%
`%+%` <- function(a,b) paste(a,b,sep="")
"new" %+% "string"
## [1] "newstring"
`%+%`("new","string")
## [1] "newstring"
6.5.2 替换函数
替换函数看上就就像对参数进行原地修改,通常只有两个参数x和value,必须返回被修改对象。
`second<-` <- function(x,value){
x[2] <- value
x
}
x <- 1:10
second(x) <- 5
x
## [1] 1 5 3 4 5 6 7 8 9 10
如果想提供附加参数,可以放在x和value之间
`modify<-` <- function(x,position,value){
x[position] <- value
x
}
modify(x,1) <- 10
x
## [1] 10 5 3 4 5 6 7 8 9 10
经常把替换与子集选取一起使用
x <- c(a=1,b=2,c=3)
names(x)
## [1] "a" "b" "c"
names(x)[2] <- "two"
names(x)
## [1] "a" "two" "c"
6.6 返回值
一个函数中最后一个被计算的表达式成为函数的返回值
可以返回一个包含任意数量对象的列表
实参传递给形参后,实参失效。复制后修改,对函数的参数进行修改不会改变参数的原始值。
当函数退出时可以使用on.exit()来触发其他事件。