File Coverage

blib/lib/PDL/Reduce.pm
Criterion Covered Total %
statement 35 36 97.2
branch 17 22 77.2
condition 6 12 50.0
subroutine 5 6 83.3
pod 0 2 0.0
total 63 78 80.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::Reduce -- a C function for PDL
4              
5             =head1 DESCRIPTION
6              
7             Many languages have a C function used to reduce
8             the rank of an N-D array by one. It works by applying a selected
9             operation along a specified dimension. This module implements
10             such a function for PDL by providing a simplified interface
11             to the existing projection functions (e.g. C,
12             C, C, etc).
13              
14             =head1 SYNOPSIS
15              
16             use PDL::Reduce;
17             $x = sequence 5,5;
18             # reduce by adding all
19             # elements along 2nd dimension
20             $y = $x->reduce('add',1);
21             @ops = $x->canreduce; # return a list of all allowed operations
22              
23             =head1 FUNCTIONS
24              
25             =cut
26              
27             # in a very similar vein we want the following methods
28             # (1) accumulate
29             # (2) outer
30             # what's reduceat ??
31              
32             # TODO
33             # - aliases (e.g. plus -> add)
34             # - other binary ops?
35             # - allow general subs?
36              
37             package PDL::Reduce;
38 1     1   1629 use strict;
  1         1  
  1         30  
39 1     1   5 use warnings;
  1         5  
  1         75  
40 1     1   7 use PDL::Core ''; # barf
  1         1  
  1         6  
41 1     1   5 use PDL::Exporter;
  1         1  
  1         4  
42              
43             our @ISA = qw/PDL::Exporter/;
44             our @EXPORT_OK = qw/reduce canreduce/;
45             our %EXPORT_TAGS = (Func=>[@PDL::Reduce::EXPORT_OK]);
46              
47             # maps operations onto underlying PDL primitives
48             my %reduce = (
49             add => 'sumover',
50             '+' => 'sumover',
51             plus => 'sumover',
52             mult => 'prodover',
53             '*' => 'prodover',
54             dadd => 'dsumover',
55             dmult => 'dprodover',
56             avg => 'average',
57             davg => 'daverage',
58             and => 'andover',
59             band => 'bandover',
60             bor => 'borover',
61             or => 'orover',
62             median => 'medover',
63             integral => 'intover',
64             max => 'maximum',
65             min => 'minimum',
66             oddmedian => 'oddmedover',
67             iszero => 'zcover',
68             );
69              
70             =head2 reduce
71              
72             =for ref
73              
74             reduce dimension of ndarray by one by applying an operation
75             along the specified dimension
76              
77             =for example
78              
79             $x = sequence 5,5;
80             # reduce by adding all
81             # elements along 2nd dimension
82             $y = $x->reduce('add',1);
83             $y = $x->reduce('plus',1);
84             $y = $x->reduce('+',1); # three ways to do the same thing
85              
86             [ As an aside: if you are familiar with broadcasting you will see that
87             this is actually the same as
88              
89             $y = $x->mv(1,0)->sumover
90              
91             ]
92              
93             NOTE: You should quote the name of the operation (1st arg) that
94             you want C to perform. This is important since some of the
95             names are identical to the names of the actual PDL functions
96             which might be imported into your namespace. And you definitely
97             want a string as argument, not a function invocation! For example,
98             this will probably fail:
99              
100             $y = $x->reduce(avg,1); # gives an error from invocation of 'avg'
101              
102             Rather use
103              
104             $y = $x->reduce('avg',1);
105              
106             C provides a simple and unified interface to the
107             I functions and makes people coming from other
108             data/array languages hopefully feel more at home.
109              
110             =for usage
111              
112             $result = $pdl->reduce($operation [,@dims]);
113              
114             C applies the named operation along the specified
115             dimension(s) reducing the input ndarray dimension by as many
116             dimensions as supplied as arguments. If the
117             dimension(s) argument is omitted the operation is applied along the first
118             dimension. To get a list of valid operations see L.
119              
120             NOTE - new power user feature: you can now supply a code
121             reference as operation to reduce with.
122              
123             =for example
124              
125             # reduce by summing over dims 0 and 2
126             $result = $pdl->reduce(\&sumover, 0, 2);
127              
128             It is your responsibility to ensure that this is indeed a
129             PDL projection operation that turns vectors into scalars!
130             You have been warned.
131              
132             =cut
133              
134             *reduce = \&PDL::reduce;
135             sub PDL::reduce ($$;$) {
136 5     5 0 32 my ($pdl, $op, @dims) = @_;
137             barf "trying to reduce using unknown operation"
138 5 50 66     20 unless exists $reduce{$op} || ref $op eq 'CODE';
139 5         7 my $dim;
140 5 100       11 if (@dims > 1) {
141 1         4 my $n = $pdl->getndims;
142 1 50       3 @dims = map { $_ < 0 ? $_ + $n : $_ } @dims;
  2         7  
143 1         1 my $min = $n;
144 1         2 my $max = 0;
145 1 100       2 for (@dims) { $min = $_ if $_ < $min; $max = $_ if $_ > $max }
  2 100       6  
  2         12  
146 1 50 33     6 barf "dimension out of bounds (one of @dims >= $n)"
147             if $min >= $n || $max >= $n;
148 1         2 $dim = $min; # this will be the resulting dim of the clumped ndarray
149 1         3 $pdl = $pdl->clump(@dims);
150             } else {
151 4 100       10 $dim = @dims > 0 ? $dims[0] : 0;
152             }
153 5 100 66     19 if (defined $dim && $dim != 0) { # move the target dim to the front
154 2         7 my $n = $pdl->getndims;
155 2 50       6 $dim += $n if $dim < 0;
156 2 50 33     36 barf "dimension out of bounds" if $dim <0 || $dim >= $n;
157 2         20 $pdl = $pdl->mv($dim,0);
158             }
159 5 100       14 my $method = ref $op eq 'CODE' ? $op : $reduce{$op};
160 5         221 return $pdl->$method();
161             }
162              
163             =head2 canreduce
164              
165             =for ref
166              
167             return list of valid named C operations
168             Some common operations can be accessed using a
169             number of names, e.g. C<'+'>, C and C
170             all sum the elements along the chosen dimension.
171              
172             =for example
173              
174             @ops = PDL->canreduce;
175              
176             This list is useful if you want to make sure which
177             operations can be used with C.
178              
179             =cut
180              
181             *canreduce = \&PDL::canreduce;
182             sub PDL::canreduce {
183 0     0 0   sort keys %reduce;
184             }
185              
186             =head1 AUTHOR
187              
188             Copyright (C) 2000 Christian Soeller (c.soeller@auckland.ac.nz). All
189             rights reserved. There is no warranty. You are allowed to redistribute
190             this software / documentation under certain conditions. For details,
191             see the file COPYING in the PDL distribution. If this file is
192             separated from the PDL distribution, the copyright notice should be
193             included in the file.
194              
195             =cut
196              
197             1;