如何在Python中高效地计算非常大的数据集的基数?

16

我一直在工作中处理一些特别大的数据集,通常包含数十亿个元素,它们都存储在Memcached云中,并定期转储到文件中。对于我的一个任务,我正在尝试计算该集合的基数。

为了提供一些背景信息,每个项都包含IP和一些其他属性,用base64编码,项大小为20字节。通过删除某些字段来减小项的大小并不是一种选择。

这里有一个像内存版本的数据集,模拟了我的数据集(感谢这篇文章提供的字符串生成方法):

import base64, os

dataset_size = 10000000000 # that's 10 billion, be careful if you run it !
big_dataset = [base64.b64encode(os.urandom(10)) for i in range(dataset_size)]

我的第一种方法是使用哈希集合,就像这样:

uniques = set(big_dataset)
print "Cardinality: %d" % len(uniques)
理论上在小的数据集中这很好用,但是你会发现一个问题:
- 我不能假设我的数据是唯一的。我的数据集可能有50%是唯一的,也可能全部都是唯一的。这些数据是动态生成的,在规则时间间隔内变化,取决于许多因素(例如一天中的时间)。 - 数据集大小为100亿。每个项目以base64编码占用20个字节,乘以100亿大约是数百GB。不幸的是,我没有一台拥有那么多RAM的机器!
我已经做了功课,并找到了一些研究论文或一些晦涩的库,但部分目标是要理解哪种方法有效以及原因。
所以我向Python用户求助,您是否知道任何算法可以帮助我高效地估计基数?当我说复杂性时,我的意思是我并不太关心运行时间复杂度,而是更关注空间复杂度。如果能大大提高性能,我不介意牺牲一些准确性(所以我不一定需要知道唯一数的确切数字,即使这是理想的,但可能不是可行的方法)。对于这个项目,我正在寻找特别是使用Python实现的算法。
感谢您提供的任何帮助!
正如一些人指出的那样,我可以使用Hadoop/MR,但对于这个特定的项目,我们不想走MR的路线,而是想探索在一个单独的机器上高效完成此操作的算法,因为这可能适用于其他几个不同的项目。

1
这只是一个建议,但这听起来像是适合Map-Reduce框架的东西——将数据中的元素映射到字典或其他计数中。为此,您可以使用Yelp创建的Python Map-Reduce框架MRJob。在Amazon EC2上运行MRJob对您也可能是个好主意。我以前用它来计算大型语料库中的单词频率。我想这取决于您如何解析单个数据元素。 - ely
谢谢您的建议,是的,我已经考虑过MR(事实上在其他项目中我经常使用它),但对于这个特定的问题,MR/Hadoop不是一个选择,我们想研究算法来在单台机器上完成这个问题,作为概念验证的一部分。 - Charles Menguy
如果100%准确性不重要,也许一个布隆过滤器可以在内存中给你5%的误差? 如果不行,而且需要一台单独的机器,你可以简单地使用一些带有唯一键的简单nosql数据库,它会将数据存储在磁盘上并删除重复项。 它会很慢,但无论你有多少内存,它都能工作。 你仍然可以并行化实际的插入操作。 - Not_a_Golfer
3
最近在Hacker News上有一篇关于一家公司(Clearspring)在现实世界中进行此项工作的优秀文章...[大数据计数:如何仅使用1.5KB内存计算十亿个不同对象] (http://highscalability.com/blog/2012/4/5/big-data-counting-how-to-count-a-billion-distinct-objects-us.html) - btown
哇,太棒了,这个链接看起来正是我需要的!真希望我之前就看到它了。 - Charles Menguy
2个回答

8

太棒了,它与之前帖子中提到的布隆过滤器相比如何?您知道使用LogLog / HyperLog的利弊吗? - Charles Menguy
4
这些技术的主要问题在于它们对小基数(<2000)的不准确性。在以下图表Bloom过滤器 vs 哈希草图中,您可以看到对于近2000个元素的小基数,其误差大于5%,但对于更大的基数,误差低于您所需的5%。尽管没有Bloom过滤器那样准确,但是通过观察这个,您可以发现这两种技术在空间上更加高效。 - goncalvesnelson
@goncalvesnelson 是否有可用于产生这两个图形的源代码? - Max Ghenis
@MaxGhenis 很久以前我已经更换了电脑,但我找到了这些文件: https://www.dropbox.com/sh/omgxqmmt4hnjgfq/AABHAu42sEAz_UrGkXL2fUbJa?dl=0这应该是benchmark和sizebenchmark文件,但我不确定它们是否为最终版本。希望这仍然有帮助。 - goncalvesnelson

3
我建议您尝试使用布隆过滤器。即使有大量数据,您也可以使用适度的系统要求实现极低的错误率。考虑到您将使用(大致)最优的k=ln(2)*(布隆过滤器大小(以比特为单位))/(100亿),您可以通过-((100亿)*ln(期望的误报率))/ln(2)^2来计算布隆过滤器的大小(以比特为单位)。
例如,只需不到2GB的内存,您就可以获得0.1%的错误率。 所有这些的非常快速和极其简单的实现都在http://mike.axiak.net/python-bloom-filter/docs/html/上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接