File Coverage

blib/lib/Statistics/RVector.pm
Criterion Covered Total %
statement 15 137 10.9
branch 0 26 0.0
condition 0 9 0.0
subroutine 5 28 17.8
pod 22 23 95.6
total 42 223 18.8


line stmt bran cond sub pod time code
1             package Statistics::RVector;
2              
3 1     1   43670 use strict;
  1         3  
  1         120  
4 1     1   7 use warnings;
  1         2  
  1         29  
5 1     1   6 use Carp ();
  1         5  
  1         43  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11 1     1   1210 use Math::Complex;
  1         20086  
  1         387  
12              
13             our $VERSION = 0.1;
14             our @EXPORT = qw(rv);
15             our @EXPORT_OK = qw(rv);
16             our %EXPORT_TAGS;
17             $EXPORT_TAGS{'all'} = [@EXPORT_OK];
18              
19             use overload
20 1         9 '@{}' => 'as_array',
21             '""' => 'to_string',
22             '+' => 'vectoradd',
23             '*' => 'vectormult',
24             '-' => 'vectorsub',
25             '/' => 'vectordiv',
26 1     1   14 '**' => 'vectorpower';
  1         2  
27              
28             =head1 NAME
29              
30             Statistics::RVector - Mathematical/statistical vector implementation mimicking that of R stats
31              
32             =head1 DESCRIPTION
33              
34             The RVector class is a perl implementation of the base R stats language mathematical
35             vector to enable better statistical/numerical/mathematical analysis of data in Perl.
36             This implementation is still very beta, but should work sufficiently for vector
37             arithmetic (+,-,*,/,**), as well as calculating sum(), mean(), var(), and sd(), which
38             are the sum of the values, the mean of the values, the variance of the sample, and
39             the standard deviation of the sample..
40              
41             =head1 SYNOPSIS
42              
43             use Statistics::RVector;
44             my $x = rv(10.4, 5.6, 3.1, 6.4, 21.7);
45             my $y = Statistics::RVector->new($vector1,0,$vector1);
46             my $v = 2 * $x + $y + 1
47              
48             =head1 EXPORT
49              
50             Exports the function rv() by default, which is a shortened form of
51             Statistics::RVector->new().
52              
53             =head1 TODO
54              
55             * Handle non-numerical and/or invalid (i.e. division by zero) cases in all
56             functions.
57              
58             * Add support for naming of entries in the vector.
59              
60             * Lots of other things that I still don't understand about R that I'm sure other people
61             will want to use.
62              
63             =head1 METHODS
64              
65             =head2 VECTOR CREATION
66              
67             =head3 Statistics::RVector->new() / rv()
68              
69             Creates a new RVector object. For example, syntax of rv(10,17,24) would create a
70             vector containing 3 entries of 10, 17, and 24 in that order.
71              
72             =cut
73              
74             sub new {
75 0     0 1   my ($class, @entries) = @_;
76 0           my $vector = {
77             vals => [],
78             names => [],
79             namemap => {},
80             };
81 0           bless $vector, $class;
82 0           foreach my $entry (@entries) {
83 0           $vector->add_value($entry);
84             }
85 0           return $vector;
86             }
87              
88             sub rv {
89 0     0 1   my (@entries) = @_;
90 0           return Statistics::RVector->new(@entries);
91             }
92              
93             =head3 $vector->add_value($val,[$name])
94              
95             Adds a new entry to the vector in question, including an (optional) name to be added
96             to the name table to fetch the value
97              
98             =cut
99              
100             sub add_value {
101 0     0 1   my ($self, $value, $name) = @_;
102 0 0 0       if (defined $name && $self->{namemap}->{$name}) {
103             # Already exists... do we warn and rename, or bail?
104             # Fix name?
105             }
106 0 0 0       if (ref($value) && ref($value) eq ref($self)) {
107             # Another vector to concatenate. Recursively call on all values.
108 0           for (my $i = 0; $i < $value->length(); $i++) {
109 0           $self->add_value($value->[$i],$value->name($i));
110             }
111             } else {
112 0           push(@{$self->{vals}},$value);
  0            
113 0 0         push(@{$self->{names}},defined $name ? $name : undef);
  0            
114 0 0         $self->{namemap}->{$name} = scalar(@{$self->{vals}}) - 1 if $name;
  0            
115             }
116             }
117              
118             =head3 $vector->name($index)
119              
120             Returns the name of a given index in the vector, if given.
121              
122             =cut
123              
124             sub name {
125 0     0 1   my ($self, $i) = @_;
126 0           return $self->{names}->[$i];
127             }
128              
129             =head2 VECTOR MODIFICATION/DUPLICATION OPERATIONS
130              
131             Below are functions which allow for modification and/or duplication of a vector.
132             These operations will result in either a modification to the existing vector, or
133             the return of a new vector altogether.
134              
135             =head3 $vector->clone()
136              
137             Returns an exact copy of the original vector in different memory.
138             Allows for modification without affecting the original vector.
139              
140             =cut
141              
142             sub clone {
143 0     0 1   my ($self) = @_;
144 0           my $return = rv();
145 0           for (my $i = 0; $i < $self->length(); $i++) {
146 0           $return->add_value($self->[$i],$self->name($i));
147             }
148 0           return $return;
149             }
150              
151             =head3 $vector->extend($len)
152              
153             Extends the length of the given vector to length $len, filling all new values with
154             repeated values from the existing array in the same offsets.
155              
156             For example, an rv(1,2,3) that is extended to 8 will then be rv(1,2,3,1,2,3,1,2).
157              
158             =cut
159              
160             sub extend {
161 0     0 1   my ($self, $newlen) = @_;
162 0           my $prevsize = scalar(@{$self->{vals}});
  0            
163 0           my $nextspot = 0;
164 0           for (my $i = $prevsize; $i < $newlen; $i++) {
165 0           $self->add_value($self->{vals}->[$nextspot]);
166 0           $nextspot++;
167             }
168 0           return 1;
169             }
170              
171             =head2 VECTOR INSPECTION OPERATIONS
172              
173             Below are the various operations that you can perform on a single vector object.
174             They will return in most cases numerical values unless otherwise specified. They
175             do not in any way change the vector, nor create any new vectors.
176              
177             =cut
178              
179             =head3 $vector->length()
180              
181             Returns the integer length of the vector.
182              
183             =cut
184              
185             sub length {
186 0     0 1   my ($self) = @_;
187 0           return scalar(@{$self->{vals}});
  0            
188             }
189              
190             =head3 $vector->range()
191              
192             Returns a vector holding the largest and smallest values in the specified vector.
193              
194             =cut
195              
196             sub range {
197 0     0 1   my ($self) = @_;
198 0 0         return rv(undef,undef) unless $self->length();
199 0           my ($min,$max) = ($self->[0],$self->[0]);
200 0           for (my $i = 1; $i < $self->length(); $i++) {
201 0 0         $min = $self->[$i] if $self->[$i] < $min;
202 0 0         $max = $self->[$i] if $self->[$i] > $max;
203             }
204 0           return rv($min,$max);
205             }
206              
207             =head3 $vector->max()
208              
209             Returns the maximum value in the vector.
210              
211             =cut
212              
213             sub max {
214 0     0 1   my ($self) = @_;
215 0           my $range = $self->range();
216 0           return $range->[1];
217             }
218              
219             =head3 $vector->min()
220              
221             Returns the minimum value in the vector.
222              
223             =cut
224              
225             sub min {
226 0     0 1   my ($self) = @_;
227 0           my $range = $self->range();
228 0           return $range->[0];
229             }
230              
231             =head3 $vector->sum()
232              
233             Returns the arithmetic sum of all entries in the vector.
234              
235             =cut
236              
237             sub sum {
238 0     0 1   my ($self) = @_;
239 0           my $ret = 0;
240 0           for (my $i = 0; $i < $self->length(); $i++) {
241 0           $ret += $self->[$i];
242             }
243 0           return $ret;
244             }
245              
246             =head3 $vector->prod()
247              
248             Returns the arithmetic product of all the entries in the vector.
249              
250             =cut
251              
252             sub prod {
253 0     0 1   my ($self) = @_;
254 0 0         return unless $self->length();
255 0           my $ret = $self->[0];
256 0           for (my $i = 1; $i < $self->length(); $i++) {
257 0           $ret *= $self->[$i];
258             }
259 0           return $ret;
260             }
261              
262             =head3 $vector->mean()
263              
264             Returns the arithmetic mean of the vector values.
265              
266             =cut
267              
268             sub mean {
269 0     0 1   my ($self) = @_;
270 0 0         return unless $self->length();
271 0           return $self->sum() / $self->length();
272             }
273              
274             =head3 $vector->var()
275              
276             Returns the sample variance of the vector values.
277              
278             =cut
279              
280             sub var {
281 0     0 1   my ($self) = @_;
282 0           my $top = ($self - $self->mean()) ** 2;
283 0           return $top->mean();
284             }
285              
286             =head3 $vector->sd()
287              
288             Returns the sample standard deviation of the vector values.
289              
290             =cut
291              
292             sub sd {
293 0     0 1   my ($self) = @_;
294 0           return sqrt($self->var());
295             }
296              
297             =head2 DEREFERENCING/ARITHMETIC OVERLOADS
298              
299             =head3 as_array($vector)
300              
301             Returns an array reference to the values in the vector.
302              
303             =cut
304              
305             sub as_array {
306 0     0 1   my ($self) = @_;
307 0           return $self->{vals};
308             }
309              
310             =head3 to_string($vector)
311              
312             Returns a pretty-printed string of the vector values
313              
314             =cut
315              
316             sub to_string {
317 0     0 1   my ($self) = @_;
318 0           my $string = sprintf('rv(%s)',join(', ',@{$self->{vals}}));
  0            
319 0           return $string;
320             }
321              
322             =head3 vectoradd($val1,$val2,$switch)
323              
324             =head3 vectorsub($val1,$val2,$switch)
325              
326             =head3 vectormult($val1,$val2,$switch)
327              
328             =head3 vectordiv($val1,$val2,$switch)
329              
330             =head3 vectorpower($val1,$val2,$switch)
331              
332             These are the functions called by the overloaded mathematical operators when interacting
333             with an RVector object. These represent +, -, *, /, and ** respectively. They take in
334             two objects to perform the arithmetic operations on and a value for whether the value
335             order has actually been switched, since the RVector object should always come first.
336             $switch is only relevant to subtraction, division, and power.
337              
338             =cut
339              
340             sub vectoradd {
341 0     0 1   my ($val1, $val2, $switch) = @_;
342 0           my $return = rv();
343 0           ($val1,$val2) = get_proper_operands($val1,$val2,$switch);
344             # Do the arithmentic
345 0           for (my $i = 0; $i < $val1->length(); $i++) {
346             # TODO: NaN check?!
347 0           $return->add_value($val1->[$i] + $val2->[$i]);
348             }
349 0           return $return;
350             }
351              
352             sub vectorsub {
353 0     0 1   my ($val1, $val2, $switch) = @_;
354 0           my $return = rv();
355 0           ($val1,$val2) = get_proper_operands($val1,$val2,$switch);
356             # Do the arithmentic
357 0           for (my $i = 0; $i < $val1->length(); $i++) {
358             # TODO: NaN check?!
359 0           $return->add_value($val1->[$i] - $val2->[$i]);
360             }
361 0           return $return;
362             }
363              
364             sub vectormult {
365 0     0 1   my ($val1, $val2, $switch) = @_;
366 0           my $return = rv();
367 0           ($val1,$val2) = get_proper_operands($val1,$val2,$switch);
368             # Now run through the entries doing multiplication
369 0           for (my $i = 0; $i < $val1->length(); $i++) {
370             # TODO: NaN check?!
371 0           $return->add_value($val1->[$i] * $val2->[$i]);
372             }
373 0           return $return;
374             }
375              
376             sub vectordiv {
377 0     0 1   my ($val1, $val2, $switch) = @_;
378 0           my $return = rv();
379 0           my ($top,$bottom) = get_proper_operands($val1,$val2,$switch);
380             # Now run through the entries doing division, where appropriate
381 0           for (my $i = 0; $i < $top->length(); $i++) {
382             # TODO: NaN check?!
383 0 0 0       if (defined $bottom->[$i] && $bottom->[$i] != 0) {
384 0           $return->add_value($top->[$i] / $bottom->[$i]);
385             } else {
386 0           $return->add_value(undef);
387             }
388             }
389 0           return $return;
390             }
391              
392             sub vectorpower {
393 0     0 1   my ($val1, $val2, $switch) = @_;
394 0           my $return = rv();
395 0           ($val1,$val2) = get_proper_operands($val1,$val2,$switch);
396             # Do the arithmentic
397 0           for (my $i = 0; $i < $val1->length(); $i++) {
398             # TODO: NaN check?!
399 0           $return->add_value($val1->[$i] ** $val2->[$i]);
400             }
401 0           return $return;
402             }
403              
404             sub get_proper_operands {
405 0     0 0   my ($val1, $val2, $switch) = @_;
406 0           my $rlen = $val1->length();
407             # Make both vectors the same size.
408 0 0         if (ref($val1) eq ref($val2)) {
409             # Both are vectors
410 0           my $seclen = $val2->length();
411 0 0         if ($seclen > $rlen) {
412 0           $val1 = $val1->clone();
413 0           $val1->extend($seclen);
414 0           $rlen = $seclen;
415             } else {
416 0           $val2 = $val2->clone();
417 0           $val2->extend($rlen);
418             }
419             } else {
420 0           $val2 = rv($val2);
421 0           $val2->extend($rlen);
422             }
423 0 0         if ($switch) {
424 0           return ($val2,$val1);
425             } else {
426 0           return ($val1,$val2);
427             }
428             }
429              
430             =head1 AUTHOR
431              
432             Josh Ballard Ejosh@oofle.comE
433              
434             =head1 COPYRIGHT
435              
436             Copyright (c) 2010 Josh Ballard.
437              
438             This library is free software; you can redistribute it and/or modify
439             it under the same terms as Perl itself, either Perl version 5.10.1 or,
440             at your option, any later version of Perl 5 you may have available.
441              
442             =head1 SEE ALSO
443              
444             For more information about RVector, see http://code.oofle.com/ and follow
445             the link to RVector. For more information about the R Stats programming
446             language, see http://r-project.org/.
447              
448             =cut
449              
450             1;
451             __END__