Arrays 计算和处理文本文件(Perl)中的出现次数

Arrays 计算和处理文本文件(Perl)中的出现次数,arrays,perl,list,hash,Arrays,Perl,List,Hash,我有一个选项卡分隔的文本文件,就像 1J L 0.5 1J P 0.4 1J K 0.2 1J L 0.3 1B K 0.7 1B L 0.2 1B P 0.3 1B L 0.6 1B L 0.3 我想通过操纵它来获得以下信息: 对于第一列中的每个元素,计算第二列中有多少重复的元素,并对第二列中的每个元素计算第三列中所有数字的平均值。所需的输出可以是另一个制表符分隔的文本文件,其中“Average”是第2列中该元素的平均数: 1st K# Average

我有一个选项卡分隔的文本文件,就像

1J  L  0.5
1J  P  0.4
1J  K  0.2
1J  L  0.3
1B  K  0.7
1B  L  0.2
1B  P  0.3
1B  L  0.6
1B  L  0.3
我想通过操纵它来获得以下信息:

对于第一列中的每个元素,计算第二列中有多少重复的元素,并对第二列中的每个元素计算第三列中所有数字的平均值。所需的输出可以是另一个制表符分隔的文本文件,其中“Average”是第2列中该元素的平均数:

1st  K#  Average  L#  Average  P# Average 
1J  1  0.2  2  0.4  1  0.4
1B  1  0.7  3  0.38  1  0.3
我应该如何进行?我曾考虑过对key=1st列的数组进行散列,但我认为这不会太有利

我还考虑创建多个名为
@L
@p
@K
的数组,以计算第一列中每个元素的出现次数;以及其他数组
@Ln
@Pn
@Kn
,它们将获取每个数组的所有数字。最后,每个数字的总和除以标量@L将得到平均数

但我在这些方面的主要问题是:如何对第一列的每个元素进行所有这些处理?

编辑:另一种可能性(我现在正在尝试)是创建第一列中所有唯一元素的数组。然后,
grep
ing并执行处理。但可能有更简单的方法

Edit2:第一列中的某些元素可能不存在第二列的某些元素-问题:被0除。例如:

1J  L  0.5
1J  P  0.4
1J  K  0.2
1J  L  0.3
1B  K  0.7
1B  L  0.2
1B  L  0.3  <- note that this is not P as in the example above.  
1B  L  0.6
1B  L  0.3
1jl0.5
1J P0.4
1J K 0.2
1J L 0.3
1B K 0.7
1B L 0.2
1B L 0.3未测试代码:

while (<>) {
    chomp;
    ($x, $y, $z) = split /\t/;
    push @{$f{$x}{$y}}, $z;   # E.g. $f{'1J'}{'L'}[1] will be 0.3
}

@cols = qw/L P K/;
foreach $x (sort keys %f) {
    print "$x\t";
    foreach $y (@cols) {
        $t = $n = 0;
        foreach $z (@{$f{$x}{$y}}) {
            $t += $z;
            ++$n;
        }
        $avg = $n ? $t / $n : 'N/A';
        print "$n\t$avg\t";
    }

    print "\n";
}
while(){
咀嚼;
($x,$y,$z)=拆分/\t/;
push@{$f{$x}{$y},$z;#例如,$f{'1J'}{'L'}[1]将为0.3
}
@cols=qw/L P K/;
foreach$x(排序键%f){
打印“$x\t”;
foreach$y(@cols){
$t=$n=0;
foreach$z(@{$f{$x}{$y}){
$t+=$z;
++$n;
}
$avg=$n?$t/$n:'n/A';
打印“$n\t$avg\t”;
}
打印“\n”;
}

对于计数和求和,我将使用散列,其中第一列是外部散列的键,第二列是内部散列的键。比如:

my (%count, %sum);
while(<>) {
    my @F = split / /, $_;

    $count{$F[0]}->{$F[1]}++;
    $sum{$F[0]}->{$F[1]} += $F[2];
}

for my $key (keys %count) {
    print $key;
    for my $subkey ("K", "L", "P") {
        my $average = defined($count{$key}->{$subkey}) ? $sum{$key}->{$subkey} / $count{$key}->{$subkey} : 0;
        ...; # and print the result
    }
    print "\n";
}
my(%count,%sum);
while(){
my@F=split/,$;
$count{$F[0]}->{$F[1]}++;
$sum{$F[0]}->{$F[1]}+=$F[2];
}
对于我的$key(key%计数){
打印$key;
对于我的$subkey(“K”、“L”、“P”){
my$average=已定义($count{$key}->{$subkey})?$sum{$key}->{$subkey}/$count{$key}->{$subkey}:0;
然后打印结果
}
打印“\n”;
}
这里有一条路要走:

my $result;
while(<DATA>){
    chomp;
    my @data = split;
    $result->{$data[0]}{$data[1]}{sum} += $data[2];
    $result->{$data[0]}{$data[1]}{nbr}++;
}
say "1st\tK#\tavg\tL#\tavg\tP#\tavg";
foreach my $k(keys %$result) {
    print "$k\t";
    for my $c (qw(K L P)) {
        if (exists($result->{$k}{$c}{nbr}) && $result->{$k}{$c}{nbr} != 0) {
            printf("%d\t%.2f\t",$result->{$k}{$c}{nbr},$result->{$k}{$c}{sum}/$result->{$k}{$c}{nbr});
        } else {
            printf("%d\t%.2f\t",0,0);
        }
    }
    print "\n";
}

__DATA__
1J  L  0.5
1J  P  0.4
1J  K  0.2
1J  L  0.3
1B  K  0.7
1B  L  0.2
1B  P  0.3
1B  L  0.6
1B  L  0.3

我很抱歉我这样做了-真的-但这里有一个“一行”(ahem),我将尝试将其翻译成一个真实的脚本并解释-作为我自己的练习:-)我希望这个公认的一行解决方案的人工示例为其他人提交的更清晰的书面和脚本示例添加一些内容

perl -anE '$seen{$F[0]}->{$F[1]}++; $sum{$F[0]}->{$F[1]} += $F[2];}{ 
for(keys %seen){say " $_:"; for $F1(sort keys $seen{$_}) { 
say "$F1","s: $seen{$_}->{$F1} avg:",$sum{$_}->{$F1}/$seen{$_}->{$F1}}}' data.txt
有关Perl的开关的更详细说明,请参阅。本质上,
perl-anE
在“autosplit”模式(
-a
)下启动perl,并创建
while
循环来读取
'
引号之间执行的代码的输入(
-n
)。
-E
打开所有最新的铃铛和哨子执行(通常使用
-E
)。下面是我试图解释它的作用

首先,在
while
循环中,这(有点像“oneliner”:

  • 使用空格作为定界符,将输入自动拆分到数组中(
    @F
    …我猜“字段”是awkish)
  • 使用该技巧计算部分数组中匹配行的出现次数。在这里,每当它在
    @F
    的第二列(
    $F[1]
    )中看到重复的一行时,它就会增加从
    @F
    的第1列(
    $F[0]
    )创建的
    %seen
    散列键的值
  • 使用哈希
    %sum
    %total
    使用
    =+
    运算符添加第三列(
    $F[2]
    )中的值。另一个例子见
然后,它在使用“
}”创建的
-n
循环中中断
{
类似于
结束
块,允许嵌套的
for
循环吐出所有内容。我使用
$F1
作为内部
for
循环的子键来提醒自己,我是从autosplit数组
@F
的第二列获取它的

输出(我们需要
printf
以获得更好的数值结果)

这使数字看起来更漂亮(使用
printf
格式化)

脚本版本。它增加从数据第二列(
$field[1]
)中提取的重复键的值;在第二个散列中,它对从第三列(
$field[2]
)中提取的键值求和.我想用一种更实用的风格或完全适合这份工作的CPAN模块给你留下深刻印象,但我不得不
$work
。干杯,一定要问更多的Perl问题

#!/usr/bin/env perl

use strict;
use warnings;

my %seen ;
my %sum ;

while(<DATA>){
   my @fields = split ;
   $seen{$fields[0]}{$fields[1]}++ ;     
   $sum{$fields[0]}{$fields[1]} += $fields[2];  
}

for(keys %seen) {
    print "  $_:\n";
    for my $f(sort keys $seen{$_}) {
    printf("%ss %d avg: %.2f\n", 
       $f, $seen{$_}->{$f}, $sum{$_}->{$f}/$seen{$_}->{$f} );
  }
}

__DATA__
1J L 0.5
1J P 0.4
1J K 0.2
1J L 0.3
1B K 0.7
1B L 0.2
1B L 0.3
1B L 0.6
1B L 0.3
!/usr/bin/env perl
严格使用;
使用警告;
我看到的百分比;
我的%sum;
while(){
我的@fields=split;
$seen{$fields[0]}{$fields[1]}++;
$sum{$fields[0]}{$fields[1]}+=$fields[2];
}
对于(已看到的键百分比){
打印“$\:\ n”;
对于我的$f(排序键$seen{$\u0}){
printf(“%ss%d平均值:%.2f\n”,
$f,$seen{$}->{$f},$sum{$}->{$f}/$seen{$}->{$f});
}
}
__资料__
1J L 0.5
1J P0.4
1J K 0.2
1J L 0.3
1B K 0.7
1B L 0.2
1B L 0.3
1B L 0.6
1B L 0.3

例如,如果1B没有
P
,而1J有,你会怎么做?我在你答案的末尾写下了我的编辑,如果我的编辑2验证了这一点,我会怎么做。如果你不同意,请随意将其完全删除!谢谢如果第二列的某个元素没有出现在第一列中,会发生什么?(请参阅e
 1B:
Ks: 1 avg:0.7
Ls: 3 avg:0.366666666666667
Ps: 1 avg:0.3
 1J:
Ks: 1 avg:0.2
Ls: 2 avg:0.4
Ps: 1 avg:0.4
perl -anE '$seen{$F[0]}->{$F[1]}++; $sum{$F[0]}->{$F[1]} += $F[2];}{ 
for(keys %seen){say " $_:"; for $F1(sort keys $seen{$_}) {  
printf("%ss %d avg: %.2f\n", $F1, $seen{$_}->{$F1}, $sum{$_}->{$F1}/$seen{$_}->{$F1})}}' data.txt
#!/usr/bin/env perl

use strict;
use warnings;

my %seen ;
my %sum ;

while(<DATA>){
   my @fields = split ;
   $seen{$fields[0]}{$fields[1]}++ ;     
   $sum{$fields[0]}{$fields[1]} += $fields[2];  
}

for(keys %seen) {
    print "  $_:\n";
    for my $f(sort keys $seen{$_}) {
    printf("%ss %d avg: %.2f\n", 
       $f, $seen{$_}->{$f}, $sum{$_}->{$f}/$seen{$_}->{$f} );
  }
}

__DATA__
1J L 0.5
1J P 0.4
1J K 0.2
1J L 0.3
1B K 0.7
1B L 0.2
1B L 0.3
1B L 0.6
1B L 0.3