《R-5程序设计讲义资料》由会员分享,可在线阅读,更多相关《R-5程序设计讲义资料(54页珍藏版)》请在金锄头文库上搜索。
1、第五讲 R的数据结构(三)程序设计,目的:,学习R中程序设计方法,内容:,程序设计方法 实例 作业,程序控制结构,R是一个表达式语言,其任何一个语句都可以看成是一个表达式。表达式之间以分号分隔或用换行分隔。表达式可以续行,只要前一行不是完整表达式(比如末尾是加减乘除等运算符,或有未配对的括号)则下一行为上一行的继续。 若干个表达式可以放在一起组成一个复合表达式,作为一个表达式使用。组合用大括号表示,如: x x R语言也提供了其它高级程序语言共有的分支、循环等程序控制结构。,分支结构,分支结构包括if结构: if (条件) 表达式1 或 if (条件) 表达式1 else 表达式2 其中的“条
2、件”为一个标量的真或假值,表达式可以是用大括号包围的复合表达式。有else 子句时一般写成: if(条件) 表达式组 else 表达式组 这样的写法可以使else不至于脱离前面的if。 if else(test,yes,no),在用R编程序时一定要时刻牢记R是一个向量语言,几乎所有操作都是对向量进行的。而R中的if语句却是一个少见的例外,它的判断条件是标量的真值或假值。比如,我们要定义一个分段函数f(x),当x为正时返回1,否则返回0,马上可以想到用if语句实现如下: if(x0) 1 else 0 当x是标量时这个定义是有效的,但是当自变量x是一个向量时,比较的结果也是一个向量,这时条件无法
3、使用。所以,这个分段函数应该这样编程: y= numeric(length(x) yx0 - 1 yx=0 - 0 有多个if语句时else与最近的一个配对。可以使用if . else if . else if . else .的多重判断结构表示多分支。多分支也可以使用switch()函数。,help(swicth) switch(base) Select One of a List of Alternatives Description switch evaluates EXPR and accordingly chooses one of the further arguments (in
4、 .). Usage switch(EXPR, .) Arguments EXPRan expression evaluating to a number or a character string.the list of alternatives, given explicitly. Details If the value of EXPR is an integer between 1 and nargs()-1 then the corresponding element of . is evaluated and the result returned. If EXPR returns
5、 a character string then that string is used to match the names of the elements in . If there is an exact match then that element is evaluated and returned if there is one, otherwise the next element is chosen, e.g., switch(cc, a=1, cc=, d=2) evaluates to 2. In the case of no match, if theres a furt
6、her argument in switch that one is returned, otherwise NULL. See Examples,Examples require(stats) centre - function(x, type) switch(type, mean = mean(x), median = median(x), trimmed = mean(x, trim = .1) x - rcauchy(10) centre(x, mean) centre(x, median) centre(x, trimmed) ccc - c(b,QQ,a,A,bb) for(ch
7、in ccc) cat(ch,:,switch(EXPR = ch, a=1, b=2:3), n) for(ch in ccc) cat(ch,:,switch(EXPR = ch, a=, A=1, b=2:3, Otherwise: last),n) # Numeric EXPR dont allow an otherwise: for(i in c(-1:3,9) print(switch(i, 1,2,3,4),循环结构,循环结构中常用的是for循环,是对一个向量或列表的逐次处理,格式为“for( name in values) 表达式”,如: for(i in seq(along=
8、x) cat(x(, i, ) = , xi, n, sep=); s - s+xi; 这个例子我们需要使用下标的值,所以用seq(along=x)生成了x的下标向量。 如果不需要下标的值,可以直接如此使用: for(xi in x) cat(xi, n) s - s + xi ,当然,如果只是要求各元素的和,只要调用sum(x)即可。从这里我们也可以看出,显式的循环经常是可以避免的,利用函数对每个元素计算值、使用sum等统计函数及apply、lapply 、sapply、tapply等函数往往可以代替循环。因为循环在R中是很慢的(S-PLUS和R都是解释语言),所以应尽量避免使用显式循环。
9、我们再举一个例子。比如,我们要计算同生日的概率。假设一共有365个生日(只考虑月、日),而且各生日的概率是相等的(这里忽略了闰年的情况以及可能存在的出生日期分布的不均匀)。设一个班有n个人,当n大于365时至少两个人生日相同是必然事件。当n小于等于365时,我们可以计算P至少有两人同生日= 1 - Pn个人生日彼此不同,这时,n个人的生日可取值数为365n ,而n个人彼此不同的可能数为365中取n个的排列数,彼此不同的概率为P3n65 。因此,为了计算n1,2,.,364的情况下的同生日概率,可以用如下循环实现:,x = numeric(365) for(i in 1:365) xi= 1 f
10、or(j in 0:(i-1) xi= xi * (365-j)/365 xi x for(n in 1:365) xn =1 - prod(365:(365-n+1)/365) 这段程序只用了1秒。注意不能直接去计算365!,这会 超出数值表示范围。(choose(n, k) 、factorial(k) ),另外要注意使用for(i in 1:n)格式的计数循环时要避免一个常见错误,即当n为零或负数时1:n是一个从大到小的循环,而我们经常需要的是当n为零或负数时就不进入循环。为达到这一点,可以在循环外层判断循环结束值是否小于开始值。 while循环是在开始处判断循环条件的当型循环,如: Wh
11、ile (b-aeps) c 0) b - c else a - c 是一段二分法解方程的程序。,还可以使用 repeat 表达式 循环,在循环体内用break跳出。 在一个循环体内用next表达式可以进入下一轮循环。 如 i=1;x=rnorm(1);repeatx=x+rnorm(1); if(x3)break;i=i+1;print(c(i,x) 分支和循环结构主要用于定义函数及模拟重复。,利用二分法求方程的根:fzero(f,1,2,1e-6) fzero0) list (fail=“finding root is fail!) else repeat if (abs(b-a)eps)
12、 break x-(a+b)/2 if (f(a)*f(b)0) b-x else a-x list(root=(a+b)/2, fun=f(x) f-function(x) x3-x-1 fzero(f,1,2,1e-6),实际上: uniroot(f,c(1,2) $root 1 1.324718 $f.root 1 -5.634261e-07 $iter 1 7 $estim.prec 1 6.103516e-05,控制语句的几个简单示例,x=NULL;for(i in 1:5)x=cbind(x,i2) ,1 ,2 ,3 ,4 ,5 1, 1 4 9 16 25 i=1;x=NULL;
13、 while(i0) y=x else y=-x+10 i=1;x=rnorm(1);repeatx=x+rnorm(1);if(x3)break;i=i+1;print(c(i,x),怎么调出Packages来使用?,Packages:libraries 敲library(),就知道有什么libraries了,缺省library是base.比如要进入mass,就敲library(MASS) 每个library都有许多数据 在每个library敲data(),就知道有什么数据了 比如敲data(Titanic),就调出数据Titanic来了. 注意:R语言对大小写敏感.,R程序设计,对于复杂一
14、些的计算问题应该编写成函数。这样做的好处是: 编写一次可以重复使用,并且便于修改 函数内的变量名是局部的,运行函数不会使函数内的局部变量被保存到当前的工作空间,可以避免在交互状态下直接赋值定义很多变量使得工作空间杂乱无章。,工作空间管理,前面我们已经提到,R在运行时保持一个变量搜索路径表,要读取某变量时依次在此路径表中查找,返回找到的第一个;给变量赋值时在搜索路径的第一个位置赋值。但是,在函数内部,搜索路径表第一个位置是局部变量名空间,所以变量赋值是局部赋值,被赋值的变量只在函数运行期间有效。 用ls()函数可以查看当前工作空间保存的变量和函数,用rm()函数可以剔除不想要的对象。如: ls(
15、)(或objects()),1 A Ai b cl cl.f fit1 g1 marks ns 10 p1 rec tmp.x x x1 x2 x3 y rm(x, x1, x2, x3) ls() 1 A Ai b cl cl.f fit1 g1 marks ns 10 p1 rec tmp.x y ls()可以指定一个pattern参数,此参数定义一个匹配模式,只返回符合模式的对象名。模式格式是UNIX中grep的格式。比如,ls(pattern=tmp.)可以返回所有以“tmp.” 开头的对象名。 rm()可以指定一个名为list的参数给出要删除的对象名,所以rm(list=ls(pat
16、tern=“tmp.”) 可以删除所有以“tmp.”开头的对象名。 rm(list=ls() 删除所有对象,清空工作空间所有对象。,函数定义,R中函数定义的一般格式为“函数名= function(参数表) 表达式”。定义函数可以在命令行进行,例如 hello = function() cat(Hello, are you a good student?n) ; cat(n) ; hello #(查看函数具体内容) function() cat(Hello, are you a good student?n) ; cat(n) ; hello() #(运行函数) Hello, are you a good student?,函数体为一个复合表达式,各表达式的之间用换行或分号分开。不带括号调用函数显示函数定义,而不是调用函数。 在命令行输入函数程序很不方便修改,所以我们一般是打开一个其