使用Perl操作多行

时间:2014-04-01 15:14:36

标签: arrays regex perl

我有几百行格式的文件

1st  2n  2p  3n  3p  4n  4p
1ABJa  2  20  8  40  3  45
1ABJb  2  40  8  80  3  45
2C3Da  4  50  5  39  2  90
2D4Da  1  10  8  90  8  65

(制表符分隔文件)

从这个文件中,我想操纵第一列中具有类似4个开头字符的所有行(即1ABJa和1ABJb)并执行:

    第1列的
  • 合并了两个维护公共字符的名称;
  • 对于列2n, 3n, 4n...,数字将相加;
  • 对于列2p, 3p, 4p, ...,数字将被平均。

(请注意,这可以通过列位置而不是名称来指定)。 然后这会产生:

1st  2n  2p  3n  3p  4n  4p
1ABJab  4  30  16  60  6  45       
2C3Da  4  50  5  39  2  90
2D4Da  1  10  8  90  8  65

你会如何解决这个问题?

这可能是执行此操作最复杂的方法,但这里有:我正在考虑创建第1列的所有4个字符的唯一元素的数组。然后,对于该数组,运行一个循环,找到与这4个字符匹配的所有实例。如果有多个实例,请识别它们,推送列并操纵它们。这是我到现在为止的观点:

#!/usr/local/bin/perl
use strict;
use warnings;
use feature 'say';
use List::MoreUtils qw(uniq);

my $dir='My\\Path\\To\\Directory';
open my $in,"<", "$dir\\my file.txt" or die;
my @uniqarray; my @lines;

#collects unique elements in 1st column and changes them to 4-character words
while (my $line = <$in>) {
    chomp $line;
    @lines= split '\t', $line;
    if (!grep /$lines[0]/, @uniqarray ){
        $lines[0] =~ s/^(.{4}).*/$1/;
        push @uniqarray,$lines[0];
    }
}

my @l;
#for @uniqarray, find all rows in the input that match them. if more than 1 row is found, manipulate the columns
while (my $something=<$in>) {
    chomp $something;
    @l= split '\t', $something;
    if ( map $something =~ m/$_/,@uniqarray){
        **[DO STUFF]**
    }
}

print join "\n", uniq(@uniqarray);

close $in;

2 个答案:

答案 0 :(得分:2)

怎么样:

my $result;
my $head = <DATA>;
while(<DATA>) {
    chomp;
    my @l = split/\s+/;
    my ($k1,$k2) = ($l[0] =~ /^(....)(.*)$/);
    $result->{$k1}{more} .= $k2 // '';
    $result->{$k1}{nbr}++;

    ;
    $result->{$k1}{n}{2} += $l[1];
    $result->{$k1}{n}{3} += $l[3];
    $result->{$k1}{n}{4} += $l[5];
    $result->{$k1}{p}{2} += $l[2];
    $result->{$k1}{p}{3} += $l[4];
    $result->{$k1}{p}{4} += $l[6];
}

print $head;
foreach my $k (keys %$result) {
    print $k,$result->{$k}{more},"\t";
    for my $c (2,3,4) {
        printf("%d\t",$result->{$k}{n}{$c});
        if (exists($result->{$k}{nbr}) && $result->{$k}{nbr} != 0) {
            printf("%d\t",$result->{$k}{p}{$c}/$result->{$k}{nbr});
        } else {
            printf("%d\t",0);
        }
    }
    print "\n";
}

<强>输出:

1st     2n  2p  3n  3p  4n  4p
2D4Da   1   10  8   90  8   65  
1ABJab  4   30  16  60  6   45  
2C3Da   4   50  5   39  2   90  

答案 1 :(得分:1)

这似乎可以满足您的需求。它为每个不同的四字符前缀保留一组哈希数据:一个在密钥n下具有相同前缀的记录数,一个数组,用于保存密钥{{下的该前缀的列总数1}},以及包含在密钥totals下为该前缀看到的所有后缀的哈希。

在第一次看到前缀时,会将前缀添加到数组suffixes中,以便输出的输出顺序与输入的顺序相同。

@prefixes数组的所有偶数列除以totals之后,只需累积数据然后以所需格式转储数据。

n

<强>输出

use strict;
use warnings;

open my $fh, '<', 'data.txt' or die $!;

print scalar <$fh>; # Copy header

my %data;
my @prefixes;

while (<$fh>) {
  chomp;
  my @fields = split /\t/;
  my ($prefix, $suffix) = shift(@fields) =~ /(.{4})(.*)/;
  push @prefixes, $prefix unless $data{$prefix};
  ++$data{$prefix}{n};
  ++$data{$prefix}{suffixes}{$suffix};
  $data{$prefix}{totals}[$_] += $fields[$_] for 0 .. $#fields;
}

for my $prefix (@prefixes) {
  my $val      = $data{$prefix};
  my $totals   = $val->{totals};
  for (my $i = 1; $i < @$totals; $i += 2) {
    $totals->[$i] /= $val->{n};
  }
  my $suffixes = join '', sort keys %{ $val->{suffixes} };
  print join("\t", "$prefix$suffixes", @$totals), "\n";
}