2013-02-23 2 views
0

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

Это пример таблицы: Мой исходный файл почти GB

gi|306963568|gb|GL429799.1|_1316857_1453052 13 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 14 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 15 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 16 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 17 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 360 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 361 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 362 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 363 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 364 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 365 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 366 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38640 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38641 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38642 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38643 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38644 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38645 1 

Я хотел бы, чтобы получить выход в Имя, начальное значение, конечное значение, среднее

gi|306963568|gb|GL429799.1|_1316857_1453052 13 17 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 360 366 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38640 38645 1 

это будет будь великим, если кто-то может поделиться своей мудростью.

+3

Используйте ['Text :: CSV_XS'] (http://search.cpan.org/dist/Text-CSV_XS/). CSV - ужасный скверный формат, который ненавидит все человечество, всегда держите хорошо протестированную библиотеку разбора между вами и CSV. –

+0

@muistooshort: Файлы с разделителями-табуляторами отличаются от CSV и почти всегда хорошо себя ведут, поскольку нет попытки указать поля, содержащие разделительный символ: символ табуляции просто недействителен в данных. – Borodin

+0

@Borodin: Не имеет отношения. Даже если это _called_ Text :: CSV/CSV_XS, он будет работать с разделителями вкладок или труб. Просто передайте конструктору 'sep_char'. –

ответ

-1

Вы могли бы сделать что-то вроде этого ....

open (FILE, 'data.txt'); 
while (<FILE>) { 
chomp; 
($name, $start_value, $end_value, $average) = split("\t"); 
print "Name: $name\n"; 
print "Start Value: $start_value\n"; 
print "End Value: $End_Value\n"; 
print "Average: %average 
print "---------\n"; 
} 
close (FILE); 
exit; 

Те похожи GenBank файлы ... так что я не уверен, где вы получаете начало, конец значения, среднее значение.

+2

В течение очень долгого времени лучше всего использовать лексические файловые дескрипторы и три параметра 'open'. Никогда не было хорошей идеи открыть файл, не проверив, удалось ли ему и распечатать '$!' В строке 'die', если нет. Также код без 'use strict' - очень плохая идея. – Borodin

1

Общая картина

use strict; 
use warnings; 

open my $fh, '<', 'myfile' or die $!; 
while (<$fh>) { 
    chomp; 
    my @fields = split /\t/; 
    ... 
} 

В цикле поля могут быть доступны, как $fields[0] через $fields[2].


Update

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

Он аккумулирует начальные и конечные значения, общее количество и количество в хэш %data и хранит список всех имен, встречающихся в @names, так что данные могут быть отображены в том порядке, как было прочитано.

Программа ожидает имя входного файла в качестве параметра в командной строке.

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

use strict; 
use warnings; 

my ($filename) = @ARGV; 
open my $fh, '<', $filename or die qq{Unable to open "$filename": $!}; 

my @names; 
my %data; 
my $current_name = ''; 
my $last_index; 

while (<$fh>) { 
    chomp; 
    my ($name, $index, $value) = split /\t/; 

    if ($current_name ne $name or $index > $last_index + 1) { 
    push @names, $name unless $data{$name}; 
    push @{ $data{$name} }, { 
     start => $index, 
     count => 0, 
     total => 0, 
    }; 
    $current_name = $name; 
    } 

    my $entry = $data{$name}[-1]; 
    $entry->{end} = $index; 
    $entry->{count} += 1; 
    $entry->{total} += $value; 
    $last_index = $index; 
} 

for my $name (@names) { 
    for my $entry (@{ $data{$name} }) { 
    my ($start, $end, $total, $count) = @{$entry}{qw/ start end total count /}; 
    print join("\t", $name, $start, $end, $total/$count), "\n"; 
    } 
} 

выход

gi|306963568|gb|GL429799.1|_1316857_1453052 13 17 1 
gi|306963568|gb|GL429799.1|_1316857_1453052 360 366 1 
gi|306963580|gb|GL429787.1|_4276355_4500645 38640 38645 1 
0

Это даст тот же результат для образца в вашем вопросе:

#!/usr/bin/env perl -n 
# 
my ($name, $i, $value) = split(/\t/); 

sub print_stats { 
    print join("\t", $prev_name, $start, $prev_i, $sum/($prev_i - $start + 1)), "\n"; 
} 

if ($prev_name eq $name && $i == $prev_i + 1) { 
    $sum += $value; 
    $prev_i = $i; 
} 
else { 
    if ($prev_name) { 
     &print_stats(); 
    } 
    $start = $i; 
    $prev_name = $name; 
    $sum = $value; 
    $prev_i = $i; 
} 
END { 
    &print_stats(); 
} 

Используйте его как:

./parser.pl < sample.txt 

UPDATE: ответы на вопросы в комментариях:

  • Для печати вывода в файл, запустить так: ./parser.pl <sample.txt> output.txt
  • $prev_name и $prev_i НЕ инициализированы, поэтому они undef на первом (= NULL)
+0

Привет, Спасибо большое. Он работает отлично. Я хотел бы знать, как вывод печати в файл. Я хотел бы знать, как вы инициализируете переменные «$ prev_name» и «$ prev_i», и как вы читаете строки. Я знаю, что это можно сделать, используя while (defined()). Я также хотел бы знать, почему вам нужно дважды использовать $ prev_name. «if ($ prev_name && $ prev_name». – user2101622

+0

Я обновил свое сообщение, чтобы ответить на ваши вопросы. «$ prev_name && ...» означает на человеческом языке, что «' $ prev_name' не равно null AND ... ». Думаю, я понял, что условие было избыточным, поэтому я удалил его из сообщения, см. обновленную версию. – janos

-1

Вот пример использования Text::CSV:

use Text::CSV; # This will implicitly use Text::CSV_XS if it's installed 

my $parser = Text::CSV->new({ sep_char => '|' }); 
open my $fh, '<', 'myfile' or die $!; 

while (my $row = $parser->getline($fh)) { 
    # $row references an array of field values from the line just read 
} 

Кроме того, в качестве вспомогательной дополнительной детали ваши данные образца разделяются символами канала, а не вкладками, хотя это может быть только для того, чтобы избежать ошибок копирования/вставки для тех, кто отвечает на ваш вопрос. Если фактические данные разделены табуляцией, установите sep_char в "\t" вместо '|'.

+0

Данные OP * * разделены на вкладку. В каждой строке есть три поля, а во-первых, что он называет 'Name', содержит несколько труб. – Borodin

+0

ОК, в этом случае в ответе говорится: «Если фактические данные разделены табуляцией, установите sep_char в' '\ t" 'вместо' '| ''. не содержит вкладок –

+0

Да, он содержит символы табуляции. Если вы скопировали визуализированный HTML, то, конечно, вы их не увидите. Вам нужно отредактировать вопрос и скопировать из окна редактирования. – Borodin

Смежные вопросы