我们到了第二站,这里居住着贪婪的人.
让我们看看创建一个有序系列的三种方法.
方法一是增长这个对象:
vec <- numeric(0)
for(i in 1:n) vec <- c(vec, i)
方法二是首先创建一个固定长度的对象,然后通过下表改变其中的值:
vec <- numeric(n)
for(i in 1:n) vec[i] <- i
方法三直接创建最终的对象:
vec <- 1:n
表2.1展示了在固定的n的情况下,用一个普通的计算机运行三种方法的秒数.
N的增长基本在重对数尺度上是线性的,但是运行时间却明显不同.
你或许会好奇为什么对象增长如此得慢.这是因为郊区城市化需要计算开销.当一个新的大小被需要时,对象所存储的地方没有足够的空间,然后这个对象需要被移动到一个更大的空间.然后当这个空间又不能满足对象的增长的时候,这个对象就又会被移动到一个更大的地方去.移动对象花去了非常多的时间.就像现实中的郊区城市化,增长的对象掠夺了一切有用的空间.最终你的存储介质中全是碎片化的可以用空间,没有大的存储块.这被称为破裂内存.
一个非常普遍---或许也是非常危险的方法是用函数rbind():
my.df <- data.frame(a=character(0), b=numeric(0))
for(i in 1:n)
{
my.df <- rbind(my.df, data.frame(a=sample(letters, 1),
b=runif(1)))
}
或许rbind()如此通用的主要原因是:每一次迭代都可能会有一个不同的数量的结果.如果这样的话,代码更可能会是这样:
my.df <- data.frame(a=character(0), b=numeric(0))
for(i in 1:n)
{
this.N <- rpois(1, 10)
my.df <- rbind(my.df, data.frame(a=sample(letters,
this.N, replace=TRUE), b=runif(this.N)))
}
通常最终对象的大小的上界是知道的,如果是这样,那就像创建一个上界大小的对象,然后对其赋值.如果最终的大小无法确定,那么你仍旧可以按照上边的做法,但是允许一段时间内对象空间的增长.
<p><span style="color:#000000;">current.N <- 10 * n</span></p><p><span style="color:#000000;">my.df <- data.frame(a=character(current.N),</span></p><p><span style="color:#000000;">b=numeric(current.N))</span></p><p><span style="color:#000000;">count <- 0</span></p><p><span style="color:#000000;">for(i in 1:n) {</span></p><p><span style="color:#000000;">this.N <- rpois(1, 10)</span></p><p><span style="color:#000000;">if(count + this.N > current.N) {</span></p><p><span style="color:#000000;">old.df <- my.df</span></p><p><span style="color:#000000;">current.N <- round(1.5 * (current.N + this.N))</span></p><p><span style="color:#000000;">my.df <- data.frame(a=character(current.N),</span></p><p><span style="color:#000000;">b=numeric(current.N))</span></p><p><span style="color:#000000;">my.df[1:count,] <- old.df[1:count, ]</span></p><p><span style="color:#000000;">}</span></p><p><span style="color:#000000;">my.df[count + 1:this.N,] <- data.frame(a=sample(letters,</span></p><p><span style="color:#000000;">this.N, replace=TRUE), b=runif(this.N))</span></p><p><span style="color:#000000;">count <- count + this.N</span></p><p><span style="color:#000000;">}</span></p><p><span style="color:#000000;">my.df <- my.df[1:count,]</span></p>
通常有一个简单地方法解决整个问题---主动创建一系列的对象块,然后最后将它们整合.
my.list <- vector(’list’, n)
for(i in 1:n)
{
this.N <- rpois(1, 10)
my.list[[i]] <- data.frame(a=sample(letters, this.N
replace=TRUE), b=runif(this.N))
}
my.df <- do.call(’rbind’, my.list)
下面的例子展示了将动态增长的对象聪明地隐藏掉的方法:每一次,当条件为真,hit就会增长.
hit <- NA
for(i in 1:one.zillion)
{
if(runif(1) < 0.3) hit[i] <- TRUE
}
消除增长的变量是提高R运行速度的最简单效果最卓著的方法之一.
假如你是用了太多的内存,R将会抱怨.关键因素是R将所有的数据都存在RAM之中.当你需要使用一个巨大的数据集时,这将是一个局限.R的上限是灵活的,特别地,R对于数据的类型从不挑剔.
你可以得到一条信息,对于一些筒子太熟悉了,比如:
Error: cannot allocate vector of size 79.8 Mb.
这个经常曲解了”我有xxx GB 的内存,为何R甚至不能分配80MB的空间”的意思这是因为R已经成功分配了大量的内存.这个错误信息是有多少内存R在运行失败点经历的.
看到过这种信息的筒筒就会问到:”那我该怎么办呢?”这里有一些简单地答案:
1.不要变成一个使用脑残程序结构的贪婪人
2.换一个大一点的计算机
3.降低问题大小
如果你认同第一天而对第二三条不敢苟同的话,你的替代选择将会很难.一种方法是重新启动R,但将是低效的.另外一个选择是检查你的代码哪里在消耗内存.你可以在代码中插入;
cat(’point 1 mem’, memory.size(), memory.size(max=TRUE), ’\n’)
这将展示你的R代码时时使用内存情况和当前任务中R使用的最大内存.
然而,或许一种更加有效更能提供信息帮助的程序是使用Rprof()输出内存.Rprof()也会输出时间使用情况.另外一种降低内存使用的方法是使用数据库.仅仅提取程序使用的那一部分数据.虽然存取数据会花上一点开销,但是这也是一种常用的方法.
一个仅仅使用R的”数据库”解决方案是保存对象在一个独立的文件中,实时使用.你的代码就可能像下边这样:
for(i in 1:n)
{
objname <- paste(’obj.’, i, sep=’’)
load(paste(objname, ’.rda’, sep=’’))
the obj <- get(objname)
rm(list=objname)
# use the obj
}
将来的超级计算机会解决这个问题吗?一些人的回答是:yes---他们的数据规模保持不便而计算机将强大的足够处理这些数据.对于另外一些人,他们认为情况会变得更糟糕哦---更强大的计算机意味着更大的数据集.如果你是第二种人.你或许现在都想使用数据库工作.
如果你拥有一台超级计算机,你或许会尝试去创建一些大型的R不能解决的问题.看:
?’Memory-limits’了解相关信息.