【问题标题】:python: filter() an iterable, count filtered and not filtered itemspython: filter() 一个可迭代的,计数过滤和未过滤的项目
【发布时间】:2019-01-21 22:41:35
【问题描述】:

我有一个很大的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


    【解决方案1】:

    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
    

    然后您可以从Filter 实例访问count_goodcount_all

    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))
    

    【讨论】:

      【解决方案3】:

      内置的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
      

      【讨论】:

      • 我也需要过滤后的值来迭代,而不仅仅是计数。在这种情况下如何访问它们?
      • @ya_dimon 我知道你现在需要什么,看看更新的答案
      • 您能解释一下为什么它与@pkqxdd 的答案不同吗? stackoverflow.com/a/51852326/2519073 ,我们需要 next 而不是 iter 吗? (我知道这是另一个问题,但以防万一你有时间..)
      • @ya_dimon 使用__next__ 的解决方案是有状态的,这意味着如果您部分迭代它,然后返回它,您将从离开的地方继续,而单独使用__iter__ 的解决方案是无状态的,每当你开始迭代它时,你就会回到起点。在这方面,这个解决方案更接近于过滤
      猜你喜欢
      • 2020-09-12
      • 1970-01-01
      • 2018-07-30
      • 1970-01-01
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      • 1970-01-01
      • 2019-02-17
      相关资源
      最近更新 更多