R语言编程艺术,第一章:快速入门
1.生成n个基于N(0,1)分布的随机数rnorm(n)
取绝对值abs(),
取平均值mean(),
取标准差sd()
2.使用批处理命令:把下列命令保存在z.R的文件中
pdf(“xh.pdf”) #保存为pdf格式的图形文件并命名为xh.pdf
hist(rnorm(100)) #调用hist函数生成直方图
dev.off() #关闭图形设备,即把生成的图形文件存盘
如何调用呢?
R CMD BATCH z.R
3.创建一个向量:
x <- c(1,2,4) #c是连接的意思
q <- c(x,x,8) #可以创建包括向量的向量 q为 1 2 4 1 2 4 8
4.元素的索引: R是从1开始索引的
x[3]表示的时索引向量中的第三个元素
索引片段:x[2:3]
可以把这些索引值存储在片段中,使用赋值符号<-使用注释是一个好的习惯,在R中的交互式命令中也可以使用,因为查看历史命令可以帮助你回忆当时是怎么思考的
5.R会内置一部分数据集,使用data()查看
6.q()退出,注意在大型的数据集中,最好保存历史会话

二 函数入门
1.计算向量中余数的个数

oddcount <- function(x){
k <- 0 # 给计数器赋初值
for(n in x){
if(n %% 2 == 1) k <- k+1 # R的取余为 %%
}
return (k)
}

 

2.在函数内部使用的变量都是局部变量,他们在函数返回值之后就撤消了
全局变量则是在函数之外创建的变量,在函数内部也可以访问:
f <- function(x) return(x+y) #定义函数
y <- 3
f(5)的结果是 8
3.关于函数调用,赋值的问题,先给定一个函数:
f <- function(x=1,y,z) return(x+y+z)
虽然函数缺省x的值,但是调用时还要小心
f(2,3) #函数将把2重新赋给x,把3赋给y,z没有值,错误
f(y=2,z=3) #指明变量的值,正确
f(2,2,3) #函数将重新把x的值赋为2,然后y是2,z是3
4.列表list
创建一个列表吧
x <- list(u=2,v="abc")
索引列表中的值用$
x$u
5.矩阵
如何创建矩阵?
行绑定:rbind() 即按行绑定:
m <- rbind(c(1,4),c(2,2))
> m
     [,1] [,2]
[1,]  1    4
[2,]  2    2
列绑定:cbind() 按列绑定:
n <- cbind(c(1,4),c(2,2))
> n
     [,1] [,2]
[1,]  1    2
[2,]  4    2
6.混合型的数据矩阵称为数据框
d <- data.frame(list(kids=c("Jack","Jill"),ages=c(12,10)))
> d
kids ages
1 Jack 12
2 Jill 10

1.添加或删除向量元素
实际上就是重新对变量赋值
x <- c(1,2,3,5)
x <- c(x[1:3],4,x[4]) #我想添加一个向量元素4使之变成(1,2,3,4,5)
x <- c(x[1:3]) #我想删除一个向量元素5使之变成(1,2,3)
2.关于循环的再认识
循环的目的是什么?是遍历向量中各个元素的值?还是不仅要求值还想要取得该元素的序列?
如果只要求使用元素的值,使用下列的写法:
for(n in x){
statement }
如果不单单要求值,还要取得元素的索引号,使用下面的写法:
for (i in 1:length(x)){
statement } #这里的x[i]与上面的n相同,代表向量中各个元素的可能取值
3.遍历取值的时候,应避免向量元素个数小于1
正常情况下是这样的:
x <- c(1,2,3)
> 1:length(x)
[1] 1 2 3
如果是这样:
x <- c()
> 1:length(x)
[1] 1 0 #这明显不是我们想要的结果
4.矩阵在内存序列中是如何储存的?
是按列存储的,不论是如何生成矩阵的,是按列绑定cbind,还是按行绑定rbind,在内存序列中
都是按列存储的
如何验证?
m <- rbind(c(1,2),c(3,4)) #这里的矩阵m是按行绑定生成的
> m
    [,1] [,2]
[1,] 1     2
[2,] 3     4
> m + 10:13
    [,1] [,2]
