######第五章:高级数据管理#######
####5.1一个数据处理难题#####
#这一章给出了一个表格,我存在了table5_1.csv文件中
#现在导入这个文件
getwd()
setwd("E:/3codes/newsets") #这里设置成自己的工作目录,记得用/或者\\
dataframe <- read.table("table5_1.csv", header = T, sep = ",") #这里的选项必须这么选
#解决的问题有以下几点:
#1、需要将科目的数据整合起来
#2、前20%的学生评定为A,接下来20%评定为B,依此类推
#3、需要按字母顺序排序
#面临的问题有以下几点:
#1、3科考试无法比较,他们的均值和标准差相差甚远
#2、为了评定等级,需要一种方法来确定某个学生在各学科的百分比排名
#3、表示名字的字段只有一个,无法排序,需要将姓名分开
#####5.2数据处理函数和字符处理函数#####
####5.2.1数学函数####
#以下列出了常用数学函数及示例
abs(x) #绝对值
sqrt(x) #平方根,与x^0.5等价
ceiling(x) #不小于x的最小整数,例如ceiling(3.475)等于4(向上取整)
floor(x) #不大于x的最大整数,例如floor(3.475)等于3(向下取整)
trunc(x) #直接取整
round(x, digits = n) #将x保留n位的小数,例如round(3.475, digits = 2)结果是3.48
signif(x, digits = n) #将x保留n位的有效数字,例如signif(3.475, digits = 2)结果是3.5
#cos(x)/sin(x)/tan(x) #余弦、正弦和正切
#acos(x)/asin(x)/atan(x) #反余弦、反正弦和反正切
#cosh(x)/sinh(x)/tanh(x) #双曲余弦、双曲正弦和双曲正切
#acosh(x)/asinh(x)/atanh(x) #反双曲余弦、反双曲正弦和反双曲正切(说实话,上面3个我还真不知道是啥⊙﹏⊙b汗)
log(x, base = n) #对x取n为底的对数
log(x) #自然对数
log10(x) #常用对数
exp(x) #指数函数,例如exp(2.3026)结果是10
####5.2.2统计函数####
#许多统计函数都有选项,例如
z <- mean(x, trim = 0.05, na.rm = TRUE)
#代表剔除最大和最小5%的数据,并剔除了缺失值
#以下为表5-3常用的统计函数
mean() #均数
median() #中位数
sd() #方差
var() #标准差
mad() #绝对中位差:所有数据减去中位数后得到的值的中位数
quantile(x, probs) #x为一组数值型向量,probs是需要求的百分位数,为0-1之间
range() #求值域(不清楚,应该用不到吧,John注)
sum() #求和
diff(x, lag = n) #滞后差分,lag用于指定滞后几项,默认值为1(不清楚,应该用不到吧,John注)
min() #求最小值
max() #求最大值
scale(x, center = TRUE, scale = TRUE) #为数据对象x按列进行中心化(center = TRUE)或标准化(center = TRUE, scale = TRUE)
#以下是5-1代码清单,前半部分是快捷运算,后半部分是分布运算
x <- c(1,2,3,4,5,6,7,8)
mean(x)
sd(x)
n <- length(x) #计算出x的长度,即有多少个数
meanx <- sum(x)/n
css <- sum((x - meanx)^2)
sdx <- sqrt(css/(n-1))
meanx
sdx
#scale()函数可以对矩阵或者数据库进行标准化,默认均值为0,标准差为1(标准正态分布?)
#后续如果需要可以参照书第85页
####5.2.3概率函数####
#概率函数通常用来生成特征已知的模拟数据,或者在用户自定义的统计函数中计算概率值。
#在R中,概率函数形如:
#[dpqr]<概率分布缩写>()
#其中第一个字母表示其所指分布的某一方面。
#d=密度函数(density)
#p=分布函数(distribution function)
#q=分位数函数(quantile function)
#r=生成随机数(随机偏差)
#以下是上面这组分布的帮助文件:
dnorm(x, mean = 0, sd = 1, log = FALSE)
pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)
rnorm(n, mean = 0, sd = 1)
#表5-4列出了常用的概率函数。
#这里我建立了一个数据框,自行运行看看,对应书第86页:
names <- c("beta分布", "二项分布", "柯西分布", "(非中心)卡方分布", "指数分布", "F分布", "Gamma分布", "几何分布", "超几何分布", "对数正态分布", "Logistic分布", "多项分布", "负二项分布", "正态分布", "泊松分布", "Wilcoxon 符号秩分布", "t分布", "均匀分布", "Weibull分布", "Wilcoxon秩和分布")
abbreviation <- c("beta", "binom", "cauchy", "chisq", "exp", "f", "gamma", "geom", "hyper", "lnorm", "logis", "multinom", "nbinom", "norm", "pois", "signrank", "t", "unif", "weibull", "wilcox")
frame5.1 <- data.frame(names, abbreviation)
frame5.1
#(John注)这张表大部分对应的内容都用得上,希望可以背下来。
#书第87页代码为正态分布示例
#如果不指定均值和标准差,则函数将假定为标准正态分布
#在[-3,3]上绘制标准正态分布
library(ggplot2) #运行ggplot2包
x <- seq(from = -3, to = 3, by = 0.1) #生成一个序列:最小值是-3,最大值是3,以0.1递增
y = dnorm(x) #dnorm(x, mean = 0, sd = 1),这里的x帮助文件中叫vector of quantiles.翻译为分位置响亮,不明白(John注)(ChatGPT说x如果是一个向量,dnorm(x)将返回与x中的每个值相对应的正太分布密度函数值)
data <- data.frame(x = x, y = y) #生成一个数据框
ggplot(data, aes(x, y)) + #制图
geom_line() +
labs(x = "Normal Deviate",
y = "Density") +
scale_x_continuous(breaks = seq(-3, 3, 1))
#位于z=1.96左侧的标准正态曲线下方面积是多少?
pnorm(1.96) #单侧0.25
#均值为500.标准差为100的正态分布的0.9分位点值为多少?
qnorm(.9, mean = 500, sd = 100)
#生成50个均值为50,标准差为10的正态随机数
rnorm(50, mean = 50, sd = 10)
#以上三个是直接出答案的(我到这里已经有点跟不上了,速度很慢,John注)
##1、设定随机数种子
#这个种子相当于一个动态指令,每次生成伪随机数字时R都会随机生成一个这样的种子,每个种子代表能产生的结果
#所以每次得到的随机数都不相同,但可以认为控制随机的种子,从而使制定种子的随机结果相同。(类似与用一个表示为已生成的随机数定位的标识:John注)
#用set.seed()来制定种子,以下是书中5-2代码
runif(5)
runif(5)
set.seed(1234)
runif(5)
set.seed(1234)
runif(5)
#runif()函数是指生成一个均匀分布的随机数向量
#其函数表达式是runif(n, min = 0, max = 1)
##2、生成多元正态数据
#在模拟研究和蒙特卡洛方法中需要获取给定均值向量和协方差阵的多元正态分布数据。
#这就需要MultiRNG包中的draw.d.variate.normal()函数,他的表达式是:
#draw.d.variate.normal(n, nvar, mean, sigma)
#其中n是想要的样本量,nvar是变量数,mean是均值向量,sigma是方差-协方差矩阵
#书88页有一个从三元正态分布中抽取500个变量值的代码如下:
install.packages("MultiRNG")
library(MultiRNG)
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 <- draw.d.variate.normal(500, 3, mean, sigma) #生成500个伪随机的观测值,
mydata <- as.data.frame(mydata) #将数据从矩阵转为数据框
names(mydata) <- c("y", "x1", "x2")
dim(mydata)
head(mydata, n =10)
#这部分看起来挺有用的,但目前不知道哪里能用,后面如果用到了再看一看把(John注)
####5.2.4字符处理函数####
#字符处理函数可以从文本中提取信息,或者为打印输出和生成报告重设文本的格式
#以下为表5-6中代码格式和解释
nchar(x) #计算x的字符数量
substr(x, start, stop) #提取或替换一个字符向量中的子串
grep(pattern, x, ignore.case = FALSE, fixed = FALSE) #在x中搜索某种模式,若fixed=FALSE,则pattern为一个正则表达式。若fixed=TRUE,则pattern为一个文本字符串。返回值为匹配的下标grep("A",c("b","A","ac","Aw"),fixed=TRUE)返回值为c(2,4)。
sub(pattern, replasement, x, ignore.case = FALSE, fixed = FALSE) #在x中搜索pattern.并以文本replacement将其替换。若fixed=FALSE.则pattern为一个正则表达式。若fixed=TRUE,则pattern为一个文本字符串。sub("\\s"".","Hello There")返回值为Hello.There。注意,“\s"是一个用来查找空白的正则表达式;使用”\\s”而不用\的原因是,后者是R中的转义字符(参见1.3.4节)(John注:这个相当于word或则Excel中全部替换的功能)
strsplit(x, split, fixed = FALSE) #在split处分割字符向量x中的元素。若fixed=FALSE,则pattern为一个正则表达式。若fixed=TRUE,则pattern为一个文本字符串。y<-strsplit("abc","")将返回一个含有1个成分、3个元素的列表,包含的内容为“a”"b"“c”unlist(y)[2]和sapply(y,"[",2)均返回“b”
paste(..., sep = "") #连接字符串,分隔符为sep,paste("x",1:3,sep="") 返回值为c("x1",“x2",“x3")。paste("x"1:3,sep="M")返回值为c("xM1","xM2"“xM3")。paste("Today is", date())返回值为Today is Thu Jul 22 10:36:14 2021
toupper(x) #大写转换
tolower(x) #小写转换
#以上部分函数提供了正则表达式的处理,如果需要的话详见书第90页。
####5.2.5其他实用函数####
#以下函数无法清晰分类,但非常实用
length(x) #求对象的长度
seq(from, to, by) #生成一个序列
rep(x, n) #将x重复n遍
cut(x, n) #将连续型变量x分割为有着n个水平的因子,使用选项order_result=TRUE可以创建一个有序型因子
cat(..., file = "myfile", append = FALSE) #连结...中的对象,并将其输出到屏幕上或文件中,以下是示例代码
name <- c("Jane")
cat("Hello", name, "\n")
#\n是新行,\t是制表符,\b为退格,\'为单引号
#以上为转义符
####5.2.6将函数应用于矩阵和数据框####
#R可以将函数运用到一系列对象上,包括标量、向量、矩阵、数组和数据框
#以下为5-4演示代码
a <- 5
sqrt(a) #求平方根
b <- c(1.243, 5.654, 2.99)
round(b) #四舍五入
c <- matrix(runif(12), nrow = 3)
c
log(c)
mean(c) #求c中12个数的均值
#apply()函数可以将任意一个函数应用到矩阵、数组、数据库的任何维度上,以下为格式:
apply(x, MARGIN, FUN, ...)
#其中x为数据对象,MARGIN是维度下标,FUN是给定的函数,...是任何向传递给FUN的参数,以下是代码清单5-5
mydata <- matrix(rnorm(30), nrow = 6)
mydata
apply(mydata, 1, mean) #MARGIN是维度下标,1代表行,2代表列
apply(mydata, 2, mean) #计算每列的均值
apply(mydata, 2, mean, trim=0.2) #计算每列的截尾均值
#apply()可以将任何函数应邀到数组的某个维度
#lapply()和sapply()可以将函数应用到列表上
####5.2.7数据处理难题的一套解决方案####
#可以直接从文件中选
dataframe <- read.table("table5_1.csv", header = T, sep = ",") #这里的选项必须这么选
roster <- as.data.frame(dataframe) #将表格转为数据框
#如果找不到文件,可以直接输入
options(digits = 2) #保留2位小数
Student <- c("John Davis", "Angela Williams", "Bullwinkle Moose", "David Jones", "Janice Markhammer", "Cheryl Cushing", "Reuven Ytzrhak", "Greg Knox", "Joel England", "Mary Rayburn")
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) #stringsAsFactors = FALSE不将字符型向量转为分类变量
#以上两种方法输入结果是一样的
z <- scale(roster[, 2:4]) #scale()是按照中心进行排序,这个中心是均值,整个过程即标准化
score <- apply(z, 1, mean) #新建score列,为z中每一行的均值
roster <- cbind(roster, score) #将score添加到roster中
y <- quantile(score, c(.8, .6, .4, .2)) #求分位数,80%、60%、40%、20%位的分数
roster$grade <- NA #新建grade列,数据为空
roster$grade[score >= y[1]] <- "A" #大于y第一个数据,即80%分数为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"
name <- strsplit((roster$Student), " ") #将roster$Student中的" "处分开,建立name数据框储存
Lastname <- sapply(name, "[", 2) #新建Lastname列,下方同理;"["表示可提取的某个对象的部分函数
Firstname <- sapply(name, "[", 1)
roster <- cbind(Firstname, Lastname, roster[, -1]) #合并Firstname和Lastname到roster表中,并删除原表第一列(对应位置前加-号)
roster <- roster[order(Lastname, Firstname),] #按照Lastname、Firstname排序
roster #输出结果
#John整理下思路:
#第一步是将所有成绩标准化,即将成绩调整为均值为0、标准差为1的数据,之后再对每个人的三科标准化值取均值,对这个值进行排序(score)
#第二步是将姓名分开
#对应书的93-96页分布解读,中间的结果可以在对应步骤查看
#####5.3控制流#####
#正常情况下,R中所有的语句都是自上而下运行的,但有时希望重复执行某些语句
#要理解本章内容,需充分理解以下内容:
#语句(statement)是一条单独的R语句或一组复合语句(包含在花括号中的一组R语句,使用分号分隔);
#条件(cond)是一条最终被解析为真(TRUE)或假(FALSE)的表达式;
#表达式(expr)是一条数值或字符串的求值语句;
#序列(seq)是一个数值或字符串序列。
####5.3.1重复和循环####
#循环结构重复地执行一个或者一系列语句,知道某个条件不为真为止。
#循环包括for结构和while结构
#1、for结构:直到某个变量的值不在包含在序列seq中为止,语法为:
for (var in seq) statement
#例:
for (i in 1:10) print("Hello")
#2、while结构:直到条件不为真为止,语法为:
while(cond) statement
#例:
i <- 10
while(i > 0) {print("Hello"); i <- i - 1}
####5.3.2条件执行####
#一组或一条语句仅在满足一个制定条件时执行
#1、If-else结构:给定某个制定条件为真时执行,也可以在制定条件为假时执行另外的语句
#语法为:
if (cond) statement
if (cond) stamement1 else statement2
#例:
if (is.character(grade)) grade <- as.factor(grade) #如果grade是一个字符向量(真),则它会转变为一个因子
if (!is.factor(grade)) grade <- as.factor(grade) else print("Grade already is a factor") #如果grade不是一个因子为真时(注意"!"),则将grade转换为因子,反之则输出一段文字
#以上并没有定义grade,所以没有输出结果,如果想得到结果可以自己定义一下(John注)
#2、ifelse结构
#是if-else的简化版本,语法是:
ifelse(cond, statement1, statement2)
#若cond为TRUE时执行第一个语句,为FALSE执行第二个语句,示例如下:
ifelse(score > 0.5, print("Passed"), print("Failed"))
outcome <- ifelse(score > 0.5, "Passed", "Failed")
#在程序的行为是二元时,或者希望结构的输入和输出均为向量时,请使用ifelse
#3、switch结构
#根据一个表达式的值选择语句执行,语法是
switch(expr, ...)
#其中...表示和expr的各种可能输出值绑定的语句
#以下为5-7代码演示
feelings <- c("sad", "afraid")
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")
)
#####5.4用户自定义函数#####
#R中的许多函数都是由已有函数构成的,结构看起来大致如下:
#myfunction <- function(arg1, arg2, ...){
#statements
#return(object)
#}
#函数中的对象只在函数内部使用
#返回的数据类型是任意的,从标量到列表皆可
#以下是一个例子,用来计算数据对象的集中和离散趋势
#代码清单5-8:mystats():一个用户自定义的描述性统计量计算函数:
mystats <- function(x, paramentric = TRUE, print = FALSE){
if (paramentric) {
center <- mean(x); spread <- sd(x)
} else {
center <- median(x); spread <- mad(x)
}
if (print & paramentric) {
cat("Mean=", center, "\n", "SD=", spread, "\n")
}else if (print & !paramentric) {
cat("Median=", center, "\n", "MAD=", spread, "\n")
}
result <- list(center = center, spread = spread)
return(result)
}
#上面这种写法可以一次性生成一整套语句(John注)
#以上部分是生成了一个mystats()的函数,具体要看这个函数的用法:
set.seed(1234)
x <- rnorm(500)
y <- mystats(x)
#此时没有标准差和均值的表达
y <- mystats(x, paramentric = TRUE, print = TRUE)
#以下是用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")
mydate("short")
mydate()
mydate("medium")
#这一部分的内容其实我不需要掌握,我需要的是如何运用已有的函数,而非自定义函数(John注)
#####5.5数据重塑####
##这部分对于我很重要,希望仔细学习(John注)
#数据重塑:通过修改数据的结构来决定数据的组织方式。
####5.5.1转置####
#转置即反转行和列,使用函数t()即可对一个矩阵或数据框进行转置
#代码清单5-9再次用到了mtcars数据
cars <- mtcars[1:5, 1:4]
cars
t(cars)
#转置结果返回的是一个矩阵,矩阵中所有变量都是数值型的,所以当原数据框中所有的变量都是数值型时转置的效果最好
#数据集中包含任何字符型变量,那么整个数据集所有变量都会转置成字符值
####5.5.2将宽表数据集格式转变为长表数据集格式####
##表5-8包含了3个国家1900年、2000年、2010年人均寿命的估计值
#表5-8将年份作为变量名,每一年的实际观测值作为变量值
#表5-9将年份和每一年的观测值都作为变量值
#以上就是书102页所说的宽表格转换为长表格
#这里对宽表格和长表格进行了定义,主要是为了在不同的统计方法中需要用到不同格式的表格
#大多数R函数使用宽表格的数据框,只有一些函数要求长表格格式
#tidyr包提供了可以将数据框从一种格式转化为另一种格式
install.packages("tidyr")
#tidyr包中的gather()函数可以将宽表格转化为长表格,语法如下:
library(tidyr)
longdata <- gather(widedata, key, value, variable list)
#widedata是要转化的数据框;
#key指定变量列的名称(本例中为Variable):
#value指定值列的名称(本例中为Life Exp);
#variablelist指定要堆叠的变量(本例中为LExp1990、LExp2000、LExp2010)。
#代码清单5-10如下:
library(tidyr)
data_wide <- data.frame(ID = c("FR","BE","GER"),
Country = c("Francec","Belgium","Germany"),
LExp1990 = c(77.0,76.1,75.3),
LExp2000 = c(79.2,77.8,78.2),
LExp2010 = c(81.8,80.3,80.5))
data_wide
data_long <- gather(data_wide, key = "Variable", value = "Life_Exp",
c(LExp1990, LExp2000, LExp2010))
data_long
#结果自行运行一下,这个函数对后面数据格式转换非常有用,希望可以背下来(John注)
#tidyr包还有一个spread()包可以将长表格转换为宽表格,语法如下
widedata <- spread(longdata, key, value)
#longdata是要转换的数据框
#key是包含变量的列
#value是包含变量值的列
data_wide2 <- spread(data_long, key = Variable, value = Life_Exp)
data_wide2
#####5.6数据汇总#####
#即将观测值组替换为根据这些观测值计算的描述性统计量(这个解释是比较通俗易懂的:John注)
#数据汇总是数据进行统计分析前的准备工作,或者是对数据进行分组统计以便在表格或图形中呈现的一种方法
#R中可以用一个或者多个by变量和一个预先制定好的函数来折叠数据
#R中通常使用aggregate()函数,其格式为:
aggregate(x, by, FUN)
#其中x是折叠的对象,by是一个变量名组成的列表,这些变量将被去掉并形成新的变量值,FUN是用来描述统计量的标量函数(标量函数是指返回值为单一变量的函数)
#书104页对应的mtcars数据进行演示
options(digits = 3)
aggdata <- aggregate(mtcars,
by = list(mtcars$cyl, mtcars$gear), #新的列表依据cy1和gear
FUN = mean, na.rm = TRUE)
aggdata
#结果中Group.1代表cy1即气缸数量, Group.2代表gear即党委数量
#结果中有两个问题:1、Group.1和Group.2表示不明,2、最后的数据包含了cy1和gear
#所以修改后的代码如下
aggdata <- aggregate(mtcars[-c(2,10)],
by = list(Cylinders = mtcars$cyl, Gears = mtcars$gear),
FUN = mean, na.rm = TRUE)
aggdata
#使用dplyr包有一种更自然的汇总方法:
install.packages("dplyr")
library(dplyr)
mtcars %>%
group_by(cyl, gear) %>%
summarise_all(list(mean), na.rm = TRUE)