Ch5高级数据管理
5.2数值和字符处理函数
5.2.1数学函数
当数学函数应用于数值向量、矩阵或数据框时,他们会作用于每一个独立的值。
5.2.2统计函数
许多统计函数拥有可以影响输出结果的可选参数。eg:
z <- mean(x, trim = 0.05, na.rm=TRUE)
即可以计算截尾平均数,即丢弃了最大5%和最小5%的数据和所有缺失值后的算术平均数。
5-1均值和标准差的计算
> x <- c(1,2,3,4,5,6,7,8)
#方法1:简洁
> mean(x)
[1] 4.5
> sd(x)
[1] 2.44949
#方法2:复杂
> n <- length(x)
> meanx <- sum(x)/n
> css <- sum((x - meanx)^2)
> sdx <- sqrt(css / (n-1))
> meanx
[1] 4.5
> sdx
[1] 2.44949
数据的标准化
使用函数scale()进行标准化
#情形一:默认情况下,对矩阵或数据框的每一列进行均值为0,标准差为1的标准化
newdata <- scale(mydata)
#情形二:可以对每一列进行任意均值M和标准差SD的标准化
newdata <- scale(mydata)*SD + M
#情形三:可以对指定列myvar进行任意均值M和标准差SD的标准化
newdata <- transform(mydata, myvar = scale(mydata)*SD + M)
注意:
在非数值型的列上使用scale()函数将会报错
5.2.3概率函数
概率函数通常用来生成特征已知的模拟数据,第一个字母表示其所指分布的某一方面:
字母 | 表示的分布的对应方面 |
---|---|
d | 密度函数 |
p | 分布函数 |
q | 分位数函数 |
r | 生成随机数(随机偏差) |
示例1:正态分布函数
#在区间[-3,3]上绘制标准正态曲线
> x <- pretty(c(-3,3), 30)
> x
[1] -3.0 -2.8 -2.6 -2.4 -2.2 -2.0
[7] -1.8 -1.6 -1.4 -1.2 -1.0 -0.8
[13] -0.6 -0.4 -0.2 0.0 0.2 0.4
[19] 0.6 0.8 1.0 1.2 1.4 1.6
[25] 1.8 2.0 2.2 2.4 2.6 2.8
[31] 3.0
> #pretty()生成等间距的数值向量
> y <- dnorm(x)
> plot(x, y,
+ type = "l",
+ xlab = "NormalDeviate",
+ yaxs = "i")
结果示意:
学习:
pretty()函数可以生成等间距的数值向量,即通过选取n+1个等间距的取整值,将一个连续型变量x分割为n个区间
示例2:设定随机数种子
每次生成伪随机数的时候,函数都会使用一个不同的种子,可以通过函数set.seed()显式指定这个种子,可以让结果重现。
若不设定随机数种子,则不便于结果的重现,因为每次生成的随机数都是不一样的
#5-2生成服从正态分布的伪随机数
> runif(5)
[1] 0.8129645 0.6562170 0.6035335 0.6396338 0.4506674
> runif(5)
[1] 0.78531156 0.16385080 0.37070431 0.46155930 0.03130664
> set.seed(1234)
> runif(5)
[1] 0.1137034 0.6222994 0.6092747 0.6233794 0.8609154
> set.seed(1234)
> runif(5)
[1] 0.1137034 0.6222994 0.6092747 0.6233794 0.8609154
例子中,后两次生成随机数的时候,设定了随机数种子,所以生成的伪随机数是一样的。
示例3:生成多元正态数据
利用MASS包中的mvrnorm()函数可以方便获取给定均值向量和协方差阵的多元正态分布数据。调用格式为:
mvrnorm(n, mean, sigma)
eg:
从一个参数如下的三元正态分布中抽取500个观测
均值 | 230.7 | 146.7 | 3.6 |
---|---|---|---|
协方差阵 | 15360.8 | 6721.2 | -47.1 |
。。。。 | 6721.2 | 4700.9 | -16.5 |
。。。。 | -47.1 | -16.5 | 0.3 |
#5-3生成服从多元正态分布的数据
> library(MASS)
> options(digits=3)
> #设定随机数种子
> set.seed(1234)
> #指定均值向量、协方差阵
> mean <- c(230.7, 146.7, 3.6)
> sigma <- matrix(c(15360.8, 6721.2, -47.1,
+ 6721.2, 4700.9, -16.5,
+ -47.1, -16.5, 0.3), nrow = 3, ncol = 3)
#生成多元正态分布数据
> mydata <- mvrnorm(500, mean, sigma)
#将生成的数据格式变为数据框
> mydata <- as.data.frame(mydata)
#设定数据框的列名
> names(mydata) <- c("y", "x1", "x2")
#确定生成的维度,即500个观测和3个变量,并输出了前10个观测
> dim(mydata)
[1] 500 3
> head(mydata, n=10)
y x1 x2
1 98.8 41.3 3.43
2 244.5 205.2 3.80
3 375.7 186.7 2.51
4 -59.2 11.2 4.71
5 313.0 111.0 3.45
6 288.8 185.1 2.72
7 134.8 165.0 4.39
8 171.7 97.4 3.64
9 167.2 101.0 3.50
10 121.1 94.5 4.10
5.2.4字符处理函数
字符处理函数可以从文本型数据中抽取信息,或者为打印输出和生成报告重设文本的格式。
初识正则表达式
^[ht]?at
该正则表达式表示可以匹配任意以0个或1个h或c开头,后接at的字符串,即可以匹配hat、cat、at,但不会匹配bat
注意
字符处理函数中grep()、sub()和strsplit()能够搜索某个文本字符串(fixed=TRUE)或某个正则表达式(fixed=FALSE,默认值为FALSE)
5.2.5字符处理函数
输出时转义字符的使用方法
\ n 表示新行
\ t 表示制表符
\ ` 表示单引号
\ b表示退格
5.2.6将函数应用于矩阵和数据框
#5-4将函数应用于数据对象
> a <- 5
> sqrt(a)
[1] 2.24
> b <- c(1.243, 5.654, 2.99)
> round(b)
[1] 1 6 3
> c <- matrix(runif(12), nrow=3)
> c
[,1] [,2] [,3] [,4]
[1,] 0.9636 0.216 0.289 0.913
[2,] 0.2068 0.240 0.804 0.353
[3,] 0.0862 0.197 0.378 0.931
> log(c)
[,1] [,2] [,3] [,4]
[1,] -0.0371 -1.53 -1.241 -0.0912
[2,] -1.5762 -1.43 -0.218 -1.0402
[3,] -2.4511 -1.62 -0.972 -0.0710
> mean(c)
[1] 0.465
注意:
1、用mean(矩阵)出来的是对矩阵中的所有元素求的均值。
2、if要求各行或各列的均值,可以用apply()函数,该函数可将一个任意函数应用到矩阵、数组、数据框的任何维度上。
apply()函数使用格式为:
apply(x, MARGIN, FUN, …)
其中,x为数据对象,MARGIN为维度下标,FUN为指定函数,…为任何想传递给FUN的参数。在矩阵或数据框中,MARGIN=1表示行,MARGIN=2表示列。
3、apply()函数可以把函数应用到数组的某个维度上,lapply()函数和sapply()函数可以把函数应用到列表(list)上
#5-5将一个函数应用到矩阵的所有行(列)
> #生成数据6*5矩阵
> mydata <- matrix(rnorm(30), nrow = 6)
> mydata
[,1] [,2] [,3] [,4] [,5]
[1,] 0.459 1.203 1.234 0.591 -0.281
[2,] -1.261 0.769 -1.891 -0.435 0.812
[3,] -0.527 0.238 -0.223 -0.251 -0.208
[4,] -0.557 -1.415 0.768 -0.926 1.451
[5,] -0.374 2.934 0.388 1.087 0.841
[6,] -0.604 0.935 0.609 -1.944 -0.866
> #计算每行的均值
> apply(mydata, 1, mean)
[1] 0.641 -0.401 -0.194 -0.136 0.975 -0.374
> #计算每列的均值
> apply(mydata, 2, mean)
[1] -0.478 0.777 0.148 -0.313 0.292
> #计算每列的截尾均值
> apply(mydata, 2, mean, trim=0.2)
[1] -0.516 0.786 0.386 -0.255 0.291
5.3处理实际问题
即5.1问题的提出
表:学生成绩数据
姓名 | 数学 | 科学 | 英语 |
---|---|---|---|
John Davis | 502 | 95 | 25 |
Angela Williams | 600 | 99 | 22 |
Bullwinkle Moose | 412 | 80 | 18 |
David Jones | 358 | 82 | 15 |
Jan Mark | 495 | 75 | 20 |
Chery Crush | 512 | 85 | 28 |
Renve Ytzrk | 410 | 80 | 15 |
Greg Knox | 625 | 95 | 30 |
Joel England | 573 | 89 | 27 |
Mary Raybrun | 522 | 86 | 18 |
将学生的各科考试成绩组合为单一的成绩衡量指标、基于相对名次(前20%,下20%等)给出A到F的评分、根据学生姓氏和名字的首字母对花名册进行排序
5.3问题的解决
Step1:先构造数据框
#先构造数据框
> options(digits=2) #限定输出小数点后数字的位数为2位
> Student <- c("John Davis", "Angela Williams", "Bullwinkle Moose",
+ "David Jones", "Jan Mark", "Chery Crush", "Renve Ytzrk",
+ "Greg Knox", "Joel England","Mary Raybrun")
> Math <- c(502, 600, 412, 358, 495, 512, 410, 625, 573, 522)
> Science <- c(95, 99, 80, 82, 75, 85, 80, 95, 89, 86)
> English <- c(25, 22, 18, 15, 20, 28, 15, 30, 27, 18)
> roster <- data.frame(Student, Math, Science, English,
+ stringsAsFactors = FALSE)
> roster
Student Math Science English
1 John Davis 502 95 25
2 Angela Williams 600 99 22
3 Bullwinkle Moose 412 80 18
4 David Jones 358 82 15
5 Jan Mark 495 75 20
6 Chery Crush 512 85 28
7 Renve Ytzrk 410 80 15
8 Greg Knox 625 95 30
9 Joel England 573 89 27
10 Mary Raybrun 522 86 18
Step2:由于成绩的分值不同,不便于比较。所以将其用scale()函数先标准化,化为统一尺度
#再标准化
> z <- scale(roster[,2:4])
> z
Math Science English
[1,] 0.013 1.078 0.587
[2,] 1.143 1.591 0.037
[3,] -1.026 -0.847 -0.697
[4,] -1.649 -0.590 -1.247
[5,] -0.068 -1.489 -0.330
[6,] 0.128 -0.205 1.137
[7,] -1.049 -0.847 -1.247
[8,] 1.432 1.078 1.504
[9,] 0.832 0.308 0.954
[10,] 0.243 -0.077 -0.697
attr(,"scaled:center")
Math Science English
501 87 22
attr(,"scaled:scale")
Math Science English
86.7 7.8 5.5
Step3:计算综合得分,并使用cbind()函数将综合得分添加到原数据框中。综合得分为标准化后的成绩的均值
#计算标准化后各行的均值以获得综合得分
> score <- apply(z, 1, mean)
#使用cbind()函数将综合得分添加到数据框中
> roster <- cbind(roster, score)
> roster
Student Math Science English score score
1 John Davis 502 95 25 0.56 0.56
2 Angela Williams 600 99 22 0.92 0.92
3 Bullwinkle Moose 412 80 18 -0.86 -0.86
4 David Jones 358 82 15 -1.16 -1.16
5 Jan Mark 495 75 20 -0.63 -0.63
6 Chery Crush 512 85 28 0.35 0.35
7 Renve Ytzrk 410 80 15 -1.05 -1.05
8 Greg Knox 625 95 30 1.34 1.34
9 Joel England 573 89 27 0.70 0.70
10 Mary Raybrun 522 86 18 -0.18 -0.18
Step4:使用函数quantile(),计算综合得分的百分位数,这将是划分学生成绩的依据
#使用函数quantile()计算百分位数
> y <- quantile(roster$score, c(.8, .6, .4, .2))
> y
80% 60% 40% 20%
0.74 0.44 -0.36 -0.89
Step5:使用逻辑运算符,将百分位数重编码为类别型成绩变量
> roster$grade[score >= y[1]] <- "A"
> roster$grade[score < y[1] & score >= y[2]] <- "B"
> roster$grade[score < y[2] & score >= y[3]] <- "C"
> roster$grade[score < y[3] & score >= y[4]] <- "D"
> roster$grade[score < y[4]] <- "F"
> roster
Student Math Science English score grade
1 John Davis 502 95 25 0.56 B
2 Angela Williams 600 99 22 0.92 A
3 Bullwinkle Moose 412 80 18 -0.86 D
4 David Jones 358 82 15 -1.16 F
5 Jan Mark 495 75 20 -0.63 D
6 Chery Crush 512 85 28 0.35 C
7 Renve Ytzrk 410 80 15 -1.05 F
8 Greg Knox 625 95 30 1.34 A
9 Joel England 573 89 27 0.70 B
10 Mary Raybrun 522 86 18 -0.18 C
Step6:使用strsplit()函数以空格为界,拆分学生的姓氏和名字。strsplit()函数应用到一个字符串组成的向量上会返回一个列表
> name <- strsplit((roster$Student), " ")
> name
[[1]]
[1] "John" "Davis"
[[2]]
[1] "Angela" "Williams"
[[3]]
[1] "Bullwinkle" "Moose"
[[4]]
[1] "David" "Jones"
[[5]]
[1] "Jan" "Mark"
[[6]]
[1] "Chery" "Crush"
[[7]]
[1] "Renve" "Ytzrk"
[[8]]
[1] "Greg" "Knox"
[[9]]
[1] "Joel" "England"
[[10]]
[1] "Mary" "Raybrun"
Step7:利用函数sapply()可以提取学生的姓氏和名字,并放入不同的变量中。cbind()函数在数据框中加入需要的,删除不需要的
#利用函数sapply()可以提取列表中每个成分的第一个元素,放进一个变量;并提取第二个元素,放进另一个变量
> #“[”是一个可以用来提取某个对象的一部分的函数
> Firstname <- sapply(name, "[", 1)
> Lastname <- sapply(name, "[", 2)
> #利用函数cbind()进行连接,又因为第一列Student已经不需要了,所以在下标前面加减号,将其删除
> roster <- cbind(Firstname, Lastname, roster[, -1])
> roster
Firstname Lastname Math Science English score grade
1 John Davis 502 95 25 0.56 B
2 Angela Williams 600 99 22 0.92 A
3 Bullwinkle Moose 412 80 18 -0.86 D
4 David Jones 358 82 15 -1.16 F
5 Jan Mark 495 75 20 -0.63 D
6 Chery Crush 512 85 28 0.35 C
7 Renve Ytzrk 410 80 15 -1.05 F
8 Greg Knox 625 95 30 1.34 A
9 Joel England 573 89 27 0.70 B
10 Mary Raybrun 522 86 18 -0.18 C
Step8:利用函数order()依照学生的姓氏和名字对数据集进行排序
> roster[order(Lastname,Firstname),]
Firstname Lastname Math Science English score grade
6 Chery Crush 512 85 28 0.35 C
1 John Davis 502 95 25 0.56 B
9 Joel England 573 89 27 0.70 B
4 David Jones 358 82 15 -1.16 F
8 Greg Knox 625 95 30 1.34 A
5 Jan Mark 495 75 20 -0.63 D
3 Bullwinkle Moose 412 80 18 -0.86 D
10 Mary Raybrun 522 86 18 -0.18 C
2 Angela Williams 600 99 22 0.92 A
7 Renve Ytzrk 410 80 15 -1.05 F
5.4控制流
通常,R从上到下顺序来执行语句,有时需要重复执行某些语句的时候,需要控制流来进行操作。
5.4.1重复和循环
1、for结构
for循环重复执行一个语句,直至某个变量的值不再包含在序列seq中为止
for (var in seq) statement
eg:
for (i in 1:10) print(“Hello”)
Hello输出了10次
注意:
for后面,括号里面的 i in,要在一个向量范围里,才可以进行循环
2、while结构
while循环重复执行一个语句,直至条件cond不为真
while (cond) statement
eg:
i <- 10
while (i > 0) {print(“Hello”); i <- i-1}
Hello输出了10次
5.4.2条件执行
1、if-else结构
if (cond) statement
if (cond) statement1 else statement2
2、ifelse结构
ifelse (cond, statement1, statement2)
若cond为TRUE,则执行statement1,否则执行statement2
3、switch结构
switch根据一个表达式的值来选择语句执行
> #5-7一个switch示例
> feelings <- c("sad", "happy")
> for (i in feelings)
+ print(
+ switch(i,
+ happy = "I am glad you are happy",
+ afraid = "There is nothing to fear",
+ sad = "Cheer up",
+ angry = "Calm down now")
+ )
[1] "Cheer up"
[1] "I am glad you are happy"
5.5用户自编函数
函数结构
myfunction <- function(参数1,参数2,…){
语句
return(object)
}
示例1:描述性统计量计算函数
#5-8mystats():一个由用户编写的描述性统计量计算函数
> mystats <- function(x, parametric = TRUE, print = FALSE){
+ if (parametric) {center <- mean(x); spread <- sd(x)}
+ else {center <- median(x); spread <- mad(x)}
+ if (print & parametric) {cat("Mean=", center, "\n", "SD=", spread, "\n")}
+ else if (print & !parametric) {cat("Median=", center, "\n", "Mad=", spread, "\n")}
+ result <- list(center=center, spread=spread)
+ return(result)
+ }
> #生成数据,测试函数
> set.seed(1234)
> x <- rnorm(500)
> y1 <- mystats(x)
> y1
$center
[1] 0.001838821
$spread
[1] 1.034814
> y2 <- mystats(x, parametric = TRUE, print = FALSE)
> y2
$center
[1] 0.001838821
$spread
[1] 1.034814
> y3 <- mystats(x, parametric = FALSE, print = TRUE)
Median= -0.02070734
Mad= 1.000984
注意:
一个函数,参数的设置不同会造成很大的影响。
在y3里print设置为TRUE,,就会自动输出,但前面的y1、y2,print默认为FALSE,就不会输出。
示例2:利用switch()函数设置当天日期的输出格式
#设置日期输出格式
> mydate <- function(type="long") {
+ switch(type,
+ long = format(Sys.time(), "%A %B %d %Y"),
+ short = format(Sys.time(), "%m-%d-%y"),
+ cat(type, "is not a recognized type.\n"))
+ }
> mydate("long")
[1] "星期二 一月 04 2022"
> mydate("short")
[1] "01-04-22"
> mydate("medium")
medium is not a recognized type.
学习:
函数cat()仅会在输入日期格式不匹配"long"or"short"时执行
可以用来捕获错误输入的参数值
5.6整合与重构
5.6.1转置
利用函数t()可以对矩阵或数据框进行转置
#数据集的转置
> cars <- mtcars[1:5, 1:4]
> cars
mpg cyl disp hp
Mazda RX4 21.0 6 160 110
Mazda RX4 Wag 21.0 6 160 110
Datsun 710 22.8 4 108 93
Hornet 4 Drive 21.4 6 258 110
Hornet Sportabout 18.7 8 360 175
> t(cars)
Mazda RX4 Mazda RX4 Wag Datsun 710 Hornet 4 Drive Hornet Sportabout
mpg 21 21 22.8 21.4 18.7
cyl 6 6 4.0 6.0 8.0
disp 160 160 108.0 258.0 360.0
hp 110 110 93.0 110.0 175.0
5.6.2整合数据
使用一个或多个by变量和一个预先定义好的函数可以折叠数据,格式为:
aggregate(x, by, FUN)
x为待折叠的对象,by是一个变量名组成的列表,FUN是标量函数
示例:整合数据
根据汽缸数和挡位数来整合mtcars数据,并返回各个数值型变量的均值。
> options(digits = 3)
> attach(mtcars)
> aggdata <- aggregate(mtcars, by = list(cyl,gear), FUN = mean, na.rm = TRUE)
> aggdata
Group.1 Group.2 mpg cyl disp hp drat wt qsec vs am gear carb
1 4 3 21.5 4 120 97 3.70 2.46 20.0 1.0 0.00 3 1.00
2 6 3 19.8 6 242 108 2.92 3.34 19.8 1.0 0.00 3 1.00
3 8 3 15.1 8 358 194 3.12 4.10 17.1 0.0 0.00 3 3.08
4 4 4 26.9 4 103 76 4.11 2.38 19.6 1.0 0.75 4 1.50
5 6 4 19.8 6 164 116 3.91 3.09 17.7 0.5 0.50 4 4.00
6 4 5 28.2 4 108 102 4.10 1.83 16.8 0.5 1.00 5 2.00
7 6 5 19.7 6 145 175 3.62 2.77 15.5 0.0 1.00 5 6.00
8 8 5 15.4 8 326 300 3.88 3.37 14.6 0.0 1.00 5 6.00
> detach(mtcars)
注意:
整合数据使用aggregate()函数时,by中的变量必须在一个列表中(即使只有一个变量)
5.6.3reshape包
首次使用前需要安装reshape包
install.packages("reshape")
首先将数据“融合”(melt),以使每一行都是一个唯一的标识符-变量组合。
再将数据“重铸”(cast)为任何想要的形状,在重铸的过程中,可以使用任何函数对数据进行整合
1、“融合”(melt)
library(reshape)
md <- melt(mydata, id=(c("id", "time")))
融合mydata数据集,id和time是唯一确定每个测量所需的变量。融合之后变成每个测量变量独占一行。
2、“重铸”(cast)
调用格式:
newdata <- cast(md, formula, FUN)
其中,md 为已经融合的数据,formula描述了想要的最后结果,FUN是(可选的)数据整合函数。
公式形如:
rowvar1 + rowvar2 + … ~ colvar1 + colvar2 + …
其中,rowvar1 + rowvar2 + … 定义了要划掉的变量合集,以确定各行的内容。colvar1 + colvar2 + …定义了要划掉的,确定各列内容的变量合集。
书上的示例,让我感受到了这个函数的使用灵活性