您好
我需要过滤掉所有不包含来自巨大“必要”列表的符号的行,例如代码:
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:查看此帖子评论中的真实示例数据。我也有兴趣用“水果”来分类结果。谢谢!
... 结束编辑 ...
答案 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)
,其中N
是necessary
的元素数量,k
是fruit
的长度,只是{ {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)
有问题的代码差异:
我们不首先将整个文件加载到内存中(就像使用file.readlines()
时一样)。相反,我们会在读入文件时处理每一行。为简洁起见,我在此使用fileinput
模块,但也可以使用line = file.readline()
和while line:
循环。
一旦找到匹配项,我们就会停止遍历necessary
列表。
我们修改了正则表达式模式并使用re.match
而不是re.findall
。这假设每行只包含一个“fruit = ...”条目。
如果输入文件的格式一致,你可以通过完全摆脱正则表达式来挤出更多的性能。
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
对象来加速后续运行,而不是每次都重建它。
key 应该是一个带有list元素的单参数函数 返回一个比较键 元件。然后使用列表对列表进行排序 比较键。
/* 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你需要遍历整个列表)你可以跳过剩下的,它应该优化搜索操作。