从多个文件中搜索和排序数据
我有一组1000个文本文件,名字像是 in_s1.txt
、in_s2.txt
这样的。每个文件里有几百万行数据,每行有7列,格式大概是这样的:
ccc245 1 4 5 5 3 -12.3
对我来说,最重要的是第一列和第七列的值;比如这对值 ccc245 , -12.3
我需要做的是在所有的 in_sXXXX.txt
文件中,找到第七列值最小的10个案例,并且还要知道每个值在哪个文件里。我需要的结果大概是这样的:
FILE 1st_col 7th_col
in_s540.txt ccc3456 -9000.5
in_s520.txt ccc488 -723.4
in_s12.txt ccc34 -123.5
in_s344.txt ccc56 -45.6
我在考虑用Python和Bash来实现这个目标,但目前还没有找到一个实用的方法。我现在会做的事情是:
- 把所有的
in_
文件合并成一个IN.TXT
文件 - 在这个文件里找出最小的值,使用的命令是:
for i in IN.TXT ; do sort -k6n $i | head -n 10; done
- 根据前十名的第一列和第七列的值,使用
grep -n VALUE in_s*
来过滤出对应的文件名,这样我就能知道每个值在哪个文件里
这个方法可以用,但有点繁琐。我在想有没有更快的方法,只用Bash、Python,或者两者结合,或者用其他更好的语言来实现。
谢谢
6 个回答
在Python中试试这样做:
min_values = []
def add_to_min(file_name, one, seven):
# checks to see if 7th column is a lower value than exiting values
if len(min_values) == 0 or seven < max(min_values)[0]:
# let's remove the biggest value
min_values.sort()
if len(min_values) != 0:
min_values.pop()
# and add the new value tuple
min_values.append((seven, file_name, one))
# loop through all the files
for file_name in os.listdir(<dir>):
f = open(file_name)
for line in file_name.readlines():
columns = line.split()
add_to_min(file_name, columns[0], float(columns[6]))
# print answers
for (seven, file_name, one) in min_values:
print file_name, one, seven
我没有测试过,但这应该能帮你入门。
版本2,只运行一次排序(这是在S. Lott的建议下做的):
values = []
# loop through all the files and make a long list of all the rows
for file_name in os.listdir(<dir>):
f = open(file_name)
for line in file_name.readlines():
columns = line.split()
values.append((file_name, columns[0], float(columns[6]))
# sort values, print the 10 smallest
values.sort()
for (seven, file_name, one) in values[:10]
print file_name, one, seven
刚刚重新看了你的问题,如果有几百万行数据,你可能会用完内存……
我想:
- 先拿出前10个项目,
- 对它们进行排序,然后
- 对于从文件中读取的每一行,把这个元素插入到这前10个中:
- 如果它的值比当前前10个中的最大值还小,
- (为了提高性能,保持排序)
我不想在这里贴出完整的程序,因为这看起来像是作业。
是的,如果不是10个,这样做就不是最优的了。
在Python中,可以使用heapq模块里的nsmallest函数,这个函数就是为了处理这种任务而设计的。
下面是一个在Python 2.5和2.6中测试过的例子:
import heapq, glob
def my_iterable():
for fname in glob.glob("in_s*.txt"):
f = open(fname, "r")
for line in f:
items = line.split()
yield fname, items[0], float(items[6])
f.close()
result = heapq.nsmallest(10, my_iterable(), lambda x: x[2])
print result
在上面的回答被接受后更新
查看Python 2.6的源代码,似乎它有可能会执行list(iterable)
并在其上进行操作……如果是这样的话,对于有成千上万的文件,每个文件都有数百万行的数据,这种方法就不太适用了。如果第一个答案让你遇到MemoryError等错误,这里有一个替代方案,它将列表的大小限制为n(在你的情况下n等于10)。
注意:仅适用于2.6;如果你需要在2.5中使用,可以按照文档中解释的方法使用条件heapreplace()
。这个方法使用了heappush()
和heappushpop()
,但它们没有key
参数 :-( 所以我们得想办法绕过这个问题。
import glob
from heapq import heappush, heappushpop
from pprint import pprint as pp
def my_iterable():
for fname in glob.glob("in_s*.txt"):
f = open(fname, "r")
for line in f:
items = line.split()
yield -float(items[6]), fname, items[0]
f.close()
def homegrown_nlargest(n, iterable):
"""Ensures heap never has more than n entries"""
heap = []
for item in iterable:
if len(heap) < n:
heappush(heap, item)
else:
heappushpop(heap, item)
return heap
result = homegrown_nlargest(10, my_iterable())
result = sorted(result, reverse=True)
result = [(fname, fld0, -negfld6) for negfld6, fname, fld0 in result]
pp(result)