differential-0.1.1.0: src/Differential.hs
{- Differential
Gregory W. Schwartz
Collects the functions pertaining to finding the differential between groups for
each entity.
-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TupleSections #-}
module Differential
( getDifferentials
, differentialMatrixObsRow
, differentialMatrixFeatRow
, getDifferential
, edgeR
) where
-- Standard
import Control.Monad (guard)
import Data.Int (Int32)
import Data.List
import Data.Semigroup
import TextShow (showt)
import qualified Control.Foldl as Fold
import qualified Control.Lens as L
import qualified Data.Aeson.Lens as L
import qualified Data.Foldable as F
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.Scientific as Scientific
import qualified Data.Sequence as Seq
import qualified Data.Sparse.Common as S
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as V
import qualified H.Prelude as H
import qualified Statistics.Test.KruskalWallis as Stat
import qualified Statistics.Types as Stat
-- Cabal
import Language.R as R
import Language.R.Instance as R
import Language.R.Literal as R
import Language.R.QQ
-- Local
import Types
import Utility
-- | Get unique pairings of a list. From
-- http://stackoverflow.com/questions/34044366/how-to-extract-all-unique-pairs-of-a-list-in-haskell
pairs :: [a] -> [(a, a)]
pairs l = [(x,y) | (x:ys) <- tails l, y <- ys]
-- | Find the p-value of two samples.
differential :: [Double] -> [Double] -> R s (Maybe PValue)
differential xs ys = [r| suppressWarnings(wilcox.test(xs_hs, ys_hs))$p.value |]
>>= (\x -> return . Just . PValue $ ((R.fromSomeSEXP x) :: Double))
-- | Find the p-value of two samples using the Kruskal-Wallis test.
differentialKW :: [Double] -> [Double] -> Maybe PValue
differentialKW xs ys = do
guard ((> 1) . Set.size . Set.fromList $ xs <> ys) -- Ensure not everything is the same rank
res <- Stat.kruskalWallisTest [V.fromList xs, V.fromList ys]
return . PValue . Stat.pValue . Stat.testSignificance $ res
-- | For two lists, xs and ys, find the log2 fold change as mean ys / mean xs.
getLog2Diff :: [Double] -> [Double] -> Log2Diff
getLog2Diff xs ys = Log2Diff . logBase 2 $ getMean ys / getMean xs
where
getMean = Fold.fold Fold.mean
-- | Get a comparison using the Kruskal-Wallis test.
getDifferential :: Status
-> Status
-> [Double]
-> [Double]
-> (Comparison, Log2Diff, Maybe PValue)
getDifferential (Status !s1) (Status !s2) !l1 !l2 = (comp, diff, pVal)
where
pVal = differentialKW l1 l2
diff = getLog2Diff l1 l2
comp = Comparison (s2 <> "/" <> s1)
-- | Get all comparisons of a Name.
getNameDifferentials :: Map.Map Status (Seq.Seq Double) -> ComparisonMap
getNameDifferentials m = ComparisonMap . Map.fromList . fmap comp $ comparisons
where
comparisons = pairs . Map.keys $ m
comp (!s1, !s2) = (\(x, _, z) -> (x, z))
$ getDifferential
s1
s2
(F.toList . (Map.!) m $ s1)
(F.toList . (Map.!) m $ s2)
-- | Convert a ComparisonMap to an OutputMap.
comparisonMapToOutputMap :: ComparisonMap -> OutputMap
comparisonMapToOutputMap = OutputMap
. Map.map (maybe "NA" (showt . unPValue))
. Map.mapKeys unComparison
. unComparisonMap
-- | Get all p-values in all relevant combinations.
getDifferentials :: NameMap -> [(Name, OutputMap)]
getDifferentials (NameMap nameMap) =
Map.elems
. Map.mapWithKey (\ !k -> (k,)
. comparisonMapToOutputMap
. getNameDifferentials
)
$ nameMap
-- | Get differentials between columns (features) of select rows (observations)
-- of bs / as, where as and bs are lists of row indices.
differentialMatrixObsRow :: [Int] -- ^ as
-> [Int] -- ^ bs
-> S.SpMatrix Double
-> [(Log2Diff, Maybe PValue, Maybe FDR)]
differentialMatrixObsRow as bs = differentialMatrixFeatRow as bs . S.transpose
-- | Get differentials between columns (observations) of select rows (features)
-- of bs / as, where as and bs are lists of row indices.
differentialMatrixFeatRow :: [Int] -- ^ as
-> [Int] -- ^ bs
-> S.SpMatrix Double
-> [(Log2Diff, Maybe PValue, Maybe FDR)]
differentialMatrixFeatRow as bs = withFDR . fmap obsToDiff . S.toRowsL
where
withFDR xs = zipWith (\(!l, !p) fdr -> (l, p, fdr)) xs
. getFDR 0.05
. fmap snd
$ xs
obsToDiff vec = (\(_, !l, !p) -> (l, p))
. getDifferential
(Status "A")
(Status "B")
(obsToVals vec as)
$ (obsToVals vec bs)
obsToVals features = fmap (flip S.lookupDenseSV features)
-- | Get edgeR differential expression from a two dimensional matrix.
edgeR :: Int -> TwoDMat -> R s [(Name, Double, PValue, FDR)]
edgeR topN mat = do
let ss = fmap (T.unpack . unStatus) . _colStatus $ mat
topN32 = fromIntegral topN :: Int32
rMat <- fmap unRMat $ twoDMatToRMat mat
resR <- [r| library(edgeR)
group = factor(ss_hs)
y = DGEList(counts = rMat_hs, group = group)
# Keep genes with at least 1 count per million (cpm) in at least two samples.
countsPerMillion = cpm(y)
countCheck = countsPerMillion > 1
keep = which(rowSums(countCheck) >= 2)
y = y[keep,]
# Normalize
y = calcNormFactors(y)
design = model.matrix(~ group)
y = estimateDisp(y, design)
fit = glmFit(y, design)
lrt = glmLRT(fit, coef = 2)
res = topTags(lrt, n = topN32_hs)$table
# return(jsonlite::toJSON(res))
return(res)
|]
-- let df = R.fromSomeSEXP resR :: String
-- genes = fmap Name $ df L.^.. L.values . L.key "_row" . L._String
-- vals = fmap Scientific.toRealFloat
-- $ df L.^.. L.values . L.key "logFC" . L._Number
-- pVals = fmap (PValue . Scientific.toRealFloat)
-- $ df L.^.. L.values . L.key "PValue" . L._Number
-- fdrs = fmap (FDR . Scientific.toRealFloat)
-- $ df L.^.. L.values . L.key "FDR" . L._Number
genesR <- [r| row.names(resR_hs) |]
valsR <- [r| resR_hs$logFC |]
pValsR <- [r| resR_hs$PValue |]
fdrsR <- [r| resR_hs$FDR |]
let genes = fmap (Name . T.pack) (R.dynSEXP genesR :: [String])
vals = R.dynSEXP valsR :: [Double]
pVals = fmap PValue (R.dynSEXP pValsR :: [Double])
fdrs = fmap FDR (R.dynSEXP fdrsR :: [Double])
return . zip4 genes vals pVals $ fdrs