0

我有一个制表符分隔的数据。我想使用 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

如果有人能分享他们的智慧,那就太好了。

4

4 回答 4

1

一般模式是

use strict;
use warnings;

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

在循环内,可以通过 访问$fields[0]字段$fields[2]


更新

我已经更好地理解了你的问题,我认为这个解决方案对你有用。请注意,它假设输入数据已排序,如您在问题中所示。

它在 hash 中累积开始值和结束值、总数和计数%data,并保留遇到的所有名称的列表,@names以便数据可以按照读取的顺序显示。

该程序需要输入文件名作为命令行上的参数。

您需要考虑平均值的格式,因为它是一个浮点值。就目前而言,它会将值显示为 16 位有效数字,您可能希望使用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
于 2013-02-23T05:42:49.743 回答
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

更新:评论中问题的答案:

  • 要将输出打印到文件,运行如下:./parser.pl < sample.txt > output.txt
  • $prev_name并且$prev_i没有初始化,所以它们undef一开始是(= NULL)
于 2013-02-23T07:09:04.360 回答
-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"而不是'|'.

于 2013-02-23T10:06:52.550 回答
-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 文件......所以我不确定你从哪里得到开始,结束值,平均值。

于 2013-02-23T05:21:14.433 回答