打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
揭开R语言中环境空间的神秘面纱 | 粉丝日志

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-environments/

前言

环境空间(environment)对于大部分的R使用者来说,都是比较陌生的。虽然我们不了解它的运行原理,但也不影响我们使用R语言。环境空间是R语言中关于计算机方面的底层设计,主要用于R语言是环境加载器。通过环境空间,封装了加载器的运行过程,让使用者在不知道底层细节的情况下,可以任意加载使用到的第三方的R语言程序包。

本文将揭开R语言中环境空间的神秘面纱。

目录

  1. R语言的环境空间
  2. 环境空间的特征
  3. 环境空间的访问

1 R语言的环境空间

在R语言中,不管是变量,对象,或者函数,都存在于R的环境空间中,R程序在运行时都自己的运行时空间。R语言的环境(environment)是由内核定义的一个数据结构,由一系列的、有层次关系的框架(frame)组成,每个环境对应一个框架,用来区别不同的运行时空间(scope)。

环境空间有一些特征,比如 每个环境空间要有唯一的名字;环境空间是引入类型的,非赋值类型;环境空间都有父环境空间,空环境是最顶层的环境空间,没有父空间;子环境空间会继承父环境空间的变量等。

本文的系统环境

  • Linux: Ubuntu Server 12.04.2 LTS 64bit
  • R: 3.0.1 x86_64-pc-linux-gnu

为了方便我们检查对象的类型,引入pryr包作为辅助工具。关于pryr包的介绍,请参考文章:撬动R内核的高级工具包pryr

# 加载pryr包> library(pryr)

1.1 创建一个环境

查看new.env()函数的定义。

new.env(hash = TRUE, parent = parent.frame(), size = 29L)

参数列表:

  • hash 默认值是TRUE,使用Hash table的结构。
  • parent 指定要创建环境的父环境。
  • size 初始化的环境空间大小。

运行函数new.env(),创建一个新环境。

# 创建环境e1> e1 <- new.env()# 输出e1> e1<environment: 0x3d7eef0># 查看e1类型> class(e1)[1] "environment"# otype查看e1类型,属于基本类型> otype(e1)[1] "primitive"

接下来,我们在e1环境中定义一个变量。

# 定义变量a> e1$a <- 10# 输出变量a> e1$a[1] 10# 列出当前环境中的变量> ls()[1] "e1"# 列出e1环境中的变量> ls(e1)[1] "a"

这时,我们看到了两个环境空间,当前环境空间和e1环境空间。e1做为一个变量在当前的环境中被定义,而变量a是在e1环境中被定义。

1.2 环境空间的层次结构

R语言的环境是一种有层次关系的结构,每个环境都有上一层环境,直到最顶层的空环境。R语言中有5种环境的定义 全局环境,内部环境,父环境,空环境 和 包环境。

  • 当前环境,即用户环境,是用户程序运行的环境空间。
  • 内部环境,构造出来的环境,可以是通过 new.env()函数显示创建的环境空间,也可以是匿名的环境空间。
  • 父环境,即上一层环境,环境空间的上一层。
  • 空环境,即顶层环境,没有父环境空间。
  • 包环境,包封装的环境空间。
# 当前环境> environment()<environment: R_GlobalEnv># 内部环境> e1 <- new.env()> e1<environment: 0x3e28948># 父环境> parent.env(e1)<environment: R_GlobalEnv># 空环境> emptyenv()<environment: R_EmptyEnv># 包环境> baseenv()<environment: base>

可以用search() 函数查看当前环境中加载的R包。

# 查看环境空间> search() [1] ".GlobalEnv"        "package:pryr"      "package:stats" [4] "package:graphics"  "package:grDevices" "package:utils" [7] "package:datasets"  "package:methods"   "Autoloads"[10] "package:base"# 当前的环境空间> .GlobalEnv<environment: R_GlobalEnv>> parent.frame()<environment: R_GlobalEnv>

查看父环境空间

# e1环境的父环境空间> parent.env(e1)<environment: R_GlobalEnv># 当前环境的父环境空间> parent.env(environment())<environment: package:pryr>attr(,"name")[1] "package:pryr"attr(,"path")[1] "/home/conan/R/x86_64-pc-linux-gnu-library/3.0/pryr"# base包环境的父环境空间> parent.env(baseenv())<environment: R_EmptyEnv># 空环境的父环境空间,因没有父环境,所以出现错误> parent.env(emptyenv())Error in parent.env(emptyenv()) : the empty environment has no parent

