循环遍历数组以使用 Perl 比较两个值

Plu*_*end 2 perl foreach if-statement

我有一个大型数据集(29 列 x 19000 行),我希望能够比较每一行的值并打印描述性输出。

具体来说,我想查询 A 列(@WTcall)中的值,这实际上是一个通过/失败语句。如果数据失败,我想打印“失败声明”并移至下一行,但如果数据通过,我想继续描述数据。

接下来的问题是确定 X (@positive) 和 Y (@negative) 列中的数据属于哪种分类:

(前任:

如果 X 列和 Y 列 >= 0.6,则打印“ABC”

如果 X 列和 Y 列 < 0.6,则打印“CBA”

如果 X 列 >= 0.6 但 Y 列 < 0.6 打印“DEF”

如果 X 列 < 0.6 但 Y 列 >= 0.6 打印“FED”

否则打印“缺少数据”。)

我已经包含了我在下面编写的代码以及示例数据的子集。

我在发布之前运行的测试在代码中被注释掉了。简而言之,如果我注释掉“if 和 elsif 语句”列表,请打印“@WTcall\t@positive\t@negative\n”并将其通过 head 命令传送 - 我的变量似乎正在提取正确的信息。

问题出现在实际比较中,因为每一行都被分类为“Methylated\tMethylated\n”描述。我不清楚这是为什么。我知道我有 @WTcall 列应该匹配 $BadPosition(通过/失败检查)的条目。此外,如果我再次注释掉“if 语句”,则打印“@WTcall\n$BadPosition”并将其通过 sort 和 uniq 进行管道传输 - 我只得到“No_WT_Concensus”的一个值,因此那里应该没有拼写错误或匹配问题这些值。

我确信这个问题很明显并且正直盯着我的脸,所以我真的很感激任何帮助。

谢谢你。

代码:

#!/usr/bin/perl

use strict;
use warnings;
use autodie;
die "Usage: $0 Filename\n" if @ARGV != 1;

my $file = shift;
my @line;
my @positive; 
my @negative; 
my @WTcall;
my $BadPosition = 'No_WT_Concensus';
my $i;

open my $infh, '<', $file;

while (<$infh>) {
    chomp;
    @line = split(/\t/,$_);
    $WTcall[0]=$line[0];
    $positive[0]=$line[14];
    $negative[0]=$line[29];

    #foreach $1 (<$infh>) {
    foreach $1 (@WTcall) {
        if (@WTcall eq $BadPosition){
            print "No_WT_Concensus\tNo_WT_Concensus\n";
        } elsif (@positive >= 0.6 && @negative >= 0.6){
            print "Methylated\tMethylated\n";
        } elsif (@positive <= 0.6 && @negative <= 0.6){
            print "Under-methylated\tUnder-methylated\n";
        } elsif(@positive >= 0.6 && @negative <=0.6){
            print "Hemimethylated (m6A)\tHemimethylated (A)\n";
        } elsif(@positive <= 0.6 && @negative >= 0.6){
            print "Hemimethylated (A)\tHemimethylated (m6A)\n";
        } else{
            print "Missing_Site\tMissing_Site\n";
        }
        #print "@WTcall\n$BadPosition\n";
        #print "@WTcall\t@positive\t@negative\n"
        #print "@negative\n";
    }
}

close $infh;
Run Code Online (Sandbox Code Playgroud)

样本数据:

Methylated  coding gene 619 thrA    NC_000913.3 pos 3   1   0.9535  1   NC_000913.3 619 +   18  0.8889  Methylated  coding gene 620 thrA    NC_000913.3 neg 3   0.9429  0.9756  0.9714  NC_000913.3 620 -   14  1
No_WT_Concensus coding gene 195410  ispU    NC_000913.3 pos 2   0.5789  0.766   0.6071  NC_000913.3 195410  +   39  0.5897  Methylated  coding gene 195411  ispU    NC_000913.3 neg 3   0.75    0.9074  0.9306  NC_000913.3 195411  -   21  0.8095
Under-methylated    pseudogene  3632965 yhiL    NC_000913.3 pos 0   0.0323  0.1429  0.0962  NC_000913.3 3632965 +   22  0.0909  Under-methylated    pseudogene  3632966 yhiL    NC_000913.3 neg 0   0.1463  0.175   0.1429  NC_000913.3 3632966 -   23  0
Methylated  intergenic  164636  hrpB-mrcB   NC_000913.3 pos 3   0.7381  0.7647  0.7273  NC_000913.3 164636  +   25  0.8 Methylated  intergenic  164637  hrpB-mrcB   NC_000913.3 neg 3   0.7 0.7931  0.7213  NC_000913.3 164637  -   25  0.4
Methylated  intergenic  269287  ykfA-perR   NC_000913.3 pos 3   0.875   0.8833  0.931   NC_000913.3 269287  +   22  0.8182  Methylated  intergenic  269288  ykfA-perR   NC_000913.3 neg 3   0.8077  0.6866  0.6491  NC_000913.3 269288  -   17  0.5294
Methylated  coding gene 4397856 mutL    NC_000913.3 pos 3   0.9245  0.9831  0.9661  blank   blank   blank   blank   blank   Methylated  coding gene 4397857 mutL    NC_000913.3 neg 3   0.9783  0.9808  0.9683  NC_000913.3 4397857 -   1   0
Methylated  coding gene 4397969 mutL    NC_000913.3 pos 3   0.9643  0.9524  1   blank   blank   blank   blank   blank   Methylated  coding gene 4397970 mutL    NC_000913.3 neg 3   1   1   1   blank   blank   blank   blank   blank
Methylated  coding gene 2761    thrA    NC_000913.3 pos 3   0.9259  0.8654  0.9242  NC_000913.3 2761    +   31  1   Methylated  coding gene 2762    thrA    NC_000913.3 neg 3   0.913   0.9636  0.9767  NC_000913.3 2762    -   29  0.9655
Methylated  coding gene 3073    thrB    NC_000913.3 pos 3   0.9677  0.8983  1   NC_000913.3 3073    +   29  1   Methylated  coding gene 3074    thrB    NC_000913.3 neg 3   1   0.9038  0.9778  NC_000913.3 3074    -   31  1
Run Code Online (Sandbox Code Playgroud)

bri*_*foy 7

以下是您的要求,经过编辑以显示并行结构:

  • X >= 0.6 和 Y >= 0.6 然后“ABC”

  • X < 0.6 和 Y < 0.6 然后是“CBA”

  • X >= 0.6 但 Y < 0.6 然后“DEF”

  • X < 0.6 但 Y >= 0.6 然后“美联储”

一些要求是< 0.6,但在您的代码中您有<= 0.6

你有两件事要测试,你应该首先寻找一种结构,每件事只测试一次。这是表达这一点的伪代码:

if X >= 0.6
    if Y >= 0.6
        "ABC"
    else
        "DEF"
else
    if Y >= 0.6
        "FED"
    else
        "CBA"
Run Code Online (Sandbox Code Playgroud)

一旦你知道一个值是否大于或等于一个值,你也知道它是否小于,所以你不必再次测试。测试只是分叉;如果您不选择一个分支,则必须选择另一个分支。

您的代码有点不同,因为它同时匹配$x >= 0.6or $x <= 0.6,并且相同 for $y。这意味着,如果两个$x$y0.6,那么任何brnach可以匹配,你会得到在链中的第一个。这似乎是代码中的错误,因为它不是您在文本中所描述的。

摆脱结构

我不得不做许多项目,这些项目有很长的这类选择列表,许多项目有数百项要测试。

现在,诀窍是将其转换为 Perl。请记住,子例程返回最后计算的表达式,因此它有效:

my @x_y = (
    [ 0.1, 0.7 ],
    [ 0.1, 0.1 ],
    [ 0.7, 0.1 ],
    [ 0.7, 0.7 ]
    );

foreach my $x_y ( @x_y ) {
    printf "X: %.1f Y: %.1f --> %s\n", @$x_y, get_value( @$x_y );
    }

sub get_value {
    my( $x, $y ) = @_;

    if( $x >= 0.6 ) { $y >= 0.6 ? 'ABC' : 'DEF' }
    else            { $y >= 0.6 ? 'FED' : 'CBA' }
    }
Run Code Online (Sandbox Code Playgroud)

如果我不传递一个值,我什至可能会参数化枢轴值并给它一个值:

sub get_value {
    my( $x, $y, $pivot ) = @_;
    $pivot //= 0.6;  # default value

    if( $x >= $pivot ) { $y >= $pivot ? 'ABC' : 'DEF' }
    else               { $y >= $pivot ? 'FED' : 'CBA' }
    }
Run Code Online (Sandbox Code Playgroud)

使用(实验性)子程序签名,这会更清晰一些,因为我可以设置默认值:

use v5.22;
use feature qw(signatures);
no warnings qw(experimental::signatures);

sub get_value ( $x, $y, $pivot = 0.6 ){
    if( $x >= $pivot ) { $y >= $pivot ? 'ABC' : 'DEF' }
    else               { $y >= $pivot ? 'FED' : 'CBA' }
    }
Run Code Online (Sandbox Code Playgroud)

但事情会变得更有趣。我已经对 重复了那个测试$y,但我可以保存比较的结果:

use v5.22;
use feature qw(signatures);
no warnings qw(experimental::signatures);

sub get_value ( $x, $y, $pivot = 0.6 ){
    my $boolean = ($y >= $pivot);
    if( $x >= $pivot ) { $boolean ? 'ABC' : 'DEF' }
    else               { $boolean ? 'FED' : 'CBA' }
    }
Run Code Online (Sandbox Code Playgroud)

但是,我真的在这里做什么?我正在尝试选择一个值。我在代码中将其表示为决策树。如果我能把它翻过来呢?我可以使用以下方法执行相同的保存结果技巧$x

use v5.22;
use feature qw(signatures);
no warnings qw(experimental::signatures);

sub get_value ( $x, $y, $pivot = 0.6 ){
    my $y_boolean = ($y >= $pivot);
    my $x_boolean = ($x >= $pivot);

    if( $x_boolean ) { $y_boolean ? 'ABC' : 'DEF' }
    else             { $y_boolean ? 'FED' : 'CBA' }
    }
Run Code Online (Sandbox Code Playgroud)

所以现在我遇到了布尔组合 (0,0)、(0,1)、(1,0) 和 (1,1) 的情况。我将使用它们作为数组索引。该state做持久varaible所以我不需要每次调用子程序的时间来重新定义它。Perl v5.28 允许您在 中初始化数组和散列state,对于早期版本,您只需使用引用:

use v5.28;
use feature qw(signatures);
no warnings qw(experimental::signatures);

sub get_value ( $x, $y, $pivot = 0.6 ) {
    state @table = (
        [ qw(CBA FED) ],
        [ qw(DEF ABC) ]
        );

    my $y_boolean = ($y >= $pivot);
    my $x_boolean = ($x >= $pivot);

    $table[$x_boolean][$y_boolean];
    }
Run Code Online (Sandbox Code Playgroud)

或者,稍微紧凑一点,我可以将比较放在数组索引中:

use v5.28;
use feature qw(signatures);
no warnings qw(experimental::signatures);

sub get_value ( $x, $y, $pivot = 0.6 ){
    state @table = (
        [ qw(CBA FED) ],
        [ qw(DEF ABC) ]
        );
    $table[$x >= $pivot][$y >= $pivot];
    }
Run Code Online (Sandbox Code Playgroud)

现在这些值与机制分离了——我在掌握 Perl 中花了很多时间。这也可以是一个参数,尽管现在它必须是一个数组引用,因为数组参数不能有默认值:

use v5.28;
use feature qw(signatures);
no warnings qw(experimental::signatures);

my @x_y = (
    [ 0.1, 0.7 ],
    [ 0.1, 0.1 ],
    [ 0.7, 0.1 ],
    [ 0.7, 0.7 ]
    );

my $pivot = 0.6;
my @table = ( [ qw(CBA FED) ], [ qw(DEF ABC) ] );

foreach my $x_y ( @x_y ) {
    printf "X: %.1f Y: %.1f --> %s\n", @$x_y,
        get_value( @$x_y, $pivot, \@table );
    }


sub get_value ( $x, $y, $pivot = 0.6,
    @table = ([ qw(DEF FED) ], [ qw(ABC CBA) ]) )
    {
    $table[$x >= $pivot][$y >= $pivot];
    }
Run Code Online (Sandbox Code Playgroud)

这更容易管理。您可以在机制不变的情况下调整枢轴和返回的值。

更进一步,将这些值完全从程序中取出,并将它们放入一个配置文件中。同一个程序可以处理许多其他情况,而无需您对其进行编辑。

对于您的情况

回到你的代码。choroba 向您展示了这一点,它解决了一些问题,但仍然保留<=问题:

while (<$infh>) {
    chomp;
    my @columns = split /\t/;
    my ($wt_call, $positive, $negative) = @columns[0, 14, 29];

    if ($wt_call eq $BadPosition) {
        print "No_WT_Concensus\tNo_WT_Concensus\n";
    } elsif ($positive >= 0.6 && $negative >= 0.6) {
        print "Methylated\tMethylated\n";
    } elsif ($positive <= 0.6 && $negative <= 0.6) {
        print "Under-methylated\tUnder-methylated\n";
    } elsif ($positive >= 0.6 && $negative <=0.6) {
        print "Hemimethylated (m6A)\tHemimethylated (A)\n";
    } elsif ($positive <= 0.6 && $negative >= 0.6) {
        print "Hemimethylated (A)\tHemimethylated (m6A)\n";
    } else {
        print "Missing_Site\tMissing_Site\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

稍微清理一下,你有一个while控制处理每一行的部分,以及一个处理控制值的部分的子程序。

use v5.28;
use feature qw(signatures);
no warnings qw(experimental::signatures);

while( <$infh> ) {
    chomp;
    my( $wt_call, $positive, $negative ) = (split /\t/)[0,14,29];
    if( $wt_call eq ... ) {
        print "No_WT_Concensus\tNo_WT_Concensus\n";
        next;
        }
    say get_value( $positive, $negative );
    }


sub get_value ( $x, $y, $pivot = 0.6 ){
    state @table = (
        [ qw(CBA FED) ],
        [ qw(DEF ABC) ]
        );
    $table[$x >= $pivot][$y >= $pivot];
    }
Run Code Online (Sandbox Code Playgroud)

请注意,else由于您已经涵盖了所有情况,因此条件无法达到,因此我省略了这一点。如果在另一种情况下您想查看空字段(null 与 0),您可以在get_value. 一种方法是查看字段的长度。如果为 0(无字符),则不计算。您可以有 0、1 或 2 个空字段,这些可能是不同的情况:

while( <$infh> ) {
    chomp;
    my( $wt_call, $positive, $negative ) = (split /\t/)[0,14,29];
    if( $wt_call eq ... ) {
        print "No_WT_Concensus\tNo_WT_Concensus\n";
        next;
        }
    # what if this is 1?
    unless( 2 == grep { length } ($positive, $negative) ) {
        print "No_WT_Concensus\tNo_WT_Concensus\n";
        next;
        }
    say get_value( $positive, $negative );
    }
Run Code Online (Sandbox Code Playgroud)