Поиск и сортировка данных из нескольких файлов - PullRequest
2 голосов
/ 12 января 2010

У меня есть набор из 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 для этой цели, но на данный момент я не нашел практического подхода. Все, что я знаю, это:

  1. объединить все in_ файлы в IN.TXT
  2. искать там самые низкие значения, используя: for i in IN.TXT ; do sort -k6n $i | head -n 10; done
  3. с учетом значений 1st_col и 7th_col из первой десятки списка, использовать их для фильтрации файлов in_s, используя grep -n VALUE in_s*, поэтому я получаю для каждого значения имя файла

Это работает, но это немного утомительно. Меня интересует более быстрый подход, использующий только bash, python или оба. Или другой лучший язык для этого.

Спасибо

Ответы [ 6 ]

3 голосов
/ 12 января 2010

В python используйте функцию nsmallest в модуле heapq - она ​​предназначена именно для такого рода задач.

Пример (проверено) для 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)
2 голосов
/ 12 января 2010

Я бы:

  • взять первые 10 предметов,
  • рассортируйте их и затем
  • для каждой строки, прочитанной из файлов, вставьте элемент в эти топ10:
    • в случае, если его значение ниже наибольшего из текущих top10,
    • (сохранение сортировки по производительности)

Я бы не стал публиковать здесь полную программу, так как она выглядит как домашняя работа.

Да, если бы не десять, это было бы не оптимально

1 голос
/ 12 января 2010

Попробуйте что-то вроде этого в 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, только один раз запускает сортировку (после продюсера С. Лотта):

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

Просто перечитайте свой вопрос, с миллионами строк вы можете исчерпать ОЗУ ....

0 голосов
/ 12 января 2010

Это может быть близко к тому, что вы ищете:

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
0 голосов
/ 12 января 2010

Если ваши файлы имеют миллион строк, вы можете рассмотреть возможность использования буферизации. приведенный ниже скрипт проходит через эти миллионы строк, каждый раз сравнивая поле 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
0 голосов
/ 12 января 2010

Небольшое улучшение вашей оболочки:

$ 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...