【问题标题】:Perl Multidimensional array column compare and display whole content with satisfied conditionPerl多维数组列比较并显示满足条件的全部内容
【发布时间】:2017-08-24 09:34:22
【问题描述】:

我在获取数组索引进行比较和显示结果方面几乎没有问题。我有一个制表符分隔文件,有 9 列和 100 多行。我想将第 i 行的第 8 列元素与第 i+1 行的第 7 列元素进行比较。如果它小于第 7 列元素,则打印整行,如果大于第 7 列元素,则比较两行的第 6 个元素,如果第 6 个元素更大,则仅打印该行。

示例文件

Recep_L_domain  PF01030.22      112     sp|P00533|EGFR_HUMAN    2.50E-30        104.7   57      167     Receptor
Furin-like      PF00757.18      149     sp|P00533|EGFR_HUMAN    4.10E-29        101.3   185     338     Furin-like
Recep_L_domain  PF01030.22      112     sp|P00533|EGFR_HUMAN    3.60E-28        97.8    361     480     Receptor
GF_recep_IV     PF14843.4       132     sp|P00533|EGFR_HUMAN    1.60E-46        157.2   505     636     Growth
Pkinase PF00069.23      264     sp|P00533|EGFR_HUMAN    2.70E-39        135     712     964     Protein
Pkinase_Tyr     PF07714.15      260     sp|P00533|EGFR_HUMAN    8.40E-88        293.9   714     965     Protein

例如,如果我们比较最后两行,那么第 8 列元素大于下一行的第 7 列元素,那么在这种情况下,它应该比较两个第 6 列元素并打印唯一更大的行。所以从这两行它应该只打印最后一行。对我来说,下面的代码只打印较小的值,但我想问如果第 8 列较大,如何比较第 6 个元素并打印结果?

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

open(IN,"<samplecode.txt");

my @Alifrom;
my @Alito;
my @data; ## multidimensional array

while(<IN>){
    chomp $_;
    #next if $_=undef;
    my @line = split("\t", $_);
    ##my($a, $b, $c, $d, $e, $f, $g, $h, $i) = split(/\t/,$_); // catch data and storing into multiple scalar variable

    push @data, [@line];
}

for (my $i = 0; $i < @data ; $i++){

    if ($data[$i][7] gt $data[$i][6]){

        for (my $j = 0; $j < @{$data[$i]}; $j++){
            #@Alifrom = map $data[$i][$j+6], @data;
            print "$data[$i][$j]\t";
        }
    }
    #else
    print "\n";
}

【问题讨论】:

  • 有什么问题?它是否给出了错误的输出?它对我来说运行没有错误。
  • 你好像忘了问一个问题。你有什么问题?
  • 是的,它编译但尚未实现第 6 个元素比较逻辑,它现在正在打印所有行,而不是我需要的。任何指导都应该有所帮助。
  • 我的问题和信息清楚吗?

标签: arrays perl multidimensional-array


【解决方案1】:

您的问题中的描述并不完全清楚,但我在进行有根据的猜测。

首先,您不应该将整个文件读入数组。如果你的文件真的只有 100 行,那不是问题,但如果有更多行,这将消耗大量内存。

您说您想比较每一行 i 中的值与行 i+1 中的值,所以基本上在每一行中您都想查看下一行中的值排。这意味着您一次最多需要在内存中保留两行。由于这是线性的,您可以只读取第一行,然后读取第二行,进行比较,完成后将第二行作为新的第一行。

在您的循环中,您总是读取第二行,并在之前的迭代中将第一行作为第二行读取时保持在第一行附近。

为此,将读取和拆分变成一个函数是有意义的。您可以将文件句柄传递给它。在上面的示例中,我使用了 DATA__DATA__ 部分,但您可以只使用 open my $fh, '&lt;', 'samplecode.txt' 并传递 $fh

因为在某些情况下您想打印整行,所以您不应该只是以破坏性的方式使用chompsplit,而是保留包括换行符在内的实际整行。因此,我们使读取和拆分函数返回两个值:作为标量字符串的整行,以及清理列的数组引用。

如果没有更多行要读取,我们返回一个隐含的undef,这将使while 循环停止。因此,您永远无法处理文件的最后一行。

比较时,注意 Perl 中的列表索引总是从零开始,所以第 7 列是索引[6]

这是一个示例实现。

use strict;
use warnings;

# this function reads a line from the filehandle that's passed in and returns
# the row as a string and an array ref of all columns, or undef if there are
# no more lines to read
sub read_and_split {
    my $fh = shift;

    # read one line and return undef if there is no more data
    my $row = <$fh>;
    return unless defined $row;

    # split into columns
    my @cols = split /\s+/, $row;    # Stack Overflow does not like tabs, use \t

    # only chomp after splitting so we retain the original line for printing
    chomp $cols[-1];

    # return both things
    return $row, \@cols;
}

# read the first line
my ( $row_i, $cols_i ) = read_and_split( \*DATA );

# read subsequent lines
while ( my ( $row_i_plus_one, $cols_i_plus_one ) = read_and_split( \*DATA ) ) {

    # 7th col of i is smaller than 6th col of i+1
    if ( $cols_i->[7] < $cols_i_plus_one->[6] ) {
        print $row_i;
    }
    else {
        # compare the 6th element of both row and only print
        # if the row if the 6th element is bigger
        if ( $cols_i->[5] > $cols_i_plus_one->[5] ) {
            print $row_i;
        }
    }

    # turn the current i+1 into i for the next iteration
    $row_i  = $row_i_plus_one;
    $cols_i = $cols_i_plus_one;
}

__DATA__
Recep_L_domain  PF01030.22      112     sp|P00533|EGFR_HUMAN    2.50E-30        104.7   57      167     Receptor
Furin-like      PF00757.18      149     sp|P00533|EGFR_HUMAN    4.10E-29        101.3   185     338     Furin-like
Recep_L_domain  PF01030.22      112     sp|P00533|EGFR_HUMAN    3.60E-28        97.8    361     480     Receptor
GF_recep_IV     PF14843.4       132     sp|P00533|EGFR_HUMAN    1.60E-46        157.2   505     636     Growth
Pkinase PF00069.23      264     sp|P00533|EGFR_HUMAN    2.70E-39        135     712     964     Protein
Pkinase_Tyr     PF07714.15      260     sp|P00533|EGFR_HUMAN    8.40E-88        293.9   714     965     Protein

它输出这些行:

Recep_L_domain  PF01030.22      112     sp|P00533|EGFR_HUMAN    2.50E-30        104.7   57      167     Receptor
Furin-like      PF00757.18      149     sp|P00533|EGFR_HUMAN    4.10E-29        101.3   185     338     Furin-like
Recep_L_domain  PF01030.22      112     sp|P00533|EGFR_HUMAN    3.60E-28        97.8    361     480     Receptor
GF_recep_IV     PF14843.4       132     sp|P00533|EGFR_HUMAN    1.60E-46        157.2   505     636     Growth

请注意,您的问题中关于比较第 6 列的部分不是很清楚。我假设我们比较了第六列,如果匹配,则打印第 i 行。如果我们要打印第 i+1 行,我们最终可能会打印该行两次。

【讨论】:

  • 非常感谢,是的,您在这里提出的想法很有帮助。现在比较第 6 行是,如果我们比较最后两行,那么第 8 列元素(964)大于下一行的第 7 列元素(714),那么在这种情况下,它应该比较两个第 6 列元素(135 和293.9) 并打印此处唯一较大的行,即具有 293.9 值的行。所以从这两行它应该只打印最后一行。我希望我在这里不太清楚
  • @Kanhu 应该很容易改变。我会把它留给你。
  • 是的,当然。我只是想在这里明确我的观点.. :-)
  • @Kanhu 如果我弄错了,您也可以提出对答案的编辑,只要您保持代码的质量并更改文本以匹配。 :) 您可能还想再次编辑问题以修正误导我的拼写错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-17
  • 2019-06-12
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 1970-01-01
  • 2021-12-28
相关资源
最近更新 更多