File Coverage

blib/lib/Fsdb/Filter/dbcolsregression.pm
Criterion Covered Total %
statement 21 98 21.4
branch 0 20 0.0
condition 0 3 0.0
subroutine 7 16 43.7
pod 5 5 100.0
total 33 142 23.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             #
4             # dbcolsregression.pm
5             # Copyright (C) 1997-2015 by John Heidemann
6             # $Id: 43fd25b3a4a5ea88ef37b0bb42c578f9cd848703 $
7             #
8             # This program is distributed under terms of the GNU general
9             # public license, version 2. See the file COPYING
10             # in $dblibdir for details.
11             #
12              
13             package Fsdb::Filter::dbcolsregression;
14              
15             =head1 NAME
16              
17             dbcolsregression - compute linear regression between two columns
18              
19             =head1 SYNOPSIS
20              
21             dbcolsregression [-a] column1 column2
22              
23             =head1 DESCRIPTION
24              
25             Compute linear regression over C and C.
26             Outputs slope, intercept, and correlation coefficient.
27              
28             =head1 OPTIONS
29              
30             =over 4
31              
32             =item B<-a> or B<--include-non-numeric>
33              
34             Compute stats over all records (treat non-numeric records
35             as zero rather than just ignoring them).
36              
37             =item B<-f FORMAT> or B<--format FORMAT>
38              
39             Specify a L-style format for output statistics.
40             Defaults to C<%.5g>.
41              
42             =back
43              
44             =for comment
45             begin_standard_fsdb_options
46              
47             This module also supports the standard fsdb options:
48              
49             =over 4
50              
51             =item B<-d>
52              
53             Enable debugging output.
54              
55             =item B<-i> or B<--input> InputSource
56              
57             Read from InputSource, typically a file name, or C<-> for standard input,
58             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
59              
60             =item B<-o> or B<--output> OutputDestination
61              
62             Write to OutputDestination, typically a file name, or C<-> for standard output,
63             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
64              
65             =item B<--autorun> or B<--noautorun>
66              
67             By default, programs process automatically,
68             but Fsdb::Filter objects in Perl do not run until you invoke
69             the run() method.
70             The C<--(no)autorun> option controls that behavior within Perl.
71              
72             =item B<--help>
73              
74             Show help.
75              
76             =item B<--man>
77              
78             Show full manual.
79              
80             =back
81              
82             =for comment
83             end_standard_fsdb_options
84              
85              
86             =head1 SAMPLE USAGE
87              
88             =head2 Input:
89              
90             #fsdb x y
91             160 126
92             180 103
93             200 82
94             220 75
95             240 82
96             260 40
97             280 20
98              
99             =head2 Command:
100              
101             cat DATA/xy.fsdb | dbcolsregression x y | dblistize
102              
103             =head2 Output:
104              
105             #fsdb -R C slope intercept confcoeff n
106             slope: -0.79286
107             intercept: 249.86
108             confcoeff: -0.95426
109             n: 7
110              
111             # | dbcolsregression x y
112             # confidence intervals assume normal distribution and small n.
113             # | dblistize
114              
115             Sample data from
116             L
117             by Stefan Waner and Steven R. Costenoble.
118              
119             =head1 SEE ALSO
120              
121             L,
122             L,
123             L.
124              
125              
126             =head1 CLASS FUNCTIONS
127              
128             =cut
129              
130             @ISA = qw(Fsdb::Filter);
131             $VERSION = 2.0;
132              
133 1     1   4152 use strict;
  1         2  
  1         27  
134 1     1   6 use Pod::Usage;
  1         1  
  1         79  
135 1     1   6 use Carp;
  1         2  
  1         43  
136              
137 1     1   5 use Fsdb::Filter;
  1         1  
  1         15  
138 1     1   4 use Fsdb::IO::Reader;
  1         2  
  1         22  
139 1     1   3 use Fsdb::IO::Writer;
  1         2  
  1         36  
140 1     1   7 use Fsdb::Support qw($is_numeric_regexp);
  1         5  
  1         1289  
