Bio::Tools::CodonTable::is_start_codon: check in case of ambiguous codons (#266)
[bioperl-live.git] / lib / Bio / Matrix / Generic.pm
blob07997bbd4c88ef32c6b6f5462b7af381e233bbf1
2 # BioPerl module for Bio::Matrix::Generic
4 # Please direct questions and support issues to <bioperl-l@bioperl.org>
6 # Cared for by Jason Stajich <jason-at-bioperl-dot-org>
8 # Copyright Jason Stajich
10 # You may distribute this module under the same terms as perl itself
12 # POD documentation - main docs before the code
14 =head1 NAME
16 Bio::Matrix::Generic - A generic matrix implementation
18 =head1 SYNOPSIS
20 # A matrix has columns and rows
21 my $matrix = Bio::Matrix::Generic->new;
22 $matrix->add_column(1,$column1);
23 $matrix->add_column(2,$column2);
25 my $element = $matrix->entry_by_num(1,2);
26 $matrix->entry_by_num(1,2,$newval);
28 my $entry = $matrix->entry('human', 'mouse');
30 $matrix->entry('human','mouse', $newval);
33 =head1 DESCRIPTION
35 This is a general purpose matrix object for dealing with row+column
36 data which is typical when enumerating all the pairwise combinations
37 and desiring to get slices of the data.
39 Data can be accessed by column and row names or indexes. Matrix
40 indexes start at 0.
42 =head1 FEEDBACK
44 =head2 Mailing Lists
46 User feedback is an integral part of the evolution of this and other
47 Bioperl modules. Send your comments and suggestions preferably to
48 the Bioperl mailing list. Your participation is much appreciated.
50 bioperl-l@bioperl.org - General discussion
51 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
53 =head2 Support
55 Please direct usage questions or support issues to the mailing list:
57 I<bioperl-l@bioperl.org>
59 rather than to the module maintainer directly. Many experienced and
60 reponsive experts will be able look at the problem and quickly
61 address it. Please include a thorough description of the problem
62 with code and data examples if at all possible.
64 =head2 Reporting Bugs
66 Report bugs to the Bioperl bug tracking system to help us keep track
67 of the bugs and their resolution. Bug reports can be submitted via the
68 web:
70 https://github.com/bioperl/bioperl-live/issues
72 =head1 AUTHOR - Jason Stajich
74 Email jason-at-bioperl-dot-org
76 =head1 APPENDIX
78 The rest of the documentation details each of the object methods.
79 Internal methods are usually preceded with a _
81 =cut
83 package Bio::Matrix::Generic;
85 use strict;
88 use base qw(Bio::Root::Root Bio::Matrix::MatrixI);
90 =head2 new
92 Title : new
93 Usage : my $obj = Bio::Matrix::Generic->new();
94 Function: Builds a new Bio::Matrix::Generic object
95 Returns : an instance of Bio::Matrix::Generic
96 Args : -values => arrayref of arrayrefs of data initialization
97 -rownames => arrayref of row names
98 -colnames => arrayref of col names
99 -matrix_id => id of the matrix
100 -matrix_name=> name of the matrix
101 -matrix_init_value => default value to initialize empty cells
103 =cut
105 sub new {
106 my($class,@args) = @_;
108 my $self = $class->SUPER::new(@args);
109 my ($values, $rownames, $colnames,
110 $id,$name,$init_val) =
111 $self->_rearrange([qw(VALUES ROWNAMES COLNAMES
112 MATRIX_ID MATRIX_NAME
113 MATRIX_INIT_VALUE)],@args);
114 $self->matrix_id($id) if defined $id;
115 $self->matrix_name($name) if defined $name;
116 if( defined $rownames && defined $colnames ) {
117 if( ref($rownames) !~ /ARRAY/i ) {
118 $self->throw("need an arrayref for the -rownames option");
120 # insure we copy the values
121 $self->{'_rownames'} = [ @$rownames ];
122 my $count = 0;
123 %{$self->{'_rownamesmap'}} = map { $_ => $count++ } @$rownames;
125 if( ref($colnames) !~ /ARRAY/i ) {
126 $self->throw("need an arrayref for the -colnames option");
128 # insure we copy the values
129 $self->{'_colnames'} = [ @$colnames ];
130 $count = 0;
131 %{$self->{'_colnamesmap'}} = map { $_ => $count++ } @$colnames;
133 $self->{'_values'} = [];
134 if( defined $values ) {
135 if( ref($values) !~ /ARRAY/i ) {
136 $self->throw("Need an arrayref of arrayrefs (matrix) for -values option");
138 for my $v ( @$values ) {
139 if( ref($v) !~ /ARRAY/i ) {
140 $self->throw("Need and array of arrayrefs (matrix) for -values option");
142 push @{$self->{'_values'}}, [@$v];
144 } else {
145 my @fill = ($init_val) x scalar @$colnames; # undef init_val will be default
146 for ( @$rownames ) {
147 push @{$self->{'_values'}}, [@fill];
150 } elsif( ! defined $rownames && ! defined $colnames && ! defined $values ) {
151 $self->{'_values'} = [];
152 $self->{'_rownames'} = [];
153 $self->{'_colnames'} = [];
154 } else {
155 $self->throw("Must have either provided no values/colnames/rownames or provided all three");
158 return $self;
162 =head2 matrix_id
164 Title : matrix_id
165 Usage : my $id = $matrix->matrix_id
166 Function: Get/Set the matrix ID
167 Returns : scalar value
168 Args : [optional] new id value to store
171 =cut
173 sub matrix_id{
174 my $self = shift;
175 return $self->{'_matid'} = shift if @_;
176 return $self->{'_matid'};
181 =head2 matrix_name
183 Title : matrix_name
184 Usage : my $name = $matrix->matrix_name();
185 Function: Get/Set the matrix name
186 Returns : scalar value
187 Args : [optional] new matrix name value
190 =cut
192 sub matrix_name{
193 my $self = shift;
194 return $self->{'_matname'} = shift if @_;
195 return $self->{'_matname'};
199 =head2 entry
201 Title : entry
202 Usage : my $entry = $matrix->entry($row,$col,$value)
203 Function: Get the value for a specific cell as specified
204 by the row and column names
205 Returns : scalar value or undef if row or col does not
206 exist
207 Args : $rowname - name of the row
208 $colname - column name
209 $value - [optional] New value for the entry
211 =cut
213 sub entry{
214 my ($self,$row,$column,$newvalue) = @_;
215 if( ! defined $row || ! defined $column ) {
216 $self->throw("Need at least 2 ids");
219 my ($rownum) = $self->row_num_for_name($row);
220 my ($colnum) = $self->column_num_for_name($column);
221 return $self->entry_by_num($rownum,$colnum,$newvalue);
224 =head2 get_entry
226 Title : get_entry
227 Usage : my $entry = $matrix->get_entry($rowname,$columname,$value)
228 Function: Get the entry for a given row,column pair
229 Returns : scalar
230 Args : $row name
231 $column name
232 $value
235 =cut
237 sub get_entry{ $_[0]->entry($_[1],$_[2]) }
239 =head2 entry_by_num
241 Title : entry_by_num
242 Usage : my $entry = $matrix->entry_by_num($rownum,$colnum)
243 Function: Get an entry by row and column numbers instead of by name
244 (rows and columns start at 0)
245 Returns : scalar value or undef if row or column name does not
246 exist
247 Args : $row - row number
248 $col - column number
249 [optional] $newvalue to store at this cell
251 =cut
253 sub entry_by_num {
254 my ($self,$row,$col,$newvalue) = @_;
255 if( ! defined $row || ! defined $col ||
256 $row !~ /^\d+$/ ||
257 $col !~ /^\d+$/ ) {
258 $self->warn("expected to get 2 number for entry_by_num");
259 return;
262 if( defined $newvalue ) {
263 return $self->_values->[$row][$col] = $newvalue;
264 } else {
265 return $self->_values->[$row][$col];
269 sub get_element {
270 my $self = shift;
271 $self->entry(@_);
275 =head2 column
277 Title : column
278 Usage : my @col = $matrix->column('ALPHA');
280 $matrix->column('ALPHA', \@col);
281 Function: Get/Set a particular column
282 Returns : Array (in array context) or arrayref (in scalar context)
283 of values.
284 For setting will warn if the new column is of a different
285 length from the rest of the columns.
286 Args : name of the column
287 [optional] new column to store here
289 =cut
291 sub column{
292 my ($self,$column,$newcol) = @_;
294 if( ! defined $column ) {
295 $self->warn("Need at least a column id");
296 return;
298 my $colnum = $self->column_num_for_name($column);
299 if( ! defined $colnum ) {
300 $self->warn("could not find column number for $column");
301 return;
303 return $self->column_by_num($colnum,$newcol);
307 =head2 get_column
309 Title : get_column
310 Usage : my @row = $matrix->get_column('ALPHA');
311 Function: Get a particular column
312 Returns : Array (in array context) or arrayref (in scalar context)
313 of values
314 Args : name of the column
317 =cut
319 sub get_column { $_[0]->column($_[1]) }
322 =head2 column_by_num
324 Title : column_by_num
325 Usage : my @col = $matrix->column_by_num(1);
327 $matrix->column_by_num(1,\@newcol);
328 Function: Get/Set a column by its number instead of name
329 (cols/rows start at 0)
330 Returns : Array (in array context) or arrayref (in scalar context)
331 of values
332 Args : name of the column
333 [optional] new value to store for a particular column
335 =cut
337 sub column_by_num{
338 my ($self,$colnum,$newcol) = @_;
339 if( ! defined $colnum ) {
340 $self->warn("need at least a column number");
341 return;
343 my $rowcount = $self->num_rows;
344 my $colcount = $self->num_columns;
345 my $ret;
347 if( defined $newcol ) {
348 if( ref($newcol) !~ /ARRAY/i) {
349 $self->warn("expected a valid arrayref for resetting a column");
350 return;
352 if( scalar @$newcol != $rowcount ) {
353 $self->warn("new column is not the correct length ($rowcount) - call add or remove row to shrink or grow the number of rows first");
354 return;
356 for(my $i=0; $i < $rowcount; $i++) {
357 $self->entry_by_num($i,$colnum,$newcol->[$i]);
359 $ret = $newcol;
360 } else {
361 $ret = [];
362 for(my $i=0; $i < $rowcount; $i++) {
363 push @$ret,$self->entry_by_num($i,$colnum);
366 if( wantarray ) { return @$ret }
367 return $ret;
371 =head2 row
373 Title : row
374 Usage : my @row = $matrix->row($rowname);
376 $matrix->row($rowname,\@rowvalues);
377 Function: Get/Set the row of the matrix
378 Returns : Array (in array context) or arrayref (in scalar context)
379 Args : rowname
380 [optional] new value of row to store
383 =cut
385 sub row {
386 my ($self,$row,$newrow) = @_;
387 if( ! defined $row) {
388 $self->warn("Need at least a row id");
389 return;
391 my $rownum = $self->row_num_for_name($row);
392 return $self->row_by_num($rownum,$newrow);
396 =head2 get_row
398 Title : get_row
399 Usage : my @row = $matrix->get_row('ALPHA');
400 Function: Get a particular row
401 Returns : Array (in array context) or arrayref (in scalar context)
402 of values
403 Args : name of the row
405 =cut
407 sub get_row { $_[0]->row($_[1]) }
409 =head2 row_by_num
411 Title : row_by_num
412 Usage : my @row = $matrix->row_by_num($rownum);
414 $matrix->row($rownum,\@rowvalues);
415 Function: Get/Set the row of the matrix
416 Returns : Array (in array context) or arrayref (in scalar context)
417 Args : rowname
418 [optional] new value of row to store
420 =cut
422 sub row_by_num{
423 my ($self,$rownum,$newrow) = @_;
424 if( ! defined $rownum ) {
425 $self->warn("need at least a row number");
426 return;
428 my $colcount = $self->num_columns;
429 my $ret;
430 if( defined $newrow ) {
431 if( ref($newrow) !~ /ARRAY/i) {
432 $self->warn("expected a valid arrayref for resetting a row");
433 return;
435 if( scalar @$newrow != $colcount ) {
436 $self->warn("new row is not the correct length ($colcount) - call add or remove column to shrink or grow the number of columns first");
437 return;
439 for(my $i=0; $i < $colcount; $i++) {
440 $self->entry_by_num($rownum,$i, $newrow->[$i]);
442 $ret = $newrow;
443 } else {
444 $ret = [];
445 for(my $i=0; $i < $colcount; $i++) {
446 # we're doing this to explicitly
447 # copy the entire row
448 push @$ret, $self->entry_by_num($rownum,$i);
451 if( wantarray ) { return @$ret }
452 return $ret;
458 =head2 diagonal
460 Title : diagonal
461 Usage : my @diagonal = $matrix->get_diagonal()
462 Function: Get the diagonal of a matrix
463 Returns : Array (in array context) or arrayref (in scalar context)
464 of values which lie along the diagonal
465 Args : none
468 =cut
470 sub get_diagonal{
471 my ($self) = @_;
472 my @diag;
473 my $rowcount = $self->num_rows;
474 my $colcount = $self->num_columns;
475 for(my $i = 0; $i < $rowcount; $i++ ) {
476 push @diag, $self->entry_by_num($i,$i);
478 return @diag;
482 =head2 add_row
484 Title : add_row
485 Usage : $matrix->add_row($index,\@newrow);
486 Function: Adds a row at particular location in the matrix.
487 If $index < the rowcount will shift all the rows down
488 by the number of new rows.
489 To add a single empty row, simply call
490 $matrix->add_row($index,undef);
491 Returns : the updated number of total rows in the matrix
492 Args : index to store
493 name of the row (header)
494 newrow to add, if this is undef will add a single
495 row with all values set to undef
497 =cut
499 sub add_row{
500 my ($self,$index,$name,$newrow) = @_;
501 if( !defined $index ||
502 $index !~ /^\d+$/ ) {
503 $self->warn("expected a valid row index in add_row");
504 return;
505 } elsif( ! defined $name) {
506 $self->warn("Need a row name or heading");
507 return;
508 } elsif( defined $self->row_num_for_name($name) ) {
509 $self->warn("Need a unqiue name for the column heading, $name is already used");
510 return;
512 my $colcount = $self->num_columns;
513 my $rowcount = $self->num_rows;
515 if( $index > $rowcount ) {
516 $self->warn("cannot add a row beyond 1+last row at the end ($rowcount) not $index - adding at $rowcount instead");
517 $index = $rowcount;
520 if( ! defined $newrow ) {
521 $newrow = [];
522 $newrow->[$colcount] = undef;
523 } elsif( ref($newrow) !~ /ARRAY/i ) {
524 $self->throw("Expected either undef or a valid arrayref for add_row");
526 # add this row to the matrix by carving out space for it with
527 # splice
528 splice(@{$self->{'_values'}}, $index,0,[]);
529 for( my $i = 0; $i < $colcount; $i++ ) {
530 $self->entry_by_num($index,$i,$newrow->[$i]);
532 splice(@{$self->{'_rownames'}}, $index,0,$name);
533 # Sadly we have to remap these each time (except for the case
534 # when we're adding a new column to the end, but I don't think
535 # the speedup for that case warrants the extra code at this time.
536 my $ct = 0;
537 %{$self->{'_rownamesmap'}} = map { $_ => $ct++} @{$self->{'_rownames'}};
538 return $self->num_rows;
541 =head2 remove_row
543 Title : remove_row
544 Usage : $matrix->remove_row($colnum)
545 Function: remove a row from the matrix shifting all the rows
546 up by one
547 Returns : Updated number of rows in the matrix
548 Args : row index
551 =cut
553 sub remove_row{
554 my ($self,$rowindex) = @_;
555 my $rowcount = $self->num_rows;
557 if( $rowindex > $rowcount ) {
558 $self->warn("rowindex $rowindex is greater than number of rows $rowcount, cannot process");
559 return 0;
560 } else {
561 splice(@{$self->_values},$rowindex,1);
562 delete $self->{'_rownamesmap'}->{$self->{'_rownames'}->[$rowindex]};
563 splice(@{$self->{'_rownames'}},$rowindex,1);
565 my $ct = 0;
566 %{$self->{'_rownamesmap'}} = map { $_ => $ct++} @{$self->{'_rownames'}};
567 return $self->num_rows;
570 =head2 add_column
572 Title : add_column
573 Usage : $matrix->add_column($index,$colname,\@newcol);
574 Function: Adds a column at particular location in the matrix.
575 If $index < the colcount will shift all the columns right
576 by the number of new columns.
577 To add a single empty column, simply call
578 $matrix->add_column($index,undef);
579 Returns : the updated number of total columns in the matrix
580 Args : index to store
581 name of the column (header)
582 newcolumn to add, if this is undef will add a single
583 column with all values set to undef
586 =cut
589 sub add_column{
590 my ($self,$index,$name,$newcol) = @_;
591 if( !defined $index ||
592 $index !~ /^\d+$/ ) {
593 $self->warn("expected a valid col index in add_column");
594 return;
595 } elsif( ! defined $name) {
596 $self->warn("Need a column name or heading");
597 return;
598 } elsif( defined $self->column_num_for_name($name) ) {
599 $self->warn("Need a unqiue name for the column heading, $name is already used");
600 return;
602 my $colcount = $self->num_columns;
603 my $rowcount = $self->num_rows;
604 if( $index > $colcount ) {
605 $self->warn("cannot add a column beyond 1+last column at the end ($colcount) not $index - adding at $colcount instead");
606 $index = $colcount;
609 if( ! defined $newcol ) {
610 $newcol = [];
611 $newcol->[$rowcount] = undef; # make the array '$rowcount' long
612 } elsif( ref($newcol) !~ /ARRAY/i ) {
613 $self->throw("Expected either undef or a valid arrayref for add_row");
615 for( my $i = 0; $i < $rowcount; $i++ ) {
616 # add this column to each row
617 splice(@{$self->_values->[$i]},$index,0,[]);
618 $self->entry_by_num($i,$index,$newcol->[$i]);
620 splice(@{$self->{'_colnames'}}, $index,0,$name);
621 # Sadly we have to remap these each time (except for the case
622 # when we're adding a new column to the end, but I don't think
623 # the speedup for that case warrants the extra code at this time.
624 my $ct = 0;
625 %{$self->{'_colnamesmap'}} = map {$_ => $ct++} @{$self->{'_colnames'}};
626 return $self->num_columns;
629 =head2 remove_column
631 Title : remove_column
632 Usage : $matrix->remove_column($colnum)
633 Function: remove a column from the matrix shifting all the columns
634 to the left by one
635 Returns : Updated number of columns in the matrix
636 Args : column index
638 =cut
640 sub remove_column{
641 my ($self,$colindex) = @_;
643 my $colcount = $self->num_columns;
644 my $rowcount = $self->num_rows;
645 if( $colindex > $colcount ) {
646 $self->warn("colindex $colindex is greater than number of columns ($colcount), cannot process");
647 return 0;
648 } else {
649 for(my $i = 0; $i < $rowcount; $i++ ) {
650 splice(@{$self->_values->[$i]},$colindex,1);
652 delete $self->{'_colnamesmap'}->{$self->{'_colnames'}->[$colindex]};
653 splice(@{$self->{'_colnames'}},$colindex,1);
655 my $ct = 0;
656 %{$self->{'_colnamesmap'}} = map {$_ => $ct++} @{$self->{'_colnames'}};
657 return $self->num_columns;
660 =head2 column_num_for_name
662 Title : column_num_for_name
663 Usage : my $num = $matrix->column_num_for_name($name)
664 Function: Gets the column number for a particular column name
665 Returns : integer
666 Args : string
669 =cut
671 sub column_num_for_name{
672 my ($self,$name) = @_;
674 return $self->{'_colnamesmap'}->{$name};
677 =head2 row_num_for_name
679 Title : row_num_for_name
680 Usage : my $num = $matrix->row_num_for_name
681 Function: Gets the row number for a particular row name
682 Returns : integer
683 Args : string
686 =cut
688 sub row_num_for_name{
689 my ($self,$name) = @_;
690 return $self->{'_rownamesmap'}->{$name}
694 =head2 column_header
696 Title : column_header
697 Usage : my $name = $matrix->column_header(0)
698 Function: Gets the column header for a particular column number
699 Returns : string
700 Args : integer
703 =cut
705 sub column_header{
706 my ($self,$num) = @_;
707 return $self->{'_colnames'}->[$num];
711 =head2 row_header
713 Title : row_header
714 Usage : my $name = $matrix->row_header(0)
715 Function: Gets the row header for a particular row number
716 Returns : string
717 Args : integer
720 =cut
722 sub row_header{
723 my ($self,$num) = @_;
724 return $self->{'_rownames'}->[$num];
727 =head2 num_rows
729 Title : num_rows
730 Usage : my $rowcount = $matrix->num_rows;
731 Function: Get the number of rows
732 Returns : integer
733 Args : none
736 =cut
738 sub num_rows{
739 my ($self) = @_;
740 return scalar @{$self->_values};
744 =head2 num_columns
746 Title : num_columns
747 Usage : my $colcount = $matrix->num_columns
748 Function: Get the number of columns
749 Returns : integer
750 Args : none
753 =cut
755 sub num_columns{
756 my ($self) = @_;
757 return scalar @{$self->_values->[0] || []};
761 =head2 row_names
763 Title : row_names
764 Usage : my @rows = $matrix->row_names
765 Function: The names of all the rows
766 Returns : array in array context, arrayref in scalar context
767 Args : none
770 =cut
772 sub row_names{
773 if( wantarray ) {
774 return @{shift->{'_rownames'}};
775 } else {
776 return shift->{'_rownames'};
781 =head2 column_names
783 Title : column_names
784 Usage : my @columns = $matrix->column_names
785 Function: The names of all the columns
786 Returns : array in array context, arrayref in scalar context
787 Args : none
790 =cut
792 sub column_names{
793 if( wantarray ) {
794 return @{shift->{'_colnames'}};
795 } else {
796 return shift->{'_colnames'};
800 =head2 private methods
802 Private methods for a Generic Matrix
804 =head2 _values
806 Title : _values
807 Usage : $matrix->_values();
808 Function: get/set for array ref of the matrix containing
809 distance values
810 Returns : an array reference
811 Args : an array reference
814 =cut
816 sub _values{
817 my ($self,$val) = @_;
818 if( $val ){
819 $self->{'_values'} = $val;
821 return $self->{'_values'};