使用 Perl 遍历文件并计算每列的平均值

Use Perl to loop over files and calculate the mean of each column

我是 perl 的新手,我想了解如何使用循环。我有多个目录,每个目录都包含一个名为 data.txt 的文件。 data.txt 文件有几列。我基本上需要使用循环来计算每个 data.txt 文件的每列的平均值。

我有这个命令可以完成一个文件的工作:

perl -lane 'for $c (0..$#F){$t[$c] += $F[$c]}; END{for $c (0..$#t){print $t[$c]/$.}}' data.txt`

我想编写一个脚本来访问每个目录,读取其中的每个文件并应用命令。

示例:

data.txt:

-79.2335  0.4041    71.9143  1.3392    -0.7687  0.0212    -8.0934  1.1425 
-74.4163  0.6188    60.0468  1.8782    -0.8540  0.0305    -15.1574  1.4755 
-74.4118  0.6046    62.1771  1.8058    -0.9143  0.0304    -13.2272  1.3408 
-74.3895  0.5935    66.4264  1.6532    -0.8509  0.0223    -8.8819  1.2670 
-74.3192  0.5589    67.1619  1.4763    -0.9656  0.0274    -8.1090  1.1450 
-73.8272  0.6274    61.6632  1.7554    -0.8840  0.0256    -13.0435  1.3641 
-73.3525  0.5856    60.6622  1.7872    -0.8489  0.0222    -13.5014  1.3947 
-73.3206  0.6275    53.3129  2.2961    -0.7962  0.0337    -20.8195  1.8538 
-72.5461  0.5212    62.0359  1.4267    -0.9378  0.0240    -11.4203  1.0295 
-72.3058  0.7225    56.2304  2.1480    -0.7539  0.0293    -16.7954  1.5952 
-72.1180  0.6460    51.7954  2.0845    -0.8479  0.0265    -21.0355  1.4630 
-72.0690  0.4905    58.8372  1.3918    -0.9866  0.0333    -14.1823  1.1045 
-71.7949  0.5799    55.6006  1.9189    -0.8541  0.0313    -17.0112  1.4530 
-71.3074  0.4482    45.9271  2.1135    -0.6637  0.0354    -25.9309  1.8761 
-71.2542  0.4879    57.3196  1.5406    -0.9523  0.0281    -14.9113  1.2705 
-71.2421  0.5480    47.9065  2.2445    -0.8107  0.0352    -24.2489  1.7997 
-70.3751  0.5278    49.5489  1.8395    -0.8208  0.0371    -21.5205  1.4994 
-69.2181  0.4823    54.8234  1.0645    -0.9897  0.0246    -15.3506  0.9369 
-69.0456  0.4650    40.3798  2.0117    -0.6476  0.0360    -29.3403  1.7013 
-66.5402  0.5006    42.1805  1.7872    -0.7692  0.0356    -25.1431  1.4522

输出:

-72.354355   0.552015   56.297505   1.77814   -0.845845   0.029485   -16.88618   1.408235

我不是 Perl 专家,但这对我有用。它将结果打印到终端。或者,您可以根据需要将其重定向到文件,或者直接写入文件而不是打印到终端。

use 5.28.2;
use warnings;
use File::Find;

my ($inf, @sum);
for my $dir (glob "/mainDirectory/*"){ # finds files/subdirectories
    if (! -d $dir) {
        next;        # keeps only directories
    }
    $inf= "$dir/data.txt";
    say "$inf";
    find(\&sum_columns, $inf);
}
  
sub sum_columns{
    open (IN,  "<", "$inf" )   or die "Cannot open file.\n $!";
    while (<IN>){
        my $line = $_;
        chomp $line;
        my @columns = split(/\s+/,$line);    
        for my $item (0 .. $#columns){
            $sum[$item] += $columns[$item];
            }
    }
    say "@sum";
    @sum=();
}

由于您的评论暗示您有一个简单的目录结构,其中有一个名为 mean 的主目录和 100 个子目录,每个子目录都有一个名为 data.txt 的文件,可以轻松编译文件列表有了 glob,数学就相当简单了。这是如何完成的建议。

我不会使用 $. 作为计算平均值的方法,因为它可能会被其他因素破坏。但是只需为每个文件使用一个计数变量,并计算 non-blank 行。

use strict;
use warnings;
use feature 'say';

for my $data (glob "mean/*/data.txt") {    # get list of files
    open my $fh, '<', $data or die "Cannot open file '$data': $!";
    my @sum;
    my $count = 0;
    while (<$fh>) {
        $count++ if /\S/;                  # count non-blank lines
        my @fields = split;                # split on whitespace
        for (0 .. $#fields) {
            $sum[$_] += $fields[$_];       # sum columns
        }
    }
    say $data;                             # file name
    say join "\t",            # 3. ...join them with tab and print
        map $_/$count,        # 2. ...for each sum, divide by count
        @sum;                 # 1. Take list of sums...
}

输出:

mean/A/data.txt
-72.354355      0.552015        56.297505       1.77814 -0.845845       0.029485        -16.88618       1.408235
mean/B/data.txt
-142.354355     0.552015        56.297505       1.77814 -0.845845       0.029485        -16.88618       1.408235
mean/C/data.txt
-72.354355      17.152015       56.297505       1.77814 -0.845845       0.029485        -16.88618       1.408235