【R语言实战】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.7146.73.6
协方差阵15360.86721.2-47.1
。。。。6721.24700.9-16.5
。。。。-47.1-16.50.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 Davis5029525
Angela Williams6009922
Bullwinkle Moose4128018
David Jones3588215
Jan Mark4957520
Chery Crush5128528
Renve Ytzrk4108015
Greg Knox6259530
Joel England5738927
Mary Raybrun5228618

将学生的各科考试成绩组合为单一的成绩衡量指标、基于相对名次(前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 + …定义了要划掉的,确定各列内容的变量合集。

书上的示例,让我感受到了这个函数的使用灵活性

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值