[1,] 11  14
[2,] 14  17
显然,矩阵m按列存储为一个四元向量(1,3,2,4),加上(10,11,12,13)得到(11,14,14,17)
5.同多数脚本语言一样,R不需要事先声明变量,注意这里的"不需要"只是其中的一种方法.
创建向量的另一种方法:(声明变量法)
y <- vector(length=2) #为变量y分配两个存储空间,没有赋值的情况下,两个都是FALSE
y[1] <- 5 #一旦对其中任何一个元素赋值,其它元素自动置为0,此时y[2]为0
y[2] <- 12 #生成一个(5,12)的二元向量
说明一下为什么需要上述的方法:如果在循环体中每次使用类似于y <- c(5,12)的赋值,会
减慢代码的运行速度,因此使用上述方法在一开始就分配内存空间,而进入循环体后只是填充
空间.
另一种方法就是常见的
y <- c(5,12)
6.循环补齐
(1)向量与向量相加
如果长度不相等,短的就会自动与长的补齐,
注意,这里不是补零!而是循环补
c(1,2,3)
c(1,2,3,1,2) #补成5位的
c(1,2,3,1,2,3,1) #补成7位的
(2)向量与矩阵相加
注意,因为矩阵无法循环补齐,故只能是短向量与长矩阵相加时,会出现短向量循环补齐
(3)矩阵与矩阵相加
只能是相同行和列的矩阵相加,不存在补齐的情况!
7.索引不连续的向量
x <- c(1,5,2,0,5,6,7,8)
> x[c(1,3,4)] #索引x向量的第一位,第三位和第四位元素
[1] 1 2 0
> x[1:3] #索引连续元素的方法:x向量的前三个元素
[1] 1 5 2
> x[c(1,1)] #重复索引第一位的元素
[1] 1 1
以上都是对R说我想要某某元素,还有一种方法是:我不想要某某元素,其它的都要:
> x[c(-1)] #除了第一位元素,其它都要
[1] 5 2 0 5 6 7 8
> x[c(-3:-5)] #除了第三到第五位元素,其它都要
[1] 1 5 6 7 8
8.创建向量
回忆下我们目前知道的创建向量的方法:
x <- c(1,2,3,4,5,7,5,9)
如果想创建连续值得向量(注意他们的步进都是1):
x <- 1:8 #正向
x <- 5:-2 #负向
想改变步进的大小?没问题,使用seq()函数:
x <- seq(from=12,to=29,by=3)
> x #注意这里终止于27,而不是29,因为它们之间的间隔小于步进3
[1] 12 15 18 21 24 27 #上述函数可以理解为生成开始于12,步进为3,最大不超过29的序列
同样生成负向的序列:
> seq(from=0.8,to=-0.1,length=10) #这里规定了向量的总长度,自动计算步进
[1] 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0.0 -0.1
seq()的另一个作用:
seq(x)与1:length(x)的作用相同,甚至更优,因为对于空值x
x <- c()
> 1:length(x)
[1] 1 0 #这明显不是我们想要的结果
> seq(x)
integer(0) #该结果会是上面循环迭代0次,符合我们的期望
创建重复序列的向量:rep(x,times)
> rep(c(1,3,8),4)
[1] 1 3 8 1 3 8 1 3 8 1 3 8
> rep(c(1,3,8),each=4) #每个元素重复4遍
[1] 1 1 1 1 3 3 3 3 8 8 8 8
9.布尔值求解any(),all()
x <- 1:9
> any(x>5) #x中有>5的元素吗?
[1] TRUE
> all(x>5) #x中所有元素都>5吗?
[1] FALSE



实例一:寻找连续出现1的游程
x <- c(1,0,0,1,1,1,0,1,1)

#version 1.0
findruns <- function(x,k){ #k是指连续出现1的个数
n <- length(x)
runs <- NULL
for (i in 1:(n-k+1)){ #为什么不是1:n?因为要保证连续的k个数比较
if(all(x[i:(i+k-1)]==1)) #这个判断值真的是太绝妙了!
runs <- c(runs,i) #重复赋值,可能会拖慢代码的运行速度
}
return(runs)
}
#version 2.0
findruns <- function(x,k){
n <- length(x)
runs <- vector(length=n) #不同于上个版本的地方,先声明runs的变量
count <- 0 #为runs分配的空间是用不完的,count计算用了多少位
for (i in 1:(n-k+1)){
if (all(x[i:(i+k-1)]==1)){
count <- count + 1
runs[count] <- i #这里count也充当runs的序列索引号
}
}
if (count > 0){
runs <- runs[1:count] #释放未利用的内存空间
} else runs <- NULL
return (runs)
}

 

实例二:预测离散值时间序列

preda <- function(x,y){
n <- length(x)
k2 <- k/2 #如果在连续k天内1的数量>= k/2

pred <- vector(length=n-k)
for (i in 1:(n-k)) {
if (sum(x[i:(i+k-1)]) >= k2) #如果在连续k天内1的数量>= k/2
pred[i] <- 1 #那么预测下一个值为1
else
pred[i] <- 0 
}
return(mean(abs(pred-x[(k+1):n])))
}

 

