第一部分:数学、统计和字符处理函数
- 数学和统计函数:R提供了丰富的数学和统计函数,用于执行各种计算和分析。这些函数可以帮助用户快速完成复杂的数学运算、统计分析等任务,例如计算均值、方差、相关系数、进行假设检验等。
- 字符处理函数:R也提供了多种字符处理函数,用于操作和处理字符串数据。这些函数可以用于字符串的拼接、分割、替换、查找等操作,对于文本数据的处理非常有用。
第二部分:循环和条件执行以及自编函数
- 循环和条件执行:这部分将讲解如何使用循环(如
for
、while
)和条件语句(如if
、else
)来控制程序的流程。这些控制结构是编程中非常重要的部分,可以帮助用户实现复杂的逻辑和重复的操作。 - 自编函数:用户可以自己编写函数来完成特定的数据处理和分析任务。这部分将介绍如何定义函数、设置函数的参数、返回值等,并且讲解如何在实际的数据处理中调用这些自编函数。
第三部分:数据整合与重塑
- 数据整合与概述:这部分将介绍如何对数据进行整合和概述。整合数据时,可以使用内建函数或自编函数来获取数据的统计信息、汇总信息等,从而对数据有一个全面的了解。
- 数据集的重塑和重构:数据集的重塑和重构是指将数据从一种格式转换为另一种格式,以满足不同的分析需求。例如,将宽格式的数据转换为长格式,或者将数据进行分组、合并等操作。
5.1 一个数据处理难题
问题背景
假设有一组学生参加了数学、科学和英语三门课程的考试。现在需要完成以下任务:
- 组合成绩:将三门课程的成绩组合成一个单一的成绩衡量指标。
- 评定等级:根据组合后的成绩,将学生分为不同的等级(A、B、C、D、E),每个等级包含20%的学生。
- 排序:按字母顺序对学生进行排序。
数据示例
表5-1展示了部分学生的成绩数据:
学生姓名 | 数学 | 科学 | 英语 |
---|---|---|---|
John Davis | 502 | 95 | 25 |
Angela Williams | 600 | 99 | 22 |
Bullwinkle Moose | 412 | 80 | 18 |
David Jones | 358 | 82 | 15 |
Janice Markhammer | 495 | 75 | 20 |
Cheryl Cushing | 512 | 85 | 28 |
Reuven Ytzrhak | 410 | 80 | 15 |
Greg Knox | 625 | 95 | 30 |
Joel England | 573 | 89 | 27 |
Mary Rayburn | 522 | 86 | 18 |
面临的挑战
-
成绩不可比:
- 三门课程的成绩范围和分布不同(例如数学成绩可能在几百分,而科学和英语成绩可能在0-100分之间)。
- 直接对这些成绩求平均是没有意义的,需要将它们转换为可比较的单位(例如标准化或归一化)。
-
评定等级:
- 需要一种方法来确定每个学生在组合成绩中的百分比排名。
- 根据百分比排名将学生分为不同的等级(A、B、C、D、E),每个等级包含20%的学生。
-
排序问题:
- 学生姓名是一个整体字段,包含姓和名。
- 为了按字母顺序排序,需要将姓和名拆开,分别处理。
解决方案的思路
这段内容的核心是通过R语言中的数值和字符处理函数来解决上述问题。具体步骤可能包括:
- 标准化或归一化成绩:
- 使用数学函数(如
scale()
)将不同范围的成绩转换为可比较的单位。
- 使用数学函数(如
- 计算百分比排名:
- 使用统计函数(如
rank()
或ecdf()
)计算每个学生的百分比排名。
- 使用统计函数(如
- 评定等级:
- 根据百分比排名将学生分为不同的等级。
- 拆分姓名字段:
- 使用字符处理函数(如
strsplit()
)将学生姓名拆分为姓和名。
- 使用字符处理函数(如
- 排序:
- 使用排序函数(如
order()
)对学生进行排序。
- 使用排序函数(如
5.2 数值和字符处理函数
5.2.1 数学函数
R提供了丰富的数学函数,这些函数可以用于执行各种数学运算和变换。以下是一些常用的数学函数及其描述:
函数 | 描述 | 用例 |
---|---|---|
abs(x) | 绝对值 | abs(-4) 返回值为4 |
sqrt(x) | 平方根 | sqrt(25) 返回值为5,和25^(0.5) 等价 |
ceiling(x) | 不小于x的最小整数 | ceiling(3.475) 返回值为4 |
floor(x) | 不大于x的最大整数 | floor(3.475) 返回值为3 |
trunc(x) | 向0的方向截取的x中的整数部分 | trunc(5.99) 返回值为5 |
round(x, digits=n) | 将x舍入为指定位的小数 | round(3.475, digits=2) 返回值为3.48 |
signif(x, digits=n) | 将x舍入为指定的有效数字位数 | signif(3.475, digits=2) 返回值为3.5 |
cos(x)、sin(x)、tan(x) | 余弦、正弦和正切 | cos(2) 返回值为–0.416 |
acos(x)、asin(x)、atan(x) | 反余弦、反正弦和反正切 | acos(-0.416) 返回值为2 |
cosh(x)、sinh(x)、tanh(x) | 双曲余弦、双曲正弦和双曲正切 | sinh(2) 返回值为3.627 |
acosh(x)、asinh(x)、atanh(x) | 反双曲余弦、反双曲正弦和反双曲正切 | asinh(3.627) 返回值为2 |
log(x, base=n) | 对x取以n为底的对数 | log(10) 返回值为2.3026 |
log10(x) | 对x取以10为底的对数 | log10(10) 返回值为1 |
exp(x) | 指数函数 | exp(2.3026) 返回值为10 |
数据变换
数学函数的一个主要用途是对数据进行变换。例如,对于存在明显偏倚的变量(如收入),在进一步分析之前取对数是一种常见的做法。数学函数也常用于公式中,用于绘图函数(例如x
对sin(x)
)和在输出结果之前对数值进行格式化。
5.2.2 统计函数
统计函数概述
R提供了丰富的统计函数,用于计算各种描述性统计量。这些函数不仅可以直接应用于数值向量,还可以通过参数调整来满足不同的需求。以下是一些常用的统计函数及其描述:
函数 | 描述 | 用例 |
---|---|---|
mean(x) | 平均数 | mean(c(1,2,3,4)) 返回值为2.5 |
median(x) | 中位数 | median(c(1,2,3,4)) 返回值为2.5 |
sd(x) | 标准差 | sd(c(1,2,3,4)) 返回值为1.29 |
var(x) | 方差 | var(c(1,2,3,4)) 返回值为1.67 |
mad(x) | 绝对中位差(median absolute deviation) | mad(c(1,2,3,4)) 返回值为1.48 |
quantile(x, probs) | 求分位数 | quantile(x, c(0.3, 0.84)) 返回x的30%和84%分位点 |
range(x) | 求值域 | range(c(1,2,3,4)) 返回值为c(1,4) |
diff(range(x)) | 求值域的差 | diff(range(c(1,2,3,4))) 返回值为3 |
sum(x) | 求和 | sum(c(1,2,3,4)) 返回值为10 |
diff(x, lag=n) | 滞后差分 | diff(c(1,5,23,29)) 返回值为c(4,18,6) |
min(x) | 求最小值 | min(c(1,2,3,4)) 返回值为1 |
max(x) | 求最大值 | max(c(1,2,3,4)) 返回值为4 |
scale(x, center=TRUE, scale=TRUE) | 中心化或标准化 | 代码清单5-6中给出了示例 |
参数的灵活使用
许多统计函数提供了可选参数,这些参数可以显著影响输出结果。例如:
y <- mean(x) # 计算x的算术平均数
z <- mean(x, trim = 0.05, na.rm = TRUE) # 计算截尾平均数,丢弃最大5%和最小5%的数据,并忽略缺失值
trim
参数用于计算截尾平均数,可以去除极端值的影响。na.rm
参数用于处理缺失值,设置为TRUE
时会忽略缺失值。
代码示例:均值和标准差的计算
代码清单5-1展示了如何计算某个数值向量的均值和标准差的两种方式。
x <- c(1,2,3,4,5,6,7,8)
mean(x) # 使用R内置函数计算均值
[1] 4.5
sd(x) # 使用R内置函数计算标准差
[1] 2.449490
# 手动计算均值和标准差
n <- length(x) # 获取向量长度
meanx <- sum(x) / n # 计算均值
css <- sum((x - meanx)^2) # 计算修正平方和
sdx <- sqrt(css / (n - 1)) # 计算标准差
meanx # 手动计算的均值
[1] 4.5
sdx # 手动计算的标准差
[1] 2.449490
修正平方和的计算过程
在手动计算标准差的过程中,修正平方和(css)的计算步骤如下:
- 计算向量
x
的平均值meanx
。 - 从
x
的每个元素中减去meanx
,得到偏差向量。 - 对偏差向量的每个元素求平方,得到平方偏差向量。
- 对平方偏差向量的所有元素求和,得到修正平方和
css
。
数据的标准化
数据标准化是数据预处理中的一个重要步骤,它有助于改善分析结果的质量和可靠性。在R中,scale()
函数提供了一种方便的方式来对数据进行标准化处理。
默认标准化
默认情况下,scale()
函数对矩阵或数据框的指定列进行标准化,使得每列的均值为0,标准差为1。这在很多统计分析和机器学习算法中非常有用,因为它们通常假设数据是标准化的。
newdata <- scale(mydata)
mydata
:输入的矩阵或数据框。newdata
:标准化后的数据。
自定义均值和标准差
如果你希望对每一列进行任意均值和标准差的标准化,可以使用以下代码:
newdata <- scale(mydata) * SD + M
M
:目标均值。SD
:目标标准差。
这种标准化方式允许你将数据转换为具有特定均值和标准差的新分布。例如,你可能希望将数据标准化为均值为50,标准差为10的分布:
newdata <- scale(mydata) * 10 + 50
指定列的标准化
如果你只想对数据框中的某一列进行标准化,可以使用transform()
函数。例如,将变量myvar
标准化为均值为50,标准差为10的变量:
newdata <- transform(mydata, myvar = scale(myvar) * 10 + 50)
mydata
:原始数据框。myvar
:需要标准化的列名。newdata
:新的数据框,其中myvar
列已被标准化。
注意事项
- 在非数值型的列上使用
scale()
函数将会报错。因此,在应用scale()
之前,需要确保数据中不包含非数值型列,或者先将这些列从数据中分离或转换。 - 数据标准化是许多数据分析和机器学习任务的前提,它有助于提高模型的性能和稳定性。
总结
scale()
函数是R中进行数据标准化的强大工具。通过它可以轻松地将数据标准化为均值为0、标准差为1的格式,也可以通过简单的数学变换调整为目标均值和标准差。此外,transform()
函数可以方便地对数据框中的指定列进行标准化处理。这些技巧在数据预处理阶段非常有用,尤其是在处理具有不同量纲和分布的数据时。
5.2.3 概率函数
概率函数概述
概率函数在统计学中扮演着重要角色,它们不仅用于生成模拟数据,还用于计算概率值。在R中,概率函数通常遵循特定的命名规则,以便于理解和使用。
命名规则
R中的概率函数命名规则如下:
d
:密度函数(density)p
:分布函数(distribution function),通常也称为累积分布函数(CDF)q
:分位数函数(quantile function),用于找到给定概率值对应的分位点r
:生成随机数(random deviates)
常用概率函数
表5-4列出了R中常用的一些概率分布及其缩写:
分布名称 | 缩写 |
---|---|
Beta 分布 | beta |
二项分布 | binom |
柯西分布 | cauchy |
卡方分布 | chisq |
指数分布 | exp |
F 分布 | f |
Gamma 分布 | gamma |
几何分布 | geom |
正态分布 | norm |
泊松分布 | pois |
t 分布 | t |
均匀分布 | unif |
Weibull 分布 | weibull |
正态分布函数
表5-5展示了正态分布的四个基本函数及其用法示例:
问题 | 解法 |
---|---|
在区间[–3, 3]上绘制标准正态曲线 | 使用dnorm 函数计算密度,然后使用plot 绘制曲线 |
位于z=1.96左侧的标准正态曲线下方面积是多少? | 使用pnorm(1.96) 计算累积概率,结果为0.975 |
均值为500,标准差为100的正态分布的0.9分位点值为多少? | 使用qnorm(.9, mean=500, sd=100) 计算分位点,结果为628.16 |
生成50个均值为50,标准差为10的正态随机数 | 使用rnorm(50, mean=50, sd=10) 生成随机数 |
设置随机数种子
为了确保结果的可重现性,可以使用set.seed()
函数设置随机数种子。这样,每次生成的随机数序列将是相同的。
生成多元正态数据
在模拟研究和蒙特卡洛方法中,经常需要从多元正态分布中抽取数据。MASS
包中的mvrnorm()
函数可以方便地实现这一需求。其调用格式为:
mvrnorm(n, mean, sigma)
其中:
n
:样本大小mean
:均值向量sigma
:方差-协方差矩阵
示例:生成多元正态数据
代码清单5-3展示了如何从一个三元正态分布中抽取500个观测:
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")
dim(mydata)
head(mydata, n=10)
- 设置随机数种子以确保结果可重现。
- 指定均值向量和方差-协方差矩阵。
- 生成500个伪随机观测。
- 将结果从矩阵转换为数据框,并为变量指定名称。
- 确认数据框的维度,并输出前10个观测。
5.2.4 字符处理函数
字符处理函数概述
字符处理函数在R中用于处理文本型数据,它们可以帮助用户从文本中提取信息或重新设置文本格式。这些函数对于数据清洗、文本分析和报告生成等任务至关重要。
常用字符处理函数
表5-6列出了R中一些最常用的字符处理函数及其描述:
函数 | 描述 |
---|---|
nchar(x) | 计算字符串向量x 中每个元素的字符数量 |
substr(x, start, stop) | 提取或替换字符串向量x 中的子串 |
grep(pattern, x, ...) | 在字符串向量x 中搜索与模式pattern 匹配的元素,并返回匹配元素的下标 |
sub(pattern, replacement, x, ...) | 在字符串向量x 中搜索模式pattern ,并用文本replacement 替换 |
strsplit(x, split, ...) | 在指定的分隔符split 处分割字符串向量x 中的元素 |
paste(..., sep="") | 连接字符串,sep 参数指定连接符 |
toupper(x) | 将字符串向量x 中的所有字符转换为大写 |
tolower(x) | 将字符串向量x 中的所有字符转换为小写 |
正则表达式
正则表达式提供了一种强大的方式来匹配文本模式。例如,正则表达式^[hc]?at
可以匹配以0个或1个h
或c
开头,后接at
的任意字符串,如hat
、cat
和at
,但不会匹配bat
。
示例
以下是一些示例,展示了如何使用这些字符处理函数:
x <- c("ab", "cde", "fghij")
nchar(x[3]) # 返回值为5
x <- "abcdef"
substr(x, 2, 4) # 返回值为"bcd"
substr(x, 2, 4) <- "22222" # x 将变成"a222ef"
grep("A", c("b", "A", "c"), fixed=TRUE) # 返回值为2
sub("\\s", ".", "Hello There") # 返回值为Hello.There
y <- strsplit("abc", "") # 返回一个列表,包含"a" "b" "c"
unlist(y)[2] # 返回"b"
paste("x", 1:3, sep="") # 返回值为c("x1", "x2", "x3")
paste("x", 1:3, sep="M") # 返回值为c("xM1", "xM2", "xM3")
toupper("abc") # 返回值为"ABC"
tolower("ABC") # 返回值为"abc"
5.2.5 其他实用函数
在R中,除了专门针对数值和字符处理的函数外,还有一些实用函数,它们在数据管理和处理中发挥着重要作用,但不容易归入特定的分类。以下是一些常用的实用函数:
表5-7 其他实用函数
函数 | 描述 |
---|---|
length(x) | 返回对象x 的长度(元素数量) |
seq(from, to, by) | 生成一个从from 到to ,步长为by 的序列 |
rep(x, n) | 将对象x 重复n 次 |
cut(x, n) | 将连续型变量x 分割为n 个水平的因子,可指定ordered_result 创建有序因子 |
pretty(x, n) | 创建美观的分割点,将连续型变量x 分割为n 个区间,常用于绘图 |
cat(... , file ="myfile", append =FALSE) | 连接对象并输出到屏幕或文件中,可使用转义字符控制格式 |
示例
以下是一些示例,展示了如何使用这些实用函数:
x <- c(2, 5, 6, 9)
length(x) # 返回值为4
indices <- seq(1, 10, 2) # 生成序列c(1, 3, 5, 7, 9)
y <- rep(1:3, 2) # 返回值为c(1, 2, 3, 1, 2, 3)
# 将连续型变量x分割为3个水平的因子
cut(x, 3)
# 创建美观的分割点,将连续型变量x分割为3个区间
pretty(x, 3)
firstname <- c("Jane")
cat("Hello", firstname, "\n") # 输出Hello Jane
name <- "Bob"
cat("Hello", name, "\b.\n", "Isn\'t R", "\t", "GREAT?\n")
# 输出:
# Hello Bob.
# Isn't R GREAT?
转义字符
在cat()
函数中,可以使用转义字符来控制输出格式:
\n
:新行\t
:制表符\'
:单引号\b
:退格
例如,在输出时,可以在句号之前使用退格转义字符(\b
),以避免在句号后面多出一个空格。
5.2.6 将函数应用于矩阵和数据框
R函数的一个强大特性是它们可以应用于多种数据对象,包括标量、向量、矩阵、数组和数据框。这使得R在数据处理方面非常灵活和强大。
示例:将函数应用于数据对象
代码清单5-4展示了如何将函数应用于不同类型的数据对象:
a <- 5
sqrt(a) # 返回值为2.236068
b <- c(1.243, 5.654, 2.99)
round(b) # 返回值为c(1, 6, 3)
c <- matrix(runif(12), nrow=3)
c # 生成一个3x4的矩阵
log(c) # 对矩阵c中的每个元素取对数
mean(c) # 计算矩阵c中所有元素的均值,结果为0.444
应用函数到矩阵和数据框的特定维度
如果你希望对矩阵或数据框的行或列应用函数,可以使用apply()
函数。apply()
函数的格式如下:
apply(x, MARGIN, FUN, ...)
x
:数据对象(矩阵、数组或数据框)。MARGIN
:维度的下标(在矩阵或数据框中,MARGIN=1
表示行,MARGIN=2
表示列)。FUN
:要应用的函数。...
:传递给FUN
的任何参数。
示例:将函数应用到矩阵的所有行和列
代码清单5-5展示了如何将函数应用到矩阵的所有行和列:
mydata <- matrix(rnorm(30), nrow=6)
mydata # 生成一个6x5的矩阵
apply(mydata, 1, mean) # 计算每行的均值
apply(mydata, 2, mean) # 计算每列的均值
apply(mydata, 2, mean, trim=0.2) # 计算每列的截尾均值(忽略最高和最低20%的数据)
- 第一个
apply()
计算了6行的均值。 - 第二个
apply()
计算了5列的均值。 - 第三个
apply()
计算了每列的截尾均值,基于中间60%的数据,忽略最高和最低20%的值。
总结
apply()
函数是一种强大的机制,可以将任意R函数应用到矩阵、数组或数据框的任何维度上。这使得它在数据处理和分析中非常有用。此外,lapply()
和sapply()
函数可以将函数应用到列表上,提供了更多的灵活性。
5.3 数据处理难题的一套解决方案
在5.1节中,我们提出了一个数据处理问题,涉及学生考试成绩的组合、评分和排序。现在,我们将通过代码清单5-6展示解决这一问题的详细步骤。
代码清单5-6 示例的一种解决方案
-
设置输出格式:
options(digits=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)
-
标准化考试成绩:
z <- scale(roster[,2:4]) score <- apply(z, 1, mean) roster <- cbind(roster, score)
这里,我们首先使用
scale()
函数对数学、科学和英语成绩进行标准化,然后计算每个学生的标准化成绩均值,并将这些均值添加到数据框中。 -
计算成绩等级:
y <- quantile(score, c(.8,.6,.4,.2)) 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"
使用
quantile()
函数计算成绩的分位数,并根据这些分位数将学生成绩分为A到F等级。 -
拆分姓名并排序:
name <- strsplit((roster$Student), " ") Lastname <- sapply(name, "[", 2) Firstname <- sapply(name, "[", 1) roster <- cbind(Firstname, Lastname, roster[,-1]) roster <- roster[order(Lastname, Firstname),]
使用
strsplit()
函数拆分学生姓名为姓和名,然后使用sapply()
提取姓和名,最后按姓和名对数据框进行排序。
总结
这个解决方案展示了如何使用R的基本函数来解决实际的数据处理问题。通过标准化考试成绩、计算成绩等级、拆分和排序姓名,我们能够将原始数据转换为一个结构化、易于理解的格式。这些步骤不仅解决了提出的问题,还展示了R在数据处理方面的强大能力。
这个过程也强调了R中函数的灵活性和实用性,无论是在处理标量、向量、矩阵还是数据框时。通过这些基本的数据处理技术,可以为更复杂的数据分析任务打下坚实的基础。