简体   繁体   English

如何在Perl中按元素求和多个数组?

[英]How to sum multiple arrays element-wise in Perl?

There is a question very similar to this already but I would like to do this for multiple arrays. 已经有一个与此非常相似的问题,但是我想针对多个阵列执行此操作。 I have an array of arrays. 我有一个数组数组。

my @AoA = (
    $arr1 = [ 1, 0, 0, 0, 1 ],
    $arr2 = [ 1, 1, 0, 1, 1 ],
    $arr3 = [ 2, 0, 2, 1, 0 ]
);

I want to sum the items of all the three (or more) arrays to get a new one like 我想对所有三个(或更多)数组的项求和以得到一个新的数组

( 4, 1, 2, 2, 2 )

The use List::MoreUtils qw/pairwise/ requires two array arguments. use List::MoreUtils qw/pairwise/需要两个数组参数。

@new_array = pairwise { $a + $b } @$arr1, @$arr2;

One solution that comes to mind is to loop through @AoA and pass the first two arrays into the pairwise function. 想到的一种解决方案是通过循环@AoA第一两个数组传递到pairwise函数。 In the subsequent iterations, I will pass the next @$arr in @AoA and the @new_array into the pairwise function. 在随后的迭代中,我将@AoA的下一个@$arr@new_array @AoA@new_array函数。 In the case of an odd sized array of arrays, after I've passed in the last @$arr in @AoA , I will pass in an equal sized array with elements of 0's. 在数组大小为奇数的情况下,在传入@AoA中的最后一个@$arr @AoA ,我将传入元素为0的大小相等的数组。

Is this a good approach? 这是一个好方法吗? And if so, how do I implement this? 如果是这样,我该如何实现呢? thanks 谢谢

You can easily implement a “n-wise” function: 您可以轻松实现“ n-wise”功能:

