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