数据科学03 | R语言程序设计-给符号赋值
The following article is from 珠江肿瘤 Author 李雪纯 冯文清
R赋值规则
例:定义了名为lm的函数,使用lm符号时,赋值新定义的lm函数还是stats包中拟合线性模型的lm函数?
lm<-function(x){x * x}
lm
function(x){x * x}
给符号赋值时,R将在一系列的环境中进行搜索在从而找到合适的值。
搜索列表顺序:
search()
[1] ".GlobalEnv" "tools:rstudio" "package:stats" "package:graphics"
[5] "package:grDevices" "package:utils" "package:datasets" "package:methods"
[9] "Autoloads" "package:base"
全局环境(工作区)
当前载入 R 的所有 R 包的命名空间
base 包在搜索列表的最后一位
1.每加载一个包,这个包的命名空间会排在搜索列表的第二位,其它包向后移一位。2.函数和非函数在 R 中有不同的命名空间,存在一个名为c的向量与名为c的函数不冲突,但全局环境中只能有一个名为c的符号。
library(stringr)
search()
[1] ".GlobalEnv" "package:stringr" "tools:rstudio"
[4] "package:stats" "package:graphics" "package:grDevices"
[7] "package:utils" "package:datasets" "package:methods"
[10] "Autoloads" "package:base"
mean <- 1:10
mean(mean)
[1] 5.5
mean
[1] 1 2 3 4 5 6 7 8 9 10
名为mean的向量和已有的函数mean()不冲突,但全局环境中只有一个名为mean的符号,给符号mean赋值时,首先在全局环境中找到的是名为mean的向量。
R 的作用域规则
➢函数的变量类型
形式参数,通过函数定义传入函数;
自由变量,存在于函数中的非参数的变量或符号。
f <- function(x, y) {
x^2+y/z
}
x、y是形式参数;符号 z 是自由变量。一个语言的作用域规则决定了如何给自由变量赋值。
・词法作用域(静态作用域)lexical scoping:R语言・动态作用域dynamic scoping:其他编程语言
➢词法作用域规则:
在定义函数的环境中搜索自由变量的值。
环境是符号值对的集合。环境可能有一个父环境(上层环境)和多个子环境。空环境没有父环境。
一般而言,一个函数会在定义它的环境中查找自由变量的值,再到它的父环境中查找,继续在父环境的父环境中查找,以此类推,直到找到自由变量的值或到达顶层环境。
通常顶层环境就是全局环境;如果函数在包中定义,那么顶层环境是那个包的命名空间,搜索到达顶层环境后,会沿着搜索列表继续进行直到到达空环境。
如果到达空环境仍未查找到相应的自由变量符号,就会报错,错误信息是无法为这个符号找到对应的值。
定义全局变量可以在工作区里定义的不同函数中通用。
在 R 中关键不同在于,构造性函数能在函数里面构造另一个函数。被构造的目标函数的环境就不是全局环境,而是在构造性函数内部。
例:
make.power <- function(n) {
pow <- function(x) {
x^n }
pow
}
自由变量n不是在 pow() 内部定义,而是在 make.power() 内部定义,make.power() 内部也是定义 pow() 的环境,因此pow() 能在这个环境中找到n的值。
cube <- make.power(3) #调用make.power()返回函数cube
square <- make.power(2) #调用make.power()返回函数square
cube(3)
[1] 27
square(3)
[1] 9
ls ()函数查看定义这个函数的环境。
ls(environment(cube))
[1] "n" "pow"
get("n", environment(cube)) #在cube函数内部查找自由变量n的值
[1] 3
ls(environment(square))
[1] "n" "pow"
get("n", environment(square))
[1] 2
➢比较词法作用域和动态作用域
y <- 10
f <- function(x){
y <- 2
y^2+g(x)
}
g <- function(x){
x*y
}
f(3)
34
词法作用域:g() 中 y 的值是在定义这个函数的环境即全局环境中查到的,所以函数 g() 中 y 取值为10。
动态作用域:y 的值要考虑调用这个函数的调用环境 (calling environment),调用环境里的 y 等于2,所以 g() 中 y 取值为2。
在全局环境中定义函数,并在全局环境中调用该函数,这样定义环境和调用环境就是同一个环境。
g <- function(x) {
a<-3
x+a+y
}
g(2)
Error in g(2) : 找不到对象'y'
#x是形式参数;a是局部变量,a不是形式参数但在函数中定义;y是自由变量,g()会在全局环境中寻找y的值
y<-3
g(2)
[1] 8
#定义y等于3,g(2)就能在全局环境中找到y
词法作用域要求所有对象都必须保存在内存里。
优化 (optimization)
➢处理优化问题的基本思想
利用构造函数构造一个目标函数,把目标函数所依赖的所有参数、数据等都整合在目标函数的定义环境中,每次调用函数时不必特意说明,只需要声明参数的值。代码不需要加上很长的参数列表,更简洁。
➢词法作用域应用:优化
R 中自带优化函数optim(),nlm() 和 optimize()默认求目标函数的最小值。优化函数调整目标函数的参数来实现函数的最小化。要实现目标函数的最大化,先取它的负值再把它进行最小化。
例:构造一个负的对数似然函数 (log likelihood),对这个负的对数似然函数进行最小化。
定义构造函数make.NegLogLik()构造目标函数——负的对数似然函数
make.NegLogLik <- function(data, fixed=c(FALSE,FALSE)) {
params <- fixed
function(p) {
params[!fixed] <- p
mu <- params[1]
sigma <- params[2]
a <- -0.5*length(data)*log(2*pi*sigma^2)
b <- -0.5*sum((data-mu)^2) / (sigma^2)
-(a+b)
}
}
・make.NegLogLik() 函数的第一个参数data是目标函数依赖的数据;・第二个参数fixed是一个逻辑向量,决定是否要固定目标函数的某些参数;・构造函数内部定义了目标函数,目标函数的参数是p(需要调整优化的参数向量);・构造函数返回正态分布(mu,sigma)的负的对数似然函数。
调用构造函数make.NegLogLik()返回目标函数nLL()
#模拟随机变量数据,服从正态分布(1,2)
set.seed(1); normals <- rnorm(100, 1, 2)
#对随机变量数据调用构造函数,构造目标函数nLL()
nLL <- make.NegLogLik(normals)
nLL
function(p) {
params[!fixed] <- p
mu <- params[1]
sigma <- params[2]
a <- -0.5*length(data)*log(2*pi*sigma^2)
b <- -0.5*sum((data-mu)^2) / (sigma^2)
-(a+b)
}
<bytecode: 0x7fde86c5e8d8>
<environment: 0x7fde894427e8>
#ls()查看 nLL() 函数的环境
ls(environment(nLL))
[1] "data" "fixed" "params"
・返回的目标函数nLL()最下面的标签指示了目标函数的内部环境的位置;・目标函数nLL() 中的变量data、fixed、params都是自由变量,可以在make.NegLogLik() 函数的定义环境中找到,不必再声明。
make.NegLogLik
function(data, fixed=c(FALSE,FALSE)) {
params <- fixed
function(p) {
params[!fixed] <- p
mu <- params[1]
sigma <- params[2]
a <- -0.5*length(data)*log(2*pi*sigma^2)
b <- -0.5*sum((data-mu)^2) / (sigma^2)
-(a+b)
}
}
<bytecode: 0x7fddf9fc2080>
通常在全局环境中定义函数,如make.NegLogLik函数,则不会有用来标注环境的标签;当在一个函数内部定义了另一个函数的时候,就必须有一个指针 (pointer) 指向定义环境,R才能记住所有自由变量的值。
调用optim()对目标函数的参数进行调整优化
optim(c(mu = 0, sigma = 1), nLL)$par
mu sigma
1.218239 1.787343
・optim()函数的par元素为最优解取值;・c(mu=0, sigma=1)为optim()函数中名为par的参数,设定各变量的初始值;・优化结果mu的估计值是1.2,sigma的估计值是1.78,接近真实值 (mu = 1, sigma = 2)。
可以固定参数,对其他参数优化,需要通过调用make.NegLogLik()重新构建目标函数。
#把变量fixed设置为(FALSE, 2),固定sigma
nLL <- make.NegLogLik(normals, c(FALSE, 2))
#optimize()最小化单变量函数
optimize(nLL, c(-1, 3))$minimum
[1] 1.217775
・调用make.NegLogLik(),固定mu=1,然后画出关于sigma的负的对数似然函数曲线。
nLL <- make.NegLogLik(normals, c(1, FALSE)) #构建了函数 nLL()
x <- seq(1.7, 1.9, len = 100)
y <- sapply(x, nLL) #对x中的值调用nLL()函数
plot(x, exp(-(y - min(y))), type = "l")
・固定 sigma=2,画出关于mu的负的对数似然函数曲线。
nLL <- make.NegLogLik(normals, c(FALSE, 2))
x <- seq(0.5, 1.5, len = 100)
y <- sapply(x, nLL)
plot(x, exp(-(y - min(y))), type = "l")
相关阅读
不看后悔!GEO数据库10X单细胞测序原始测序测序数据(fastq)下载指南来啦