Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 对元组列表应用多个筛选器_Python_Filter_Tuples - Fatal编程技术网

Python 对元组列表应用多个筛选器

Python 对元组列表应用多个筛选器,python,filter,tuples,Python,Filter,Tuples,我正在寻找一种高效的pythonic方法来对元组列表应用多个过滤器 例如,假设过滤器如下所示: def f1(t): return t[3]<10 def f2(t): return t[0]!=1 def f3(t): return t[1] in ("lisa","eric") def f4(t): return t[3]>2 tuples=[ (0,'tom','...',8), (1,'john','...',17), (2,'lisa','...',1), (3,'eri

我正在寻找一种高效的pythonic方法来对元组列表应用多个过滤器

例如,假设过滤器如下所示:

def f1(t): return t[3]<10
def f2(t): return t[0]!=1
def f3(t): return t[1] in ("lisa","eric")
def f4(t): return t[3]>2
tuples=[
(0,'tom','...',8),
(1,'john','...',17),
(2,'lisa','...',1),
(3,'eric','...',18)
]
以下工作:

def nFilter(filters,tuples):
    if filters and tuples:
        return nFilter(filters,filter(filters.pop(),tuples))
    else: return tuples
结果如下:

>>> nFilter([f1,f2,f3],tuples)
[(2, 'lisa', '...', 1)]

但我想知道是否有更直接的方法;我想到的是类似于函数组合的东西(例如,对于任意函数列表,f1(f2(…fn(元组)…))。文档中有对包含
compose
函数的引用,但链接都已失效

此外,由于我计划在相当大的数据集上使用此方法,并且可能在生产web服务中使用大量过滤器,因此它必须是高效的,而且我不能确定此解决方案是否可行

欢迎提出任何建议或改进。

改进:用迭代替换递归 实际上并不存在“任意函数列表的组合函数”;但是,使用简单的for循环构建过滤器链非常容易:

def nFilter(filters, tuples):
    for f in filters:
        tuples = filter(f, tuples)
    return tuples
改进:通过限制性和速度订购过滤器 链式迭代器的运行速度非常快,总运行时间往往由对谓词函数的调用控制

通过对谓词排序以最小化总工作量,可以获得最佳结果。一般来说,最好在昂贵的测试之前进行便宜的测试,在没有过滤掉很多情况的测试之前进行限制性更强的测试

例子 在本例中,谓词的成本大致相同(函数调用、元组索引和与常量的比较),但它们的限制性不同(itertools导入产品的
t[2]==4
过滤掉80%的情况,而
t[0]>1
t[1]>>
>>>过滤器=[lambda t:t[2]==4,lambda t:t[0]>1,lambda t:t[1]>>用于nFilter中的tup(过滤器,产品(范围(5),重复=3)):
打印(tup)
(2, 0, 4)
(2, 1, 4)
(2, 2, 4)
(3, 0, 4)
(3, 1, 4)
(3, 2, 4)
(4, 0, 4)
(4, 1, 4)
(4, 2, 4)
从评论中升起的注释
  • 当输入iterable为空时,filter函数对谓词的应用为零。这就像在空列表上执行for循环一样

  • 每个过滤器都会减少输入到封闭过滤器中的数据量。因此,每个过滤器只应用于通过前面过滤器的数据

  • 不要担心示例中的
    lambda
    。它与常规
    def
    具有相同的功能。这只是编写过滤器列表的一种方便方法

  • 在Python3中,函数被更新为返回迭代器而不是列表


你在找这样的东西吗

filters = (f1,f2,f3,f4)
filtered_list = filter( lambda x: all(f(x) for f in filters), your_list )
这样做的好处是,只要单个筛选器返回
False
,该列表元素就不会被包括在内。

类似于@Raymond Hettinger

不过,我建议使用itertools中的ifilter作为生成器

from itertools import ifilter

def nFilter(filters,tuples):
      return ifilter(lambda t: all(f(t) for f in filters), tuples)

生成器表达式似乎是最惯用的方法(您可以免费获得惰性):

或同等标准(可以说更具可读性):


好吧,这里没有花哨的itertools之类的工具,只是避免了使用简单循环的递归和生成器的开销:

def for_loop(filters, tuples):
    for f in filters:
        tuples = filter(f, tuples)
        if not tuples: 
            return tuples
    return tuples

下面是一个有点脏的基准:

import datetime
from itertools import ifilter
from timeit import Timer

def f1(t): return t[3]<10
def f2(t): return t[0]!=1
def f3(t): return t[1] in ("lisa","eric")
def f4(t): return t[3]>2

def original(filters,tuples):
    if filters and tuples:
        return original(filters,filter(filters.pop(),tuples))
    else: 
        return tuples

def filter_lambda_all(filters, tuples):
    return filter(lambda t: all(f(t) for f in filters), tuples)

def loop(filters, tuples):
    while filters and tuples:
        f = filters[0]
        del filters[0]
        tuples = filter(f, tuples)
    return tuples

def pop_loop(filters, tuples):
    while filters and tuples:
        tuples = filter(filters.pop(), tuples)
    return tuples

def for_loop(filters, tuples):
    for f in filters:
        tuples = filter(f, tuples)
        if not tuples: 
            return tuples
    return tuples


def with_ifilter(filters, tuples):
    for f in filters:
        tuples = ifilter(f, tuples)
    return tuples

_filters = [f1, f2, f3, f4]

def time(f):
    def t():
        return [    (0,'tom','...',8),
                    (1,'john','...',17),
                    (2,'lisa','...',1),
                    (3,'eric','...',18)
                ]*1000
    for i in xrange(4):
        list(f(_filters[i:] * 15,t()))

if __name__=='__main__':
    for f in (original,filter_lambda_all,loop,pop_loop,with_ifilter,for_loop):
        t = Timer(lambda: time(f))
        d = t.timeit(number=400)
        print f.__name__, d
导入日期时间
从itertools导入ifilter
从timeit导入计时器
def f1(t):返回t[3]2
def原件(过滤器、元组):
如果过滤器和元组:
返回原始值(过滤器,过滤器(filters.pop(),元组))
其他:
返回元组
def filter_lambda_all(过滤器、元组):
返回筛选器(lambda t:all(f(t)表示筛选器中的f),元组)
def循环(过滤器、元组):
而过滤器和元组:
f=过滤器[0]
del过滤器[0]
元组=过滤器(f,元组)
返回元组
def pop_循环(过滤器、元组):
而过滤器和元组:
tuples=filter(filters.pop(),tuples)
返回元组
_循环的定义(过滤器、元组):
对于过滤器中的f:
元组=过滤器(f,元组)
如果不是元组:
返回元组
返回元组
带过滤器的def(过滤器、元组):
对于过滤器中的f:
tuples=ifilter(f,tuples)
返回元组
_过滤器=[f1、f2、f3、f4]
def时间(f):
def t():
返回[(0,'tom','…',8),
(1,"约翰","约翰","约翰","约翰",,
(2,'lisa','…',1),
(3,'eric','…',18)
]*1000
对于X范围内的i(4):
列表(f(_过滤器[i:]*15,t())
如果“名称”=“\uuuuuuuu主要”:
对于f in(原始、过滤器λ、循环、弹出循环、带过滤器、用于循环):
t=计时器(λ:时间(f))
d=t.timeit(数字=400)
打印f.\uuuu名称,d
结果:

原件7.23815271085
过滤器λ全部14.1629812265
环路7.23445844453
pop_loop 7.3084566637
使用过滤器9.27674205
对于_循环7.02854999945


我建议使用以下模式在发电机上大量应用一系列/一系列过滤器:

from functools import reduce, partial
from itertools import ifilter

filtered = reduce(lambda s,f: ifilter(f,s), filter_set, unfiltered)
简而言之,它在生成器上从左到右设置了一个过滤器链,并返回生成器,该生成器是对原始对象应用所有过滤器的结果

如果您希望获得列表,以下内容就足够了:

[reduce(lambda s,f: ifilter(f,s), (f1,f2,f3,), tuples)]
如果您希望获得单个函数,可以将其定义为:

chain_filters = partial(reduce, lambda s,f: ifilter(f,s))
并用作:

[chain_filters((f1,f2,f3,), tuples)]

请注意,此解决方案不构成过滤器(如
all()
),而是将它们链接起来。如果您使用的是一些繁重的计算,您可能希望将更激进的过滤器放在链的开头,例如,在数据库查询过滤器之前放置bloom过滤器,等等。

您可以定义@tokland的替代方法

superFilter=lambda x:all(filter(x) for filter in filters)
newTuples=filter(superFilter,tuples)
还是一艘班轮

 newTuples=filter(lambda x:all(filter(x) for filter in filters),tuples)

如果你只做了一次超级过滤,这会更好。

太好了,我刚刚开始对它进行基准测试,结果是:-)哇,你把它放在了你喜欢的地方。例如,
f1
chain_filters = partial(reduce, lambda s,f: ifilter(f,s))
[chain_filters((f1,f2,f3,), tuples)]
superFilter=lambda x:all(filter(x) for filter in filters)
newTuples=filter(superFilter,tuples)
 newTuples=filter(lambda x:all(filter(x) for filter in filters),tuples)