Hash Table Performance in R: Part I(转)
What Is It?
A hash table, or associative array, is a well known key-value data structure. In R there is no equivalent, but you do have some options. You can use a vector of any type, a list, or an environment.
But as you’ll see with all of these options their performance is compromised in some way. In the average case a lookupash tabl for a key should perform in constant time, or O(1), while in the worst case it will perform in O(n) time,n being the number of elements in the hash table.
For the tests below, we’ll implement a hash table with a few R data structures and make some comparisons. We’ll create hash tables with only unique keys and then perform a search for every key in the table. Here’s our unique random string creator function:
library(plyr)
library(ggplot2) # Create n unique strings. We use upper and lower case letters only. Create
# length 1 strings first, and if we haven't satisfied the number of strings
# requested, we'll create length 2 strings, and so on until we've created n
# strings.
#
# Returns them in random order
#
unique_strings <- function(n){
string_i <- 1
string_len <- 1
ans <- character(n)
chars <- c(letters,LETTERS)
new_strings <- function(len,pfx){
for(i in 1:length(chars)){
if (len == 1){
ans[string_i] <<- paste(pfx,chars[i],sep='')
string_i <<- string_i + 1
} else {
new_strings(len-1,pfx=paste(pfx,chars[i],sep=''))
}
if (string_i > n) return ()
}
}
while(string_i <= n){
new_strings(string_len,'')
string_len <- string_len + 1
}
sample(ans)
}
Using a Vector
All vectors in R can be named, so let’s see how looking up a vector element by name compares to looking up an element by index.
Here’s our fake hash table creator:
# Create a named integer vector size n. Note that elements are initialized to 0
#
fake_hash_table <- function(n){
ans <- integer(n)
names(ans) <- unique_strings(n)
ans
}
Now let’s create hash tables size 210 thru 215 and search each element:
timings1 <- adply(2^(10:15),.mar=1,.fun=function(i){
ht <- fake_hash_table(i)
data.frame(
size=c(i,i),
seconds=c(
system.time(for (j in 1:i)ht[names(ht)[j]]==0L)[3],
system.time(for (k in 1:i)ht[k]==0L)[3]),
index = c('1_string','2_numeric')
)
})
We perform element lookup by using an equality test, so
ht[names(ht)[j]]==0L
performs the lookup using a named string, while
ht[k]==0L
performs the lookup by index.
p1 <- ggplot(timings1,aes(x=size,y=seconds,group=index)) +
geom_point(aes(color=index)) + geom_line(aes(color=index)) +
scale_x_log10(
breaks=2^(10:15),
labels=c(expression(2^10), expression(2^11),expression(2^12),
expression(2^13), expression(2^14), expression(2^15))
) +
theme(
axis.text = element_text(colour = "black")
) +
ggtitle('Search Time for Integer Vectors')
p1

So we see that as the hash size increases the time it takes to search all the keys by name is exponential, whereas searching by index is constant.
And note that we’re talking over 30 seconds to search the 215 size hash table, and I’ve got a pretty performant laptop with 8Gb memory, 2.7Ghz Intel processor with SSD drives.
Using a List
List elements can be named, right? They’re also more flexible than integer vectors since they can store any R object. What does their performance look like comparing name lookup to index lookup?
timings2 <- adply(2^(10:15),.mar=1,.fun=function(i){
strings <- unique_strings(i)
ht <- list()
lapply(strings, function(s) ht[[s]] <<- 0L)
data.frame(
size=c(i,i),
seconds=c(
system.time(for (j in 1:i) ht[[strings[j]]]==0L)[3],
system.time(for (k in 1:i) ht[[k]]==0L)[3]),
index = c('1_string','2_numeric')
)
})
p2 <- ggplot(timings2,aes(x=size,y=seconds,group=index)) +
geom_point(aes(color=index)) + geom_line(aes(color=index)) +
scale_x_log10(
breaks=2^(10:15),
labels=c(expression(2^10), expression(2^11),expression(2^12),
expression(2^13), expression(2^14), expression(2^15))
) +
theme(
axis.text = element_text(colour = "black")
) +
ggtitle('Search Time for Lists')
p2

A little better but still exponential growth with named indexing. We cut our 30 seconds down to over 6 seconds for the large hash table, though.
Let’s see if we can do better.
Using an Environment
R environments store bindings of variables to values. In fact they are well suited to implementing a hash table since internally that’s how they are implemented!
e <- new.env() # Assigning in the environment the usual way
with(e, foo <- 'bar') e
## <environment: 0x3e81d48>
ls.str(e)
## foo : chr "bar"
You can also use environments the same way one would use a list:
e$bar <- 'baz'
ls.str(e)
## bar : chr "baz"
## foo : chr "bar"
By default R environments are hashed, but you can also create them without hashing. Let’s evaluate the two:
timings3 <- adply(2^(10:15),.mar=1,.fun=function(i){
strings <- unique_strings(i)
ht1 <- new.env(hash=TRUE)
ht2 <- new.env(hash=FALSE)
lapply(strings, function(s){ ht1[[s]] <<- 0L; ht2[[s]] <<- 0L;})
data.frame(
size=c(i,i),
seconds=c(
system.time(for (j in 1:i) ht1[[strings[j]]]==0L)[3],
system.time(for (k in 1:i) ht2[[strings[k]]]==0L)[3]),
envir = c('2_hashed','1_unhashed')
)
})
Note that we’re performing the lookup by named string in both cases. The only difference is that ht1 is hashed and ht2 is not.
p3 <- ggplot(timings3,aes(x=size,y=seconds,group=envir)) +
geom_point(aes(color=envir)) + geom_line(aes(color=envir)) +
scale_x_log10(
breaks=2^(10:15),
labels=c(expression(2^10), expression(2^11),expression(2^12),
expression(2^13), expression(2^14), expression(2^15))
) +
theme(
axis.text = element_text(colour = "black")
) +
ggtitle('Search Time for Environments')
p3

Bam! See that blue line? That’s near constant time for searching the entire 215 size hash table!
An interesting note about un-hashed environments: they are implemented with lists underneath the hood! You’d expect the red line of the above plot to mirror the red line of the list plot, but they differ ever so slightly in performance, with lists being faster.
In Conclusion
When you need a true associative array indexed by string, then you definitely want to use R hashed environments. They are more flexible than vectors as they can store any R object, and they are faster than lists since they are hashed. Plus you can work with them just like lists.
But using environments comes with caveats. I’ll explain in Part II.
转自:http://jeffreyhorner.tumblr.com/post/114524915928/hash-table-performance-in-r-part-i
Hash Table Performance in R: Part I(转)的更多相关文章
- [转载] 散列表(Hash Table)从理论到实用(上)
转载自:白话算法(6) 散列表(Hash Table)从理论到实用(上) 处理实际问题的一般数学方法是,首先提炼出问题的本质元素,然后把它看作一个比现实无限宽广的可能性系统,这个系统中的实质关系可以通 ...
- DHT(Distributed Hash Table) Translator
DHT(Distributed Hash Table) Translator What is DHT? DHT is the real core of how GlusterFS aggregates ...
- 数据结构 : Hash Table
http://www.cnblogs.com/lucifer1982/archive/2008/06/18/1224319.html 作者:Angel Lucifer 引子 这篇仍然不讲并行/并发. ...
- Hash Map (Hash Table)
Reference: Wiki PrincetonAlgorithm What is Hash Table Hash table (hash map) is a data structure use ...
- 数据结构基础-Hash Table详解(转)
理解Hash 哈希表(hash table)是从一个集合A到另一个集合B的映射(mapping). 映射是一种对应关系,而且集合A的某个元素只能对应集合B中的一个元素.但反过来,集合B中的一个元素可能 ...
- 散列表(hash table)——算法导论(13)
1. 引言 许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表 在介绍散列 ...
- 哈希表(Hash Table)
参考: Hash table - Wiki Hash table_百度百科 从头到尾彻底解析Hash表算法 谈谈 Hash Table 我们身边的哈希,最常见的就是perl和python里面的字典了, ...
- Berkeley DB的数据存储结构——哈希表(Hash Table)、B树(BTree)、队列(Queue)、记录号(Recno)
Berkeley DB的数据存储结构 BDB支持四种数据存储结构及相应算法,官方称为访问方法(Access Method),分别是哈希表(Hash Table).B树(BTree).队列(Queue) ...
- 几种常见 容器 比较和分析 hashmap, map, vector, list ...hash table
list支持快速的插入和删除,但是查找费时; vector支持快速的查找,但是插入费时. map查找的时间复杂度是对数的,这几乎是最快的,hash也是对数的. 如果我自己写,我也会用二叉检索树,它在 ...
随机推荐
- AngularJS进入使用前的准备工作
安装 AngularJS是以JavaScript文件形式发布的,可以通过 script 标签添加到网页中. 下载地址:https://github.com/angular/angular.js/rel ...
- JS常用特效方法总结
1.按Ctrl提交 <body onkeydown="if(event.ctrlKey&&event.keyCode=='13') form1.submit.click ...
- sublime text3 在ubutun下的下载和配置
最近在学习 Javascript,在 w3c school 上把教程看完了,也算个刚刚入门的水平,一直都是在 win 系统 上练习. 但是因为写 python 代码的 pycharm 和 git 配置 ...
- WebGL 高级技术
1.如何实现雾化 实现雾化的方式由多种,这里使用最简单的一种:线性雾化(linear fog).在线性雾化中,某一点的雾化程度取决于它与视点之间的距离,距离越远雾化程度越高.线性雾化有起点和终点,起点 ...
- Android -- 从源码带你从EventBus2.0飚到EventBus3.0(一)
1,最近看了不少的面试题,不管是百度.网易.阿里的面试题,都会问到EventBus源码和RxJava源码,而自己只是在项目中使用过,却没有去用心的了解它底层是怎么实现的,所以今天就和大家一起来学习学习 ...
- 自动生成数学题型三 (框架Struts2)题型如 a+b=c(a、b、c都为分数)
1. 约分分数 1.1 保留质数 /** * 将数值放入到fraction数组中 * @param fen 简要放的 int类型数值 */ public void fenshu(int fen) { ...
- 栈实现getMin
题目 实现一个特殊的栈,在实现栈的基本功能的基础上,在实现返回栈中最小元素的操作. 要求 pop.push.getMin操作的时间复杂度都是O(1). 设计的栈类型可以使用现成的栈结构. 解答 在设计 ...
- SourceTree 无法查看组织仓库
error log: Pushing to remote: Repository not found. 在使用SourceTree进行版本控制,代替git命令行,而且作为免费 跨平台 功能完备的git ...
- 过滤器Filter(17/4/8)
1:是JavaWeb三大组件之一: Servlet.Lisener(2个感知监听器不需要配置).Filter 2:过滤器 它会在一组资源(jsp.servlet.css.html等等)的前面执行! 它 ...
- C语言学习第四章
今天学习C语言循环结构,为什么要用循环呢?因为有时候我们对一堆的数字进行重复的处理的时候要重复的编写一些相同或者差不多的代码,让程序显得很臃肿,而且写着也麻烦,如果用循环来写的话能简化很多,出错的话也 ...