既然环境空间是有层次关系的,那么我们打印这个层次结构,从自定义的e1环境到空环境。

# 递归打印父环境空间> parent.call<-function(e){+   print(e)+   if(is.environment(e) & !identical(emptyenv(),e)){+     parent.call(parent.env(e))+   }+ }# 运行函数> parent.call(e1)<environment: 0x366bf18><environment: R_GlobalEnv><environment: package:pryr>attr(,"name")[1] "package:pryr"attr(,"path")[1] "/home/conan/R/x86_64-pc-linux-gnu-library/3.0/pryr"<environment: package:stats>attr(,"name")[1] "package:stats"attr(,"path")[1] "/usr/lib/R/library/stats"<environment: package:graphics>attr(,"name")[1] "package:graphics"attr(,"path")[1] "/usr/lib/R/library/graphics"<environment: package:grDevices>attr(,"name")[1] "package:grDevices"attr(,"path")[1] "/usr/lib/R/library/grDevices"<environment: package:utils>attr(,"name")[1] "package:utils"attr(,"path")[1] "/usr/lib/R/library/utils"<environment: package:datasets>attr(,"name")[1] "package:datasets"attr(,"path")[1] "/usr/lib/R/library/datasets"<environment: package:methods>attr(,"name")[1] "package:methods"attr(,"path")[1] "/usr/lib/R/library/methods"<environment: 0x20cb5d0>attr(,"name")[1] "Autoloads"<environment: base><environment: R_EmptyEnv>

通过找父环境空间,我们看到整个环境空间的层次结构,如图所示。

通过层次结构图,又可以发现R包的加载顺序。 最先加载的是base包,然后通过base::Autoloads()函数,分别加载6个基础包,上层的pryr包则是我手动加载的,最后以R_GlobalEnv环境为当前运行环境空间,内部环境空间是R_GlobalEnv环境的下层环境空间。

2. 环境空间的特征

上面中提到环境空间有一些特征,下面我们分别介绍一下。

2.1 每个环境空间中的对象名字要唯一

在当前环境空间中定义变量名x,并对x进行操作。

# 定义变量x> x<-10;x[1] 10# 查看x地址> address(x)[1] "0x2874068"# 对x改变赋值> x<-11;x[1] 11# 查看x地址> address(x)[1] "0x28744c8"

这样我们可以看到,x变量在每次赋值的时候,内存地址都会发生改变,但是x的名字还是x。

在不同的环境空间中,再定义一个变量x。

# 创建环境空间e1> e1<-new.env()# 在e1中定义变量x> e1$x<-20# 输出x> x;e1$x[1] 12[1] 20

在不同的环境空间中,可以有同名的变量名字。

2.2 环境空间变量的赋值

如果把e1环境空间变量,赋值给另一个变量f,再修改其环境内部变量,会是什么结果呢?

# 把e1赋值给f> f <- e1# 修改e1中a变量的值> e1$a <- 1111# 查看f环境空间的a值> f$a[1] 1111# 比较f环境和e1环境,是相等的> identical(f,e1)[1] TRUE# 查看e1和f的环境地址,是完全相同的> e1<environment: 0x3e28948>> f<environment: 0x3e28948>

所以,环境空间的赋值,是一种引入的传递,而不是新创建一个环境空间。

2.3 定义更上层的环境空间

空环境是最顶层的环境空间,然后是base包的环境空间,我们可以尝试创建一个靠近顶层的环境空间,让父环境空间是base包的环境空间。

# 创建e2环境,以base为父环境> e2 <- new.env(parent = baseenv())> e2<environment: 0x37cab18># 查看e2环境的父环境列表> parent.call(e2)<environment: 0x37cab18><environment: base><environment: R_EmptyEnv>

这样e2环境空间就位于了环境空间中的第三层。

2.4 子环境空间会继承父环境空间的变量

在当前环境中,定义一个变量x, 子环境e1中,对x重新赋值。

# 在当前环境,定义变量x> x<-1:5# 新建环境空间e1> e1 <- new.env()# e1环境空间中定义变量x> e1$x<-1# 在e1环境空间中定义函数,并对父环境空间的x变量重新赋值> e1$fun<-function(y){+   print('e1::fun')+   x<<-y+ }# 运行e1环境空间中的函数,将x赋值为50> e1$fun(50)[1] "e1::fun"# 当前环境x变量被修改> x[1] 50# e1环境x变量没有变化> e1$x[1] 1

