3

我有很多数据,通常在一个文件中。我想计算一些数量,所以我有这种功能:

def mean(iterator):
    n = 0
    sum = 0.
    for i in iterator:
      sum += i
      n += 1
    return sum / float(n)

我还有许多其他类似的功能(var,,size...)

现在我有一个迭代器遍历数据:iter_data。我可以计算我想要的所有数量:m = mean(iter_data); v = var(iter_data)等等,但问题是我要迭代很多次,这在我的情况下很昂贵。实际上 I/O 是最昂贵的部分。

所以问题是:我可以计算我的数量只m, v, ...迭代一次而不是iter_data 保持分开的功能meanvar...,这样很容易添加新的?

我需要的是类似于boost::accumulators的东西

4

4 回答 4

5

例如使用对象和回调,如:

class Counter():
    def __init__(self):
        self.n = 0
    def __call__(self, i):
        self.n += 1

class Summer():
    def __init__(self):
        self.sum = 0
    def __call__(self, i):
        self.sum += i


def process(iterator, callbacks):
    for i in iterator:
        for f in callbacks: f(i)

counter = Counter()
summer = Summer()
callbacks = [counter, summer]
iterator = xrange(10) # testdata
process(iterator, callbacks)

# process results from callbacks
n = counter.n
sum = summer.sum

这很容易扩展,并且只迭代数据一次。

于 2013-08-21T15:28:30.907 回答
2

您可以使用itertools.tee和生成器魔法(我说魔法是因为它不完全好读):

import itertools

def mean(iterator):
    n = 0
    sum = 0.
    for i in iterator:
         sum += i
         n += 1
         yield
    yield sum / float(n)

def multi_iterate(funcs, iter_data):
    iterators = itertools.tee(iter_data, len(funcs))
    result_iterators = [func(values) for func, values in zip(funcs, iterators)]
    for results in itertools.izip(*result_iterators):
        pass
    return results

mean_result, var_result = multi_iterate([mean, var], iter([10, 20, 30]))

print(mean_result)    # 20.0

顺便说一句,您可以用mean更简单的方式编写:

def mean(iterator):
    total = 0.
    for n, item in enumerate(iterator, 1):
         total += i
         yield
    yield total / n

您不应该命名变量sum,因为这会影响具有相同名称的内置函数。

于 2013-08-21T15:16:09.800 回答
1

如果没有类,您可以调整以下内容:

def my_mean():
    total = 0.
    length = 0
    while True:
        val = (yield)
        if val is not None:
            total += val
            length += 1
        else:
            yield total / length

def my_len():
    length = 0
    while True:
        val = (yield)
        if val is not None:
            length += 1
        else:
            yield length

def my_sum():
    total = 0.
    while True:
        val = (yield)
        if val is not None:
            total += val
        else:
            yield total


def process(iterable, **funcs):
    fns = {name:func() for name, func in funcs.iteritems()}
    for fn in fns.itervalues():
        fn.send(None)
    for item in iterable:
        for fn in fns.itervalues():
            fn.send(item)
    return {name:next(func) for name, func in fns.iteritems()}


data = [1, 2, 3]
print process(data, items=my_len, some_other_value=my_mean, Total=my_sum)
# {'items': 3, 'some_other_value': 2.0, 'Total': 6.0}
于 2013-08-21T15:55:47.637 回答
0

你想要的是有一个主Calc来迭代数据,应用不同的计算meanvar等,然后可以通过接口返回这些值。您可以通过让计算在主计算之前将自己注册到此类来使其更通用,然后通过接口中的新访问器获得其结果。

于 2013-08-21T15:21:43.117 回答