File Coverage

blib/lib/RandomJungle/File/OOB.pm
Criterion Covered Total %
statement 72 72 100.0
branch 14 14 100.0
condition 5 5 100.0
subroutine 16 16 100.0
pod 9 10 90.0
total 116 117 99.1


line stmt bran cond sub pod time code
1             package RandomJungle::File::OOB;
2              
3             =head1 NAME
4              
5             RandomJungle::File::OOB - Low level access to the data in the RandomJungle OOB output file
6              
7             =cut
8              
9 1     1   743 use strict;
  1         1  
  1         26  
10 1     1   4 use warnings;
  1         1  
  1         20  
11              
12 1     1   4 use Carp;
  1         1  
  1         42  
13 1     1   5 use Data::Dumper;
  1         1  
  1         33  
14 1     1   766 use Devel::StackTrace;
  1         3245  
  1         772  
15              
16             =head1 VERSION
17              
18             Version 0.05
19              
20             =cut
21              
22             our $VERSION = 0.05;
23             our $ERROR; # used if new() fails
24              
25             =head1 SYNOPSIS
26              
27             RandomJungle::File::OOB provides access to the data contained within RandomJungle's OOB output file.
28             See RandomJungle::Jungle for higher-level methods.
29              
30             use RandomJungle::File::OOB;
31              
32             my $oob = RandomJungle::File::OOB->new( filename => $oobfile ) || die $RandomJungle::File::OOB::ERROR;
33             $oob->parse || die $oob->err_str;
34              
35             my $file = $oob->get_filename; # returns the filename of the OOB file
36             my $aref = $oob->get_matrix; # returns an aref to the entire matrix (all lines, in file order)
37              
38             # Note $i is the index (row num) of the sample, not the IID
39             my $line = $oob->get_data_for_sample_index( $i ) || die $oob->err_str; # $line is unsplit
40              
41             my $href = $oob->get_data; # for debugging only; returns raw data structs
42              
43             # Error handling
44             $oob->set_err( 'Something went boom' );
45             my $msg = $oob->err_str;
46             my $trace = $oob->err_trace;
47              
48             =cut
49              
50             #*********************************************************************
51             # Public Methods
52             #*********************************************************************
53              
54             =head1 METHODS
55              
56             =head2 new()
57              
58             Creates and returns a new RandomJungle::File::OOB object:
59              
60             my $oob = RandomJungle::File::OOB->new( filename => $oobfile );
61              
62             The 'filename' parameter is required.
63             Returns undef and sets $ERROR on failure.
64              
65             =cut
66              
67             sub new
68             {
69             # Returns RJ::File::OOB object on success
70             # Returns undef and sets $ERROR on failure (e.g., 'filename' param not set)
71              
72 7     7 1 10931 my ( $class, %args ) = @_;
73              
74 7         12 my $obj = {};
75 7         14 bless $obj, $class;
76 7 100       24 $obj->_init( %args ) || return; # $ERROR set by _init()
77              
78 5         16 return $obj;
79             }
80              
81             =head2 parse()
82              
83             Parses the OOB file specified in new():
84              
85             my $retval = $oob->parse;
86              
87             Returns a true value on success. Sets err_str and returns undef on failure.
88              
89             =cut
90              
91             sub parse
92             {
93             # sets err_str and returns undef if error opening OOB file
94             # returns true on success
95              
96 5     5 1 670 my ( $self ) = @_;
97              
98             open( my $infh, '<', $self->{oob_file}{filename} ) or
99             do
100 5 100       197 {
101 1         8 my $msg = 'Error opening ' . $self->{oob_file}{filename} . "\n" . $!;
102 1         4 $self->set_err( $msg );
103 1         162 return;
104             };
105              
106             # Each row corresponds to a sample, each column is a tree.
107             # Assumes order of the rows (samples) is the same as in the .raw file (see IID column).
108             # Values in the matrix indicate whether the sample was used to construct the tree (0)
109             # or if the sample was out of bag (OOB, 1). See Breiman 2001 for algorithm.
110              
111 4         9 my $i = 0; # line counter for sample index
112              
113 4         91 while( my $line = <$infh> )
114             {
115             #print "Reading line $i\n" if $i % 250 == 0;
116              
117 88 100       253 next if $line =~ m/^\s*$/; # skip blank lines
118              
119 80         112 $line = localize_string( $line ); # for cross-platform compatibility
120 80         86 chomp $line;
121 80         281 $line =~ s/\s+$//; # remove trailing tab
122              
123             # my @vals = split( "\t", $line );
124             # $self->{matrix}[$i] = \@vals;
125 80         137 $self->{matrix}[$i] = $line; # note: $i is the row # of the sample, NOT the IID!
126              
127 80         194 $i++;
128             }
129              
130 4         56 close $infh;
131              
132 4         15 return 1;
133             }
134              
135             =head2 get_data_for_sample_index()
136              
137             Returns a line from the OOB file, specified by sample index (row number, not IID):
138              
139             my $line = $oob->get_data_for_sample_index( $i ) || die $oob->err_str;
140              
141             =cut
142              
143             sub get_data_for_sample_index
144             {
145             # Returns unsplit line on success
146             # Note: $i is the index (row num) of the sample, not the IID
147             # Sets err_str and returns undef on error ($i not specified or invalid)
148 4     4 1 11 my ( $self, $sample_i ) = @_;
149              
150 4 100       9 if( ! defined $sample_i )
151             {
152 1         4 $self->set_err( "No sample index specified" );
153 1         128 return;
154             }
155              
156 3 100 100     21 if( $sample_i =~ m/\D/ || ! defined $self->{matrix}[$sample_i] )
157             {
158 2         7 $self->set_err( "Invalid sample index [$sample_i]" );
159 2         240 return;
160             }
161              
162 1         4 return $self->{matrix}[$sample_i];
163             }
164              
165             =head2 get_matrix()
166              
167             Returns a reference to an array that contains the lines in the OOB file:
168              
169             my $aref = $oob->get_matrix;
170              
171             =cut
172              
173             sub get_matrix
174             {
175             # returns an aref to the entire matrix
176 1     1 1 3 my ( $self ) = @_;
177 1         3 return $self->{matrix};
178             }
179              
180             =head2 get_filename()
181              
182             Returns the name of the OOB file specified in new():
183              
184             my $file = $oob->get_filename;
185              
186             =cut
187              
188             sub get_filename
189             {
190 1     1 1 5 my ( $self ) = @_;
191 1         6 return $self->{oob_file}{filename};
192             }
193              
194             =head2 get_data()
195              
196             Returns the data structures contained in $self:
197              
198             my $href = $oob->get_data;
199              
200             This method is for debugging only and should not be used in production code.
201              
202             =cut
203              
204             sub get_data
205             {
206 1     1 1 7 my ( $self ) = @_;
207              
208 1         5 my %h = (
209             oob_file => $self->{oob_file},
210             matrix => $self->{matrix},
211             );
212              
213 1         3 return \%h;
214             }
215              
216             =head2 set_err()
217              
218             Sets the error message (provided as a parameter) and creates a stack trace:
219              
220             $oob->set_err( 'Something went boom' );
221              
222             =cut
223              
224             sub set_err
225             {
226 6     6 1 12 my ( $self, $errstr ) = @_;
227              
228 6   100     23 $self->{err_str} = $errstr || '';
229 6         29 $self->{err_trace} = Devel::StackTrace->new;
230             }
231              
232             =head2 err_str()
233              
234             Returns the last error message that was set:
235              
236             my $msg = $oob->err_str;
237              
238             =cut
239              
240             sub err_str
241             {
242 6     6 1 1669 my ( $self ) = @_;
243              
244 6         44 return $self->{err_str};
245             }
246              
247             =head2 err_trace()
248              
249             Returns a backtrace for the last error that was encountered:
250              
251             my $trace = $oob->err_trace;
252              
253             =cut
254              
255             sub err_trace
256             {
257 1     1 1 2 my ( $self ) = @_;
258              
259 1         7 return $self->{err_trace}->as_string;
260             }
261              
262             #*********************************************************************
263             # Private Methods and Routines
264             #*********************************************************************
265              
266             sub _init
267             {
268             # returns undef and sets $ERROR if $args{filename} is not defined or file does not exist
269              
270 7     7   11 my ( $self, %args ) = @_;
271              
272 7         14 @{ $self }{ keys %args } = values %args;
  7         17  
273              
274             # filename (mandatory)
275              
276 7 100       173 if( ! defined $self->{filename} )
    100          
277             {
278 1         3 $ERROR = "'filename' is not defined";
279 1         11 return;
280             }
281             elsif( ! -e $self->{filename} )
282             {
283 1         3 $ERROR = "$self->{filename} does not exist";
284 1         6 return;
285             }
286              
287 5         29 $self->{oob_file}{filename} = $self->{filename};
288             }
289              
290             sub localize_string
291             {
292 80     80 0 91 my ( $content ) = @_;
293              
294 80         78 my $localized = $content;
295 80         86 $localized =~ s[\012\015][\n]g; # added to support x0Ax0D
296 80         77 $localized =~ s[\015{1,2}\012][\n]sg; # from File::LocalizeNewlines, collapse x0Dx0A and x0Dx0Dx0A
297 80         213 $localized =~ s[\015|\012][\n]sg; # from File::LocalizeNewlines, normalize remaining
298              
299 80         128 return $localized;
300             }
301              
302             =head1 SEE ALSO
303              
304             RandomJungle::Jungle, RandomJungle::Tree, RandomJungle::Tree::Node,
305             RandomJungle::XML, RandomJungle::OOB, RandomJungle::RAW,
306             RandomJungle::DB, RandomJungle::Classification_DB
307              
308             =head1 AUTHOR
309              
310             Robert R. Freimuth
311              
312             =head1 COPYRIGHT
313              
314             Copyright (c) 2011 Mayo Foundation for Medical Education and Research. All rights reserved.
315              
316             This program is free software; you can redistribute it and/or modify
317             it under the same terms as Perl itself.
318              
319             The full text of the license can be found in the
320             LICENSE file included with this module.
321              
322             =cut
323              
324             #*********************************************************************
325             # Guts
326             #*********************************************************************
327              
328             =begin guts
329              
330             Guts:
331              
332             $self
333             oob_file
334             filename => $filename
335             matrix => [ $line_0, $line_1, ... ]
336             err_str => $errstr
337             err_trace => Devel::StackTrace object
338              
339             Note: $lines are unsplit as [ $oob_tree_0, $oob_tree_1, ...] takes too much disk space
340              
341             =cut
342              
343             1;