智能过滤器与python

时间:2010-12-09 11:49:04

标签: python optimization text filter python-2.4

您好
我需要过滤掉所有不包含来自巨大“必要”列表的符号的行,例如代码:

def any_it(iterable):
      for element in iterable:
          if element: return True
      return False

regexp = re.compile(r'fruit=([A-Z]+)')
necessary = ['YELLOW', 'GREEN', 'RED', ...] # huge list of 10 000 members
f = open("huge_file", "r") ## file with > 100 000 lines
lines = f.readlines()
f.close()

## File rows like, let's say:
# 1 djhds fruit=REDSOMETHING sdkjld
# 2 sdhfkjk fruit=GREENORANGE lkjfldk
# 3 dskjldsj fruit=YELLOWDOG sldkfjsdl
# 4 gfhfg fruit=REDSOMETHINGELSE fgdgdfg

filtered = (line for line in lines if any_it(regexp.findall(line)[0].startswith(x) for x in necessary))

我有python 2.4,所以我不能使用内置的any() 我等了很长时间才进行过滤,但有没有办法对其进行优化?例如第1行和第4行包含“RED ..”模式,如果我们发现“RED ..”模式没问题,我们可以跳过搜索10000个成员列表中第4行相同的模式吗? 还有其他方法可以优化过滤吗? 谢谢。
...的编辑 ...
UPD:查看此帖子评论中的真实示例数据。我也有兴趣用“水果”来分类结果。谢谢!
... 结束编辑 ...

8 个答案:

答案 0 :(得分:5)

如果您将necessary列表组织为trie,那么您可以查看该trie以检查fruit是否以有效前缀开头。这应该比将fruit与每个前缀进行比较更快。

例如(仅经过温和测试):

import bisect
import re

class Node(object):
    def __init__(self):
        self.children = []
        self.children_values = []
        self.exists = False

    # Based on code at http://docs.python.org/library/bisect.html                
    def _index_of(self, ch):
        i = bisect.bisect_left(self.children_values, ch)
        if i != len(self.children_values) and self.children_values[i] == ch:
            return (i, self.children[i])
        return (i, None)

    def add(self, value):
        if len(value) == 0:
            self.exists = True
            return
        i, child = self._index_of(value[0])
        if not child:
            child = Node()
            self.children.insert(i, child)
            self.children_values.insert(i, value[0])
        child.add(value[1:])

    def contains_prefix_of(self, value):
        if self.exists:
            return True
        i, child = self._index_of(value[0])
        if not child:
            return False
        return child.contains_prefix_of(value[1:])

necessary = ['RED', 'GREEN', 'BLUE', 'ORANGE', 'BLACK',
             'LIGHTRED', 'LIGHTGREEN', 'GRAY']

trie = Node()
for value in necessary:
    trie.add(value)

# Find lines that match values in the trie
filtered = []
regexp = re.compile(r'fruit=([A-Z]+)')
for line in open('whatever-file'):
    fruit = regexp.findall(line)[0]
    if trie.contains_prefix_of(fruit):
        filtered.append(line)

这会将您的算法更改为O(N * k),其中Nnecessary的元素数量,kfruit的长度,只是{ {1}}(或多或少)。它确实需要更多的内存,但这对你的案例来说可能是值得的权衡。

答案 1 :(得分:1)

经过测试(但未加标记)的代码:

import re
import fileinput

regexp = re.compile(r'^.*?fruit=([A-Z]+)')
necessary = ['YELLOW', 'GREEN', 'RED', ]

filtered = []
for line in fileinput.input(["test.txt"]):
    try:
        key = regexp.match(line).group(1)
    except AttributeError:
        continue # no match
    for p in necessary:
        if key.startswith(p):
            filtered.append(line)
            break

# "filtered" now holds your results
print "".join(filtered)

有问题的代码差异:

  1. 我们不首先将整个文件加载到内存中(就像使用file.readlines()时一样)。相反,我们会在读入文件时处理每一行。为简洁起见,我在此使用fileinput模块,但也可以使用line = file.readline()while line:循环。

    < / LI>
  2. 一旦找到匹配项,我们就会停止遍历necessary列表。

  3. 我们修改了正则表达式模式并使用re.match而不是re.findall。这假设每行只包含一个“fruit = ...”条目。

  4. 更新

    如果输入文件的格式一致,你可以通过完全摆脱正则表达式来挤出更多的性能。

    try:
        # with line = "2 asdasd fruit=SOMETHING asdasd...."
        key = line.split(" ", 3)[2].split("=")[1]
    except:
        continue # no match
    

答案 2 :(得分:1)

我会使用['fruit=RED','fruit=GREEN'...制作一个简单的['fruit='+n for n in necessary]等列表,然后使用in而不是正则表达式来测试它们。不过,我认为没有办法真正做到这一点。

filtered = (line for line in f if any(a in line for a in necessary_simple))

(any()函数与你的any_it()函数做同样的事情)

哦,摆脱file.readlines(),只是迭代文件。

答案 3 :(得分:1)

filtered=[]
for line in open('huge_file'):
    found=regexp.findall(line)
    if found:
        fruit=found[0]
        for x in necessary:
            if fruit.startswith(x):
                filtered.append(line)
                break

或者也许:

necessary=['fruit=%s'%x for x in necessary]
filtered=[]
for line in open('huge_file'):
    for x in necessary:
        if x in line:
            filtered.append(line)
            break

答案 4 :(得分:1)

我个人非常喜欢你的代码,因为你认为“fruit = COLOR”是其他人没有的模式。我想你想找到一些像memoization这样的解决方案,它可以让你跳过已经解决过的问题的测试,但我认为情况并非如此。

def any_it(iterable):       for iterable中的元素:           if element:返回True       返回False

必要= ['黄色','绿色','红色',......]

谓词= lambda行:any_it(“fruit =”+颜色必要的颜色)

filtered = ifilter(谓词,开放(“testest”))

答案 5 :(得分:1)

我确信Zach's answer走在正确的轨道上。出于好奇,我已经实现了另一个版本(包含Zach关于使用dict而不是bisect的评论)并将其折叠成符合您示例的解决方案。

#!/usr/bin/env python
import re
from trieMatch import PrefixMatch # https://gist.github.com/736416

pm = PrefixMatch(['YELLOW', 'GREEN', 'RED', ]) # huge list of 10 000 members
# if list is static, it might be worth picking "pm" to avoid rebuilding each time

f = open("huge_file.txt", "r") ## file with > 100 000 lines
lines = f.readlines()
f.close()

regexp = re.compile(r'^.*?fruit=([A-Z]+)')
filtered = (line for line in lines if pm.match(regexp.match(line).group(1)))

为简洁起见,PrefixMatch的实施是published here

如果您的necessary前缀列表是静态的或不经常更改,您可以通过挑选和重用PickleMatch对象来加速后续运行,而不是每次都重建它。

更新(关于排序结果)

根据changelog for Python 2.4

  

key 应该是一个带有list元素的单参数函数   返回一个比较键   元件。然后使用列表对列表进行排序   比较键。

也在the source code, line 1792

/* Special wrapper to support stable sorting using the decorate-sort-undecorate
   pattern.  Holds a key which is used for comparisons and the original record
   which is returned during the undecorate phase.  By exposing only the key
   .... */

这意味着您的正则表达式模式仅针对每个条目评估一次(每次比较不评估一次),因此它不应该太昂贵:

sorted_generator = sorted(filtered, key=regexp.match(line).group(1))

答案 6 :(得分:0)

未经测试的代码:

filtered = []
for line in lines:
    value = line.split('=', 1)[1].split(' ',1)[0]
    if value not in necessary:
        filtered.append(line)

这应该比将10 000个图案匹配到一条线上更快。 可能还有更快的方法。 :)

答案 7 :(得分:0)

迭代100,000个字符串不应该花太长时间,但我看到你有10,000个字符串列表,这意味着你迭代10,000 * 100,000 = 1,000,000,000次字符串,所以我不知道你期望什么。 .. 至于你的问题,如果你从列表中遇到一个单词并且你只需要1个或更多(如果你想要exacly 1你需要遍历整个列表)你可以跳过剩下的,它应该优化搜索操作。