python:filter()一个可迭代的,计算过滤和未过滤的项目

问题描述 投票:0回答:4

我有一个大

Iterable

我想使用
filter()
函数过滤它。
我如何计算(以某种优雅的方式)有多少项目被过滤?
(同样的问题可能适用于
map()
reduce()
等)

我当然可以做:

items = get_big_iterable()
count_good = 0
count_all = 0
for item in items:
    if should_keep(item):
        count_good += 1
    count_all += 1

print('keep: {} of {}'.format(count_good, count_all))

filter()
有可能吗?

items = filter(should_keep, get_big_iterable()) 
for item in items:
    #... using values here ..
    #possible count not filtered items here too? 

我不应该迭代两次,并且想使用

filter()
或类似的解决方案

python python-3.x dictionary filter iterator
4个回答
2
投票

使用

enumerate
和一些基本算术应该非常简单:

def should_keep(x):
    return x % 3 == 0

items = range(1, 28)


def _wrapper(x):
    return should_keep(x[1])

filtered_with_counts = enumerate(filter(_wrapper, enumerate(items, 1)), 1)

for i, (j, item) in filtered_with_counts:
    # do something with item
    print(f"Item is {item}, total: {j}, good: {i}, bad: {j-i}")

count_all = j
count_good = i
count_bad = count_all - count_good
print(f"Final: {count_all}, {count_good}, {count_bad}")

输出:

Item is 3, total: 3, good: 1, bad: 2
Item is 6, total: 6, good: 2, bad: 4
Item is 9, total: 9, good: 3, bad: 6
Item is 12, total: 12, good: 4, bad: 8
Item is 15, total: 15, good: 5, bad: 10
Item is 18, total: 18, good: 6, bad: 12
Item is 21, total: 21, good: 7, bad: 14
Item is 24, total: 24, good: 8, bad: 16
Item is 27, total: 27, good: 9, bad: 18
Final: 27, 9, 18

不过我可能不会用这个。请注意,我假设您可能不想修改

should_keep
,但您始终可以将其包装起来。


2
投票

我能想到两种方法:第一种方法很短,但可能对性能不利,并且违背了迭代器的目的:

count=len(list(your_filtered_iterable))

另一种方法是编写自己的过滤器。根据 Python 文档:

注意

filter(function, iterable)
相当于发电机 表达式
(item for item in iterable if function(item))
if 函数 不是 None 且
(item for item in iterable if item)
如果函数是 没有。

所以你可以写这样的东西:

class Filter:
    def __init__(self, func, iterable):
        self.count_good = 0
        self.count_all = 0
        self.func = func
        self.iterable = iterable

    def __iter__(self):
        if self.func is None:
            for obj in self.iterable:
                if obj:
                    self.count_good += 1
                    self.count_all += 1
                    yield obj
                else:
                    self.count_all += 1
        else:
            for obj in self.iterable:
                if self.func(obj):
                    self.count_good += 1
                    self.count_all += 1
                    yield obj
                else:
                    self.count_all += 1

然后您可以从

count_good
实例访问
count_all
Filter

items = Filter(should_keep, get_big_terable()) 
    for item in items:
        # do whatever you need with item
        print('keep: {} of {}'.format(items.count_good, items.count_all))

1
投票

内置的

filter
不提供这一点。您需要编写自己的过滤器类,实现其
__next__
__iter__
方法。

代码

class FilterCount:
    def __init__(self, function, iterable):
        self.function = function
        self.iterable = iter(iterable)
        self.countTrue, self.countFalse = 0, 0

    def __iter__(self):
        return self

    def __next__(self):
        nxt = next(self.iterable)
        while not self.function(nxt):
            self.countFalse += 1
            nxt = next(self.iterable)

        self.countTrue += 1
        return nxt

示例

lst = ['foo', 'foo', 'bar']
filtered_lst = FilterCount(lambda x: x == 'foo', lst)

for x in filtered_lst:
    print(x)
print(filtered_lst.countTrue)
print(filtered_lst.countFalse)

输出

foo
foo
2
1

0
投票

另一种选择是使用 sum() 函数,例如:

count = sum(1 for x in get_big_iterable() if should_keep(x))
© www.soinside.com 2019 - 2024. All rights reserved.