2011-09-06 30 views
2

考虑到两个文件,表单的一个包含项目:关于处理大文件的建议 - python或命令行?

label1 label2 name1 
label1 label3 name2 

和其他形式的:

label1 label2 name1 0.1 1000 
label9 label6 name7 0.8 0.5 

假设你要提取的文件其中的两个线,其前三个元素在文件一中出现一行(重要的顺序) - 关于这可能如何迅速圆顶的任何建议?

从给定的上述采样数据将是任何这样的脚本的输出文件:

label1 label2 name1 0.1 1000 

我玩弄蟒:

inp = open(file1.txt, 'r') 
look_up = [i.split() for i in inp.readlines()] 
inp.close() 

inp = open('file2', 'wt') 

holder = [] 

line = inp.readline() 
while line: 
    line = line.split() 
    if [line[0], line[1], line[2]] in look_up: 
     holder.append(line) 
    line = inp.readline() 

然而这似乎需要一段时间。这些文件相当大。

谢谢!

+3

“相当大”有多大?兆?千兆字节?兆兆字节? –

+2

我很想知道你的长期尝试是否能够在你写下这个问题并获得答案的时间内完成。对于一次性问题,最简单的解决方案通常是最好的,即使它不是最优的。 –

+0

@马克 - 我长时间运行的尝试被分成16个作业并放在一个集群上。 6小时后还在跑步! Eeek! –

回答

8

你的Python版本是相当低效的,因为你在一个列表测试入会,而不是集合或字典(即O(n)查找时间而不是O(1))。

尝试使用元组的set而不是字符串的set。元组将是一个更好的选择,因为这两个文件可以分割在不同的分隔符上,但我认为你不会看到特别大的性能差异。与测试非常长名单的成员资格相比,tuple('something'.split())相对较快。

此外,没有必要拨打inp.readlines()。换句话说,你可以只是做

look_up = set(tuple(line.split()) for line in inp) 

,你应该看到一个显著加速,而不必改变比tuple(line[:3])而非[line[0], line[1], line[2]]其他代码的其他任何部分。

其实,grep和庆典都相当适合这个...(未经测试,但它应该工作。)

while read line 
do 
    grep "$line" "file2.txt" 
done < "file1.txt" 

,看看哪一个更快,我们可以generate some test data(〜4500把钥匙在file1.txt和在file2.txt中有1000000行),并且对同一事物的简单Python版本进行基准测试(粗略地...行将以与grep版本不同的顺序打印)。

with open('file1.txt', 'r') as keyfile: 
    lookup = set(tuple(line.split()) for line in keyfile) 

with open('file2.txt', 'r') as datafile: 
    for line in datafile: 
     if tuple(line.split()[:3]) in lookup: 
      print line, 

的Python版本原来是〜70倍速度快:

[email protected]:~/so> time sh so_temp149.sh > a 

real 1m47.617s 
user 0m51.199s 
sys  0m54.391s 

[email protected]:~/so> time python so_temp149.py > b 

real 0m1.631s 
user 0m1.558s 
sys  0m0.071s 

当然,这两个例子中完全不同的方式接近问题。我们真的比较了两种算法,而不是两种实现。例如,如果我们在file1中只有几条关键线,则bash/grep解决方案很容易获胜。 (bash是否有一个内置的O(1)查找成员的容器?(我认为bash 4可能有一个哈希表,但我不知道任何关于它......)它会是有趣的尝试在bash执行类似的算法,以蟒蛇上面的例子,以及...)

+2

你不能拥有一个列表的集合。每个'set'元素必须是可散列的(完全不可变的)。 –

+0

@John Y - 对!我没在想,也没有测试任何东西......谢谢! –

+1

'set(元组(line.split())forin inp)'可能是更好的方法。 –

1

您可以尝试使用字符串“label1 label2 name1”作为关键字,而不是三重值。

1

我会使用散列来存储第一个文件的值。这并不是说容错虽然(且只有1个各项目之间的空间),但你会得到的总体思路...

#!/usr/bin/env python 

labels={} 
with open('log') as fd: 
    for line in fd: 
     line=line.strip() 
     labels[line]=True 

with open('log2') as fd: 
    for line in fd: 
     if " ".join(line.split()[0:3]) in labels: 
      print line 
3

哈克庆典/排序/ Perl的解决方案:

$ cat > 1 
label1 label2 name1 
label1 label3 name2 

$ cat > 2 
label1 label2 name1 0.1 1000 
label9 label6 name7 0.8 0.5 

$ (cat 1; cat 2;) | sort | perl -ne 'INIT{$pattern_re="(?:\\S+) (?:\\S+) (?:\\S+)"; $current_pattern="";} if(/^($pattern_re)$/o){$current_pattern=$1} else {if(/^($pattern_re)/o) { print if $1 eq $current_pattern} }' 
label1 label2 name1 0.1 1000 

它融合了这两个文件合并成一个列表,排序, (所以我们使用相同的密钥获取数据块,从文件1开始逐行),然后使用特殊的Perl oneliner仅保留文件1之前具有“标题”前缀的格式良好的行。