第 3 章 数据管理 .. 3.1 控制结构 R语言是块状结构程序语言,块由大括号“{}”划分,程序语句由换行符或 分号分割。程序控制结构的作用是应用if条件语句、循环语句等控制程序的走 向。程序控制结构又称为流程控制。R 语言的基本控制结构有顺序结构、分支 结构和循环结构。 3.1.1 分支结构 条件分支语句在编程语言中非常常见,R 语言中,常用的条件分支语句有 if-else语句和switch语句。 1.if-else语句 创建if-else结构语句的基本格式如下。 if ( 条件) { //结果为TRUE 时执行语句组1 } else { //结果为FALSE 时执行语句组2 } 其中,条件是布尔表达式,当结果为TRUE时,执行if语句中的代码块,否 则将执行else语句中的代码块,这是最简单的分支结构。if-else结构语句还可 以多重条件嵌套,使用elseif语句完成多个条件的判断。除此之外,ifelse结构 比较紧凑,格式为ifelse(condition,statement1,statement2)。若condition为 TRUE,则执行第一个语句;若condition为FALSE,则执行第二个语句。当希 望结构的输入和输出均为向量时,可使用ifelse语句。相关示例如下所示。 > x <- c("what","is","the","Truth") > if ("Truth" %in% x) { print("Truth is found" ) + } else { print("Truth is not found") } [1] "Truth is found" 56 R 语言程序设计 > #嵌套的if-else 结构语句 > a <- -1 > if (a < 0) { + result = 0 + } else if (a < 1) { + result = 1 + } else { + result = 2 + } > result [1] 0 > #ifelse 语句 > x <- c(1,1,1,0,0,1,1) > ifelse(x != 1, 1, 0) #如果x 的值不等于1,输出1,否则输出0 [1] 0 0 0 1 1 0 0 2.switch分支语句 switch语句可以直接实现多分支语句。如果条件式结果等于n,则执行第n条分支 的处理;如果取值不符合条件,则返回“NULL”值。其格式为switch(expression,list),其 中expression为表达式,list为列表。如果表达式返回值在1到length(list)之间,则返回 列表相应位置的值,否则返回“NULL”值。当表达式等于list的变量名时,返回变量名对 应的值,否则返回“NULL”值。相关示例代码如下所示。 > switch (2, mean(1:10), 1:5, 1:10) #输出第2 个向量 [1] 1 2 3 4 5 > y <- "fruit" #输出变量名fruit 对应的值 > switch(y, fruit = "apple", vegetable = "broccoli",meat="beef") [1] "apple" 3.1.2 循环结构 常用的循环语句主要有for循环、while循环和repeat循环,常见的控制语句有break 语句和next语句,如表3-1和表3-2所示。 表3-1 常见的循环结构 循环结构描 述 for循环基本的循环语句,变量的遍历 while循环类似for语句,满足条件执行循环体 repeat循环多次执行,满足条件退出循环 第3章 数据管理 57 表3-2 常见的控制语句 控制语句描 述 break语句终止循环语句,执行循环后的下一语句 next语句跳过本次循环,执行下一次循环 1.for循环 for语句用于创建循环,格式为for(nameinexpr1){expr2}。其中,name为循环变 量,在每次循环时从expr1中顺序取值;expr1是一个向量表达式,expr2通常是一组表达 式,当name的值包含在expr1中时,执行expr2的语句,否则循环终止。在循环过程中如 果需要输出每次循环的结果,则可以使用cat()函数或print()函数。cat()函数的格式为 cat(expr1,expr2,…),其中,expr1、expr2为输出的内容,可为字符串或表达式。另外, 符号“\n”表示换行,“\n”后的语句将在下一行输出。 以下为两个使用for循环的例子。在第一个例子中,for(iinpv)表示i,即pv中的每 一个元素值,进入循环计算,i的取值是具体的成绩分值;在第二个例子中,for(iin1: length(pv))表示从1到length(pv)中每一个符合条件的i值进入循环计算,i的取值是位 置。因此,在循环中的if-else分支语句的条件表达式使用pv[i]来代表成绩。最后结果虽 然相同,但循环因子和用法是不同的,请思考这两个例子的不同点。 >#例1 > pv <- c(40, 55, 70, 95, 82, 100, 66, 90) > m <- 1 > for (i in pv){ + if (i < 60) { + result[m] <- "需要加强" + } else if (i <= 80) { + result[m] <- "继续加油" + } else { + result[m] <- "做得很好" + } + m <- m+1 + } > result [1] "需要加强" "需要加强" "继续加油" "做得很好" "做得很好" "做得很好" [7] "继续加油" "做得很好" >#例2 > pv <- c(40, 55, 70, 95, 82, 100, 66, 90) > m <- 1 > for (i in 1:length(pv)){ 58 R 语言程序设计 + if (pv[i] < 60) { + result[i] <- "需要加强" + } else if (pv[i] <= 80) { + result[i] <- "继续加油" + } else { + result[i] <- "做得很好" + } + } > result [1] "需要加强" "需要加强" "继续加油" "做得很好" "做得很好" "做得很好" [7] "继续加油" "做得很好" 2.while循环 while语句用于创建循环,格式为while(cond){expr}。其中,cond为判断条件, expr为一个或一组表达式。while循环重复执行语句expr,直到条件cond不为真为止。 注意:for是通过遍历一个向量来控制循环的次数,while是直接设置判断条件的范围,这 是两者的主要区别。示例如下。 > pv <- c(40, 55, 70, 95, 82, 100, 66, 90) > i <- 1 > while(i <=length(pv) ){ + if (pv[i] < 60) { + result[i] <- "需要加强" + } else if (pv[i] <= 80) { + result[i] <- "继续加油" + } else { + result[i] <- "做得很好" + } + i <- i + 1 + } > result [1] "需要加强" "需要加强" "继续加油" "做得很好" "做得很好" "做得很好" "继续加油" "做 得很好" 3.1.3 控制语句 repeat是无限循环语句,不能自动停止,需要配合使用break语句跳出循环,格式为 repeat{if(cond){break}}。repeat-break循环的相关示例如下所示。 > pv <- c(40, 55, 70, 95, 82, 100, 66, 90) > i <- 1 > repeat{ 第3章 数据管理 59 + if (i > length(pv)) { + break + } + if (pv[i] < 60) { + result[i] <- "需要加强" + } else if (pv[i] <= 80) { + result[i] <- "继续加油" + } else { + result[i] <- "做得很好" + } + i <- i + 1 + } > result [1] "需要加强" "需要加强" "继续加油" "做得很好" "做得很好" "做得很好" "继续加油" "做 得很好" .. 3.2 函 数 R语言大量使用函数,因此,灵活掌握函数的用法可以完成很多工作。R语言的基础 包和第三方包提供很多函数,可以直接使用。在处理复杂问题中,也可以编写自定义函数 来实现所需功能。本节重点介绍R 语言常用的数学运算函数、字符处理函数、日期处理 函数、自定义函数以及函数的嵌套等。 3.2.1 数学运算函数 1.数学函数 和其他数据分析软件一样,R语言中也有许多应用于计算和统计分析的函数,主要分 为数学函数、统计函数、概率函数等。常用的数学函数如表3-3所示。 表3-3 数学函数 函 数描 述 abs(x) 求绝对值 sqrt(x) 求平方根 ceiling(x) 求不小于x的最小整数 floor(x) 求不大于x的最大整数 trunc(x) 向0的方向截取x中的整数部分 round(x,digits=n) 将x舍入为指定位数的小数 signif(x,digits=n) 将x舍入为指定的有效数字位数 续表 60 R 语言程序设计 函 数描 述 sin(x),cos(x),tan(x) 求正弦、余弦和正切 asin(x),acos(x),atan(x) 求反正弦、反余弦和反正切 sinh(x),cosh(x),tanh(x) 求双曲正弦、双曲余弦和双曲正切 log(x,base=n) 对x取以n为底的对数 log(x) 对x取自然对数 log10(x) 对x取常用对数 exp(x) 指数函数 数学函数的示例如下所示。 > abs(-5) #求绝对值 [1] 5 > sqrt(16) #求平方根 [1] 4 > 16^(0.5) #和sqrt(16)等价 [1] 4 > ceiling(3.457) #求不小于x 的最小整数 [1] 4 > floor(3.457) #求不大于x 的最大整数 [1] 3 > trunc(5.99) #向0 的方向截取x 中的整数部分 [1] 5 > trunc(-5.99) #向0 的方向截取x 中的整数部分 [1] -5 > round(3.457, digits = 2) #将x 舍入为指定位的小数 [1] 3.46 > signif(3.457, digits = 2) #将x 舍入为指定的有效数字位数 [1] 3.5 > cos(2) #求余弦 [1] -0.4161468 > log(10, base = 10) #求以10 为底的对数 [1] 1 > log(10) #对10 取自然对数 [1] 2.302585 > log10(10) #对10 取常用对数 [1] 1 > exp(2.3026) #指数函数 [1] 10.00015 第3章 数据管理 61 2.统计函数 R语言的统计函数在应用中非常多,常用的统计函数见表3-4。 表3-4 统计函数 函 数描 述 mean(x) 求平均值 median(x) 求中位数 sd(x) 求标准差 var(x) 求方差 mad(x) 求绝对中位差 quantile(x,probs) 求分位数,其中x为待求分位数的数值型向量; probs为一个由[0,1]之间的概率值组成的向量 range(x) 求值域 sum(x) 求和 min(x) 求最小值 max(x) 求最大值 统计函数的示例如下所示。 > x <- c(1, 2, 3, 4) #向量x > mean(x) #求平均数 [1] 2.5 > median(x) #求中位数 [1] 2.5 > sd(x) #求标准差 1.290994 > var(x) #求方差 [1] 1.666667 > mad(x) #求绝对中位差 1.4826 > quantile( x, c(.3, .84 ) ) #求x 的30%和84%分位点 30% 84% 1.90 3.52 > range(x) #求值域 1 4 > sum(x) #求和 10 > min(x) #求最小值 1 62 R 语言程序设计 > max(x) #求最大值 4 3.2.2 字符处理函数 1.正则表达式 正则表达式是对字符串操作的一种逻辑公式,它不是R 语言的专属内容,而是程序 设计语言处理字符串的通用方式。正则表达式中包括普通字符,例如a到z之间的字母 以及特殊字符,称为“元字符”。常见的元字符见表3-5。 表3-5 常见的元字符 符号含 义符号含 义 \\ 转义字符() 提取匹配的字符串 | 可选项[] 选择中括号中的任意一个字符 $ 放在句尾,表示一行字符串的结束{} 前面的字符或表达式的重复次数 . 除了换行以外的任意字符* 前面的字符或表达式重复0次或多次 ^ 匹配字符串的位置+ 前面的字符或表达式重复1次或多次 ? 前面的字符或表达式重复0次或1次 正则表达式符号的运算顺序可以总结为:圆括号内的表达式最优先;然后是表示重 复次数的操作,例如*、+、{};接下来是连接运算符;最后是表示可选项的运算符|。R 语言中,将反斜杠\作为一个转义符。输入“? Quotes”可查看转义符的用法。常用的转 义符如表3-6所示。 表3-6 常用的转义符 符号含 义符号含 义 \t tab \v verticaltab \b backspace \ backslash\ \a alert(bell) ' ASCIIapostrophe' \f formfeed " ASCIIquotationmark" 2.字符处理函数 R语言提供了很多字符处理函数,常用的函数如表3-7所示。 第3章 数据管理 63 表3-7 常用的字符处理函数 函 数描 述 strsplit() 字符串分割 paste() 字符串连接 nchar() 计算字符数量 substr(x,start,stop) 提取或替换一个字符向量中的字串 strsplit(x,split,fixed=FALSE) 在split处分隔字符向量x中的元素 grep() 正则表达式函数,用于查找 sub() 正则表达式函数,用于替换 chartr() 字符串替换 toupper() 切换大写字母 tolower() 切换小写字母 字符处理函数使用方法示例如下。 > myresult <- strsplit ("123abcdefgabcdef", split = "ab" ) #用分割参数分割 > myresult [1] "123" "cdefg" "cdef" > temp_1 <- c("a", "b", "c" ) > temp_2 <- c("1", "2", "3" ) > paste ( temp_1, temp_2, sep = "_" ) #两个字符串连接:sep [1] "a_1" "b_2" "c_3" > paste( temp_1, collapse = "_" ) #一个字符串向量内部连接 "a_b_c" > paste (temp_1, temp_2, sep = "_", collapse = ":" ) #字符串内外都用的模式 [1] "a_1 : b_2 : c_3" > nchar("abc") #求字符数量 3 > substr("abcdef", start = 2, stop = 4) #提取子串 "bcd" > substring( "abcdef",first = 2) #提取子串 "bcdef" > temp_3 <- rep("abcdef", 3 ) #重复 > temp_3 "abcdef" "abcdef" "abcdef" > nchar(temp_3) 6 6 6 > substr (temp_3, 1:3, 4:6 ) #头部从1 到3,尾部从4 到6 的子串截取 "abcd" "bcde" "cdef" > x2 <- c("asfef", "qwerty", "yuiop", "b" ) 64 R 语言程序设计 > chartr(old = "sey", new = "123", x2) #字符替换 [1] "a1f2f" "qw2rt3" "3uiop" "b" 除了R语言基础包中的字符处理函数外,用户也会经常使用第三方包stringr,其函 数命名为“str_XXX”形式,具有规范和标准的参数定义,能够有效地提高代码的编写效 率。stringr包常用的字符处理函数有:字符串拼接,如str_c(),将多个字符串或向量拼 接为一个字符串;str_trim(),去掉字符串的空格;str_pad(),可以补充字符串的长度;复 制字符串,如str_dup(),用于对字符串或向量的重复复制;str_sub(),用于在指定位置截 取子字符串;此外,还有字符串值排序函数str_sort()、字符串分割函数str_split()、字符 串匹配函数str_subset()、字符串替换函数str_replace()、字符编码转换函数str_conv()以及 字符串计算函数,如str_count()和str_length()等。 3.2.3 日期处理函数 日期是重要且特殊的一类数据,通常以字符串的形式输入,但字符型的日期值无法进 行计算,因此R语言提供了相关处理函数,将字符型的日期值转换成日期变量,实现计算 需求,并转换为数值形式存储。as.Date()函数用于将字符型变量转换为日期型,但是转 换时需按照一定的规范格式:当输入默认格式“yyyy-mm-dd”时,字符可以自动转换为对 应的日期;当输入其他格式时,需要用到格式转换。同样,可使用as.character()函数将日 期转换为字符型数据,转换后可以使用一系列的字符处理函数,如取子集、替换、连接等函 数。表3-8列出了常用的日期格式,表3-9列出了常用的日期函数。 表3-8 常用的日期格式 符号含 义示例符号含 义示例 %d 数字表示的日期01~31 %Y 四位数的年份2022 %a 缩写的星期名Mon %H 24小时制小时00~23 %A 非缩写的星期名Monday %I 12小时制小时01~12 %w 数字表示0~6 %p AM/PM 指示AM/PM %m 数字表示的月份01~12 %M 十进制的分钟00~60 %b 缩写的月份Jan %S 十进制的秒00~60 %B 非缩写的月份January %y 两位数的年份21 表3-9 常用的日期函数 函 数功 能 Sys.Date() 返回系统当前的日期 Sys.time() 返回系统当前的日期和时间 date() 返回系统当前的日期和时间(返回的值为字符串) 续表 第3章 数据管理 65 函 数功 能 as.Date() 将字符串形式的日期值转换为日期变量 as.POSIXlt() 将字符串转换为包含时间和时区的日期变量 strptime() 将字符型变量转换为包含时间的日期变量 strftime() 将日期变量转换为指定格式的字符型变量 format() 将日期变量转换为指定格式的字符串 Sys.Date()、Sys.time()和date()三个函数都是返回当前日期和时间。format()函数 主要用于将日期变量转换为指定格式的字符串。 > Sys.Date() [1] "2022-01-12" > Sys.time() [1] "2022-01-12 09:44:58 CST" > date() [1] "Wed Jan 12 09:46:16 2022" > format(Sys.time(),format="%B-%d-%Y") [1] "一月-12-2022" as.Date()函数和as.POSIXlt()函数也是常用的日期处理函数。as.Date()函数将字 符串形式的日期值转换为日期变量,对于标准格式,即形如“yyyy-mm-dd”或“yyyy/mm/ dd”格式的时间数据,可以直接转换为Date类型;对于非标准格式,在as.Date()函数中可 以增加一个format选项,通过format表达式读入指定的格式。as.POSIXlt()函数将字 符串转换为包含时间及时区的日期变量,它以列表的形式把字符串型日期时间值分成年、 月、日、时、分、秒,进行存储。在返回结果中,UTC为世界标准时间,和GMT(Greenwich MeanTime,格林威治标准时间)大致等同,是世界时间参考点。CST 可同时代表不同时 区的标准时间,如美国、澳大利亚、古巴或中国的标准时间,与R 语言所使用的操作系统 时间配置相一致。 > as.Date("2022/1/10") [1] "2022-01-10" > as.Date(c("2022-1-10", "2022-1-11")) [1] "2022-01-10" "2022-01-11" > as.Date("1/10/2022", format="%m/%d/%Y") #按照月/日/年的格式输入 [1] "2022-01-10" > as.POSIXlt("1/10/2022",tz="",format="%m/%d/%Y") [1] "2022-01-10 CST" strptime()函数和strftime()函数用于数据类型的转换。strptime()函数将字符型变 量转换为包含时间的日期变量,strftime()函数将日期变量转换为指定格式的字符型 66 R 语言程序设计 变量。 > strptime("1/10/2022", format="%m/%d/%Y", tz="") [1] "2022-01-10 CST" > strftime("2022-01-10 19:33:02 CST", format="%Y/%m/%d") [1] "2022/01/10" 以下为日期处理函数的综合使用案例。 #新建一个字符型日期数据变量 > x <- c("2022-02-08 10:07:52", "2022-08-07 19:33:02") > is.character(x) #字符型 [1] TRUE > as.POSIXlt(x, tz = "", "%Y-%m-%d %H:%M:%S") [1] "2022-02-08 10:07:52 CST" "2022-08-07 19:33:02 CST " > as.Date(x, "%Y-%m-%d") [1] "2022-02-08" "2022-08-07" > (x <- strptime(x, "%Y-%m-%d %H:%M:%S")) [1] "2022-02-08 10:07:52 CST" "2022-08-07 19:33:02 CST " > strftime(x, format = "%Y/%m/%d") [1] "2022/02/08" "2022/08/07" > class(x) [1] "POSIXlt" "POSIXt" #输出的格式转换为format 定义的格式 > format(x, "%d/%m/%Y") [1] "08/02/2022" "07/08/2022" 3.2.4 自定义函数 R语言可以灵活使用自定义函数来完成较大规模的程序。自定义函数的结构如下。 myfunction <- function ( arglist ) { statements return ( object ) } 其中,myfunction为函数名称,arglist为函数中的参数列表,大括号“{}”内的语句为 函数体,函数参数是函数体内部将要处理的值。函数中的对象只能在函数内部使用。函 数体通常包括三部分: (1)异常处理,若输入的数据不能满足函数计算的要求,或者类型不符,则应设计相 应的机制提示哪个地方出现错误; (2)运算过程,包括具体的运算步骤; (3)返回值,表示函数输出的结果,一般用return()函数给出。函数在内部处理过程 第3章 数据管理 67 中,一旦遇到return()函数,就会终止运行,将return内的数据作为函数处理的结果返回。 当没有写return()函数时,R语言默认将最后一行作为返回值。如果函数的结果需要有 多个返回值,可以创建一个list()函数并返回该对象。自定义函数的示例如下。 > #例1:加法运算 > S <- function(x,y){ + a <- x+y + return(a) + } > S(2, 3) 5> #例2:求向量中的偶数个数 > Ans <- function(x){ + k=0 + stopifnot(is.numeric(x)) #异常处理 + for(i in x){ + if (i %% 2==0){ + k = k+1 + } + } + return(k) + } > Ans(1:10) 5 #1 到10 里面有5 个偶数 > #例3:z=x 的平方+y 的平方,求x+y+z 的值,不使用return()函数 > a<- function(x, y){ z <- x^2 + y^2; x+y+z } > a(0:7, 1) #x=0~7,y=1,代入函数a [1] 2 4 8 14 22 32 44 58 > (function(x, y){ z <- x^2 + y^2; x+y+z })(0:7, 1) #另一种写法 2 4 8 14 22 32 44 58 > #例4:用于将矩阵与其转置相乘,符号为%*% > norm <- function(x) sqrt(x%*%x) > norm(1:4) [,1] [1,] 5.477226 > #可以自行分解 > (1:4)%*%(1:4) #向量( 1,2,3,4) 与其转置相乘 [,1] #结果是1 行1 列的矩阵 [1,] 30 > sqrt((1:4) %*% (1:4)) [,1] [1,] 5.477226 > class(sqrt((1:4) %*% (1:4))) #结果是矩阵类型 [1] "matrix" "array" 68 R 语言程序设计 3.2.5 函数的嵌套 在R语言的数据处理中,有时为了完成某项操作通常需要使用不止一个函数,而且 需要将上一个函数的结果作为下一个函数的输入,嵌套多次之后才得到最终结果,这称为 函数的嵌套。在嵌套过程中,需要注意函数中变量的作用范围。变量分为全局变量和局 部变量两种,在函数内部对变量赋值,则这个变量属于局部变量,仅在函数内部有效;当在 函数定义之前对变量赋值,并且在函数内部使用这个变量,则这个变量属于全局变量。具 体用法如下所示。 > #例1:函数中嵌套函数,x 和y 是函数的参数 > S <- function(x,y){ + a <- x + y + b <- function(){ + return(a*2) + } + return(b()) + } > S(2,3) [1] 10 > #例2:函数中调用其他自定义函数,注意变量的作用范围 > y <- 10 > f <- function( x ){ + y <- 2 + y^2 + g(x) + } > g <- function(x){ + x * y + } > f(5) [1] 54 如例2所示,在f()函数中,y是局部变量,被赋值为2;在g()函数中,y是全局变量, y的取值是在定义g()函数时决定的,因此y的取值是10而不是2。求值f(5)时,结果为 y^2+ g(x)=2^2+g(5)=2^2+5*10=54,返回值为54。 .. 3.3 apply函数族 3.3.1 函数族 在进行数据批量处理时,虽然可以使用for循环在数据对象上重复执行表达式,但实 践中,for循环往往是最后的选择,因为每次迭代重复都是相互独立的,效率比较低,所以 第3章 数据管理 69 通常尽量使用向量化操作来代替循环操作,用更简洁、更快速的方式来实现相同的效果。 向量化操作,是同时对一批值或者一批变量做相同的计算操作,这种操作效率高、快速 简洁。 apply函数族是R语言中数据处理的一组常用核心函数的集合,可以实现对数据的 循环、分组、计算、过滤、控制,并返回结果,能够对数据进行向量化操作,解决数据for循 环处理速度慢的问题。为了面向不同的数据类型和不同的返回值要求,这些功能类似的 函数成了一个函数族,主要包括函数apply()、lapply()、sapply()、tapply()、mapply()、 rapply()、vapply()、eapply()等。apply函数族中常用的函数如表3-10所示。 表3-10 apply函数族中常用的函数 函 数 名使用对象返回结果 apply() 矩阵、数组、数据框向量、数组、列表 lapply() 列表、向量列表 sapply() 列表、数据框、向量向量、数组 tapply() 不规则数组列表 mapply() 列表、向量列表 3.3.2 apply()函数 apply()函数可以对矩阵、数据框和数组按行或列进行计算并返回计算结果,是常用 的代替for循环的函数。使用“? apply”可查看详细的说明,语法格式为apply(x, MARGIN,FUN,…),其中,x表示需要处理的数据;MARGIN 表示对哪个维度使用函 数;FUN 则是所使用的函数,既可以是自定义的函数,也可以是R自带的函数;“… ”表 示FUN 函数的其他参数。 >(x <- matrix(1:20,ncol=4)) [,1] [,2] [,3] [,4] [1,] 1 6 11 16 [2,] 2 7 12 17 [3,] 3 8 13 18 [4,] 4 9 14 19 [5,] 5 10 15 20 > apply(x,1,mean) #维度为1:行,使用mean()函数 [1] 8.5 9.5 10.5 11.5 12.5 > apply(x,2,mean) #维度为2:列 [1] 3 8 13 18 > apply(x,1,max) #维度为1:行,使用max()函数 [1] 16 17 18 19 20 Iris鸢尾花卉数据集是常用的分类实验数据集,包含150个数据样本,分为3类,每 70 R 语言程序设计 类50个数据,每个数据包含4个属性:花萼长度、花萼宽度、花瓣长度、花瓣宽度,预测鸢 尾花卉属于三个种类(Setosa,Versicolour,Virginica)中的哪一类。apply()函数在Iris数 据集上的实验如下所示。 > class(iris) [1] "data.frame" > dimnames(iris)[[2]] #显示列的名字,等同于colnames(iris) [1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species" > head(iris) #显示数据集中前面几行数据 Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa 2 4.9 3.0 1.4 0.2 setosa 3 4.7 3.2 1.3 0.2 setosa 4 4.6 3.1 1.5 0.2 setosa 5 5.0 3.6 1.4 0.2 setosa 6 5.4 3.9 1.7 0.4 setosa > apply(iris[ ,1:4],1,mean) #前四列数据,按行,求均值 [1] 2.550 2.375 2.350 2.350 2.550 2.850 2.425 2.525 2.225 2.400 2.700 2.500 [13] 2.325 2.125 2.800 3.000 2.750 2.575 2.875 2.675 2.675 2.675 2.350 2.650 [25] 2.575 2.450 2.600 2.600 2.550 2.425 2.425 2.675 2.725 2.825 2.425 2.400 [37] 2.625 2.500 2.225 2.550 2.525 2.100 2.275 2.675 2.800 2.375 2.675 2.350 [49] 2.675 2.475 4.075 3.900 4.100 3.275 3.850 3.575 3.975 2.900 3.850 3.300 [61] 2.875 3.650 3.300 3.775 3.350 3.900 3.650 3.400 3.600 3.275 3.925 3.550 [73] 3.800 3.700 3.725 3.850 3.950 4.100 3.725 3.200 3.200 3.150 3.400 3.850 [85] 3.600 3.875 4.000 3.575 3.500 3.325 3.425 3.775 3.400 2.900 3.450 3.525 [97] 3.525 3.675 2.925 3.475 4.525 3.875 4.525 4.150 4.375 4.825 3.400 4.575 [109] 4.200 4.850 4.200 4.075 4.350 3.800 4.025 4.300 4.200 5.100 4.875 3.675 [121] 4.525 3.825 4.800 3.925 4.450 4.550 3.900 3.950 4.225 4.400 4.550 5.025 [133] 4.250 3.925 3.925 4.775 4.425 4.200 3.900 4.375 4.450 4.350 3.875 4.550 [145] 4.550 4.300 3.925 4.175 4.325 3.950 > apply(iris[ ,1:4],2,mean) #前四列数据,按列,求均值 Sepal.Length Sepal.Width Petal.Length Petal.Width 5.843333 3.057333 3.758000 1.199333 3.3.3 tapply()函数 tapply()也是常用的函数,格式为tapply(x,INDEX,FUN=NULL,…,simplify= TRUE),作用是把FUN 函数根据INDEX索引应用到x数据,可以理解为将数据按照不 同方式分组,生成类似列联表形式的数据结果。tapply()函数在Iris数据集上的实验如 下所示。 > tapply(iris$Sepal.Length, iris$Species, mean) setosa versicolor virginica 5.006 5.936 6.588 第3章 数据管理 71 3.3.4 lapply()函数 lapply()函数主要用于列表等数据结构,格式为lapply(x,FUN,… ),作用是将函数 FUN 运用到列表的每一个元素,对列表、数据框等数据集进行循环,返回值为列表。 lapply函数在Iris数据集上的实验如下所示。 > lapply(iris[,1:4],mean) $`Sepal.Length` [1] 5.843333 $Sepal.Width [1] 3.057333 $Petal.Length [1] 3.758 $Petal.Width [1] 1.199333 3.3.5 sapply()函数 sapply()函数和lapply()函数类似,但是返回的数据结构更灵活。sapply()函数的格 式为sapply(x,FUN,simplify=TRUE,USE.NAMES=TRUE,…),其中,simplify参 数用来调整输出的数据格式,输入为列表,返回值为向量。sapply函数在Iris数据集上的 实验如下所示。 > sapply(iris[,1:4],mean) Sepal.Length Sepal.Width Petal.Length Petal.Width Species 5.843333 3.057333 3.758000 1.199333 NA .. 3.4 数据输入与输出 3.4.1 数据输入 R语言可以从键盘、文本文件、MicrosoftExcel和Access、流行的统计软件、特殊格 式的文件以及多种关系型数据库中导入数据。文本文件可被几乎所有的数据分析软件读 取,其数据若为类似电子表格的数据,通常带有分隔符,包括逗号分隔值(CSV)和制表符 分隔值(TXT),以后缀区分。read.table()函数读取这些文本文件,并将结果存储在一个 数据框中。如果使用Rstudio,可以使用其提供的数据导入功能。 72 R 语言程序设计 1.利用RStudio导入数据 Studio顶部菜单选择Tools->ImportDataset->From LocalFile,弹出窗口选择要 导入的数据文件,然后按照提示导入。若数据文件中包含了列名,则在Heading选择 yes;若文件中列是用逗号分隔的,则Separator选择Comma。单击Import即可导入数据 并保存对象。 2.利用函数导入数据 对于数据文件或结构化文本文件,主要使用read.table()、read.csv()等函数进行操 作。read.table()的参数包括header= TRUE,表明有标题行;sep=",",表明使用逗号 作为字段之间的分隔符;nrow 可以指定读取数据的行数;skip决定跳过文件开始的多少 行;fill= TRUE表示会使用NA 值代替缺失的值。此外还有更多选项包括覆盖默认的 行名、列名和类,指定输入文件的字符编码以及输入的字符串格式的列声明等。当数据文 件不在当前工作目录中,则需加上正确的相对或绝对路径。 对于非结构化文本文件,如果文件的结构松散,可先读入文件中的所有文本行,再对 其内容进行分析或操作,如使用readLines()函数读取文件、writeLines()函数执行写操 作。在Windows系统中,可以使用RODBC包、xlsx包等包来访问Excel文件。 > read.table("sample.txt", header=T, sep=",") #读入文本文件 > read.csv("sample.csv", header=T, sep=",") #读入csv 文件 > read.spss("sample.sav") #读入SPSS 数据 > readtemptxt <- readLines("sample.txt") #读入文件 > writeLines("sample.txt", "add a new line") #写入文件 > library(xlsx) > read.xlsx("sample.xls") #读入Excel 文件 R语言也支持网络爬虫,即抓取网络数据,rvest是较为常用的包。其他如quantmod 包,用于金融建模;RCurl包,实现HTTP的一些功能,如从服务器下载文件、保持连接、 上传文件、采用二进制格式读取、句柄重定向、密码认证等。 3.4.2 数据输出 R语言提供了多种数据输出方式,根据输出的形式分为以文本文件输出和以图片形 式输出。 1.以文本文件输出 使用write.table()将内容导出为文本文件,使用write.csv()将内容导出为csv文件。 >age <- c(22,23) >name <- c("ken", "john") 第3章 数据管理 73 >f <- data.frame(age, name) > write. table(f, file =" f. csv", row. names = FALSE, col. names = FALSE, quote = FALSE) 2.以图片形式输出 在R语言中绘制的图片可以用png、jpeg、pdf命令保存为相应格式的图片文件。 >png(file="myplot.png", bg="transparent") #保存为PNG 格式: >jpeg(file="myplot.jpeg") #保存为JPEG 格式 >pdf(file="myplot.pdf") #保存为PDF 格式 .. 3.5 综合实验 3.5.1 实验1:编写自定义函数 1.实验目标 掌握控制结构的使用方法;掌握自定义函数的方法。 2.实验内容 (1)判断101~200有多少个素数,并输出所有素数。 (2)编写一个自定义函数,求两个矩阵的乘积,并找出乘积矩阵中的最大元素。 3.实验步骤 (1)判断101~200有多少个素数,并输出所有素数。 > tmp <- 0 #存放素数个数 > i <- 101 > while (i <= 200){ #i 从101 计算到200 + fg <- 0 #用来判断是否能输出 + j <- 2 #用来除去要判断的数,从2 开始 + while (j < sqrt(i-1)){ #循环判断 + if (i %% j == 0){ #这里求素数,能被整除则不为素数 + fg = 1 #这里代表可以整除 + break #这里用来停止 + } + j <- j + 1 + } + if (fg == 0){ #判断是否可以输出 + print(i) #输出为素数的数 + tmp <- tmp + 1 74 R 语言程序设计 + } + i <- i + 1 #进行下一个数的判断 + } #即将输出结果略 > tmp [1] 23 (2)编写一个自定义函数,求两个矩阵的乘积,并找出乘积矩阵中的最大元素。 > myfunction <- function(x, y) { + m1 <- ncol(x) + n <- nrow(y) + if (m1 != n) { + #第一个矩阵的列数等于第二个矩阵的行数时才能相乘 + print("error dimension is not suitable") + return(0) + } + m <- nrow(x) + n1 <- ncol(y) + s <- matrix(0, m, n1) + for (i in 1:m) { + for (j in 1:n1) { + s[i, j] <- sum(x[i, ], y[, j]) + #相乘后第i 行第j 列的元素等于第一个矩阵第i 行乘以第j 列元素再相加 + } + } + return(s) + } > x <- matrix(c(1:10), 5, 2, byrow = TRUE) > y <- matrix(c(1:10), 2, 5, byrow = FALSE) > myfunction(x, y) [,1] [,2] [,3] [,4] [,5] [1,] 6 10 14 18 22 [2,] 10 14 18 22 26 [3,] 14 18 22 26 30 [4,] 18 22 26 30 34 [5,] 22 26 30 34 38 > s <- myfunction(x,y) > max <- s[1,1] #假设s 矩阵的第一个元素为最大值 > for(i in 1:nrow(s) ){ + for (j in 1:ncol(s) ){ + if (s[i,j] > max){