使用perl解析制表符分隔的数据

时间:2013-02-23 05:06:41

标签: 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
如果有人可以分享他们的智慧,那将会很棒。

4 个答案:

答案 0 :(得分:1)

一般模式是

use strict;
use warnings;

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

在循环中,字段可以$fields[0]$fields[2]进行访问。


<强>更新

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

它累计哈希%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

答案 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

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

  • 要将输出打印到文件,请按以下方式运行:./parser.pl < sample.txt > output.txt
  • $prev_name$prev_i未初始化,因此它们最初为undef(= NULL)

答案 2 :(得分:-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文件......所以我不确定你在哪里获得开始,结束值,平均值。

答案 3 :(得分:-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"而不是'|'