R语言

Lecture04 : R程序编写与数据处理-02

罗益民

2024-09-20

本节内容一览

  • Part01 函数
    • R内置函数
      • 数学函数
      • 统计函数
      • 概率函数
      • 字符处理函数
    • 自定义函数
  • Part02 R流程控制
    • 表达式
    • 分支结构
    • 循环结构
    • apply族函数

Part01 函数(function)

函数是编程中的一个基本概念,它是一种封装了一段代码的实体,这段代码可以执行一个特定的任务。函数通常接受输入参数(也称为“参数”或“变量”),可以返回一个或多个值,

R内置函数

内置函数是指编程语言或库中预先定义好的函数,它们可以直接被开发者调用,无需手动编写实现代码。这些函数通常是为了执行常见的任务,如数学计算、字符串处理、文件操作等。

1. 数学函数

  • abs(x):绝对值
  • sqrt(x):平方根
  • ceiling(x):向上取整
  • floor(x):向下取整
  • trunc(x):截取整数部分
  • round(x,digits=n):四舍五入(指定小数位数)
  • signif(x,digits=n):四舍五入(指定有效数字位数)
  • cos(x)、sin(x)、tan(x):余弦、正弦、正切
  • acos(x)、asin(x)、atan(x):反余弦、反正弦、反正切
  • acosh(x)、asinh(x)、atanh(x):反双曲余弦、反双曲正弦、反双曲正切
  • log(x)、log10(x)、log2(x)、log(x,base=n):对数
  • exp(x):e指数幂,即\(e^x\)

2. 统计函数

  • mean(x):均值
  • median(x):中位数
  • min(x):最小值
  • max(x):最大值
  • range(x) :最大值和最小值
  • sum(x):求和
  • diff(x,lag=n) :计算给定数据集中连续元素之间的差异,即计算相邻元素的差值
  • var(x):方差 \(\sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2\)
  • sd(x):标准差 \(\sigma = \sqrt{\frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2}\)
  • mad(x):绝对中位差 \(MAD = median(|x_i - \tilde{x}|)\)
  • quantile(x,probs):分位数,probs是分位点
  • scale(x,center=TRUE,scale=TRUE):对数据进行缩放,也称为标准化或归一化。center是否进行中心化(减去均值)。scale是否进行标准化(除以标准差)。默认为TRUE。

3. 概率函数

用来生成一定特征分布的模拟数据的函数。这些函数的名称都遵守一定的特点:[dpqr]<概率函数缩写>()

  • d=密度函数(density)
  • p=分布函数(distribution function)
  • q=分位数函数(quantile function)
  • r=生成随机数(随机偏差)

常用的概率函数有norm(正态分布)unif(均匀分布)

概率分布函数示意

字符处理函数

R语言中提供了多种字符处理函数,用于对字符串和文本进行操作

函数作用 代表函数
查找 grep
替换 sub,gsub
分割、截取 substr,strsplit
连接 paste,paste0
工具 tolower,toupper,nchar

grep

grep(pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
     fixed = FALSE, useBytes = FALSE, invert = FALSE):

在字符串向量 x 中搜索匹配正则表达式 pattern 的子串,返回匹配位置的下标

# 匹配包含字符 'o' 的元素
grep("o", c("Hello", "World", "R Markdown"))
[1] 1 2 3
# 匹配包含任意一个元音 (a, e, i, o, u) 的元素
grep("[aeiou]", c("apple", "banana", "cherry", "grape"), ignore.case = TRUE)
[1] 1 2 3 4
# 匹配不包含元音的元素
grep("[^aeiou]", c("apple", "banana", "cherry", "grape"), ignore.case = TRUE)
[1] 1 2 3 4
# 匹配以 'b' 开头的元素
grep("^b", c("banana", "blueberry", "blackberry","apple"))
[1] 1 2 3
# 匹配包含 'an' 或 'and' 的元素
grep("(an|and)", c("antenna", "stand", "on", "and", "enemy", "fight"))
[1] 1 2 4
# 匹配不包含数字的元素
grep("^\\D*$", c("abc123", "def456", "ghi", "jkl789"))
[1] 3

sub / gsub