但在实际中很少用诸如
sum(x[i:(i+k-1)])计算连续k个元素之和,因为重复计算,这会拖慢代码速度
我们可以使用cumsum()函数代替:cumsum是计算向量的累积和
y <- c(5,2,-3,8)
> cumsum(y)
[1] 5 7 4 12
比如我想计算第二个到第四个数的和
x <- cumsum(y)
x[4] - x[1]
那第一个到第三个数之和呢? 为了统一形式:
x[3] - x[0] 但R中向量元素是从零开始索引的,为了形式上的统一
我们使用如下办法:
csx <- c(0,cumsum(y))
这样以来我们就可以写成
csx[4] - scx[1] 来表示向量元素中第一个数到第三个数之和

基于此,我们更改第一个版本的代码:

preda <- function(x,y){
n <- length(x)
k2 <- k/2 #如果在连续k天内1的数量>= k/2

pred <- vector(length=n-k)
csx <- c(0,cumsum(x)) #新添的代码!
for (i in 1:(n-k)) {
if (csx[i+k] - csx[i] >= k2) #如果令i=1,k=3,该语句即为csx[4] - csx[1] #来表示向量元素中第一个数到第三个数之和
pred[i] <- 1 #那么预测下一个值为1
else
pred[i] <- 0 
}
return(mean(abs(pred-x[(k+1):n])))
}

 

第三章:矩阵和数组
1.如何创建矩阵
> y <- matrix(c(1,2,3,4),nrow=2,ncol=2) #矩阵在R中是按列存储的
> y
     [,1] [,2]
[1,] 1     3
[2,] 2     4
该方法与之前的通过行(列)绑定的方法无异:
> x <- cbind(c(1,2),c(3,4))
> x
    [,1] [,2]
[1,] 1   3
[2,] 2   4
> all(x==y)
[1] TRUE
录入数据时,有时希望录入的数据元素可以按行排列,
这时只需令 byrow=1 即可,但是注意,矩阵在R中依然是按列存储的
> m <- matrix(c(1,2,3,4,5,6),nrow=2,byrow=1)
> m
     [,1] [,2] [,3]
[1,] 1     2     3
[2,] 4     5     6

类似于向量的创建,矩阵还可以先声明,再赋值
> y <- matrix(nrow=2,ncol=2)
> y
     [,1] [,2]
[1,] NA NA
[2,] NA NA
> y[1,1] <- 1
> y[1,2] <- 3
> y[2,1] <- 2
> y[2,2] <- 4
> y
    [,1] [,2]
[1,] 1     3
[2,] 2     4

2.一般矩阵运算
这里先介绍三种:
数量乘法运算:3*y
矩阵相加运算:y+y
矩阵相乘运算:y %*% y

3.矩阵索引:
列索引: z[,2:3] #索引z矩阵的所有行,第二到第三列
行索引: z[2:3,] #索引z矩阵的第二到第三列,所有的列
不连续行的索引: z[c(1,3),]
负值索引:即索引除去该值外的所有元素
> y <- matrix(c(1,2,3,4,5,6),ncol=2) #只规定行或列即可
> y
    [,1] [,2]
[1,] 1   4
[2,] 2   5
[3,] 3   6
> y[-2,] #索引y矩阵除第二行外的所有元素
     [,1] [,2]
[1,] 1    4
[2,] 3    6

4.改变矩阵的值
> m
     [,1] [,2] [,3]
[1,] 1     2     3
[2,] 4     5     6
> m[,c(1,3)] <- matrix(c(1,2,3,4),nrow =2)
> m
     [,1] [,2] [,3]
[1,]  1     2     3

5.矩阵元素的筛选:
> y
      [,1] [,2]
[1,] 1   4
[2,] 2   5
[3,] 3   6
> y[y[,2]>=5,] #筛选出y矩阵中第二列>=5的所有行,并组成所有新的矩阵
     [,1] [,2]
[1,] 2     5
[2,] 3     6
扩展案例:如何生成协方差矩阵?

makecov <- function(rho,n){
m <- matrix(nrow=n,ncol=n) #声明一个n行n列的矩阵
m <- ifelse(row(m)==col(m),1,rho) #对角线元素为1,其它位置是协方差系数
return(m)
}

> makecov(0.2,3)

     [,1] [,2] [,3]
[1,] 1.0 0.2 0.2
[2,] 0.2 1.0 0.2
[3,] 0.2 0.2 1.0

6.apply()函数
有时我们要对矩阵的行或列做这样或那样的运算,apply()函数就派上用场了,
apply函数有三个参数apply(m,dimcode,f,fargs)
m 矩阵名称
dimcode:只有两个值,1把函数运用到每一行,2把函数运用到每一列
f 运用到每行或每列的函数
fargs 函数的参数
> copymaj <- function(rw,d){
+ maj <- sum(rw[1:d])/d #计算每行前d个数的平均值
+ return(ifelse(maj>0.5,1,0)) #>0.5返回1,否则返回0
+ }
> x <- matrix(c(1,0,1,1,0,1,1,1,1,0,1,0,0,1,1,0,1,1,1,0),nrow=4,byrow=T)
> x #参数byrow=T表示录入的元素按行排列
     [,1] [,2] [,3] [,4] [,5]
[1,] 1     0     1     1     0
[2,] 1     1     1     1     0
[3,] 1     0     0     1     1
[4,] 0     1     1     1     0
> apply(x,1,copymaj,3)
[1] 1 1 0 1 #每行的结果都是按列存储的,共有4行,所以结果是4列
> apply(x,1,copymaj,2)
[1] 0 1 0 0
> apply(x,2,copymaj,2) #对矩阵的每列进行运算,矩阵有5列,结果也是5列
[1] 1 0 1 1 0
> apply(x,2,copymaj,3)
[1] 1 0 1 1 0
扩展案例:寻找异常值:

findols <- function(x){
findol <- function(xrow){
mdn <- median(xrow) #寻找每一行的中位数
devs <- abs(xrow - mdn) #把每一行元素与中位数的差值赋给devs向量
return(which.max(devs)) #找到devs向量中最大元素的位置
}
return(apply(x,1,findol)) #把findol函数用到x矩阵的每一行
}

> y <- matrix(c(1,4,6,3,2,5,7,29,53,76,3,2,1,987,3,56,54,22,2,1,8),nrow=3,byrow=T)

> y
      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 1     4     6     3     2     5     7
[2,] 29   53   76   3     2     1   987
[3,] 3    56    54   22    2    1     8
> findols(y)
[1] 1 7 2

7.和向量一样,在循环中重复不断的创建矩阵是很浪费时间的行为,
一个比较好的办法就是,在循环体外先声明一个空的矩阵,
然后在循环中为矩阵赋值

8.避免意外降维
当我们从某一矩阵抽取其中的一行作为子矩阵时,得到的却变为了一个向量,
> z <- matrix(1:8,nrow=4)
> z
    [,1] [,2]
[1,] 1     5
[2,] 2     6
[3,] 3     7
[4,] 4     8
> r <- z[2,]
> r
[1] 2 6
因为这里
> nrow(r)
NULL #故r是一个向量,这与我们的预期不符,
解决办法就是使用drop参数
> r <- z[2,,drop=FALSE]
> r
    [,1] [,2]
[1,] 2   6
此时有:
> nrow(r)
[1] 1
这里提供另外的一个小技巧:把向量变为矩阵as.matrix()
> u <- 1:3
> u
[1] 1 2 3
> as.matrix(u)
     [,1]
[1,] 1
[2,] 2
[3,] 3

9.给矩阵的行或列取名字
> z
    [,1] [,2]
[1,] 1  5
[2,] 2  6
[3,] 3  7
[4,] 4  8

> colnames(z) <- c("a","b") #给z矩阵的列重新命名
> z
      a b
[1,] 1 5
[2,] 2 6
[3,] 3 7
[4,] 4 8
> rownames(z) <- c("甲","乙","丙","丁") #给z矩阵的行重新命名
> z
    a b
甲 1 5
乙 2 6
丙 3 7
丁 4 8
不过索引的时候也要加上引号:
> z[c("甲","丙"),]
    a b
甲 1 5
丙 3 7

10.高维数组
高维数组其实就是矩阵的集合,有三个维度
生成一个高维的数组:
> firsttest <- matrix(c(46,21,50,30,25,50),nrow=3)
> secondtest <- matrix(c(46,41,50,43,35,50),ncol=2)
> firsttest
     [,1] [,2]
[1,] 46 30
[2,] 21 25
[3,] 50 50
> secondtest
     [,1] [,2]
[1,] 46 43
[2,] 41 35
[3,] 50 50
> tests <- array(data=c(firsttest,secondtest),dim=c(3,2,2))
> tests #注意array中,有数据data部分和维度dim部分
, , 1 #其中dim=c(x,y,n)中的n是data=c(a,b,c,...)中的矩阵个数

    [,1] [,2]
[1,] 46 30
[2,] 21 25
[3,] 50 50

, , 2

    [,1] [,2]
[1,] 46 43
[2,] 41 35
[3,] 50 50

 

第四章 列表
1.一个简单地列表如下:
> j <- list(name="Joe",salary=55000,union=T)
> j
$name #各组件的名字叫做"标签"(可以不指定)
[1] "Joe"

$salary #标签
[1] 55000

$union #标签
[1] TRUE
因为列表是向量,故可以用vector创建列表:
> z <- vector(mode="list") #声明一个模式是"列表"的向量
> z[["name"]] <- "Pony" #然后可以给列表不断的赋值
> z[["salary"]] <- 33
...

2.三种方法索引列表中的各组件:
> j$name #列表名+$+组件名称
[1] "Joe"
> j[[1]] #列表名后跟双层中括号,括号内为索引组件在列表中得序号
[1] "Joe"
> j[["name"]] #列表名后跟双层中括号,括号内为带有双引号的组件名称
[1] "Joe"
#注意与使用单层中括号的区别
> j["name"] #使用单层中括号返回的是该组件及组件的内容,可以视为一个子列表
$name #使用双层中括号返回的是该组件的内容
[1] "Joe"

3.增加或删除列表元素:这里的列表元素指的是组件
只能增加或删除组件,然后给组件赋值
> z <- list(a="abc",b=12)
> z
$a
[1] "abc"

$b
[1] 12

> z$c <- "sailing" #增加一个组件c并赋值为sailing
> z
$a
[1] "abc"

$b
[1] 12

$c                #增加的组件c
[1] "sailing"  #及内容
如何删除组件b呢?
只需给组件b赋值为NULL即可
> z$b <- NULL #删除组件b的一种方法
> z
$a
[1] "abc"

$c #注意删除一个组件后,其它组件在列表中的序号可能会发生变动
[1] "sailing"
注意隐藏的类型转换
> z[1:3] <- c(168,"Cool",TRUE) #z中各组件的内容全部被视为character型
> z
[[1]]
[1] "168"

[[2]]
[1] "Cool"

[[3]]
[1] "TRUE"
> class(z[[3]])
[1] "character"

> z[1:3] <- c(168,FALSE,TRUE) #z中各组件的内容全部被视为numeric型
> z
[[1]]
[1] 168

[[2]]
[1] 0

[[3]]
[1] 1
> class(z[[3]])
[1] "numeric"
> z[1:3] <- c(TRUE,FALSE,TRUE) #这个当然都为为logical型
> z
[[1]]
[1] TRUE

[[2]]
[1] FALSE

[[3]]
[1] TRUE
> class(z[[3]])
[1] "logical"
上面三个例子说明了什么?
当要创建一个列表时,如果使用索引添加新组件,则c()中的内容会被置为统一数据类型
此时应加以关注,避免产生非预期的结果!
最后,还可以把多个列表拼接成一个:
> c(list("Joe",55000,T),list(5))
[[1]]
[1] "Joe"

[[2]]
[1] 55000

[[3]]
[1] TRUE

[[4]]
[1] 5
给同一个组件添加多个值:
> z[[1]] <- c(z[[1]],123)
> z
[[1]]
[1] "168" "123" #组件1中含有两个值

[[2]]
[1] "Cool"

[[3]]
[1] "TRUE"

4.访问列表元素和值
获取组件名称:标签
> names(j)
[1] "name" "salary" "union"
去列表化:unlist()
在去列表化的时候,只要有可能,列表的元素都强制被转换成一种共同存储模式
> wu <- unlist(j)
> wu
name salary union
"Pony" "55000" "TRUE"
> class(wu)
[1] "character"
这里的wu实际是带有名字的字符串向量,为使看起来更像向量,可以把名字置空
> names(wu) <- NULL #另一种方法是去名字化: unname()
> wu
[1] "Pony" "55000" "TRUE"

5.在列表上使用apply()函数
lapply()把函数应用到每一个列表元素中:
> j <- list(1:3,25:27) #生成一个具有两个元素的列表
> j
[[1]]
[1] 1 2 3

[[2]]
[1] 25 26 27

> lapply(j,median) #对列表中每个元素应用median函数
[[1]]
[1] 2

[[2]]
[1] 26
如果想把返回的结果转化成矩阵或向量形式,则可以使用sapply()
> sapply(j,median)
[1] 2 26

6.递归型列表
> b <- list(u=5,v=12)
> c <- list(w=13)
> a <- list("A"=b,"B"=c)
> a
$A
$A$u #第一个组件A中包含u组件
[1] 5

$A$v #和v组件
[1] 12

$B #第二个组件B中包含w组件
$B$w
[1] 13

 

第五章:数据框
1.创建数据框
> kids <- c("Jack","Jill")
> ages <- c(12,10)
> d <- data.frame(kids,ages) #创建数据框使用data.frame(col1,col2,col3...)
> d #其中列向量col1,col2,...可以是任何数据类型
kids ages
1 Jack 12
2 Jill 10
2.访问数据框中各组件
> d$kids #和列表的方式一样
[1] Jack Jill
Levels: Jack Jill
> d[[1]]
[1] Jack Jill
Levels: Jack Jill
> str(d) #str()函数查看d的内部结构
'data.frame': 2 obs. of 2 variables:
$ kids: Factor w/ 2 levels "Jack","Jill": 1 2
$ ages: num 12 10
3.提取子数据框
首先创建一个考试成绩的数据框
> Exam.1 <- c(2.0,3.3,4.0,2.3,2.3,3.3)
> Exam.2 <- c(3.3,2.0,4.0,0.0,1.0,3.7)
> Quiz <- c(4.0,3.7,4.0,3.3,3.3,4.0)
> examsquiz <- data.frame(Exam.1,Exam.2,Quiz)
> examsquiz
Exam.1 Exam.2 Quiz
1 2.0 3.3 4.0
2 3.3 2.0 3.7
3 4.0 4.0 4.0
4 2.3 0.0 3.3
5 2.3 1.0 3.3
6 3.3 3.7 4.0 
> examsquiz[2:5,] #提取2到5行
Exam.1 Exam.2 Quiz
2 3.3 2 3.7
3 4.0 4 4.0
4 2.3 0 3.3
5 2.3 1 3.3
> examsquiz[2:5,2,drop=FALSE] #为了避免"降维",使用"drop=FALSE"这个参数
Exam.2
2 2
3 4
4 0
5 1
数据筛选
> examsquiz[examsquiz$Exam.1>=3,]
Exam.1 Exam.2 Quiz
2 3.3 2.0 3.7
3 4.0 4.0 4.0
6 3.3 3.7 4.0
或者使用subset()函数
> subset(examsquiz,Exam.1>=2.8)
Exam.1 Exam.2 Quiz
2 3.3 2.0 3.7
3 4.0 4.0 4.0
6 3.3 3.7 4.0

