[go: up one dir, main page]

package Chart::GGPlot::Stat;

# ABSTRACT: The stat role

use Chart::GGPlot::Role qw(:pdl);
use namespace::autoclean;

our $VERSION = '0.002003'; # VERSION

use List::AllUtils qw(reduce pairmap);
use Types::Standard qw(ArrayRef CodeRef Str InstanceOf Bool);
use Types::PDL -types;

use Data::Frame;
use Chart::GGPlot::Trans;
use Chart::GGPlot::Types qw(:all);
use Chart::GGPlot::Util qw(remove_missing stat);

has retransform => ( is => 'ro' );

with qw(
  Chart::GGPlot::HasRequiredAes
  Chart::GGPlot::HasDefaultAes
  Chart::GGPlot::HasNonMissingAes
  Chart::GGPlot::HasParams
  Chart::GGPlot::HasCollectibleFunctions
);

method setup_data ( $data, $params ) { $data }

method setup_params ( $data, $params ) { $params }

method compute_layer ( $data, $params, $layout ) {
    $self->check_required_aes(
        [ @{ $data->names }, @{ $params->names } ] );

    $data = remove_missing(
        $data,
        na_rm  => $params->at('na_rm'),
        vars   => [ @{ $self->required_aes }, @{ $self->non_missing_aes } ],
        name   => ref($self),
        finite => true,
    );

    # Trim off extra parameters
    my $params =
      $params->slice( $params->names->intersect($self->parameters) );

    my $splitted = $data->split( $data->at('PANEL') );
    return (
        reduce { $a->cbind($b) } $splitted->keys->map(
            sub {
                my ($panel_id) = @_;
                my $d = $splitted->{$panel_id};

                my $scales = $layout->get_scales( $d->at('PANEL')->at(0) );

                try {
                    return $self->compute_panel( $d, $scales, $params );
                }
                catch ($e) {
                    die sprintf( "Computation failed in '%s': $e", ref($self) );
                }
            }
        )->flatten
    );
}

method compute_panel ( $data, $scales, $params ) {
    return Data::Frame->new() if ( $data->isempty );

    my $groups = $data->split( $data->at('group') );
    my $stats = {
        pairmap { $a => $self->compute_group( $b, $scales, $params ); }
        %$groups
    };

    # cbind the result of compute_group() with splitted df,
    #  and also rbind to form one df that corresponds to the $data.
    $stats = [
        map {   # $_ is group id
            my $new_df  = $stats->{$_};
            my $old_df  = $groups->{$_};
            my $missing = $old_df->names->setdiff($new_df->names);
            for my $colname (@$missing) {
                $new_df->set( $colname,
                    $old_df->at($colname)->slice( pdl( [0] ) ) );
            }
            $new_df;
        } sort { $a <=> $b } @{ $stats->keys }
    ];

    return reduce { $a->rbind($b) } @$stats;
}

method compute_group ( $data, $scales ) { ... }

method finish_layer ( $data, $param ) { $data }

method aesthetics () {
    my @names = List::AllUtils::uniq( @{ $self->required_aes },
        @{ $self->default_aes->names }, 'group' );
    return \@names;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Chart::GGPlot::Stat - The stat role

=head1 VERSION

version 0.002003

=head1 DESCRIPTION

This module is a Moose role for "stats".

For users of Chart::GGPlot you would mostly want to look at
L<Chart::GGPlot::Stat::Functions> instead.

=head1 AUTHOR

Stephan Loyd <sloyd@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2019-2023 by Stephan Loyd.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut