File Coverage

blib/lib/Statistics/Data/Dichotomize.pm
Criterion Covered Total %
statement 135 137 98.5
branch 66 94 70.2
condition 7 14 50.0
subroutine 13 13 100.0
pod 7 7 100.0
total 228 265 86.0


line stmt bran cond sub pod time code
1             package Statistics::Data::Dichotomize;
2 7     7   821908 use strict;
  7         18  
  7         203  
3 7     7   36 use warnings FATAL => 'all';
  7         14  
  7         362  
4 7     7   36 use base qw(Statistics::Data);
  7         14  
  7         6623  
5 7     7   179123 use Carp qw(croak);
  7         14  
  7         321  
6 7     7   39 use Number::Misc qw(is_numeric);
  7         13  
  7         283  
7 7     7   5261 use Statistics::Lite qw(mean median mode);
  7         10696  
  7         13123  
8            
9             $Statistics::Data::Dichotomize::VERSION = '0.04';
10            
11             =head1 NAME
12            
13             Statistics::Data::Dichotomize - Dichotomize one or more numerical or categorical sequences into a single two-valued one
14            
15             =head1 VERSION
16            
17             This is documentation for B of Statistics-Data-Dichotomize.
18            
19             =head1 SYNOPSIS
20            
21             use Statistics::Data::Dichotomize 0.04;
22             my $ddat = Statistics::Data::Dichotomize->new();
23             my $aref;
24            
25             $ddat->load(23, 24, 7, 55); # numerical data
26             $aref = $ddat->cut(value => 'median',); # - or by precise value or function
27             $aref = $ddat->swing(); # by successive rises and falls of value
28             $aref = $ddat->shrink(rule => sub { return $_->[0] >= 20 ? : 1 : 0 }, winlen => 1); # like "cut" if winlen only 1
29             $aref = $ddat->binate(oneis => 7); # returns (0, 0, 1, 0)
30            
31             # - alternatively, call any method giving data directly, without prior load():
32             $aref = $ddat->cut(data => [23, 24, 7, 55], value => 20);
33             $aref = $ddat->pool(data => [$aref1, $aref2]);
34            
35             # or by a multi-sequence load: - by named arefs:
36             $ddat->load(foodat =>[qw/c b c a a/], bardat => [qw/b b b c a/]); # arbitrary names
37             $aref = $ddat->binate(data => 'foodat', oneis => 'c',); # returns (1, 0, 1, 0, 0)
38            
39             # - or by anonymous arefs:
40             $ddat->load([qw/c b c a a/], [qw/b b b c a/]); # categorical (stringy) data
41             $aref = $ddat->match(); # returns [0, 1, 0, 0, 1]
42            
43             =head1 DESCRIPTION
44            
45             A module for binary transformation of one or more sequences of numerical or categorical data (array of numbers or strings). That is, given an array, the methods return a binary, binomial, dichotomous, two-valued sequence. Each method returns the dichotomized sequence as a reference to an array of 0s and 1s.
46            
47             There are methods to do this for: (1) I, either (a) dichotomized ("L") about a specified or function-returned value, or a central statistic (mean, median or mode), or (b) dichtomotized according to successive rises and falls in value ("L"); (2) I, collapsed ("Led") into a single dichotomous sequence according to the rank order of their values; (3) a single categorical sequence where one value is set to equal 1 and all others equal 0 ("L"); (4) I, collapsed into a single dichotomous sequence according to their pairwise "L"; and (5) a I dichotomized according to whether or not independent slices of the data meet a specified rule ("L").
48            
49             All arguments are given as an anonymous hash of key => value pairs, which (not shown in examples) can also be given as a hash reference.
50            
51             =head1 SUBROUTINES/METHODS
52            
53             =head2 new
54            
55             To create class object directly from this module, inheriting all the L methods.
56            
57             =head2 load, add, access, unload
58            
59             Methods for loading, updating and retrieving data are inherited from L. See that manpage for details.
60            
61             =cut
62            
63             =head2 Numerical data: Single sequence dichotomization
64            
65             =head3 cut
66            
67             ($aref, $val) = $ddat->cut(data => \@data, value => \&Statistics::Lite::median); # cut the given data at is median, getting back median too
68             $aref = $ddat->cut(value => 'median', equal => 'gt'); # cut the last previously loaded data at its median
69             $aref = $ddat->cut(value => 23); # cut anonymously cached data at a specific value
70             $aref = $ddat->cut(value => 'mean', data => 'blues'); # cut named data (previously loaded as such) at its mean
71            
72             Returns a reference to an array of dichotomously transformed values of a given array of numbers by categorizing its values as to whether they're numerically higher or lower than a particular value, e.g., their median, mean, mode or some given number, or some function that, given the array (unreferenced) returns a single value. Called in list context, returns a reference to the transformed values, and then the cut-value itself.
73            
74             So the following data, when cut over values greater than or equal to 5, yield the dichotomous (Boolean) sequence:
75            
76             @orig_data = (4, 3, 3, 5, 3, 4, 5, 6, 3, 5, 3, 3, 6, 4, 4, 7, 6, 4, 7, 3);
77             @cut_data = (0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0);
78            
79             The order of the original values is reflected in the returned "cut data", but their order is not taken into account in making up the dichotomy - in contrast to the L method.
80            
81             Optional arguments, as follow, specify what value or measure to cut by (default is the median), and how to handle ties with the cut-value (default is to skip them).
82            
83             =over 4
84            
85             =item value => 'mean|median|mode' - or a specific numerical value, or code reference
86            
87             Specifies the value at which the data will be cut. This could be the mean, median or mode (as calculated by L), or a numerical value within the range of the data, or some appropriate subroutine - one that takes an array (not a reference to one) and returns a single value (presumably a descriptive of the values in the array). The default is the I. The cut-value, as specified by B, can be retrieved as the second element returned if calling for an array.
88            
89             =item equal => 'I|I|I<0>'
90            
91             Specifies how to cut the data should the cut-value (as specified by B) be present in the data. The default value is 0: observations equal to the cut-value are skipped. If B 'I'>: all data-values I the cut-value will take on one code, and all data-values less than the cut-value will take on another. Alternatively, to cut all values I the criterion value into one code, and all higher values into another, use B 'I'>.
92            
93             =back
94            
95             =cut
96            
97             sub cut {
98 5     5 1 7313 my ( $self, @args ) = @_;
99 5 50       21 my $args = ref $args[0] ? $args[0] : {@args};
100 5 100       26 my $dat = ref $args->{'data'} ? $args->{'data'} : $self->access($args);
101 5 50       63 croak __PACKAGE__,
102             '::cut All data must be numeric for dichotomizing about a cut-value'
103             if !$self->all_numeric($dat);
104 5 50       1319 $args->{'value'} = 'median' if !defined $args->{'value'};
105 5 50       16 $args->{'equal'} = 'gt' if !defined $args->{'equal'};
106 5         11 my ( $val, @seqs ) = ();
107            
108             # Get a cut-value:
109 5 100       16 if ( !is_numeric( $args->{'value'} ) ) {
110 3         50 my $code = \&{ delete $args->{'value'} };
  3         8  
111 3         4 $val = $code->( @{$dat} );
  3         12  
112             }
113             else {
114 2         29 $val = $args->{'value'};
115             }
116            
117             # Categorize by number of observations above and below the cut_value:
118             push @seqs,
119             $_ > $val ? 1
120             : $_ < $val ? 0
121             : $args->{'equal'} eq 'gt' ? 1
122             : $args->{'equal'} eq 'lt' ? 0
123             : 1
124 5 0       145 foreach @{$dat};
  5 50       152  
    100          
    100          
125 5 50       30 return wantarray ? ( \@seqs, $val ) : \@seqs;
126             }
127            
128             =head3 swing
129            
130             $aref = $ddat->swing(data => [3, 4, 7, 6, 5, 1, 2, 3, 2]); # "swing" these data
131             $aref = $ddat->swing(label => 'reds'); # name a pre-loaded dataset for "swinging"
132             $aref = $ddat->swing(); # use the last-loaded dataset
133            
134             Returns a reference to an array of dichotomously transformed values of a single sequence of numerical values according to their consecutive rises and falls. Each value is subtracted from its successor, and the result is replaced with a 1 if the difference represents an increase, or 0 if it represents a decrease. For example (from Wolfowitz, 1943, p. 283), the following numerical sequence produces the subsequent dichotomous sequence.
135            
136             @values = (qw/3 4 7 6 5 1 2 3 2/);
137             @dichot = (qw/1 1 0 0 0 1 1 0/);
138            
139             Dichotomously, the data commence with an ascending run of length 2 (from 3 to 4, and from 4 to 7), followed by a descending run of length 3 (from 7 to 6, 6 to 5, and 5 to 1), followed by an ascent of length 2 (from 1 to 2, from 2 to 3), and so on. The number of resulting dichotomous observations is 1 less than the original sample-size (elements in the given array).
140            
141             =over 4
142            
143             =item equal => 'I|I|I|I<0>'
144            
145             The default result when the difference between two successive values is zero is to skip the observation, and move onto the next succession (B 0>). Alternatively, you may wish to repeat the result for the previous succession; skipping only a difference of zero should it occur as the first result (B 'rpt'>). Or, a difference greater than or equal to zero is counted as an increase (B 'gt'>), or a difference less than or equal to zero is counted as a decrease. For example,
146            
147             @values = (qw/3 3 7 6 5 2 2/);
148             @dicho_def = (qw/1 0 0 0/); # First and final results (of 3 - 3, and 2 - 2) are skipped
149             @dicho_rpt = (qw/1 0 0 0 0/); # First result (of 3 - 3) is skipped, and final result repeats the former
150             @dicho_gt = (qw/1 1 0 0 0 1/); # Greater than or equal to zero is an increase
151             @dicho_lt = (qw/0 1 0 0 0 0/); # Less than or equal to zero is a decrease
152            
153             =back
154            
155             =cut
156            
157             sub swing {
158 7     7 1 8530 my ( $self, @args ) = @_;
159 7 50       28 my $args = ref $args[0] ? $args[0] : {@args};
160 7 100       27 my $dat = ref $args->{'data'} ? $args->{'data'} : $self->access($args);
161 7 50       52 croak __PACKAGE__, '::swing All data must be numeric for dichotomizing'
162             if !$self->all_numeric($dat);
163 7 100       1110 $args->{'equal'} = 0 if !defined $args->{'equal'}; #- no default??
164 7         12 my ( $i, $res, @seqs ) = ();
165            
166             # Replace observations with the succession of rises and falls:
167 7         10 for ( $i = 0 ; $i < ( scalar @{$dat} - 1 ) ; $i++ ) {
  86         204  
168 79         122 $res = $dat->[ ( $i + 1 ) ] - $dat->[$i];
169 79 100       161 if ( $res > 0 ) {
    100          
170 31         52 push @seqs, 1;
171             }
172             elsif ( $res < 0 ) {
173 40         67 push @seqs, 0;
174             }
175             else {
176 8         18 for ( $args->{'equal'} ) {
177 8 100       29 if (/^rpt/xsm) {
    100          
    100          
178 2 100       10 push @seqs, $seqs[-1] if scalar @seqs;
179             }
180             elsif (/^gt/xsm) {
181 2         6 push @seqs, 1;
182             }
183             elsif (/^lt/xsm) {
184 2         6 push @seqs, 0;
185             }
186             else {
187 2         6 next;
188             }
189             }
190             }
191             }
192 7         27 return \@seqs;
193             }
194            
195             =head2 Numerical data: Two sequence dichotomization
196            
197             See also the methods for categorical data where it is ok to ignore any order and intervals in numerical data.
198            
199             =head3 pool
200            
201             $aref = $ddat->pool(data => [$aref1, $aref2]); # give data directly to function
202             $aref = $ddat->pool(data => [$ddat->access(index => 0), $ddat->access(index => 1)]); # after $ddat->load(\@aref1, $aref2);
203             $aref = $ddat->pool(data => [$ddat->access(label => '1'), $ddat->access(label => '2')]); # after $ddat->load(1 => $aref1, 2 => $aref2);
204            
205             Returns a reference to an array of dichotomously transformed values of two sequences of I data as a ranked pool, i.e., by pooling the data from each sequence according to the magnitude of their values at each trial, from lowest to heighest. Specifically, the values from both sequences are pooled and ordered from lowest to highest, and then dichotomized into runs according to the sequence from which neighbouring values come from. Another run occurs wherever there is a change in the source of the values. A non-random effect of, say, higher or lower values consistently coming from one sequence rather than another would be reflected in fewer runs than expected by chance.
206            
207             This is typically used for a Wald-Walfowitz test of difference between two samples - ranking by median.
208            
209             =cut
210            
211             sub pool {
212 2     2 1 6278 my ( $self, @args ) = @_;
213 2 50       8 my $args = ref $args[0] ? $args[0] : {@args};
214 2 50       8 my $dat = ref $args->{'data'} ? $args->{'data'} : $self->access($args);
215 2         3 $self->all_numeric($_) foreach @{$dat};
  2         14  
216 2         401 my ( $dat1, $dat2 ) = @{$dat};
  2         5  
217 2         2 my $sum = scalar @{$dat1} + scalar @{$dat2};
  2         4  
  2         3  
218             my @dat =
219 2         4 ( [ sort { $a <=> $b } @{$dat1} ], [ sort { $a <=> $b } @{$dat2} ] );
  30         48  
  2         7  
  34         50  
  2         5  
220            
221 2         5 my ( $i, $x, $y, @seqs ) = (0);
222 2         8 while ( scalar(@seqs) < $sum ) {
223 32         51 $x = $dat[0]->[0];
224 32         42 $y = $dat[1]->[0];
225 32 100 66     206 $i = defined $x && defined $y ? $x < $y ? 0 : 1 : defined $x ? 0 : 1;
    50          
    100          
226 32         34 shift @{ $dat[$i] };
  32         52  
227 32         74 push @seqs, $i;
228             }
229 2         11 return \@seqs;
230             }
231             ## DEV: consider: List::AllUtils::pairwise:
232             # @x = pairwise { $a + $b } @a, @b; # returns index-by-index sums
233            
234             =head2 Categorical data: Single sequence dichotomization
235            
236             =head3 binate
237            
238             $aref = $ddat->binate(oneis => 'E'); # optionally specify a state in the sequence to be set as "1"
239             $aref = $ddat->binate(data => \@ari, oneis => 'E'); # optionally specify a state in the sequence to be set as "1"
240            
241             Returns a reference to an array of dichotomously transformed values of an array by setting the first element in the list to 1 (by default, or whatever is specified as B) on all its occurrences in the array, and all other values in the array as zero.
242            
243             =cut
244            
245             sub binate {
246 2     2 1 6174 my ( $self, @args ) = @_;
247 2 50       9 my $args = ref $args[0] ? $args[0] : {@args};
248 2 50       14 my $dat = ref $args->{'data'} ? $args->{'data'} : $self->access($args);
249             my $oneis =
250             defined $args->{'oneis'}
251 2 100       63 ? delete $args->{'oneis'}
252             : $dat->[0]; # What value set to 1 and others to zero?
253 2 100       4 my $dats = [ map { $_ eq $oneis ? 1 : 0 } @{$dat} ]
  10         26  
  2         5  
254             ; # replace observations with 1s and 0s
255 2         8 return $dats;
256             }
257            
258             =head2 Categorical data: Two-sequence dichotomization
259            
260             =head3 match
261            
262             $aref = $ddat->match(data => [\@aref1, \@aref2], lag => signed integer, loop => 0|1); # with optional crosslag of the two sequences
263             $aref = $ddat->match(data => [$ddat->access(index => 0), $ddat->access(index => 1)]); # after $ddat->load(\@aref1, \@aref2);
264             $aref = $ddat->match(data => [$ddat->access(label => '1'), $ddat->access(label => '2')]); # after $ddat->load(1 => \@aref1, 2 => \@aref2);
265            
266             Returns a reference to an array of dichotomously transformed values of two paired arrays according to the match between the elements at each of their indices. Where the data-values are equal at a certain index, they are represented with a 1; otherwise a 0. Numerical or stringy data can be equated. For example, the following two arrays would be reduced to the third, where a 1 indicates a match (i.e., the values are "indexically equal").
267            
268             @foo_dat = (qw/1 3 3 2 1 5 1 2 4/);
269             @bar_dat = (qw/4 3 1 2 1 4 2 2 4/);
270             @bin_dat = (qw/0 1 0 1 1 0 0 1 1/);
271            
272             The following options may be specified.
273            
274             =over 4
275            
276             =item lag => I (where I < number of observations I I > -1 (number of observations) )
277            
278             Match the two data-sets by shifting the first named set ahead or behind the other data-set by B observations. The default is zero. For example, one data-set might be targets, and another responses to the targets:
279            
280             targets = cbbbdacdbd
281             responses = daadbadcce
282            
283             Matched as a single sequence of hits (1) and misses (0) where B = B<0> yields (for the match on "a" in the 6th index of both arrays):
284            
285             0000010000
286            
287             With B => 1, however, each response is associated with the target one ahead of the trial for which it was observed; i.e., each target is shifted to its +1 index. So the first element in the above responses (I) would be associated with the second element of the targets (I), and so on. Now, matching the two data-sets with a B<+1> lag gives two hits, of the 4th and 7th elements of the responses to the 5th and 8th elements of the targets, respectively:
288            
289             000100100
290            
291             making 5 runs. With B => 0, there are 3 runs. Lag values can be negative, so that B => -2 will give:
292            
293             00101010
294            
295             Here, responses necessarily start at the third element (I), the first hits occurring when the fifth response-element corresponds to the the third target element (I). The last response (I) could not be used, and the number of elements in the hit/miss sequence became n-B less the original target sequence. This means that the maximum value of lag must be one less the size of the data-sets, or there will be no data.
296            
297             =item loop => 0|1
298            
299             Implements circularized lagging if B => 1, where all lagged data are preserved by looping any excess to the start or end of the criterion data. The number of observations will then always be the same, regardless of the lag; i.e., the size of the returned array is the same as that of the given data. For example, matching the data in the example above with a lag of +1, with looping, creates an additional match between the final response and the first target (I); i.e., the last element in the "response" array is matched to the first element of the "target" array:
300            
301             1000100100
302            
303             =back
304            
305             =cut
306            
307             sub match {
308 6     6 1 8146 my ( $self, @args ) = @_;
309 6 50       30 my $args = ref $args[0] ? $args[0] : {@args};
310 6 50       21 my $dat = ref $args->{'data'} ? $args->{'data'} : $self->access($args);
311             $dat = $self->crosslag(
312             lag => $args->{'lag'},
313             data => [ $dat->[0], $dat->[1] ],
314             loop => $args->{'loop'}
315 6 100       27 ) if $args->{'lag'};
316             my $lim =
317 6         10 scalar @{ $dat->[0] } <= scalar @{ $dat->[1] }
  6         13  
318 6         11 ? scalar @{ $dat->[0] }
319 6 50       10 : scalar @{ $dat->[1] }; # ensure criterion data-set is smallest
  0         0  
320 6         10 my (@seqs) = ();
321 6         14 for my $i ( 0 .. $lim ) {
322 61 100 66     190 next if !defined $dat->[0]->[$i] || !defined $dat->[1]->[$i];
323 55 100       120 $seqs[$i] = $dat->[0]->[$i] eq $dat->[1]->[$i] ? 1 : 0;
324             }
325 6         27 return \@seqs;
326             }
327            
328             =head2 Numerical or categorical data: Single sequence dichotimisation
329            
330             =head3 shrink, boolwin
331            
332             $aref = $ddat->shrink(winlen => INT, rule => CODE)
333            
334             Returns a reference to an array of dichotomously transformed values of a numerical or categorical sequence by taking non-overlapping slices, or windows, as given in the argument B, and making a true/false sequence out of them according to whether or not each slice passes a B. The B is a code reference that gets the data as reference to an array, and so might be something like this:
335            
336             sub { return Statistics::Lite::mean(@{$_}) > 2 ? 1 : 0; }
337            
338             If B is set to 3, this rule by means would make the following numerical sequence of 9 elements shrink into the following dichotomous (Boolean) sequence of 3 elements:
339            
340             @data = (1, 2, 3, 3, 3, 3, 4, 2, 1);
341             @means = (2, 3, 2.5 );
342             @dico = (0, 1, 1 );
343            
344             The B method must, of course, return dichotomous values to dichotomize the data, and B should make up equally sized segments (no error is thrown if this isn't the case, the remainder just gets figured in the same way).
345            
346             =cut
347            
348             sub shrink {
349 1     1 1 3314 my ( $self, @args ) = @_;
350 1 50       6 my $args = ref $args[0] ? $args[0] : {@args};
351 1 50       9 my $dat = ref $args->{'data'} ? $args->{'data'} : $self->access($args);
352 1         35 my $lim = scalar @{$dat};
  1         2  
353 1         2 my $len = int $args->{'winlen'};
354 1   50     4 $len ||= 1;
355 1         2 my $code = delete $args->{'rule'};
356 1 50 33     11 croak __PACKAGE__, '::shrink Need a code to Boolean shrink'
357             if not $code
358             or ref $code ne 'CODE';
359 1         2 my ( $i, @seqs );
360            
361 1         4 for ( $i = 0 ; $i < $lim ; $i += $len )
362             { # C-style for clear greater-than 1 increments per loop
363 3         71 push @seqs, $code->( [ @{$dat}[ $i .. ( $i + $len - 1 ) ] ] );
  3         10  
364             }
365 1         31 return \@seqs;
366             }
367             *boolwin = \&shrink;
368            
369             =head2 Utilities
370            
371             =head3 crosslag
372            
373             @lagged_arefs = $ddat->crosslag(data => [\@ari1, \@ari2], lag => signed integer, loop => 0|1);
374             $aref_of_arefs = $ddat->crosslag(data => [\@ari1, \@ari2], lag => signed integer, loop => 0|1); # same but not "wanting array"
375            
376             Takes two arrays and returns them cross-lagged against each other, shifting and popping values according to the number of "lags". Typically used when wanting to L the two arrays against each other.
377            
378             =over 4
379            
380             =item lag => signed integer up to the number of elements
381            
382             Takes the first array sent as "data" as the reference or "target" array for the second "response" array to be shifted so many lags before or behind it. With no looping of the lags, this means the returned arrays are "lag"-elements smaller than the original arrays. For example, with lag => +1 (and loop => 0, the default), and with data => [ [qw/c p w p s/], [qw/p s s w r/] ],
383            
384             (c p w p s) becomes (p w p s)
385             (p s s w r) becomes (p s s w)
386            
387             So, whereas the original data gave no matches across the two arrays, now, with the second of the two arrays shifted forward by one index, it has a match (of "p") at the first index with the first of the two arrays.
388            
389             =item loop => 0|1
390            
391             For circularized lagging, B => 1, and the size of the returned array is the same as those for the given data. For example, with a lag of +1, the last element in the "response" array is matched to the first element of the "target" array:
392            
393             (c p w p s) becomes (p w p s c) (looped with +1)
394             (p s s w r) becomes (p s s w r) (no effect)
395            
396             In this case, it might be more efficient to simply autolag the "target" sequence against itself.
397            
398             =back
399            
400             =cut
401            
402             sub crosslag {
403 5     5 1 1280 my ( $self, @args ) = @_;
404 5 50       19 my $args = ref $args[0] ? $args[0] : {@args};
405 5         9 my $lag = $args->{'lag'};
406 5         7 my $dat1 = $args->{'data'}->[0];
407 5         8 my $dat2 = $args->{'data'}->[1];
408 5         7 my $loop = $args->{'loop'};
409             return ( wantarray ? ( $dat1, $dat2 ) : [ $dat1, $dat2 ] )
410             if not $lag
411 5 0 33     14 or abs $lag >= scalar @{$dat1};
  5 50       22  
412            
413 5         6 my @dat1_lagged = @{$dat1};
  5         16  
414 5         6 my @dat2_lagged = @{$dat2};
  5         15  
415            
416 5 100       12 if ( $lag > 0 ) {
    50          
417 4         9 foreach ( 1 .. abs $lag ) {
418 4 100       9 if ($loop) {
419 2         7 unshift @dat1_lagged, pop @dat1_lagged;
420             }
421             else {
422 2         3 shift @dat1_lagged;
423 2         5 pop @dat2_lagged;
424             }
425             }
426             }
427             elsif ( $lag < 0 ) {
428 1         4 foreach ( 1 .. abs $lag ) {
429 2 50       6 if ($loop) {
430 0         0 push @dat1_lagged, shift @dat1_lagged;
431             }
432             else {
433 2         3 pop @dat1_lagged;
434 2         4 shift @dat2_lagged;
435             }
436             }
437             }
438             return wantarray
439 5 50       26 ? ( \@dat1_lagged, \@dat2_lagged )
440             : [ \@dat1_lagged, \@dat2_lagged ];
441             }
442            
443             =head1 AUTHOR
444            
445             Roderick Garton, C<< >>
446            
447             =head1 REFERENCES
448            
449             Burdick, D. S., & Kelly, E. F. (1977). Statistical methods in parapsychological research. In B. B. Wolman (Ed.), I (pp. 81-130). New York, NY, US: Van Nostrand Reinhold. [Describes window-boolean reduction.]
450            
451             Swed, F., & Eisenhart, C. (1943). Tables for testing randomness of grouping in a sequence of alternatives. I, I<14>, 66-87. doi: L<10.1214/aoms/1177731494|http://dx.doi.org/10.1214/aoms/1177731494> [Describes pool method and test example.]
452            
453             Wolfowitz, J. (1943). On the theory of runs with some applications to quality control. I, I<14>, 280-288. doi: L<10.1214/aoms/1177731421|http://dx.doi.org/10.1214/aoms/1177731421> [Describes swings "runs up and down" and test example.]
454            
455             =head1 BUGS
456            
457             Please report any bugs or feature requests to C, or through
458             the web interface at L. I will be notified, and then you'll
459             automatically be notified of progress on your bug as I make changes.
460            
461             =head1 SUPPORT
462            
463             You can find documentation for this module with the perldoc command.
464            
465             perldoc Statistics::Data::Dichotomize
466            
467             You can also look for information at:
468            
469             =over 4
470            
471             =item * RT: CPAN's request tracker (report bugs here)
472            
473             L
474            
475             =item * AnnoCPAN: Annotated CPAN documentation
476            
477             L
478            
479             =item * CPAN Ratings
480            
481             L
482            
483             =item * Search CPAN
484            
485             L
486            
487             =back
488            
489             =head1 LICENSE AND COPYRIGHT
490            
491             Copyright 2012-2016 Roderick Garton.
492            
493             This program is free software; you can redistribute it and/or modify it
494             under the terms of either: the GNU General Public License as published
495             by the Free Software Foundation; or the Artistic License.
496            
497             See http://dev.perl.org/licenses/ for more information.
498            
499             =cut
500            
501             1; # End of Statistics::Data::Dichotomize