-- |-- Module : Criterion.Analysis-- Copyright : (c) Bryan O'Sullivan 2009---- License : BSD-style-- Maintainer : bos@serpentine.com-- Stability : experimental-- Portability : GHC---- Analysis code for benchmarks.moduleCriterion.Analysis(Outliers(..),OutlierVariance(..),analyseMean,countOutliers,classifyOutliers,noteOutliers,outlierVariance)whereimportControl.Monad(when)importCriterion.Config(Config)importCriterion.IO(note)importCriterion.Measurement(secs)importData.Array.Vector(foldlU)importData.Int(Int64)importData.Monoid(Monoid(..))importStatistics.Function(sort)importStatistics.Quantile(weightedAvg)importStatistics.Resampling.Bootstrap(Estimate(..))importStatistics.Sample(mean)importStatistics.Types(Sample)-- | Outliers from sample data, calculated using the boxplot-- technique.dataOutliers=Outliers{samplesSeen::{-# UNPACK #-}!Int64,lowSevere::{-# UNPACK #-}!Int64-- ^ More than 3 times the IQR below the first quartile.,lowMild::{-# UNPACK #-}!Int64-- ^ Between 1.5 and 3 times the IQR below the first quartile.,highMild::{-# UNPACK #-}!Int64-- ^ Between 1.5 and 3 times the IQR above the third quartile.,highSevere::{-# UNPACK #-}!Int64-- ^ More than 3 times the IQR above the third quartile.}deriving(Eq,Read,Show)-- | A description of the extent to which outliers in the sample data-- affect the sample mean and standard deviation.dataOutlierVariance=Unaffected-- ^ Less than 1% effect.|Slight-- ^ Between 1% and 10%.|Moderate-- ^ Between 10% and 50%.|Severe-- ^ Above 50% (i.e. measurements-- are useless).deriving(Eq,Ord,Show)instanceMonoidOutlierswheremempty=Outliers00000mappend=addOutliersaddOutliers::Outliers->Outliers->OutliersaddOutliers(Outlierssabcd)(Outlierstwxyz)=Outliers(s+t)(a+w)(b+x)(c+y)(d+z){-# INLINE addOutliers #-}-- | Classify outliers in a data set, using the boxplot technique.classifyOutliers::Sample->OutliersclassifyOutlierssa=foldlU((.outlier).mappend)memptyssawhereoutliere=Outliers{samplesSeen=1,lowSevere=ife<=loSthen1else0,lowMild=ife>loS&&e<=loMthen1else0,highMild=ife>=hiM&&e<hiSthen1else0,highSevere=ife>=hiSthen1else0}loS=q1-(iqr*3)loM=q1-(iqr*1.5)hiM=q3+(iqr*1.5)hiS=q3+(iqr*3)q1=weightedAvg14ssaq3=weightedAvg34ssassa=sortsaiqr=q3-q1{-# INLINE classifyOutliers #-}-- | Compute the extent to which outliers in the sample data affect-- the sample mean and standard deviation.outlierVariance::Estimate-- ^ Bootstrap estimate of sample mean.->Estimate-- ^ Bootstrap estimate of sample-- standard deviation.->Double-- ^ Number of original iterations.->(OutlierVariance,Double)outlierVarianceµσa=(effect,varOutMin)whereeffect|varOutMin<0.01=Unaffected|varOutMin<0.1=Slight|varOutMin<0.5=Moderate|otherwise=SeverevarOutMin=(minByvarOut1(minBycMax0µgMin))/σb2varOutc=(ac/a)*(σb2-ac*σg2)whereac=a-cσb=estPointσµa=estPointµ/aµgMin=µa/2σg=min(µgMin/4)(σb/sqrta)σg2=σg*σgσb2=σb*σbminByfqr=min(fq)(fr)cMaxx=fromIntegral(floor(-2*k0/(k1+sqrtdet))::Int)wherek1=σb2-a*σg2+adk0=-a*adad=a*dd=k*2wherek=µa-xdet=k1*k1-4*σg2*k0-- | Count the total number of outliers in a sample.countOutliers::Outliers->Int64countOutliers(Outliers_abcd)=a+b+c+d{-# INLINE countOutliers #-}-- | Display the mean of a 'Sample', and characterise the outliers-- present in the sample.analyseMean::Config->Sample->Int-- ^ Number of iterations used to-- compute the sample.->IODoubleanalyseMeancfgaiters=doletµ=meananotecfg"mean is %s (%d iterations)\n"(secsµ)itersnoteOutlierscfg.classifyOutliers$areturnµ-- | Display a report of the 'Outliers' present in a 'Sample'.noteOutliers::Config->Outliers->IO()noteOutlierscfgo=doletfracn=(100::Double)*fromIntegraln/fromIntegral(samplesSeeno)check::Int64->Double->String->IO()checkktd=when(frack>t)$notecfg" %d (%.1g%%) %s\n"k(frack)doutCount=countOutliersowhen(outCount>0)$donotecfg"found %d outliers among %d samples (%.1g%%)\n"outCount(samplesSeeno)(fracoutCount)check(lowSevereo)0"low severe"check(lowMildo)1"low mild"check(highMildo)1"high mild"check(highSevereo)0"high severe"