File Coverage

blib/lib/Text/BoxPlot.pm
Criterion Covered Total %
statement 64 65 98.4
branch 6 8 75.0
condition 2 4 50.0
subroutine 10 10 100.0
pod 1 1 100.0
total 83 88 94.3


line stmt bran cond sub pod time code
1 1     1   52260 use 5.008001;
  1         4  
  1         40  
2 1     1   6 use strict;
  1         2  
  1         28  
3 1     1   6 use warnings;
  1         2  
  1         54  
4              
5             package Text::BoxPlot;
6             # ABSTRACT: Render ASCII box and whisker charts
7             our $VERSION = '0.001'; # VERSION
8              
9 1     1   1988 use Moo;
  1         27402  
  1         7  
10 1     1   3894 use MooX::Types::MooseLike::Base qw/Bool/;
  1         9699  
  1         232  
11 1     1   1159 use MooX::Types::MooseLike::Numeric qw/PositiveNum/;
  1         2040  
  1         134  
12 1     1   1276 use List::AllUtils qw/min max/;
  1         8430  
  1         125  
13              
14             use constant {
15 1         1422 NAME => 0,
16             MIN => 1,
17             Q1 => 2,
18             MED => 3,
19             Q3 => 4,
20             MAX => 5,
21 1     1   11 };
  1         2  
22              
23              
24              
25             has width => (
26             is => 'ro',
27             isa => PositiveNum,
28             default => sub { 72 },
29             );
30              
31              
32             has label_width => (
33             is => 'ro',
34             isa => PositiveNum,
35             default => sub { 10 },
36             );
37              
38              
39             has box_weight => (
40             is => 'ro',
41             isa => PositiveNum,
42             default => sub { 1 },
43             );
44              
45              
46             has with_scale => (
47             is => 'ro',
48             isa => Bool,
49             );
50              
51              
52             sub render {
53 122     122 1 72965 my ( $self, @datasets ) = @_;
54 122   50     612 my $gamma = 2 * max( 0, $self->box_weight || 1 );
55 122         340 my $adj_width = $self->width - $self->label_width - 2;
56              
57 122         190 my $smallest_min = min( map { $_->[MIN] } @datasets );
  183         545  
58 122         190 my $smallest_q1 = min( map { $_->[Q1] } @datasets );
  183         468  
59 122         193 my $biggest_q3 = max( map { $_->[Q3] } @datasets );
  183         437  
60 122         189 my $biggest_max = max( map { $_->[MAX] } @datasets );
  183         424  
61              
62 122   50     295 my $span = ( $biggest_q3 - $smallest_q1 ) || 1;
63 122         270 my $factor = $adj_width * $gamma / ( 2 + $gamma ) / $span;
64              
65 122         229 my $origin = int( $factor * ( $smallest_q1 - $span / $gamma ) );
66 122         220 my $edge = int( $factor * ( $biggest_q3 + $span / $gamma ) );
67             ## warn "SPAN: $span; FACTOR: $factor; ORIGIN: $origin; EDGE: $edge; AW: $adj_width (" . ($edge - $origin) . ")\n";
68              
69 122         135 my @str;
70 122 50       306 if ( $self->with_scale ) {
71 0         0 push @str,
72             ( " " x ($self->label_width) )
73             . sprintf( " |%-*g%*g|",
74             $adj_width / 2,
75             $origin / $factor,
76             $adj_width / 2,
77             $edge / $factor );
78             }
79              
80 122         203 for my $d (@datasets) {
81 183         544 my ( $name, @copy ) = @$d;
82             ## warn "PRECOPY: @copy\n";
83 183         279 my @scaled = ( $name, map { int( $factor * $_ ) } @copy );
  915         1737  
84             ## warn "POSTCOPY: @scaled\n";
85 183         569 push @str, _render_one( \@scaled, $origin, $edge, $adj_width, $self->label_width );
86             }
87              
88 122 50       551 return wantarray ? @str : $str[0];
89             }
90              
91             sub _render_one {
92 183     183   264 my ( $data, $origin, $edge, $frame_size, $label_width ) = @_;
93             ## warn "DATA: @$data\n";
94 183         239 my $str = '';
95 183         534 $str .= q{ } x ( max( $data->[MIN] - $origin, 0 ) );
96 183         474 $str .= q{-} x ( $data->[Q1] - max( $data->[MIN], $origin ) );
97 183         317 $str .= q{=} x ( $data->[MED] - $data->[Q1] );
98 183         206 $str .= "O";
99 183         280 $str .= q{=} x ( $data->[Q3] - $data->[MED] );
100 183         442 $str .= q{-} x ( min( $data->[MAX], $edge ) - $data->[Q3] );
101 183         386 $str .= q{ } x ( max( $edge - $data->[MAX], 0 ) );
102             ## warn "STR: " . length($str) . "\n";
103 183         376 $str = substr( $str, 0, $frame_size );
104             ## $str =~ s{^(.{0,$frame_size})}{$1};
105             ## warn "STR: " . length($str) . "\n";
106              
107 183 100       408 if ( substr( $str, 0, 1 ) eq '-' ) {
108 61         101 substr( $str, 0, 1, "<" );
109             }
110              
111 183 100       383 if ( substr( $str, -1, 1 ) eq '-' ) {
112 122         196 substr( $str, -1, 1, "->" );
113             }
114              
115 183         584 $str =~ s{\s+$}{};
116 183         322 my $name = substr($data->[NAME],0, $label_width);
117 183         1147 return sprintf( "%*s %s", $label_width, $name, $str );
118             }
119              
120             1;
121              
122              
123             # vim: ts=4 sts=4 sw=4 et:
124              
125             __END__