sub 和 gsub 都是R语言中用于替换字符串中匹配正则表达式的函数。sub 只替换第一个匹配的子串,而 gsub 替换所有匹配的子串。

text <- "I have a cat, and my friend has a dog."
sub(" a ", " an ", text)
[1] "I have an cat, and my friend has a dog."
gsub(" a ", " an ", text)
[1] "I have an cat, and my friend has an dog."
date <- "Today is 03/15/2024."
# 替换第一个日期格式 MM/DD/YYYY 为 Month Day, Year
sub("/", "-", date)
[1] "Today is 03-15/2024."
gsub("/", "-", date)
[1] "Today is 03-15-2024."

substr

substr(string, start, stop) 函数用于提取字符串的子串,它需要起始位置和长度作为参数。

text <- "Hello World"
# 提取从位置1开始的3个字符
substr(text, 1,3)
[1] "Hel"

strsplit 函数用于根据分隔符分割字符串,它返回一个列表。

text <- "apple,banana,cherry"
# 使用逗号作为分隔符分割字符串
splitted_text <- strsplit(text, split = ",")
splitted_text
[[1]]
[1] "apple"  "banana" "cherry"
# 获取分割后的向量
splitted_text[[1]]
[1] "apple"  "banana" "cherry"

paste / paste0

paste 函数用于将两个或多个字符向量或字符串连接成一个字符串,基本形式为paste(..., sep = " ", collapse = NULL, recycle0 = FALSE)

paste的用法灵活多样,应熟练掌握:

paste("a","b","c")#默认分隔符是空格" "
[1] "a b c"
paste("a","b",sep = "=")#指定分隔符
[1] "a=b"
paste("a",1:5,sep = "")#会自动每个元素与a相连
[1] "a1" "a2" "a3" "a4" "a5"
paste("a",1:5,".pdf", sep = "")#批量输出文件名
[1] "a1.pdf" "a2.pdf" "a3.pdf" "a4.pdf" "a5.pdf"

collapse参数表示元素间的折叠坍缩

paste(c("a","b","c"),collapse = "+")
[1] "a+b+c"
paste("a",1,collapse = "+")# 思考:为何此处collapse无效?
[1] "a 1"

paste0paste的一个简化版本,它连接字符串时不使用任何分隔符。相当于指定paste(...,sep="")

paste0("Hello", "World")
[1] "HelloWorld"
paste0("row", 1:4)
[1] "row1" "row2" "row3" "row4"

其它实用内置函数

length(x)计算对象的长度

# 根据随机长度生成一个数值向量
randomVector <- runif(sample(1:10, 1), min = 1, max = 100)
length(randomVector)
[1] 6

seq(from,to,by)生成一个序列

seq(1,10,2)
[1] 1 3 5 7 9

rep(x,n)将x重复n次

rep("a",5)
[1] "a" "a" "a" "a" "a"
rep(c("a","b","c"),3)
[1] "a" "b" "c" "a" "b" "c" "a" "b" "c"

cut(x,breaks,labels=NULL)将数值型向量x分割为具有n个水平的因子

# 创建一个数值向量
ages <- 1:100

# 使用cut函数将年龄分组,注意:如果指定labels参数,labels参数的数量必须和区间数量一致
age_groups <- cut(ages, breaks = c(18, 25, 35, 55, 100), labels = c("Youth", "Young Adult", "Adult", "Senior"))
print(age_groups)
  [1] <NA>        <NA>        <NA>        <NA>        <NA>        <NA>       
  [7] <NA>        <NA>        <NA>        <NA>        <NA>        <NA>       
 [13] <NA>        <NA>        <NA>        <NA>        <NA>        <NA>       
 [19] Youth       Youth       Youth       Youth       Youth       Youth      
 [25] Youth       Young Adult Young Adult Young Adult Young Adult Young Adult
 [31] Young Adult Young Adult Young Adult Young Adult Young Adult Adult      
 [37] Adult       Adult       Adult       Adult       Adult       Adult      
 [43] Adult       Adult       Adult       Adult       Adult       Adult      
 [49] Adult       Adult       Adult       Adult       Adult       Adult      
 [55] Adult       Senior      Senior      Senior      Senior      Senior     
 [61] Senior      Senior      Senior      Senior      Senior      Senior     
 [67] Senior      Senior      Senior      Senior      Senior      Senior     
 [73] Senior      Senior      Senior      Senior      Senior      Senior     
 [79] Senior      Senior      Senior      Senior      Senior      Senior     
 [85] Senior      Senior      Senior      Senior      Senior      Senior     
 [91] Senior      Senior      Senior      Senior      Senior      Senior     
 [97] Senior      Senior      Senior      Senior     