sub nwise (&@) # ← take a code block, and any number of further arguments
{
  my ($code, @arefs) = @_;
  return map {$code->( do{ my $i = $_; map $arefs[$_][$i], 0 .. $#arefs } )}
             0 .. $#{$arefs[0]};
}

That code is a bit ugly because Perl does not support slices of multidimensional arrays. 该代码有点丑陋,因为Perl不支持多维数组的切片。 Instead I use nested map s. 相反,我使用嵌套的map

A quick test: 快速测试:

use Test::More;
my @a = (1, 0, 0, 0, 1);
my @b = (1, 1, 0, 1, 1);
my @c = (2, 0, 2, 1, 0);
is_deeply [ nwise { $_[0] + $_[1] + $_[2] } \@a, \@b, \@c], [4, 1, 2, 2, 2];

I prefer passing the arrays as references instead of using the \\@ or + prototype: This allows you to do 我更喜欢将数组作为引用传递,而不是使用\\@+原型:这允许您执行

my @arrays = (\@a, \@b, \@c);
nwise {...} @arrays;

From List::MoreUtils you could have also used each_arrayref : List::MoreUtils您还可以使用each_arrayref

use List::Util qw/sum/;
use List::MoreUtils qw/each_arrayref/;
my $iter = each_arrayref @arrays;
my @out;
while (my @vals = $iter->()) {
  push @out, sum @vals;
}
is_deeply \@out, [4, 1, 2, 2, 2];

Or just plain old loops: 或者只是普通的旧循环:

my @out;
for my $i (0 .. $#a) {
  my $accumulator = 0;
  for my $array (@arrays) {
    $accumulator += $array->[$i];
  }
  push @out, $accumulator;
}
is_deeply \@out, [4, 1, 2, 2, 2];

The above all assumed that all arrays were of the same length. 以上全部假设所有数组的长度相同。


A note on your snippet: 片段中的注释:

Your example of the array structure is of course legal perl, which will even run as intended, but it would be best to leave out the inner assignments: 数组结构的示例当然是Legal perl,它甚至可以按预期运行,但是最好省略内部分配:

my @AoA = (
    [ 1, 0, 0, 0, 1 ],
    [ 1, 1, 0, 1, 1 ],
    [ 2, 0, 2, 1, 0 ],
);

You might actually be looking for PDL , the Perl Data Language. 您可能实际上正在寻找PDL (Perl数据语言)。 It is a numerical array module for Perl. 它是Perl的数值数组模块。 It has many functions for processing arrays of data. 它具有处理数据数组的许多功能。 Unlike other numerical array modules for other languages it has this handy ability to use its functionality on arbitrary dimensions and it will do what you mean. 与其他语言的其他数值数组模块不同,它具有在任意维度上使用其功能的便捷功能,它将按您的意思进行操作。 Note that this is all done at the C level, so it is efficient and fast! 请注意,这些都是在C级别完成的,因此它高效而快捷!

In your case you are looking for the projection method sumover which will take an N dimensional object and return an N-1 dimensional object created by summing over the first dimension. 在您的情况下,您正在寻找投影方法sumover ,该sumover将采用N维对象并返回通过对第一维求和而创建的N-1维对象。 Since in your system you want to sum over the second we first have to transpose by exchanging dimensions 0 and 1. 由于在您的系统中要对第二个进行求和,我们首先必须通过交换尺寸0和1进行转置。

#!/usr/bin/env perl

use strict;
use warnings;

use PDL;

my @AoA = (
    [ 1, 0, 0, 0, 1 ],
    [ 1, 1, 0, 1, 1 ],
    [ 2, 0, 2, 1, 0 ],
);

my $pdl = pdl \@AoA;

my $sum = $pdl->xchg(0,1)->sumover;
print $sum . "\n"; 
# [4 1 2 2 2]

The return from sumover is another PDL object, if you need a Perl list you can use list sumover的返回是另一个PDL对象,如果您需要Perl列表,则可以使用list

print "$_\n" for $sum->list;

Here's a simple iterative approach. 这是一个简单的迭代方法。 It probably will perform terribly for large data sets. 对于大型数据集,它可能会执行得非常糟糕。 If you want a better performing solution you will probably need to change the data structure, or look on CPAN for one of the statistical packages. 如果您想要性能更好的解决方案,则可能需要更改数据结构,或在CPAN上查找其中一个统计数据包。 The below assumes that all arrays are the same size as the first array. 下面假设所有数组的大小与第一个数组相同。

$sum = 0;
@rv = ();
for ($y=0; $y < scalar @{$AoA[0]}; $y++) {
    for ($x=0; $x < scalar @AoA; $x++) {
        $sum += ${$AoA[$x]}[$y];
    }
    push @rv, $sum;
    $sum = 0;
}

print '('.join(',',@rv).")\n";

Assumptions: 假设:

  • each row in your AoA will have the same number of columns as the first row. AoA中的每一行将具有与第一行相同的列数。
  • each value in the arrayrefs will be a number (specifically, a value in a format that "works" with the += operator) arrayrefs中的每个值都将是一个数字(具体来说,是使用+ =运算符“有效”的格式的值)
  • there will be at least one "row" with sat least one "column" 至少有一个“行”和至少一个“列”

Note: "$#{$AoA[0]}" means, "the index of the last element ($#) of the array that is the first arrayref in @AoA ({$AoA[0]})" 注意:“ $#{$ AoA [0]}”的意思是,“数组的最后一个元素($#)的索引,它是@AoA({$ AoA [0]})中的第一个arrayref”

(shebang)/usr/bin/perl
use strict;
use warnings;

my @AoA = (
    [ 1, 0, 0, 0, 1 ],
    [ 1, 1, 0, 1, 1 ],
    [ 2, 0, 2, 1, 0 ]
);

my @sums;

foreach my $column (0..$#{$AoA[0]}) {
  my $sum;
  foreach my $aref (@AoA){
    $sum += $aref->[$column];
  }
  push @sums,$sum;
}

use Data::Dumper;
print Dumper \@sums;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM