File Coverage

blib/lib/PDL/Factor.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package PDL::Factor;
2             $PDL::Factor::VERSION = '0.002';
3 1     1   72600 use strict;
  1         2  
  1         42  
4 1     1   7 use warnings;
  1         2  
  1         28  
5              
6 1     1   476 use Moo;
  1         12874  
  1         5  
7 1     1   1742 use PDL::Lite;
  1         129147  
  1         30  
8 1     1   569 use Tie::IxHash;
  1         2834  
  1         23  
9 1     1   337 use Tie::IxHash::Extension;
  0            
  0            
10             use Data::Rmap qw(rmap);
11             use Storable qw(dclone);
12             use Scalar::Util qw(blessed);
13             use Test::Deep::NoTest qw(eq_deeply);
14              
15             extends 'PDL';
16             with 'PDL::Role::Enumerable';
17              
18             # after stringifiable role is added, the string method will exist
19             eval q{
20             use overload (
21             '""' => \&PDL::Factor::string,
22             '==' => \&PDL::Factor::equal,
23             '!=' => \&PDL::Factor::not_equal,
24             );
25             };
26              
27             around new => sub {
28             my $orig = shift;
29             my ($class, @args) = @_;
30             my $data;
31             # TODO UGLY! create a better interface
32             #
33             # new( integer => $enum, levels => $level_arrayref )
34             # new( $data_arrayref, levels => $level_arrayref )
35             # etc.
36             #
37             # Look at how R does it.
38             if( @args % 2 != 0 ) {
39             $data = shift @args; # first arg
40             }
41             my %opt = @args;
42              
43             my $levels = Tie::IxHash->new;
44             my $enum = $opt{integer} // dclone($data);
45             if( exists $opt{levels} ) {
46             # add the levels first if given levels option
47             for my $l (@{ $opt{levels} } ) {
48             $levels->Push( $l => 1 );
49             }
50             # TODO what if the levels passed in are not unique?
51             # TODO what if the integer enum data outside the range of level indices?
52             } else {
53             rmap {
54             my $v = $_;
55             $levels->Push($v => 1); # add value to hash if it doesn't exist
56             $_ = $levels->Indices($v); # assign index of level
57             } $enum;
58             }
59              
60             unshift @args, _data => $enum;
61             unshift @args, _levels => $levels;
62              
63             # TODO how do I pass the prefered type to PDL->new()?
64             my $self = $orig->($class, @args);
65             $self->{PDL} = $self->{PDL}->long;
66              
67             $self;
68             };
69              
70             sub FOREIGNBUILDARGS {
71             my ($self, %args) = @_;
72             ( $args{_data} );
73             }
74              
75             sub initialize {
76             bless { PDL => PDL::null() }, shift;
77             }
78              
79             around string => sub {
80             my $orig = shift;
81             my ($self, %opt) = @_;
82             my $ret = $orig->(@_);
83             if( exists $opt{with_levels} ) {
84             my @level_string = grep { defined } $self->{_levels}->Keys();
85             $ret .= "\n";
86             $ret .= "Levels: @level_string";
87             }
88             $ret;
89             };
90              
91             # TODO overload, compare factor level sets
92             #
93             #R
94             # > g <- iris
95             # > levels(g$Species) <- c( levels(g$Species), "test")
96             # > iris$Species == g$Species
97             # : Error in Ops.factor(iris$Species, g$Species) :
98             # : level sets of factors are different
99             #
100             # > g <- iris
101             # > levels(g$Species) <- levels(g$Species)[c(3, 2, 1)]
102             # > iris$Species == g$Species
103             # : # outputs a logical vector where only 'versicolor' indices are TRUE
104             sub equal {
105             my ($self, $other, $d) = @_;
106             # TODO need to look at $d to determine direction
107             if( blessed($other) && $other->isa('PDL::Factor') ) {
108             if( eq_deeply($self->_levels, $other->_levels) ) {
109             return $self->{PDL} == $other->{PDL};
110             # TODO return a PDL::Logical
111             } else {
112             die "level sets of factors are different";
113             }
114             } else {
115             # TODO hacky. need to test this more
116             my $key_idx = $self->_levels->Indices($other);
117             return $self->{PDL} == $key_idx;
118             }
119             }
120              
121             sub not_equal {
122             return !equal(@_);
123             }
124              
125              
126             1;
127              
128             __END__