Levels: Youth Young Adult Adult Senior

自定义函数

在R语言中,自定义函数可以让你编写一段可重复使用的代码块,这些代码块可以接受参数并返回结果。创建自定义函数可以使用 function 关键字。

定义自定义函数的基本语法:

my_function <- function(arg1, arg2, ...) {
  # 函数体
  # ...
  # 返回值
}

示例:定义一个简单的自定义函数

add_numbers <- function(a, b) {
  result <- a + b
  return(result)
}
# 使用自定义函数
sum <- add_numbers(5, 3)
print(sum)
[1] 8

若自定义函数不含return语句,那么函数将默认返回最后一个表达式的结果。

add_numbers <- function(a, b) {
  a + b 
}

## 使用自定义函数
print(add_numbers(5, 3))
[1] 8

带默认参数的自定义函数

greet <- function(name, message = "Hello") {
  full_message <- paste(message, name)
  return(full_message)
}

# 使用自定义函数,不提供第二个参数,使用默认值
greeting <- greet("Alice")
print(greeting)
[1] "Hello Alice"
# 提供第二个参数,覆盖默认值
greeting <- greet("Bob", "Hi")
print(greeting)
[1] "Hi Bob"

返回多个值的自定义函数

calculate_stats <- function(data) {
  mean_value <- mean(data)
  sd_value <- sd(data)
  list(mean = mean_value, sd = sd_value)
}

# 使用自定义函数
stats <- calculate_stats(c(1, 2, 3, 4, 5))
print(stats)
$mean
[1] 3

$sd
[1] 1.581139

自定义函数举例:阶乘

factorial <- function(n) {
  if (n < 0) {
    #stop终止函数执行,并打印错误消息到控制台
    stop("阶乘定义域为非负整数")
  }
  result <- 1
  for (i in 1:n) {
    result <- result * i
  }
  result
}

# 调用函数计算 5 的阶乘
print(factorial(5))
[1] 120

Part02 R流程控制

表达式

  • R是一个表达式语言, 其任何一个语句都可以看成是一个表达式。
  • 表达式之间以分号分隔或用换行分隔。 表达式可以续行, 只要前一行不是完整表达式(比如末尾是加减乘除等运算符, 或有未配对的括号)则下一行为上一行的继续。下面例子均为有效表达式:
5
3.14
2 + 2 

x <- 4;y <- 6
x <- 4
y <- 6
z <- 23;
a <- 1

x > 5
sqrt(16)
c(1, 2, 3)
1:5
matrix(1:9, nrow = 3)
  • 若干个表达式可以放在一起组成一个复合表达式, 作为一个表达式使用,复合表达式的值为最后一个表达式的值, 组合用大括号表示。
{
  x <- 15
  x
}

分支结构

R中共有4种形式的分支结构

  1. if(条件) 表达式1:最基本的条件判断结构。如果条件为TRUE,则执行表达式1
  2. if(条件) 表达式1 else 表达式2:条件为真时执行表达式1,如果条件为FALSE,则执行表达式2。
  3. ifelse(条件,表达式1,表达式2):if-else结构的简化写法1
  4. switch(条件,表达式1,表达式2):基于条件的不同值执行不同的代码块

分支结构举例

当代码块只包含一条语句时,可以省略大括号,写在一行里

x <- 10
# 基本用法
if(x > 5) {
  cat("x大于5")
}
x大于5
# 也可以写在一行里
if(x>5) cat("x大于5")
x大于5
# 带有else
if(x > 5) {
  cat("x大于5")
} else {
  cat("x不大于5")
}
x大于5
# 写在一行里(不推荐的写法:代码风格问题)❌
if(x > 5) cat("x大于5") else cat("x不大于5")
x大于5
# 使用紧凑的ifelse写法✔
ifelse(x > 5,"x大于5","x不大于5")
[1] "x大于5"
# 下面的代码报错
#ifelse(x > 5,cat("x大于5"),cat("x不大于5"))

注意:ifelse用于生成结果,而不是执行操作。上面的最后一个例子中,cat函数是用来打印输出的,它不返回任何值,所以不能直接用在ifelse中。

在向量和数据框中使用分支

R是一个向量语言,一般很少对单个标量进行操作,下面是分支结构用在向量上的例子:

x <- c(1, 5, 10)
ifelse(x > 5, "大于5", "不大于5")
[1] "不大于5" "不大于5" "大于5"  

也可以用在数据框和矩阵上:

ifelse(mtcars>=20,TRUE,FALSE)
                      mpg   cyl disp   hp  drat    wt  qsec    vs    am  gear
Mazda RX4            TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Mazda RX4 Wag        TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Datsun 710           TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Hornet 4 Drive       TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Hornet Sportabout   FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Valiant             FALSE FALSE TRUE TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
Duster 360          FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Merc 240D            TRUE FALSE TRUE TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
Merc 230             TRUE FALSE TRUE TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
Merc 280            FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Merc 280C           FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Merc 450SE          FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Merc 450SL          FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Merc 450SLC         FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Cadillac Fleetwood  FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Lincoln Continental FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Chrysler Imperial   FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Fiat 128             TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Honda Civic          TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Toyota Corolla       TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Toyota Corona        TRUE FALSE TRUE TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
Dodge Challenger    FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
AMC Javelin         FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Camaro Z28          FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Pontiac Firebird    FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Fiat X1-9            TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Porsche 914-2        TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Lotus Europa         TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Ford Pantera L      FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Ferrari Dino        FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Maserati Bora       FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Volvo 142E           TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
                     carb
Mazda RX4           FALSE
Mazda RX4 Wag       FALSE
Datsun 710          FALSE
Hornet 4 Drive      FALSE
Hornet Sportabout   FALSE
Valiant             FALSE
Duster 360          FALSE
Merc 240D           FALSE
Merc 230            FALSE
Merc 280            FALSE
Merc 280C           FALSE
Merc 450SE          FALSE
Merc 450SL          FALSE
Merc 450SLC         FALSE
Cadillac Fleetwood  FALSE
Lincoln Continental FALSE
Chrysler Imperial   FALSE
Fiat 128            FALSE
Honda Civic         FALSE
Toyota Corolla      FALSE
Toyota Corona       FALSE
Dodge Challenger    FALSE
AMC Javelin         FALSE
Camaro Z28          FALSE
Pontiac Firebird    FALSE
Fiat X1-9           FALSE
Porsche 914-2       FALSE
Lotus Europa        FALSE
Ford Pantera L      FALSE
Ferrari Dino        FALSE
Maserati Bora       FALSE
Volvo 142E          FALSE

嵌套if语句

R没有提供其它语言中常见的elseif关键字,但我们可以通过嵌套多个if语句来进行模拟:

num <- sample(1:100,1)
num
[1] 5
if (num < 30) {
  print("level0")
} else if (num >= 30 & num < 60) {
  print("level1")
} else if (num >= 60 & num < 80) {
  print("level2")
} else {
  print("level3")
} 
[1] "level0"
# 使用简化的ifelse结构
ifelse(num < 30, "level0", ifelse(num >= 30 & num < 60, "level1", "x不大于3"))
[1] "level0"

在面对多个分支的情况时,多个if-else结构有点力不从心,这时可以使用switch结构

switch语句

在R语言中,switch语句用于基于表达式的值选择执行不同的代码块。switch语句的基本语法如下:

switch(expr,
   value1 = expr1,
   value2 = expr2,
   ...
   valueN = exprN,
   otherwise = exprOtherwise)
  • expr:是要评估的表达式,其值将用于匹配switch语句中的每个value。
  • value1, value2, …,valueN:是expr可能的值,每个值后面跟着一个等号=和要执行的表达式。
  • expr1, expr2, …, exprN:是与每个value相对应的表达式,如果expr的值与某个value匹配,那么对应的表达式将被执行。
  • otherwise:是一个可选的表达式,如果expr的值不匹配任何value,那么这个表达式将被执行。
