从多个文件中搜索和排序数据

2 投票
6 回答
1483 浏览
提问于 2025-04-15 17:55

我有一组1000个文本文件,名字像是 in_s1.txtin_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来实现这个目标,但目前还没有找到一个实用的方法。我现在会做的事情是:

  1. 把所有的 in_ 文件合并成一个 IN.TXT 文件
  2. 在这个文件里找出最小的值,使用的命令是: for i in IN.TXT ; do sort -k6n $i | head -n 10; done
  3. 根据前十名的第一列和第七列的值,使用 grep -n VALUE in_s* 来过滤出对应的文件名,这样我就能知道每个值在哪个文件里

这个方法可以用,但有点繁琐。我在想有没有更快的方法,只用Bash、Python,或者两者结合,或者用其他更好的语言来实现。

谢谢

6 个回答

1

在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

刚刚重新看了你的问题,如果有几百万行数据,你可能会用完内存……

2

我想:

  • 先拿出前10个项目,
  • 对它们进行排序,然后
  • 对于从文件中读取的每一行,把这个元素插入到这前10个中:
    • 如果它的值比当前前10个中的最大值还小,
    • (为了提高性能,保持排序)

我不想在这里贴出完整的程序,因为这看起来像是作业。

是的,如果不是10个,这样做就不是最优的了。

3

在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)

撰写回答