как обрезать файл - для строк, которые с одинаковым значением в двух столбцах сохраняют только строку с максимальным значением в других столбцах - PullRequest
0 голосов
/ 24 июня 2011

У меня сейчас проблема с обрезкой файла. Я хотел бы обрезать строки в файле с разделителями табуляции.

Правило таково: для строк с одинаковым значением в двух столбцах следует сохранять только строку с наибольшим значением в третьем столбце. Может быть различное количество таких избыточных строк, определенных двумя столбцами. Если есть связь для наибольшего значения в третьем столбце, сохраните первый (после упорядочивания файла).

(1) Мой файл выглядит так (разделенный табуляцией, с несколькими миллионами строк):

1 100 25 T
1 101 26 A
1 101 27 G
1 101 30 A
1 102 40 A
1 102 40 T

(2) Вывод, который я хочу:

1 100 25 T
1 101 30 A
1 102 40 T

С этой проблемой сталкивается моя настоящая учеба, а не домашняя работа. Я рассчитываю на вашу помощь, потому что у меня ограниченные навыки программирования. Я предпочитаю эффективный способ вычислений, потому что в моем файле данных столько строк. Ваша помощь будет очень ценной для меня.

Ответы [ 4 ]

2 голосов
/ 24 июня 2011

Вот решение, которое будет полагаться на входной файл, уже отсортированный соответствующим образом.Он будет построчно сканировать строки с одинаковым началом (например, два одинаковых первых столбца), проверяет значение третьего столбца и сохраняет строку с наибольшим значением - или строку, которая была первой в файле.При обнаружении нового начала он печатает старую строку и начинает проверку снова.

В конце входного файла выводится максимальная строка в памяти.

use warnings;
use strict;

my ($max_line, $start, $max) = parse_line(scalar <DATA>);
while (<DATA>) {
    my ($line, $nl_start, $nl_max) = parse_line($_);
    if ($nl_start eq $start) {
        if ($nl_max > $max) {
            $max_line = $line;
            $max = $nl_max;
        }
    } else {
        print $max_line;
        $start = $nl_start;
        $max = $nl_max;
        $max_line = $line;
    }
}

print $max_line;

sub parse_line {
    my $line = shift;
    my ($start, $max) = $line =~ /^([^\t]+\t[^\t]+\t)(\d+)/;
    return ($line, $start, $max);
}
__DATA__
1   100 25  T
1   101 26  A
1   101 27  G
1   101 30  A
1   102 40  A
1   102 40  T

Вывод:

1       100     25      T
1       101     30      A
1       102     40      A

Вы указали

Если в третьем столбце есть связь для наибольшего значения, сохраните первое (после упорядочивания файла).

что довольно загадочно.Затем вы запросили вывод, который, казалось бы, противоречил этому, где вместо первого было напечатано last .

Я предполагаю, что вы имели в виду «сохранить первое значение».Если вы действительно имели в виду «сохранить последнее значение», просто измените знак > в if ($nl_max > $max) на >=.Это эффективно сохранит последнее значение равным первому.

Если вы, однако, подразумеваете какую-то сортировку, которая, по-видимому, подразумевает «после упорядочивания файла», то у меня недостаточно информации, чтобы знать, что выимел в виду.

1 голос
/ 24 июня 2011

В Python тоже, но чище IMO

import csv
spamReader = csv.reader(open('eggs'), delimiter='\t')
select = {}
for row in spamReader:
    first_two, three = (row[0], row[1]), row[2]
    if first_two in select:
         if select[first_two][2] > three:
             continue
    select[first_two] = row

spamWriter = csv.writer(open('ham', 'w'), delimiter='\t')
for line in select:
    spamWrite.writerow(select[line])
1 голос
/ 24 июня 2011

В Python вы можете попробовать следующий код:

res = {}
for line in (line.split() for line in open('c:\\inpt.txt','r') if line):
    line = tuple(line)
    if not line[:2] in res:
        res[line[:2]] = line[2:]
        continue
    elif res[line[:2]][0] <= line[3]:
        res[line[:2]] = line[2:]

f = open('c:\\tst.txt','w')
[f.write(line) for line in ('\t'.join(k+v)+'\n' for k,v in res.iteritems())]
f.close()
1 голос
/ 24 июня 2011

Вот один подход

use strict;
use warnings;
use constant 
    { LINENO => 0
    , LINE   => 1
    , SCORE  => 2
    };
use English qw<$INPUT_LINE_NUMBER>;

my %hash;
while ( <> ) { 
    # split the line to get the fields
    my @fields = split /\t/;
    # Assemble a key for everything except the "score"
    my $key    = join( '-', @fields[0,1] );
    # locally cache the score
    my $score  = $fields[SCORE];

    # if we have a score, and the current is not greater, then next
    next unless ( $hash{ $key } and $score > $hash{ $key }[SCORE];
    # store the line number, line text, and score
    $hash{ $key } = [ $INPUT_LINE_NUMBER, $_, $score ]; 
}

# sort by line number and print out the text of the line stored.
foreach my $struct ( sort { $a->[LINENO] <=> $b->[LINENO] } values %hash ) {
    print $struct->[LINE]; 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...