letter <- sample(LETTERS, 1)
letter
[1] "P"
result <- switch(letter,
        "A"="答案是A",
        "B"="答案是B",
        "C"="答案是C",
        "答案不是A, B或C")
print(result)
[1] "答案不是A, B或C"

注意:switch (expr, value1 = expr1,...)函数根据第一个参数是字符串还是数字,工作方式有两种不同的表现。

  • 如果 expr 的值不是字符串,它将被强制转换为字符串。因子也会被强制转换。
  • 如果 expr 被评估为字符串,则该字符串将与value中元素的名称进行精确匹配。

下面的代码将会报错:

num <- sample(1:3, 1)
result <- switch(num,
       1 = "num等于1",
       2 = "num等于2",
       3 = "num等于3",
       "num不是1, 2, 或3")

正确的写法应该是:

num <- sample(1:3, 1)
num
[1] 1
result <- switch(num,
       "1"="num等于1",
       "2"="num等于2",
       "3"="num等于3",
       "num不是1, 2, 或3")
print(result)
[1] "num等于1"

循环结构

R提供了几种类型的循环结构,包括for循环、while循环和repeat循环。

1. for循环

对向量每个元素、数据框和矩阵每行、每列循环处理,语法为for(循环变量 in 序列) 语句,其中的语句一般是复合语句。

#简单的for循环:打印数值
for (i in 1:5) {
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
#遍历向量并进行简单运算
vec <- c(2, 4, 6, 8, 10)
for (i in vec) {
  print(i * 2)
}
[1] 4
[1] 8
[1] 12
[1] 16
[1] 20
#带有条件判断的for循环
vec <- c(3, 7, 10, 15, 20)
for (i in vec) {
  if (i > 10) {
    print(paste(i, "大于10"))
  } else {
    print(paste(i, "小于等于10"))
  }
}
[1] "3 小于等于10"
[1] "7 小于等于10"
[1] "10 小于等于10"
[1] "15 大于10"
[1] "20 大于10"
#嵌套for循环:生成矩阵
matrix_data <- matrix(0, nrow=3, ncol=3) # 创建一个3x3的0矩阵
for (i in 1:3) {
  for (j in 1:3) {
    matrix_data[i, j] <- i * j
  }
}
print(matrix_data)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    2    4    6
[3,]    3    6    9

1. for循环

for循环中也可以结合break和next:

#结合break和next的for循环
for (i in 1:10) {
  if (i == 5) {
    print("跳过 5")
    next # 跳过当前迭代
  }
  if (i == 8) {
    print("提前终止循环")
    break # 提前终止循环
  }
  print(i)
} 
[1] 1
[1] 2
[1] 3
[1] 4
[1] "跳过 5"
[1] 6
[1] 7
[1] "提前终止循环"

2. while和repeat循环

while循环在条件为真时重复执行代码块。

i <- 1
while (i <= 5) {
  print(i)
  i <- i + 1
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

repeat循环会无限循环,直到内部的break语句被执行。

i <- 1
repeat {
  print(i)
  i <- i + 1
  if (i > 5) {
    break
  }
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

★3. apply族函数

在处理大型数据框和矩阵、列表等数据集时,for循环操作麻烦且效率比较低下,显得力不从心。

apply族函数是一组强大的函数,它们可以对数组和矩阵进行操作,实现高效的循环和映射。这些函数包括:

  • apply:对矩阵的行或列应用函数
  • lapply、sapply、vapply:对列表或向量的每个元素应用函数
  • mapply:lapply的多参数版本,可以同时处理多个列表或向量
  • tapply:对因子的每个级别应用函数

apply:处理矩阵和数据框

apply(X, MARGIN, FUN, ..., simplify = TRUE)

  • X: 矩阵或数组。
  • MARGIN: 指定操作的维度,1为行,2为列。
  • FUN: 要应用的函数。
  • …: 传递给函数的额外参数。
  • simplify:是否尽可能地以简单的数据结构返回结果
mat <- matrix(c(1:10, 11:20, 21:30), nrow = 10, ncol = 3)
mat
      [,1] [,2] [,3]
 [1,]    1   11   21
 [2,]    2   12   22
 [3,]    3   13   23
 [4,]    4   14   24
 [5,]    5   15   25
 [6,]    6   16   26
 [7,]    7   17   27
 [8,]    8   18   28
 [9,]    9   19   29
[10,]   10   20   30
# 对每一行应用sum函数
apply(mat, 1, sum)
 [1] 33 36 39 42 45 48 51 54 57 60
# 求每一列的长度
apply(mat, 2, length)
[1] 10 10 10
# 对每一列应用自定义函数
apply(mat, 2, function (x) x*2)
      [,1] [,2] [,3]
 [1,]    2   22   42
 [2,]    4   24   44
 [3,]    6   26   46
 [4,]    8   28   48
 [5,]   10   30   50
 [6,]   12   32   52
 [7,]   14   34   54
 [8,]   16   36   56
 [9,]   18   38   58
[10,]   20   40   60

apply:处理矩阵和数据框

注意:无法在维度小于2维的数据(如向量)上使用apply,以下代码将报错:

vec <- c(1:10)
apply(vec, 1, sum)

apply传参:

f_add <- function (x,y){
  x+y
}
apply(mat,1,f_add,5)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    6    7    8    9   10   11   12   13   14    15
[2,]   16   17   18   19   20   21   22   23   24    25
[3,]   26   27   28   29   30   31   32   33   34    35
f_add_more <- function (x,y,z){
  x+y+z
}
apply(mat,1,f_add_more,y=5,z=8)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]   14   15   16   17   18   19   20   21   22    23
[2,]   24   25   26   27   28   29   30   31   32    33
[3,]   34   35   36   37   38   39   40   41   42    43
# 依次传入参数时可省略参数名
apply(mat,1,f_add_more,5,8)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]   14   15   16   17   18   19   20   21   22    23
[2,]   24   25   26   27   28   29   30   31   32    33
[3,]   34   35   36   37   38   39   40   41   42    43

lapply, sapply 和 vapply :处理向量和列表

lapply

vec <- 1:10
lapply(vec, sum)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3

[[4]]
[1] 4

[[5]]
[1] 5

[[6]]
[1] 6

[[7]]
[1] 7

[[8]]
[1] 8

[[9]]
[1] 9

[[10]]
[1] 10

这个函数并没有像我们预期的那样将值相加。这是因为 lapply将向量视为列表,并将函数应用于向量中的每个点。

my_lst<-list(c(1:9),c(1:12),c(2:20))
lapply(my_lst, sum)
[[1]]
[1] 45

[[2]]
[1] 78

[[3]]
[1] 209

sapplylapply 类似,但如果可能的话会简化输出。它将返回一个向量,而不是像 lapply 那样返回列表。

vec <- 1:10
sapply(vec, sum)
 [1]  1  2  3  4  5  6  7  8  9 10
my_lst<-list(c(1:9),c(1:12),c(2:20))
sapply(my_lst, sum)
[1]  45  78 209

vapply和sapply相似,但允许拟指定返回类型:

vec <- 1:10
vapply(vec, sum,FUN.VALUE = numeric(length = 1))
 [1]  1  2  3  4  5  6  7  8  9 10

mapply

mapply将函数运用于多个向量:mapply(FUN, …, MoreArgs = NULL, SIMPLIFY = TRUE,注意函数写在前面

  • FUN: 要应用的函数。
  • …: 一个或多个向量或数组,它们的元素将被传递给 FUN。
  • MoreArgs: 可选参数列表,可以包含额外的参数或命名参数。
  • SIMPLIFY: 如果为 TRUE(默认值),结果将被简化为向量或矩阵。如果为 FALSE,结果将被保留为列表。
x <- c(1,2,3)
y <- c(4,5,6)
z <- c(7,8,9)
mapply(sum, x, y)
[1] 5 7 9
mapply(function(a, b, c) a + b + c, x, y, z)
[1] 12 15 18

tapply

tapply用于对数据进行分组并应用函数。它通常用于将数据集分成由因子定义的组,并对每组数据应用一个函数,比如求和、平均、最大值等。

tapply(X, INDEX, FUN, ...)

  • X: 要处理的向量。
  • INDEX: 用于分组的因子。
  • FUN: 要应用的函数。
  • …: 传递给函数的其他参数。
# 创建一个向量和分组因子
values <- c(10, 20, 30, 40, 50, 60)
groups <- factor(c("A", "A", "B", "B", "C", "C"))

# 使用tapply对每个组应用求和函数
tapply(values, groups, sum)
  A   B   C 
 30  70 110 
# 使用tapply对每个组应用平均值函数
tapply(values, groups, mean)
 A  B  C 
15 35 55 

随堂练习:在真实数据集上使用apply

我们将使用MASS包中的state.77数据集演示apply函数的使用,它包含了美国各个州的犯罪率等数据,首先进行安装

if(!require("MASS"))
  install.packages("MASS")

library(MASS)
head(state.x77)
           Population Income Illiteracy Life Exp Murder HS Grad Frost   Area
Alabama          3615   3624        2.1    69.05   15.1    41.3    20  50708
Alaska            365   6315        1.5    69.31   11.3    66.7   152 566432
Arizona          2212   4530        1.8    70.55    7.8    58.1    15 113417
Arkansas         2110   3378        1.9    70.66   10.1    39.9    65  51945
California      21198   5114        1.1    71.71   10.3    62.6    20 156361
Colorado         2541   4884        0.7    72.06    6.8    63.9   166 103766

分析步骤

1:使用apply获取汇总数据

apply(state.x77, 2, mean)
Population     Income Illiteracy   Life Exp     Murder    HS Grad      Frost 
 4246.4200  4435.8000     1.1700    70.8786     7.3780    53.1080   104.4600 
      Area 
70735.8800 
apply(state.x77, 2, median)
Population     Income Illiteracy   Life Exp     Murder    HS Grad      Frost 
  2838.500   4519.000      0.950     70.675      6.850     53.250    114.500 
      Area 
 54277.000 
apply(state.x77, 2, sd)
  Population       Income   Illiteracy     Life Exp       Murder      HS Grad 
4.464491e+03 6.144699e+02 6.095331e-01 1.342394e+00 3.691540e+00 8.076998e+00 
       Frost         Area 
5.198085e+01 8.532730e+04 
  1. 保存apply的结果
state.summary<- apply(state.x77, 2, function(x) c(mean(x), sd(x))) 
state.summary
     Population    Income Illiteracy  Life Exp  Murder   HS Grad     Frost
[1,]   4246.420 4435.8000  1.1700000 70.878600 7.37800 53.108000 104.46000
[2,]   4464.491  614.4699  0.6095331  1.342394 3.69154  8.076998  51.98085
         Area
[1,] 70735.88
[2,] 85327.30
state.range <- apply(state.x77, 2, function(x) c(min(x), median(x), max(x)))
state.range
     Population Income Illiteracy Life Exp Murder HS Grad Frost   Area
[1,]      365.0   3098       0.50   67.960   1.40   37.80   0.0   1049
[2,]     2838.5   4519       0.95   70.675   6.85   53.25 114.5  54277
[3,]    21198.0   6315       2.80   73.600  15.10   67.30 188.0 566432

  1. 使用 mapply 计算新变量
population <- state.x77[1:50]
area <- state.area
pop.dens <- mapply(function(x, y) x/y, population, area)
pop.dens
 [1] 0.070045922 0.000618899 0.019419010 0.039733353 0.133578671 0.024374802
 [7] 0.618886005 0.281477880 0.141342213 0.083752293 0.134573643 0.009729885
[13] 0.198528369 0.146399934 0.050826079 0.027715647 0.083847011 0.078437030
[19] 0.031853078 0.389713529 0.704129829 0.156503367 0.046640815 0.049061112
[25] 0.068406854 0.005070070 0.019993008 0.005337434 0.087274291 0.935809086
[31] 0.009402791 0.364611909 0.103468604 0.009014364 0.260419194 0.038830647
[37] 0.023551005 0.261619571 0.766886326 0.090677830 0.008838761 0.098783259
[43] 0.045773344 0.014166941 0.049120616 0.122038466 0.052190873 0.074397254
[49] 0.081721694 0.003840105
  1. 使用tapply按区域探索人口
region.info <- tapply(population, state.region, function(x) c(min(x), median(x), max(x)))
region.info
$Northeast
[1]   472  3100 18076

$South
[1]   579.0  3710.5 12237.0

$`North Central`
[1]   637  4255 11197

$West
[1]   365  1144 21198