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

时间:2010-01-12 12:15:18

标签: python bash

我有一组1000个文本文件,名称为in_s1.txtin_s2.txt等。每个文件包含数百万行,每行有7列,如:

ccc245 1 4 5 5 3 -12.3

对我来说,最重要的是第一列和第七列的值;对ccc245 , -12.3

我需要做的是在所有in_sXXXX.txt个文件之间找到第7列值最低值的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_
  2. 中的所有IN.TXT个文件
  3. 使用for i in IN.TXT ; do sort -k6n $i | head -n 10; done
  4. 搜索那里的最低值
  5. 给出前十个列表的1st_col和7th_col值,使用它们来过滤in_s文件,使用grep -n VALUE in_s*,因此我为每个值获取文件名
  6. 它有效,但有点单调乏味。我想知道只使用bash或python或两者的更快的方法。或者另一种更好的语言。

    由于

6 个答案:

答案 0 :(得分:3)

在python中,使用nsmallest function in the heapq module - 它专为此类任务而设计。

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 arg :-(所以我们必须伪造它。

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)

答案 1 :(得分:2)

我会:

  • 前10项,
  • 对它们进行排序然后
  • 对于从文件读取的每一行,将元素插入到top10中:
    • 如果其值低于当前top10中的最高值,
    • (保持性能分类)

我不会在这里发布完整的程序,因为它看起来像是家庭作业。

是的,如果不是十,那么这将不是最佳的

答案 2 :(得分: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

重新阅读你的问题,有数百万行,你可能会用完RAM ......

答案 3 :(得分:0)

shell解决方案的一个小改进:

$ cat in.txt
in_s1.txt
in_s2.txt
...
$ cat in.txt | while read i
do
  cat $i | sed -e "s/^/$i /" # add filename as first column
done |
sort -n -k8 | head -10 | cut -d" " -f1,2,8

答案 4 :(得分:0)

如果您的文件是百万行,您可能需要考虑使用“缓冲”。下面的脚本遍历那些百万行,每次将字段7与缓冲区中的字段进行比较。如果某个值小于缓冲区中的值,则缓冲区中的一个值将替换为新的较低值。

  for file in in_*.txt
    do
        awk -vt=$t 'NR<=10{
            c=c+1
            val[c]=$7
            tag[c]=$1
        }
        NR>10{
            for(o=1;o<=c;o++){
                if ( $7 <= val[o] ){
                    val[o]=$7
                    tag[o]=$1
                    break
                }
            }
        }
        END{
            for(i=1;i<=c;i++){
                print val[i], tag[i] | "sort"
            }

        }' $file
    done

答案 5 :(得分:0)

这可能接近你正在寻找的东西:

for file in *; do sort -k6n "$file" | head -n 10 | cut -f1,7 -d " " | sed "s/^/$file /" > "${file}.out"; done

cat *.out | sort -k3n | head -n 10 > final_result.out