Created
November 20, 2018 17:54
-
-
Save Akii/f42e14e1c70fbaabfa1e062064c4919c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module IQR | |
( Sample | |
, mkSample | |
, singletonSample | |
, insertSample | |
, insertSample' | |
, restrictSampleSize | |
, withoutDuplicates | |
, median | |
, lowerQuartile | |
, upperQuartile | |
, iqr | |
, lowerResiduals | |
, upperResiduals | |
, residuals | |
, isResidual | |
) where | |
import ClassyPrelude hiding (head) | |
import Data.List (nub, insert) | |
import Data.List.NonEmpty (NonEmpty (..)) | |
import Prelude (head, (!!)) | |
newtype Sample = Sample | |
{ getSample :: [Double] | |
} deriving (Show) | |
instance Semigroup Sample where | |
sample1 <> sample2 = Sample . sort $ (getSample sample1 <> getSample sample2) | |
mkSample :: NonEmpty Double -> Sample | |
mkSample = Sample . sort . toList | |
singletonSample :: Double -> Sample | |
singletonSample = Sample . pure | |
insertSample :: Double -> Sample -> Sample | |
insertSample value = Sample . insert value . getSample | |
insertSample' :: Double -> Sample -> Sample | |
insertSample' value s@(Sample sample) = | |
if value `elem` sample | |
then s | |
else insertSample value s | |
withoutDuplicates :: Sample -> Sample | |
withoutDuplicates = Sample . nub . getSample | |
-- | This will keep the last n items of the sample. | |
restrictSampleSize :: Int -> Sample -> Sample | |
restrictSampleSize n | |
| n < 1 = error "Cannot use sample size smaller than 1" | |
| otherwise = Sample . reverse . take n . reverse . getSample | |
median :: Sample -> Double | |
median (Sample sample) = | |
if odd sampleLength | |
then sample !! middleIndex | |
else (sample !! (middleIndex - 1) + sample !! middleIndex) / 2 | |
where | |
sampleLength = length sample | |
middleIndex = sampleLength `div` 2 | |
lowerQuartile :: Sample -> Double | |
lowerQuartile (Sample sample) = | |
if null lowerHalf | |
then head sample | |
else median (Sample lowerHalf) | |
where | |
lowerHalf = take (length sample `div` 2) sample | |
upperQuartile :: Sample -> Double | |
upperQuartile (Sample sample) = | |
if null upperHalf | |
then head (reverse sample) | |
else median (Sample upperHalf) | |
where | |
upperHalf = drop (ceiling $ fromIntegral (length sample) / (2 :: Double)) sample | |
iqr :: Sample -> Double | |
iqr sample = upperQuartile sample - lowerQuartile sample | |
lowerResiduals :: Sample -> [Double] | |
lowerResiduals sample = | |
let q1 = lowerQuartile sample | |
lowerLimit = q1 - 1.5 * iqr sample | |
in filter (< lowerLimit) (getSample sample) | |
upperResiduals :: Sample -> [Double] | |
upperResiduals sample = | |
let q3 = upperQuartile sample | |
upperLimit = q3 + 1.5 * iqr sample | |
in filter (> upperLimit) (getSample sample) | |
residuals :: Sample -> [Double] | |
residuals sample = lowerResiduals sample ++ upperResiduals sample | |
isResidual :: Double -> Sample -> Bool | |
isResidual value sample = value `elem` residuals (insertSample value sample) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment