首页 > 解决方案 > 用于管理 Perl 中不相邻行之间的计算的数组数组

问题描述

我正在处理一些数据以计算样本的平均值,并且当差异很大(平均值超过 +/- 2 个标准差)时,进行插值,但我需要强大的帮助!

困难的部分是选择让计算尊重它们所属的样本的行。样本标识符放在不容易管理的依赖逻辑中的第一列或第二列或第三列(下面的示例中没有第三列)。

我的数据看起来像这样(直接从我的文件中复制和粘贴)。输入文件也可在https://gofile.io/?c=3PLR8m获得。列是制表符分隔的,每个标识符在字符前都有一个空格。

ENTITY-CODE     XX  YY  ZZ  AA  BB  CC  att 1
/P1
 ^/A1/S1        143.07  124.05  -159.24 -160.53 0.39    3.31    15
 ^<S2       143.45  123.69  -157.19 -160.74 0.43    1.5 14.8
     +A1/S1 143.87  122.84  -157.08 -147.56 -30.37  3.07    4.9
     ^<S2   152.09  120.29  -155.42 -145.61 -67.13  0.37    3.3
     ^<S3   161.5   120.13  -153.34 -134.92 -73.39  -3.93   3.4
     ^<S4   27.76   122.15  -152.59 -103.01 -74.37  -20 2.9
     ^<S5   179.58  125.71  -153.46 -90.21  -73.6   -21.68  2.8
     ^<S6   189.23  128.85  -152.9  -86.28  -72.54  -19.89  2.4
     ^<S7   196.23  135.77  -152.82 -73.48  -75.22  -19.93  2.1
     ^<S8   195.49  147.85  -150.64 -63.59  -80.44  -32.27  1.5
 ^<S3       143.07  124.1   -157.05 -145.58 -1.81   6.34    16
     +A1/S1 142.03  123.41  -156.23 -72.07  -19.45  -0.4    5.5
     ^<S2   134.29  121.27  -153.31 -76.28  -3.92   -2.37   3.8
     ^<S3   128.55  119.39  -152.31 -73.1   6.95    0.04    2.7
     ^<S4   120.87  115.88  -150.91 -69.62  8.05    0.63    2.7
     ^<S5   115.31  112.83  -151.31 -76.97  7.45    -2.31   2.4
     ^<S6   108.54  110.71  -149.38 -86.09  5.68    -6.48   1.5
 ^<S4       143.49  123.63  -155.79 -175.31 14.3    12.22   13.7
     +A1/S1 143.5   124.75  -155.22 175.69  25.35   25.61   5.9
     ^<S2   145.63  130.57  -156.39 141.67  42.19   31.94   5.3
     ^<S3   153.77  131.23  -153.8  71.9    34.43   20.11   3.6
     ^<S4   160.99  132.18  -149.31 89.71   35.44   14.31   2.6
     ^<S5   166.86  133.6   -146.6  93.88   34.73   11.46   1.8
     +A2/S1 143.63  122.79  -155.05 65.04   4.77    -16.93  3.5
     ^<S6   144.71  122.02  -151.41 56.49   -7.71   -16.1   2.8
     ^<S6   146.83  120.14  -148.52 61.14   24.37   48.58   2.9
     ^<S6   154.06  115.65  -149.29 60.87   20.18   13.8    2.5
 ^<S5       143.32  33.32   -153.16 -127.03 8.59    9.07    12.4
 ^<S6       143.49  121.69  -150.07 -127.26 9.04    10.85   12.5

基本上,具有标识符的行在同一列中包含“A”的行需要进行计算(连同“A”的行)以检查超出范围的值,因为它们属于同一样本. 如果在同一列中存在另一个包含“A”的标识符,则表示正在开始另一组属于另一个样本并需要进行另一个计算的行。

在我在这里发布的示例中,我想要一个脚本,它从第一个开始^/A1/S1识别第一列中具有标识符的所有行,并检查它们的XX,YYZZ值。

+A1/S1如果标识符在第二列或其他列中,脚本也应该这样做。

在实践中,每次有一个包含“A”的标识符意味着正在开始一个样本,该样本的其他元素在同一列中具有 S 类型标识符(直到另一个 A 类型标识符)。

S 型标识符中包含的数字不相关。因此,例如,具有相同标识符的三行(紧挨输入示例的末尾)必须被视为三组不同的值。

输出的格式应该与输入的格式相同,只是插值的不同之处发生了变化。插值应包括对样本的平均值和标准偏差的计算(同一列中具有标识符的行,从标有“A”的行到同一列中具有“A”的另一个标识符之前的最后一个)并检查一个值是否超过平均值 +/- 2 个标准差 ( mean±(2*dev.st))。如果是,则用样本均值替换一个值。

在此处的示例中,我想获得与输入相同的内容,除了:XX第八行中的值 (27.76),应替换为XX根据同一样本行的值计算的平均值,这些值是前一个和下一行(在第二列中分别具有^<S3和作为标识符)和(ii)第三十行(33.32)中的值应替换为在第一列中具有和的行上计算的平均值。^<S5YY^<S4^<S6