4.缺失值NA的处理
我们为examsquiz添加一行带有NA的数据
> examsquiz <- rbind(examsquiz,list(3.7,NA,3.8))
> examsquiz
Exam.1 Exam.2 Quiz
1 2.0 3.3 4.0
2 3.3 2.0 3.7
3 4.0 4.0 4.0
4 2.3 0.0 3.3
5 2.3 1.0 3.3
6 3.3 3.7 4.0
7 3.7 NA 3.8
计算每一列的平均值:
> apply(examsquiz,2,mean)
Exam.1 Exam.2 Quiz
2.985714 NA 3.728571
第二列的均值竟然是NA!这显然不是我们所期望的,
我们可以为mean函数增加一个参数使之可以忽视NA
> apply(examsquiz,2,mean,na.rm=TRUE)
Exam.1 Exam.2 Quiz
2.985714 2.333333 3.728571 #计算Exam.2的均值时忽视了第七个学生的成绩
5.为数据框添加新的列(行)的另一种方法
上面已展示了如何使用rbind()或cbind()为数据框增添元素
还可以使用这样:
> examsquiz$Examdiff <- examsquiz$Exam.1 - examsquiz$Exam.2
> examsquiz
Exam.1 Exam.2 Quiz Examdiff
1 2.0 3.3 4.0 -1.3
2 3.3 2.0 3.7 1.3
3 4.0 4.0 4.0 0.0
4 2.3 0.0 3.3 2.3
5 2.3 1.0 3.3 1.3
6 3.3 3.7 4.0 -0.4
7 3.7 NA 3.8 NA
6.上面的NA看着很讨厌人,这里有一个方法去除
> examsquiz[complete.cases(examsquiz),]
Exam.1 Exam.2 Quiz Examdiff
1 2.0 3.3 4.0 -1.3
2 3.3 2.0 3.7 1.3
3 4.0 4.0 4.0 0.0
4 2.3 0.0 3.3 2.3
5 2.3 1.0 3.3 1.3
6 3.3 3.7 4.0 -0.4
这里的complete.cases()作用是这样的:
> complete.cases(examsquiz)
[1] TRUE TRUE TRUE TRUE TRUE TRUE FALSE
可以检测出哪一行的数据是不完整的
7.合并数据框
先给出两个含有相同元素的数据框
> d1 <- data.frame(kids=c("Jalk","Jill","Jillian","John"),states=c("CA","MA","MA","HI"))
> d1
kids states
1 Jack CA
2 Jill MA
3 Jillian MA
4 John HI
> d2 <- data.frame(ages=c(10,7,12),kids=c("Jill","Lillian","Jack"))
> d2
ages kids
1 10 Jill
2 7 Lillian
3 12 Jack
合并
> d <- merge(d1,d2) #这两个数据框中都有kids这个组件,合并相同的值"Jack","Jill"
> d
kids states ages #组件states来自于d1,组件ages来自于d2
1 Jack CA 12
2 Jill MA 10
另一种情况是:即使组件名字不同,但是内容一样时,依然可以通过一种方法合并
> d3 <- data.frame(pals=c("Jack","Jill","Lillian"),ages=c(12,10,7))
> d3
pals ages
1 Jack 12
2 Jill 10
3 Lillian 7
> d1 <- cbind(d1,ages=c(10,7,12,9))
> d1
kids states ages
1 Jack CA 10
2 Jill MA 7
3 Jillian MA 12
4 John HI 9
> merge(d1,d3,by.x="kids",by.y="pals")
kids states ages.x ages.y #把d1中的kids与d2中的pals进行合并
1 Jack CA 10 12
2 Jill MA 7 10
但如果两个数据框含有两个或两个以上的组件,且各有相同的值,那么如何确定合并
的组件?
> d1
kids states ages
1 Jack CA 10
2 Jill MA 7
3 Jillian MA 12
4 John HI 9
> d2
ages kids
1 10 Jill
2 7 John
3 12 Jack
> merge(d1,d2) #R也不知该按kids合并还是按ages合并了
[1] kids ages states
<0 行> (或0-长度的row.names)
这时,可以加个参数用于指定按哪个组件进行合并
> merge(d1,d2,by.x="kids",by.y="kids") #把d1中的kids与d2中的kids合并
kids states ages.x ages.y
1 Jack CA 10 12
2 Jill MA 7 10
3 John HI 9 7
> merge(d1,d2,by.x="ages",by.y="ages") #把d1中的ages与d2中的ages合并
ages kids.x states kids.y
1 7 Jill MA John
2 10 Jack CA Jill
3 12 Jillian MA Jack
但这个数据框好像只能应用于两个数据框,我试验了三个,但直接被无视了:
> d3
pals ages
1 Jack 3
2 Jill 6
3 Lillian 8
> merge(d1,d2,d3,by.x="kids",by.y="kids",by.z="pals")
kids states ages.x ages.y #d3中的数据未被合并进来
1 Jack CA 10 12
2 Jill MA 7 10
3 John HI 9 7
8.应用于数据框的函数
> d1
kids states ages
1 Jack CA 10
2 Jill MA 7
3 Jillian MA 12
4 John HI 9
> lapply(d1,sort) #对数据框的每列进行排序,返回的是列表
$kids
[1] "Jack" "Jill" "Jillian" "John"

$states
[1] CA HI MA MA
Levels: CA HI MA

$ages
[1] 7 9 10 12

> as.data.frame(lapply(d1,sort)) #把列表转化成数据框,但结果没什么意义了
kids states ages
1 Jack CA 7
2 Jill HI 9
3 Jillian MA 10
4 John MA 12

第六章 因子和表
1.因子与水平
> e <- c(2,45,13,2,4,9,6,87,12,1,2,13)
> e
[1] 2 45 13 2 4 9 6 87 12 1 2 13
> ef <- factor(e) #把向量e转化成因子ef
> ef #因子的结构包括以下:向量元素的值,向量的水平
[1] 2 45 13 2 4 9 6 87 12 1 2 13 #元素的值
Levels: 1 2 4 6 9 12 13 45 87 #按元素的值从小到大排列,且不重复.
> str(ef)
Factor w/ 9 levels "1","2","4","6",..: 2 8 7 2 3 5 4 9 6 1 ...
元素的值按水平位置的索引,比如2排在水平级的第二位,45排在水平级的第八位,
13排在水平级的第七位.
我们在定义一个因子时,可以通过定义水平而预置数据
> x <- c(5,12,13,12)
> xff <- factor(x,levels=c(5,12,13,18))
> xff
[1] 5 12 13 12
Levels: 5 12 13 18 #"水平级"告诉我们,将来因子中有可能加入18这个值
> xff[2] <- 18
> xff
[1] 5 18 13 12
Levels: 5 12 13 18
而若加入一个水平级里面没有的值,可能会得到一个警告,并且不会成功
> xff[2] <- 88
警告信息:
In `[<-.factor`(`*tmp*`, 2, value = 88) :
invalid factor level, NA generated
> xff
[1] 5 <NA> 13 12 #88未被加入
Levels: 5 12 13 18
2.因子常用函数
tspply(x,f,g) #x是向量,f是因子或因子列表,g是函数
tapply()执行的操作是:将向量x按因子水平分组,得到x的子向量,再把函数g应用到这些子向量中
> ages <- c(25,26,55,37,21,42)
> affils <- c("R","D","D","R","U","D")

