实现第二矩流近似的Alon-Matias-Szegedy算法

时间:2016-03-20 13:06:50

标签: python random data-mining data-stream bigdata

我试图在python中重新创建一个函数来估计数据流的第二个时刻。

正如Ullman Book所述,"挖掘大规模数据集",第二时刻:

  

是m_i的平方和。有些人 -   次数称为意外数字,因为它衡量的是流中元素分布的不均匀程度。

m_i元素是流中的单义元素。

例如,有这个玩具问题\数据流:

a, b, c, b, d, a, c, d, a, b, d, c, a, a, b

我们像这样计算第二个时刻:

5^2 + 4^2 + 3^2 + 3^2 = 59

(因为' a'在数据流中出现5次,' b' 4次,依此类推)

因为我们无法将所有数据流存储在内存中,所以我们可以使用算法来估算第二时刻:

Alon-Matias-Szegedy算法(AMS算法),使用此公式估算第二个时刻:

E(n *(2 * X.value − 1))

其中X是流的单义元素,随机选择,X.value是一个计数器,当我们读取流时,每个加1 从我们选择它的那一刻起,我们会遇到另一个x元素。

n表示数据流的长度," E"是平均符号。

使用之前的数据流做一个示例,假设我们选择了" a"在数据流的第13位," d"在8日和" c"在3日。我们还没有选择" b"。

a, b, c, b, d, a, c, d, a, b, d, c, a, a, b
1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
      x              x              x

像这样选择,我们有:

X.element = "a"   X.value = 2
X.element = "c"   X.value = 3
X.element = "d"   X.value = 2

AMS算法的估算是:

(15*(2 * 2 - 1) + 15*(2 * 3 - 1) + 15*(2 * 2 - 1))/3 = 55 

这非常接近(59)之前计算的第二个时刻的真实值。

现在专注于我的代码,我已经编写了这个函数来计算" true"第二个时刻,通过矢量(1d数组)和for:

模拟数据流
def secondMoment(vector):
    mydict = dict()
    for el in vector:
        if el not in mydict:
            mydict[el] = 1
        else:
            mydict[el] += 1
    return (sum([pow(value, 2) for key, value in mydict.items()]))

和计算第二时刻估计值的AMS函数:

def AMSestimate(vector):
    lenvect = len(vector)
    elements = dict()
    for el in vector:
        if el in elements:
            elements[el] += 1
        elif random.choice(range(0, 10)) == 0:
            elements[el] = 1
    # E(n * (2 * x.value - 1))
    lendict = len(elements)
    estimateM2 = 0
    for key, value in elements.items():
        estimateM2 += lenvect * ((2 * value) - 1)
    print(lendict)
    if lendict > 0:
        return estimateM2/lendict

问题在于,当我尝试计算一个小玩具问题的尊重(如上面的那个)时,值有些正确,但是当我尝试将向量扩展到10000个元素时,值,真正的第二个时刻和尊重,是完全不同的。

我认为问题与我生成数据流的方式以及我决定选择X.element的方式有关。

那是:

[random.choice(string.ascii_letters) for x in range(size)]

用于生成随机向量\数据流

并且

elif random.choice(range(0, 10)) == 0:
    elements[el] = 1

对于X.element选择(在上面的代码中,在AMS函数中完成)

对于随机向量\数据流的生成,认为问题可能是由于缺乏"可变性"向量(string.ascii_letters只有52个元素)。

1 个答案:

答案 0 :(得分:3)

这是一个有趣的问题。

假设我们从

开始
import random
import string

size = 100000
seq = [random.choice(string.ascii_letters) for x in range(size)]

然后第一个实现类似于你的(注意使用collections.Counter):

from collections import Counter

def secondMoment(seq):
    c = Counter(seq)
    return sum(v**2 for v in c.values())

>>> secondMoment(seq)
192436972

但是,第二种实现方式与您的实施方式有很大不同。注意,首先找到随机索引。然后,元素仅在其中一个索引首次出现(如果有)之后计数:

from collections import defaultdict

def AMSestimate(seq, num_samples=10):
    inds = list(range(len(seq)))
    random.shuffle(inds)
    inds = sorted(inds[: num_samples])

    d = {}
    for i, c in enumerate(seq):
        if i in inds and c not in d:
            d[c] = 0
        if c in d:
            d[c] += 1
    return int(len(seq) / float(len(d)) * sum((2 * v - 1) for v in d.values()))

>>> AMSestimate(seq)
171020000

编辑问题中的原始代码

在问题的代码中,考虑你的循环

for el in vector:
    if el in elements:
        elements[el] += 1
    elif random.choice(range(0, 10)) == 0:
        elements[el] = 1

(次要)采样是有问题的:它是0.1

的硬编码概率

还要考虑:

    estimateM2 += lenvect * ((2 * value) - 1)

这缺少了采样元素数量的除法。