因此,这是我想要的输出。

  ENTITY-CODE       XX  YY  ZZ  AA  BB  CC  att 1
    /P1
     ^/A1/S1        143.07  124.05  -159.24 -160.53 0.39    3.31    15
     ^<S2       143.45  123.69  -157.19 -160.74 0.43    1.5 14.8
         +A1/S1 143.87  122.84  -157.08 -147.56 -30.37  3.07    4.9
         ^<S2   152.09  120.29  -155.42 -145.61 -67.13  0.37    3.3
         ^<S3   161.5   120.13  -153.34 -134.92 -73.39  -3.93   3.4
         ^<S4   173.59  122.15  -152.59 -103.01 -74.37  -20 2.9
         ^<S5   179.58  125.71  -153.46 -90.21  -73.6   -21.68  2.8
         ^<S6   189.23  128.85  -152.9  -86.28  -72.54  -19.89  2.4
         ^<S7   196.23  135.77  -152.82 -73.48  -75.22  -19.93  2.1
         ^<S8   195.49  147.85  -150.64 -63.59  -80.44  -32.27  1.5
     ^<S3       143.07  124.1   -157.05 -145.58 -1.81   6.34    16
         +A1/S1 142.03  123.41  -156.23 -72.07  -19.45  -0.4    5.5
         ^<S2   134.29  121.27  -153.31 -76.28  -3.92   -2.37   3.8
         ^<S3   128.55  119.39  -152.31 -73.1   6.95    0.04    2.7
         ^<S4   120.87  115.88  -150.91 -69.62  8.05    0.63    2.7
         ^<S5   115.31  112.83  -151.31 -76.97  7.45    -2.31   2.4
         ^<S6   108.54  110.71  -149.38 -86.09  5.68    -6.48   1.5
     ^<S4       143.49  123.63  -155.79 -175.31 14.3    12.22   13.7
         +A1/S1 143.5   124.75  -155.22 175.69  25.35   25.61   5.9
         ^<S2   145.63  130.57  -156.39 141.67  42.19   31.94   5.3
         ^<S3   153.77  131.23  -153.8  71.9    34.43   20.11   3.6
         ^<S4   160.99  132.18  -149.31 89.71   35.44   14.31   2.6
         ^<S5   166.86  133.6   -146.6  93.88   34.73   11.46   1.8
         +A2/S1 143.63  122.79  -155.05 65.04   4.77    -16.93  3.5
         ^<S6   144.71  122.02  -151.41 56.49   -7.71   -16.1   2.8
         ^<S6   146.83  120.14  -148.52 61.14   24.37   48.58   2.9
         ^<S6   154.06  115.65  -149.29 60.87   20.18   13.8    2.5
     ^<S5       143.32  123.41  -153.16 -127.03 8.59    9.07    12.4
     ^<S6       143.49  121.69  -150.07 -127.26 9.04    10.85   12.5

对于输入,它只有两个变化:

到目前为止我写的代码如下。我想到了数组数组,但我不确定如何设置它。

任何建议都是非常受欢迎的,因为我被迷住了。谢谢!

open (HAN, "<", "$file") || die "problems with the input file";
    my @lines = ();
    while (<HAN>) { 
    chomp; 
    push(@lines, $_); }
    #print STDERR "@lines\n";

    close (HAN);
    for ($lines[$i] =0; $i<=$#lines; $i++){
        @columns = split (/\t/, $lines[$i]);
                #print STDERR "@columns\n";
    my @p;
    my @s;

    if (( $columns[0] ne "" ) && ( $columns[1] eq "" )){
            push @p, $lines[$i] ;       
                    #print STDERR "@p\n";
        } elsif (( $columns[0] eq "" ) && ( $columns[1] ne "" )){
            push @s, $lines[$i] ;       
                    #print  STDERR "@s\n";
        print STDERR "@s\n";

标签: arraysperl

解决方案


对不起,我没有更多的时间来专注于此。也许以下内容可以帮助您找到正确的方法。

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use Syntax::Construct qw{ // };

use List::Util qw{ sum };

my @data;

while (<>) {
    chomp;
    push @data, [ split /\t/ ];
}

my (@dev_st, @mean);
for my $line_index (0 .. $#data) {
    for my $column (2, 3) {
        for my $level (0, 1) {
            if (($data[$line_index][$level] // "") =~ /A/) {
                my $to = $line_index;
                my $inner_group;
                do { ++$to } until $to > $#data
                             || $level == 1 && $data[$to][0]
                             || (($data[$to][$level] // "") =~ /A/
                                 and $inner_group = 1);
                --$to if $inner_group;

                my @group_data = map $data[$_][2],
                                 grep $data[$_][$level],
                                 $line_index .. $to;
                $mean[$level] = sum(@group_data) / @group_data;
                $dev_st[$level] = sqrt(1/(@group_data - 1) * sum(
                    map { ($_ - $mean[$level]) ** 2 } @group_data));
                # warn "$line_index: @group_data\n$mean[$level] $dev_st[$level]\n";
            }
        }

        my $value = $data[$line_index][$column] // "";
        next unless $value =~ /-?[0-9]+(?:\.[0-9]+)?/;

        my ($level) = grep $data[$line_index][$_], 0, 1;
        if (   $value > $mean[$level] + 2 * $dev_st[$level]
            || $value < $mean[$level] - 2 * $dev_st[$level]
        ) {
            $data[$line_index][$column]
                = sprintf '%.2f', $mean[$level];
        }
    }
    say join "\t", map $_ // "", @{ $data[$line_index] };
}
print "\n";

推荐阅读