> tapply(ages,affils,mean) #affils的因子水平,是D R U
D R U #x的子向量是D:26,55,42 R:25,37 U:21
41 31 21 #每个子向量求平均得 D:41 R:31 U:21
> tapply(affils,ages,mean)
21 25 26 37 42 55 #不要把affils和ages弄反了,这个是错误的!!!
NA NA NA NA NA NA
警告信息:
1: In mean.default(X[[1L]], ...) : 参数不是数值也不是逻辑值:回覆NA
2: In mean.default(X[[2L]], ...) : 参数不是数值也不是逻辑值:回覆NA
3: In mean.default(X[[3L]], ...) : 参数不是数值也不是逻辑值:回覆NA
4: In mean.default(X[[4L]], ...) : 参数不是数值也不是逻辑值:回覆NA
5: In mean.default(X[[5L]], ...) : 参数不是数值也不是逻辑值:回覆NA
6: In mean.default(X[[6L]], ...) : 参数不是数值也不是逻辑值:回覆NA
一个含有两个因子的例子:
> d <- data.frame(gender=c("M","M","F","M","F","F"),
+ age=c(47,59,21,32,33,24),
+ income=c(55000,88000,32450,76500,123000,45650))
> d #关于数据框的生成,如果使用data.frame(list(...))也是可以的
gender age income
1 M 47 55000
2 M 59 88000
3 F 21 32450
4 M 32 76500
5 F 33 123000
6 F 24 45650
我想按性别及年龄是否超过25岁分组求平均,为此我们先为数据框增加一个组件
> d$over25 <- ifelse(d$age>25,1,0)
> d
gender age income over25
1 M 47 55000 1
2 M 59 88000 1
3 F 21 32450 0
4 M 32 76500 1
5 F 33 123000 1
6 F 24 45650 0
按照性别,是否过25岁这两个维度求平均:
> tapply(d$income,list(d$gender,d$over25),mean)
0 1
F 39050 123000.00
M NA 73166.67
当然,我们可以只按性别求平均:
> tapply(d$income,d$gender,mean)
F M
67033.33 73166.67
也可以只按是否过25岁求平均:
> tapply(d$income,d$over25,mean)
0 1
39050 85625

split()函数
类比于tapply(),split()只做到:将向量x按因子水平分组,得到x的子向量这一步就结束了
> split(d$income,d$gender)
$F #女性的收入
[1] 32450 123000 45650

$M #男性的收入
[1] 55000 88000 76500

> split(d$income,list(d$over25,d$gender))
$`0.F` #未过25岁的女性的收入
[1] 32450 45650

$`1.F` #25岁以上女性的收入
[1] 123000

$`0.M` #不到25岁男性的收入
numeric(0)

$`1.M` #25岁以上男性的收入
[1] 55000 88000 76500
使用split()可以快速索引文本词汇出现的位置
split(1:length(txt),txt) #txt是一个包括所有文本词汇的因子

by()的调用方式与tapply()类似,不同的是,tapply的第一个参数必须是向量,而by()支持矩阵
和数据框

3.表的操作
> f1 <- list(c(5,12,13,12,13,5,13),c("a","b","a","a","b","a","a"))
> table(f1) #表返回的是因子f1中各元素的频数
f1.2
f1.1 a b
5 2 0
12 1 1
13 2 1
再来看一个三维的表:
> v <- data.frame(gender=c("M","M","F","M","F","F"),race=c("W","W","A","O","B","B"),pol=c("L","L","C","L","L","C"))
> v
gender race pol
1 M W L
2 M W L
3 F A C
4 M O L
5 F B L
6 F B C
> vt <- table(v) #表是以二维的结构展示的
> vt
, , pol = C

race
gender A B O W
F 1 1 0 0
M 0 0 0 0

, , pol = L

race
gender A B O W
F 0 1 0 0
M 0 0 1 2
使用addmargins()函数计算表的边际值:
> table(f1)
f1.2
f1.1 a b
5 2 0
12 1 1
13 2 1
> addmargins(table(f1))
f1.2
f1.1 a b Sum
5 2 0 2
12 1 1 2
13 2 1 3
Sum 5 2 7
使用dimnames()函数获得表的维度的名称
> dimnames(table(f1))
$f1.1
[1] "5" "12" "13"

$f1.2
[1] "a" "b"