这样我们就可以利用 <<- 赋值符号,来修改父环境中的变量。

3. 环境空间的访问

R语言中有一些辅助函数,可以帮助我们理解和使用环境空间。

  • new.env 创建一个环境空间
  • is.environment 判断是否是环境空间类型。
  • environment 查看函数的环境空间定义。
  • environmentName 查看环境空间名字。
  • env.profile 查看环境空间属性值。
  • ls 查看环境空间中的对象。
  • get 取出指定环境空间中的对象。
  • rm 删除环境空间中的对象。
  • assign 给环境空间中的变量赋值。
  • exists 查看指定环境空间中的对象是否存在。

接下来,我们进行环境空间的访问操作。

# 新建一个环境空间> e1<-new.env()# 判断e1是否是环境空间类型> is.environment(e1)[1] TRUE# 查看当前环境空间> environment()<environment: R_GlobalEnv># 查看函数的环境空间> environment(ls)<environment: namespace:base># 查看环境空间的名字> environmentName(baseenv())[1] "base"> environmentName(environment())[1] "R_GlobalEnv"# 查看e1环境空间的名字> environmentName(e1)[1] ""# 设置e1的名字> attr(e1,"name")<-"e1"> environmentName(e1)[1] "e1"# 查看e1环境空间的属性值> env.profile(e1)$size[1] 29$nchains[1] 1$counts [1] 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

环境空间中的对象操作。

# 清空当前环境空间定义的所的对象> rm(list=ls())# 定义环境空间有,和3个变量x> e1<-new.env()> x<-1:5;y<-2:10> e1$x<-10# 查看当前环境中的变量> ls()[1] "e1" "x"  "y"# 查看e1环境空间中的变量> ls(e1)[1] "x"# 取当前环境空间的x值> get("x")[1] 1 2 3 4 5# 取e1环境空间的x值> get("x",envir=e1)[1] 10# 在e1环境空间中去y值,这个y值是从当前环境空间中继承的> get("y",envir=e1)[1]  2  3  4  5  6  7  8  9 10# 禁止环境空间的继承,在e1环境空间中去y值,出错> get("y",envir=e1,inherits=FALSE)Error in get("y", envir = e1, inherits = FALSE) : object 'y' not found# 给x重新赋值> assign('x',77);x[1] 77# 给e1环境空间的x重新赋值> assign('x',99,envir=e1);e1$x[1] 99# 在没有继承的情况下,给e1空间增加y变量> assign('y',99,envir=e1,inherits=FALSE);> y[1]  2  3  4  5  6  7  8  9 10> e1$y[1] 99# 删除e1环境空间的变量x,和当前环境空间的y> rm(x,envir=e1)> e1$xNULL> x[1] 77# 查看当前环境空间,和e1环境空间>  ls()[1] "e1" "x"> ls(e1)[1] "y"# 查看x对象在当前环境空间是否存在> exists('x')[1] TRUE# 查看x对象在e1环境空间是否存在> exists('x',envir=e1)[1] TRUE# 查看x对象,在没有继承的情况下,在e1环境空间是否存在> exists('x',envir=e1,inherits=FALSE)[1] FALSE

另外,pryr包的where函数可以直接定位对象的环境空间。

# 查看mean函数定义的环境空间> where(mean)Error: is.character(name) is not TRUE> where("mean")<environment: base># 查看where函数定义的环境空间> where("where")<environment: package:pryr>attr(,"name")[1] "package:pryr"attr(,"path")[1] "/home/conan/R/x86_64-pc-linux-gnu-library/3.0/pryr"# 查看x变量定义的环境空间> where("x")<environment: R_GlobalEnv># 查看y变量定义的环境空间,由于y变量定义在e1中,e1是当前空间的子空间,所以访问不到y变量> where("y")Error: Can't find y> e1$y[1] 99# 在e1空间查看y变量> where("y",e1)<environment: 0x2545db0>

本文介绍了R语言中,环境空间的定义、结构和一些简单的使用,当然这并不是环境空间的全部内容。下一篇文章,将继续介绍函数环境空间的定义和使用,解密R语言函数的环境空间

转载请注明出处:
http://blog.fens.me/r-environments/

This entry was posted in R语言实践

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
SAP Spartacus 如何借助env-cmd 实现 B2B 和 B2C 功能启动的无缝切换
环境变量etc中environmentenvprofile设置及区别
Anconda安装
R学习:环境和函数
使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(八)
postman使用教程10-请求前参数预处理(pre-request)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服