1#!/usr/local/bin/perl
2#  ********************************************************************
3#  * COPYRIGHT:
4#  * © 2016 and later: Unicode, Inc. and others.
5#  * License & terms of use: http://www.unicode.org/copyright.html#License
6#  * Copyright (c) 2006, International Business Machines Corporation and
7#  * others. All Rights Reserved.
8#  ********************************************************************
9
10package Dataset;
11use Statistics::Descriptive;
12use Statistics::Distributions;
13use strict;
14
15# Create a new Dataset with the given data.
16sub new {
17    my ($class) = shift;
18    my $self = bless {
19        _data => \@_,
20        _scale => 1.0,
21        _mean => 0.0,
22        _error => 0.0,
23    }, $class;
24
25    my $n = @_;
26
27    if ($n >= 1) {
28        my $stats = Statistics::Descriptive::Full->new();
29        $stats->add_data(@{$self->{_data}});
30        $self->{_mean} = $stats->mean();
31
32        if ($n >= 2) {
33            # Use a t distribution rather than Gaussian because (a) we
34            # assume an underlying normal dist, (b) we do not know the
35            # standard deviation -- we estimate it from the data, and (c)
36            # we MAY have a small sample size (also works for large n).
37            my $t = Statistics::Distributions::tdistr($n-1, 0.005);
38            $self->{_error} = $t * $stats->standard_deviation();
39        }
40    }
41
42    $self;
43}
44
45# Set a scaling factor for all data; 1.0 means no scaling.
46# Scale must be > 0.
47sub setScale {
48    my ($self, $scale) = @_;
49    $self->{_scale} = $scale;
50}
51
52# Multiply the scaling factor by a value.
53sub scaleBy {
54    my ($self, $a) = @_;
55    $self->{_scale} *= $a;
56}
57
58# Return the mean.
59sub getMean {
60    my $self = shift;
61    return $self->{_mean} * $self->{_scale};
62}
63
64# Return a 99% error based on the t distribution.  The dataset
65# is desribed as getMean() +/- getError().
66sub getError {
67    my $self = shift;
68    return $self->{_error} * $self->{_scale};
69}
70
71# Divide two Datasets and return a new one, maintaining the
72# mean+/-error.  The new Dataset has no data points.
73sub divide {
74    my $self = shift;
75    my $rhs = shift;
76
77    my $minratio = ($self->{_mean} - $self->{_error}) /
78                   ($rhs->{_mean} + $rhs->{_error});
79    my $maxratio = ($self->{_mean} + $self->{_error}) /
80                   ($rhs->{_mean} - $rhs->{_error});
81
82    my $result = Dataset->new();
83    $result->{_mean} = ($minratio + $maxratio) / 2;
84    $result->{_error} = $result->{_mean} - $minratio;
85    $result->{_scale} = $self->{_scale} / $rhs->{_scale};
86    $result;
87}
88
89# subtracts two Datasets and return a new one, maintaining the
90# mean+/-error.  The new Dataset has no data points.
91sub subtract {
92    my $self = shift;
93    my $rhs = shift;
94
95    my $result = Dataset->new();
96    $result->{_mean} = $self->{_mean} - $rhs->{_mean};
97    $result->{_error} = $self->{_error} + $rhs->{_error};
98    $result->{_scale} = $self->{_scale};
99    $result;
100}
101
102# adds two Datasets and return a new one, maintaining the
103# mean+/-error.  The new Dataset has no data points.
104sub add {
105    my $self = shift;
106    my $rhs = shift;
107
108    my $result = Dataset->new();
109    $result->{_mean} = $self->{_mean} + $rhs->{_mean};
110    $result->{_error} = $self->{_error} + $rhs->{_error};
111    $result->{_scale} = $self->{_scale};
112    $result;
113}
114
115# Divides a dataset by a scalar.
116# The new Dataset has no data points.
117sub divideByScalar {
118    my $self = shift;
119    my $s = shift;
120
121    my $result = Dataset->new();
122    $result->{_mean} = $self->{_mean}/$s;
123    $result->{_error} = $self->{_error}/$s;
124    $result->{_scale} = $self->{_scale};
125    $result;
126}
127
128# Divides a dataset by a scalar.
129# The new Dataset has no data points.
130sub multiplyByScalar {
131    my $self = shift;
132    my $s = shift;
133
134    my $result = Dataset->new();
135    $result->{_mean} = $self->{_mean}*$s;
136    $result->{_error} = $self->{_error}*$s;
137    $result->{_scale} = $self->{_scale};
138    $result;
139}
140
1411;
142