141              
142              
143             =head2 new
144              
145             $filter = new Fsdb::Filter::dbcolsregression(@arguments);
146              
147             Create a new dbcolsregression object, taking command-line arguments.
148              
149             =cut
150              
151             sub new ($@) {
152 0     0 1   my $class = shift @_;
153 0           my $self = $class->SUPER::new(@_);
154 0           bless $self, $class;
155 0           $self->set_defaults;
156 0           $self->parse_options(@_);
157 0           $self->SUPER::post_new();
158 0           return $self;
159             }
160              
161              
162             =head2 set_defaults
163              
164             $filter->set_defaults();
165              
166             Internal: set up defaults.
167              
168             =cut
169              
170             sub set_defaults ($) {
171 0     0 1   my($self) = @_;
172 0           $self->SUPER::set_defaults();
173 0           $self->{_include_non_numeric} = undef;
174 0           $self->{_format} = "%.5g";
175 0           $self->{_columns} = [];
176 0           $self->{_fscode} = undef;
177             }
178              
179             =head2 parse_options
180              
181             $filter->parse_options(@ARGV);
182              
183             Internal: parse command-line arguments.
184              
185             =cut
186              
187             sub parse_options ($@) {
188 0     0 1   my $self = shift @_;
189              
190 0           my(@argv) = @_;
191             $self->get_options(
192             \@argv,
193 0     0     'help|?' => sub { pod2usage(1); },
194 0     0     'man' => sub { pod2usage(-verbose => 2); },
195             'a|include-non-numeric!' => \$self->{_include_non_numeric},
196             'autorun!' => \$self->{_autorun},
197             'd|debug+' => \$self->{_debug},
198             'f|format=s' => \$self->{_format},
199             'F|fs|cs|fieldseparator|columnseparator=s' => \$self->{_fscode},
200 0     0     'i|input=s' => sub { $self->parse_io_option('input', @_); },
201             'log!' => \$self->{_logprog},
202 0     0     'o|output=s' => sub { $self->parse_io_option('output', @_); },
203 0 0         ) or pod2usage(2);
204 0           push (@{$self->{_columns}}, @argv);
  0            
205             }
206              
207             =head2 setup
208              
209             $filter->setup();
210              
211             Internal: setup, parse headers.
212              
213             =cut
214              
215             sub setup ($) {
216 0     0 1   my($self) = @_;
217              
218 0           $self->finish_io_option('input', -comment_handler => $self->create_pass_comments_sub);
219              
220             croak $self->{_prog} . ": exactly two columns must be specified to compute a correlation.\n"
221 0 0         if ($#{$self->{_columns}} != 1);
  0            
222 0           foreach (0..$#{$self->{_columns}}) {
  0            
223 0           my $column = $self->{_columns}[$_];
224 0           $self->{_colis}[$_] = $self->{_in}->col_to_i($column);
225             croak $self->{_prog} . ": column $column does not exist in the input stream.\n"
226 0 0         if (!defined($self->{_colis}[$_]));
227             };
228              
229 0           my @output_options = (-cols => [qw(slope intercept confcoeff n)]);
230             unshift (@output_options, -fscode => $self->{_fscode})
231 0 0         if (defined($self->{_fscode}));
232 0           $self->finish_io_option('output', @output_options);
233             }
234              
235             =head2 run
236              
237             $filter->run();
238              
239             Internal: run over each rows.
240              
241             =cut
242             sub run ($) {
243 0     0 1   my($self) = @_;
244              
245 0           my($n) = 0;
246 0           my($sxy) = 0.0;
247 0           my($sx) = 0.0;
248 0           my($sy) = 0.0;
249 0           my($sxx) = 0.0;
250 0           my($syy) = 0.0;
251              
252 0           my $read_fastpath_sub = $self->{_in}->fastpath_sub();
253 0           my $fref;
254 0           my $xf = $self->{_colis}[0];
255 0           my $yf = $self->{_colis}[1];
256 0           my $x;
257             my $y;
258 0           while ($fref = &$read_fastpath_sub()) {
259 0           $x = $fref->[$xf];
260 0           $y = $fref->[$yf];
261 0 0         if ($x !~ /$is_numeric_regexp/) {
262 0 0         next if (!$self->{_include_non_numeric});
263 0           $x = 0;
264             };
265 0 0         if ($y !~ /$is_numeric_regexp/) {
266 0 0         next if (!$self->{_include_non_numeric});
267 0           $y = 0;
268             };
269 0           $n++;
270 0           $sx += $x;
271 0           $sxx += $x * $x;
272 0           $sy += $y;
273 0           $syy += $y * $y;
274 0           $sxy += $x * $y;
275             };
276              
277 0 0         croak $self->{_prog} . ": no input\n"
278             if ($n == 0);
279              
280             #
281             # Compute linear regression.
282             #
283 0           my $denom_x = ($n * $sxx - $sx * $sx);
284 0           my $denom_y = ($n * $syy - $sy * $sy);
285 0           my($slope, $intercept, $coeff);
286 0 0 0       if ($denom_x == 0 || $denom_y == 0) {
287 0           $slope = "inf";
288 0           $intercept = "nan";
289 0           $coeff = "nan";
290             } else {
291 0           $slope = (1.0 * $n * $sxy - $sx * $sy) / $denom_x;
292 0           $intercept = (1.0 * $sy - $slope * $sx) / $n;
293 0           $coeff = (1.0 * $n * $sxy - $sx * $sy) / (sqrt($denom_x) * sqrt($denom_y));
294              
295 0           $slope = $self->numeric_formatting($slope);
296 0           $intercept = $self->numeric_formatting($intercept);
297 0           $coeff = $self->numeric_formatting($coeff);
298             };
299 0           my @of = ($slope, $intercept, $coeff, $n);
300 0           $self->{_out}->write_row_from_aref(\@of);
301             }
302              
303             =head1 AUTHOR and COPYRIGHT
304              
305             Copyright (C) 1997-2015 by John Heidemann
306              
307             This program is distributed under terms of the GNU general
308             public license, version 2. See the file COPYING
309             with the distribution for details.
310              
311             =cut
312              
313             1;