bioinformatics-toolkit 0.2.0 → 0.2.1
raw patch · 13 files changed
+492/−308 lines, 13 filesdep ~http-conduit
Dependency ranges changed: http-conduit
Files
- app/viewSeq.hs +5/−5
- bioinformatics-toolkit.cabal +3/−2
- data/1.fasta +0/−30
- src/Bio/ChIPSeq.hs +2/−2
- src/Bio/Data/Bed.hs +327/−160
- src/Bio/GO/GREAT.hs +37/−39
- src/Bio/Motif.hs +3/−3
- src/Bio/Seq/IO.hs +49/−65
- src/Bio/Utils/Functions.hs +1/−1
- tests/Tests/Bed.hs +28/−0
- tests/Tests/GREAT.hs +34/−0
- tests/Tests/Motif.hs +1/−1
- tests/test.hs +2/−0
app/viewSeq.hs view
@@ -8,8 +8,8 @@ main :: IO () main = do [fl, chr, start, end] <- getArgs- g <- pack fl- s <- getSeqs g [(B.pack chr, read start, read end)] :: IO [Either String (DNA IUPAC)]- case head s of- Left err -> error err- Right x -> B.putStrLn . toBS $ x+ withGenome fl $ \g -> do+ s <- getSeq g (B.pack chr, read start, read end) :: IO (Either String (DNA IUPAC))+ case s of+ Left err -> error err+ Right x -> B.putStrLn . toBS $ x
bioinformatics-toolkit.cabal view
@@ -2,7 +2,7 @@ -- further documentation, see http://haskell.org/cabal/users-guide/ name: bioinformatics-toolkit-version: 0.2.0+version: 0.2.1 synopsis: A collection of bioinformatics tools description: A collection of bioinformatics tools license: MIT@@ -63,7 +63,7 @@ , containers >=0.5 , data-default-class , double-conversion- , http-conduit+ , http-conduit >=2.1.8 , hexpat , matrices >=0.4.3 , mtl >=2.1.3.1@@ -144,6 +144,7 @@ , Tests.ChIPSeq , Tests.Motif , Tests.Seq+ , Tests.GREAT default-language: Haskell2010 build-depends:
− data/1.fasta
@@ -1,30 +0,0 @@-MEME version 4--ALPHABET= ACGT--strands: + ---Background letter frequencies-A 0.25 C 0.25 G 0.25 T 0.25--MOTIF tree0.2_0_GGAKATWATMTC-letter-probability matrix: alength= 4 w= 12 nsites= 1 E= 0- 0.001 0.001 0.997 0.001- 0.001 0.003 0.995 0.001- 0.997 0.001 0.001 0.001- 0.001 0.001 0.474 0.524- 0.997 0.001 0.001 0.001- 0.997 0.001 0.001 0.001- 0.25 0.25 0.25 0.25- 0.25 0.25 0.25 0.25--MOTIF tree0.2_1_CTCCCTAAAATG-letter-probability matrix: alength= 4 w= 12 nsites= 1 E= 0- 0.25 0.25 0.25 0.25- 0.25 0.25 0.25 0.25- 0.001 0.001 0.997 0.001- 0.001 0.003 0.995 0.001- 0.997 0.001 0.001 0.001- 0.001 0.001 0.474 0.524- 0.997 0.001 0.001 0.001- 0.997 0.001 0.001 0.001
src/Bio/ChIPSeq.hs view
@@ -79,7 +79,7 @@ vec <- lift $ GM.replicate l 0 n <- foldMC (count vec) (0 :: Int) let factor = fromIntegral n / 1e9- lift $ liftM (G.imap (\i x -> x / factor / (fromIntegral . size) (regions V.! i)))+ lift $ liftM (G.imap (\i x -> x / factor / (fromIntegral . bedSize) (regions V.! i))) $ G.unsafeFreeze vec where count v nTags tag = do@@ -246,7 +246,7 @@ peaks' = map f peaks f b = let chr = chrom b c = (chromStart b + chromEnd b) `div` 2- in asBed chr (c-r) (c+r)+ in BED3 chr (c-r) (c+r) mergeFn xs = BED (chrom $ head xs) lo hi Nothing (Just $ fromIntegral $ length xs) Nothing where lo = minimum . map chromStart $ xs
src/Bio/Data/Bed.hs view
@@ -15,9 +15,19 @@ module Bio.Data.Bed ( BEDLike(..)++ -- * BED6 format+ , BED(..)+ -- * BED3 format+ , BED3(..)+ -- * NarrowPeak format+ , NarrowPeak(..)+ , BEDTree , bedToTree , sortedBedToTree+ , intersecting+ , isIntersected , splitBed , splitBedBySize , splitBedBySizeLeft@@ -33,14 +43,23 @@ , mergeBedWith , mergeSortedBed , mergeSortedBedWith- -- * BED6 format- , BED(..)- -- * BED3 format- , BED3(..)+ , splitOverlapped+ , hReadBed+ , hReadBed'+ , readBed+ , readBed'+ , hWriteBed+ , hWriteBed'+ , writeBed+ , writeBed'++ -- * Utilities , fetchSeq , fetchSeq'+ , motifScan+ , getMotifScore+ , getMotifPValue , compareBed- , convert ) where import Control.Arrow ((***))@@ -52,89 +71,203 @@ import qualified Data.Foldable as F import qualified Data.HashMap.Strict as M import qualified Data.IntervalMap.Strict as IM-import Data.List (groupBy)-import Data.Maybe (fromMaybe)+import Data.List (groupBy, sortBy)+import Data.Maybe (fromMaybe, fromJust) import qualified Data.Vector as V import qualified Data.Vector.Algorithms.Intro as I import System.IO+import Data.ByteString.Lex.Integral (packDecimal)+import Data.Double.Conversion.ByteString (toShortest)+import Data.Ord (comparing) +import Bio.Motif hiding (_name)+import Bio.Motif.Search import Bio.Seq import Bio.Seq.IO import Bio.Utils.Misc ( binBySizeLeft, binBySize, binBySizeOverlap, bins , readInt, readDouble ) --- | a class representing BED-like data, e.g., BED3, BED6 and BED12. BED format+-- | A class representing BED-like data, e.g., BED3, BED6 and BED12. BED format -- uses 0-based index (see documentation). class BEDLike b where- -- | construct bed record from chromsomoe, start location and end location+ -- | Construct bed record from chromsomoe, start location and end location asBed :: B.ByteString -> Int -> Int -> b - -- | convert bytestring to bed format+ -- | Convert bytestring to bed format fromLine :: B.ByteString -> b - -- | convert bed to bytestring+ -- | Convert bed to bytestring toLine :: b -> B.ByteString - -- | field accessor+ -- | Field accessor chrom :: b -> B.ByteString chromStart :: b -> Int chromEnd :: b -> Int+ bedName :: b -> Maybe B.ByteString+ bedScore :: b -> Maybe Double+ bedStrand :: b -> Maybe Bool - toBed3 :: b -> BED3- toBed3 bed = BED3 (chrom bed) (chromStart bed) (chromEnd bed)+ convert :: BEDLike b' => b' -> b+ convert bed = asBed (chrom bed) (chromStart bed) (chromEnd bed)+ {-# INLINE convert #-} - -- | return the size of a bed region- size :: b -> Int- size bed = chromEnd bed - chromStart bed+ -- | Return the size of a bed region.+ bedSize :: b -> Int+ bedSize bed = chromEnd bed - chromStart bed - hReadBed :: MonadIO m => Handle -> Source m b- hReadBed h = do eof <- liftIO $ hIsEOF h- unless eof $ do- line <- liftIO $ B.hGetLine h- yield $ fromLine line- hReadBed h- {-# INLINE hReadBed #-}+ {-# MINIMAL asBed, fromLine, toLine, chrom, chromStart, chromEnd,+ bedName, bedScore, bedStrand #-} - -- | non-streaming version- hReadBed' :: MonadIO m => Handle -> m [b]- hReadBed' h = hReadBed h $$ sinkList- {-# INLINE hReadBed' #-}+-- * BED6 format - readBed :: MonadIO m => FilePath -> Source m b- readBed fl = do handle <- liftIO $ openFile fl ReadMode- hReadBed handle- liftIO $ hClose handle- {-# INLINE readBed #-}+-- | BED6 format, as described in http://genome.ucsc.edu/FAQ/FAQformat.html#format1.7+data BED = BED+ { _chrom :: !B.ByteString+ , _chromStart :: {-# UNPACK #-} !Int+ , _chromEnd :: {-# UNPACK #-} !Int+ , _name :: !(Maybe B.ByteString)+ , _score :: !(Maybe Double)+ , _strand :: !(Maybe Bool) -- ^ True: "+", False: "-"+ } deriving (Eq, Show, Read) - -- | non-streaming version- readBed' :: MonadIO m => FilePath -> m [b]- readBed' fl = readBed fl $$ sinkList- {-# INLINE readBed' #-}+instance Default BED where+ def = BED+ { _chrom = ""+ , _chromStart = 0+ , _chromEnd = 0+ , _name = Nothing+ , _score = Nothing+ , _strand = Nothing+ } - hWriteBed :: MonadIO m => Handle -> Sink b m ()- hWriteBed handle = do- x <- await- case x of- Nothing -> return ()- Just bed -> (liftIO . B.hPutStrLn handle . toLine) bed >> hWriteBed handle- {-# INLINE hWriteBed #-}+instance BEDLike BED where+ asBed chr s e = BED chr s e Nothing Nothing Nothing - hWriteBed' :: MonadIO m => Handle -> [b] -> m ()- hWriteBed' handle beds = yieldMany beds $$ hWriteBed handle- {-# INLINE hWriteBed' #-}+ fromLine l = evalState (f (B.split '\t' l)) 1+ where+ f :: [B.ByteString] -> State Int BED+ f [] = do i <- get+ if i <= 3 then error "Read BED fail: Incorrect number of fields"+ else return def+ f (x:xs) = do+ i <- get+ put (i+1)+ bed <- f xs+ case i of+ 1 -> return $ bed {_chrom = x}+ 2 -> return $ bed {_chromStart = readInt x}+ 3 -> return $ bed {_chromEnd = readInt x}+ 4 -> return $ bed {_name = guard' x}+ 5 -> return $ bed {_score = getScore x}+ 6 -> return $ bed {_strand = getStrand x}+ _ -> return def - writeBed :: MonadIO m => FilePath -> Sink b m ()- writeBed fl = do handle <- liftIO $ openFile fl WriteMode- hWriteBed handle- liftIO $ hClose handle- {-# INLINE writeBed #-}+ guard' x | x == "." = Nothing+ | otherwise = Just x+ getScore x | x == "." = Nothing+ | otherwise = Just . readDouble $ x+ getStrand str | str == "-" = Just False+ | str == "+" = Just True+ | otherwise = Nothing+ {-# INLINE fromLine #-} - writeBed' :: MonadIO m => FilePath -> [b] -> m ()- writeBed' fl beds = yieldMany beds $$ writeBed fl- {-# INLINE writeBed' #-}+ toLine (BED f1 f2 f3 f4 f5 f6) = B.intercalate "\t"+ [ f1, (B.pack.show) f2, (B.pack.show) f3, fromMaybe "." f4, score'+ , strand' ]+ where+ strand' | f6 == Just True = "+"+ | f6 == Just False = "-"+ | otherwise = "."+ score' = case f5 of+ Just x -> (B.pack.show) x+ _ -> "."+ {-# INLINE toLine #-} - {-# MINIMAL asBed, fromLine, toLine, chrom, chromStart, chromEnd #-}+ chrom = _chrom+ chromStart = _chromStart+ chromEnd = _chromEnd+ bedName = _name+ bedScore = _score+ bedStrand = _strand + convert bed = BED (chrom bed) (chromStart bed) (chromEnd bed) (bedName bed)+ (bedScore bed) (bedStrand bed)++-- * BED3 format++data BED3 = BED3 !B.ByteString !Int !Int deriving (Eq, Show, Read)++instance BEDLike BED3 where+ asBed = BED3++ fromLine l = case B.split '\t' l of+ (a:b:c:_) -> BED3 a (readInt b) $ readInt c+ _ -> error "Read BED fail: Incorrect number of fields"+ {-# INLINE fromLine #-}++ toLine (BED3 a b c) = B.intercalate "\t"+ [a, fromJust $ packDecimal b, fromJust $ packDecimal c]+ {-# INLINE toLine #-}++ chrom (BED3 f1 _ _) = f1+ chromStart (BED3 _ f2 _) = f2+ chromEnd (BED3 _ _ f3) = f3+ bedName = const Nothing+ bedScore = const Nothing+ bedStrand = const Nothing++-- | ENCODE narrowPeak format: https://genome.ucsc.edu/FAQ/FAQformat.html#format12+data NarrowPeak = NarrowPeak+ { _npChrom :: !B.ByteString+ , _npStart :: !Int+ , _npEnd :: !Int+ , _npName :: !(Maybe B.ByteString)+ , _npScore :: !Double+ , _npStrand :: !(Maybe Bool)+ , _npSigal :: !Double+ , _npPvalue :: !(Maybe Double)+ , _npQvalue :: !(Maybe Double)+ , _npPeak :: !(Maybe Int)+ } deriving (Eq, Show, Read)++instance BEDLike NarrowPeak where+ asBed chr s e = NarrowPeak chr s e Nothing 0 Nothing 0 Nothing Nothing Nothing++ fromLine l = NarrowPeak a (readInt b) (readInt c)+ (if d == "." then Nothing else Just d)+ (readDouble e)+ (if f == "." then Nothing else if f == "+" then Just True else Just False)+ (readDouble g)+ (if readDouble h < 0 then Nothing else Just $ readDouble h)+ (if readDouble i < 0 then Nothing else Just $ readDouble i)+ (if readInt j < 0 then Nothing else Just $ readInt j)+ where+ (a:b:c:d:e:f:g:h:i:j:_) = B.split '\t' l+ {-# INLINE fromLine #-}++ toLine (NarrowPeak a b c d e f g h i j) = B.intercalate "\t"+ [ a, fromJust $ packDecimal b, fromJust $ packDecimal c, fromMaybe "." d+ , toShortest e+ , case f of+ Nothing -> "."+ Just True -> "+"+ _ -> "-"+ , toShortest g, fromMaybe "-1" $ fmap toShortest h+ , fromMaybe "-1" $ fmap toShortest i+ , fromMaybe "-1" $ fmap (fromJust . packDecimal) j+ ]+ {-# INLINE toLine #-}++ chrom = _npChrom+ chromStart = _npStart+ chromEnd = _npEnd+ bedName = _npName+ bedScore = Just . _npScore+ bedStrand = _npStrand++ convert bed = NarrowPeak (chrom bed) (chromStart bed) (chromEnd bed) (bedName bed)+ (fromMaybe 0 $ bedScore bed) (bedStrand bed) 0 Nothing Nothing Nothing+ type BEDTree a = M.HashMap B.ByteString (IM.IntervalMap Int a) -- | convert a set of bed records to interval tree, with combining function for@@ -170,6 +303,17 @@ return v {-# INLINE bedToTree #-} +intersecting :: BEDLike b => BEDTree a -> b -> IM.IntervalMap Int a+intersecting tree x = IM.intersecting (M.lookupDefault IM.empty chr tree) interval+ where+ chr = chrom x+ interval = IM.IntervalCO (chromStart x) $ chromEnd x+{-# INLINE intersecting #-}++isIntersected :: BEDLike b => BEDTree a -> b -> Bool+isIntersected tree = not . IM.null . intersecting tree+{-# INLINE isIntersected #-}+ -- | split a bed region into k consecutive subregions, discarding leftovers splitBed :: BEDLike b => Int -> b -> [b] splitBed k bed = map (uncurry (asBed chr)) . bins k $ (s, e)@@ -281,7 +425,7 @@ {-# INLINE mergeBed #-} mergeBedWith :: (BEDLike b, Monad m)- => ([b] -> b) -> [b] -> Source m b+ => ([b] -> a) -> [b] -> Source m a mergeBedWith f = mergeSortedBedWith f . sortBed {-# INLINE mergeBedWith #-} @@ -295,7 +439,7 @@ {-# INLINE mergeSortedBed #-} mergeSortedBedWith :: (BEDLike b, Monad m)- => ([b] -> b) -> Sorted (V.Vector b) -> Source m b+ => ([b] -> a) -> Sorted (V.Vector b) -> Source m a mergeSortedBedWith mergeFn (Sorted beds) = do (_, r) <- V.foldM' f acc0 . V.tail $ beds yield $ mergeFn r@@ -313,124 +457,147 @@ e' = chromEnd bed {-# INLINE mergeSortedBedWith #-} --- * BED6 format+-- | Split overlapped regions into non-overlapped regions. The input must be overlapped.+-- This function is usually used with `mergeBedWith`.+splitOverlapped :: BEDLike b => ([b] -> a) -> [b] -> [(BED3, a)]+splitOverlapped fun xs = filter ((>0) . bedSize . fst) $+ evalState (F.foldrM f [] $ init xs') x0+ where+ x0 = (\(a,b) -> (fromEither a, M.singleton (chromStart b, chromEnd b) b)) $ last xs'+ xs' = sortBy (comparing (fromEither . fst)) $ concatMap+ ( \x -> [(Left $ chromStart x, x), (Right $ chromEnd x, x)] ) xs+ f (i, x) acc = do+ (j, set) <- get+ let bed = (BED3 chr (fromEither i) j, fun $ M.elems set)+ set' = case i of+ Left _ -> M.delete (chromStart x, chromEnd x) set+ Right _ -> M.insert (chromStart x, chromEnd x) x set+ put (fromEither i, set')+ return (bed:acc)+ fromEither (Left x) = x+ fromEither (Right x) = x+ chr = chrom $ head xs+{-# INLINE splitOverlapped #-} --- | BED6 format, as described in http://genome.ucsc.edu/FAQ/FAQformat.html#format1.7-data BED = BED- { _chrom :: !B.ByteString- , _chromStart :: {-# UNPACK #-} !Int- , _chromEnd :: {-# UNPACK #-} !Int- , _name :: !(Maybe B.ByteString)- , _score :: !(Maybe Double)- , _strand :: !(Maybe Bool) -- ^ True: "+", False: "-"- } deriving (Eq, Show, Read)+-- | Read records from a bed file handler in a streaming fashion.+hReadBed :: (BEDLike b, MonadIO m) => Handle -> Source m b+hReadBed h = do eof <- liftIO $ hIsEOF h+ unless eof $ do+ line <- liftIO $ B.hGetLine h+ yield $ fromLine line+ hReadBed h+{-# INLINE hReadBed #-} -instance Default BED where- def = BED- { _chrom = ""- , _chromStart = 0- , _chromEnd = 0- , _name = Nothing- , _score = Nothing- , _strand = Nothing- }+-- | Non-streaming version.+hReadBed' :: (BEDLike b, MonadIO m) => Handle -> m [b]+hReadBed' h = hReadBed h $$ sinkList+{-# INLINE hReadBed' #-} -instance BEDLike BED where- asBed chr s e = BED chr s e Nothing Nothing Nothing+-- | Read records from a bed file in a streaming fashion.+readBed :: (BEDLike b, MonadIO m) => FilePath -> Source m b+readBed fl = do handle <- liftIO $ openFile fl ReadMode+ hReadBed handle+ liftIO $ hClose handle+{-# INLINE readBed #-} - fromLine l = evalState (f (B.split '\t' l)) 1- where- f :: [B.ByteString] -> State Int BED- f [] = do i <- get- if i <= 3 then error "Read BED fail: Incorrect number of fields"- else return def- f (x:xs) = do- i <- get- put (i+1)- bed <- f xs- case i of- 1 -> return $ bed {_chrom = x}- 2 -> return $ bed {_chromStart = readInt x}- 3 -> return $ bed {_chromEnd = readInt x}- 4 -> return $ bed {_name = guard' x}- 5 -> return $ bed {_score = getScore x}- 6 -> return $ bed {_strand = getStrand x}- _ -> return def+-- | Non-streaming version.+readBed' :: (BEDLike b, MonadIO m) => FilePath -> m [b]+readBed' fl = readBed fl $$ sinkList+{-# INLINE readBed' #-} - guard' x | x == "." = Nothing- | otherwise = Just x- getScore x | x == "." = Nothing- | otherwise = Just . readDouble $ x- getStrand str | str == "-" = Just False- | str == "+" = Just True- | otherwise = Nothing- {-# INLINE fromLine #-}+hWriteBed :: (BEDLike b, MonadIO m) => Handle -> Sink b m ()+hWriteBed handle = do+ x <- await+ case x of+ Nothing -> return ()+ Just bed -> (liftIO . B.hPutStrLn handle . toLine) bed >> hWriteBed handle+{-# INLINE hWriteBed #-} - toLine (BED f1 f2 f3 f4 f5 f6) = B.intercalate "\t" [ f1- , (B.pack.show) f2- , (B.pack.show) f3- , fromMaybe "." f4- , score'- , strand'- ]- where- strand' | f6 == Just True = "+"- | f6 == Just False = "-"- | otherwise = "."- score' = case f5 of- Just x -> (B.pack.show) x- _ -> "."- {-# INLINE toLine #-}+hWriteBed' :: (BEDLike b, MonadIO m) => Handle -> [b] -> m ()+hWriteBed' handle beds = yieldMany beds $$ hWriteBed handle+{-# INLINE hWriteBed' #-} - chrom = _chrom- chromStart = _chromStart- chromEnd = _chromEnd+writeBed :: (BEDLike b, MonadIO m) => FilePath -> Sink b m ()+writeBed fl = do handle <- liftIO $ openFile fl WriteMode+ hWriteBed handle+ liftIO $ hClose handle+{-# INLINE writeBed #-} +writeBed' :: (BEDLike b, MonadIO m) => FilePath -> [b] -> m ()+writeBed' fl beds = yieldMany beds $$ writeBed fl+{-# INLINE writeBed' #-}+ -- | retreive sequences fetchSeq :: (BioSeq DNA a, MonadIO m) => Genome -> Conduit BED m (Either String (DNA a))-fetchSeq g = do gH <- liftIO $ gHOpen g- table <- liftIO $ readIndex gH- conduitWith gH table- liftIO $ gHClose gH+fetchSeq g = mapMC f where- conduitWith h index' = do- bed <- await- case bed of- Just (BED chr start end _ _ isForward) -> do- dna <- liftIO $ getSeq h index' (chr, start, end)- case isForward of- Just False -> yield $ fmap rc dna- _ -> yield dna- conduitWith h index'- _ -> return ()+ f (BED chr start end _ _ isForward) = do+ dna <- liftIO $ getSeq g (chr, start, end)+ return $ case isForward of+ Just False -> rc <$> dna+ _ -> dna {-# INLINE fetchSeq #-} fetchSeq' :: (BioSeq DNA a, MonadIO m) => Genome -> [BED] -> m [Either String (DNA a)] fetchSeq' g beds = yieldMany beds $= fetchSeq g $$ sinkList {-# INLINE fetchSeq' #-} --- * BED3 format--data BED3 = BED3 !B.ByteString !Int !Int deriving (Eq, Show, Read)--instance Default BED3 where- def = BED3 "" 0 0--instance BEDLike BED3 where- asBed = BED3-- fromLine l = case B.split '\t' l of- (a:b:c:_) -> BED3 a (readInt b) $ readInt c- _ -> error "Read BED fail: Incorrect number of fields"- {-# INLINE fromLine #-}-- toLine (BED3 f1 f2 f3) = B.intercalate "\t" [f1, (B.pack.show) f2, (B.pack.show) f3]- {-# INLINE toLine #-}+-- | Identify motif binding sites+motifScan :: (BEDLike b, MonadIO m)+ => Genome -> [Motif] -> Bkgd -> Double -> Conduit b m BED+motifScan g motifs bg p = awaitForever $ \bed -> do+ let chr = chrom bed+ s = chromStart bed+ e = chromEnd bed+ r <- liftIO $ getSeq g (chr, s, e)+ case r of+ Left _ -> return ()+ Right dna -> mapM_ (getTFBS dna (chr, s)) motifs'+ where+ getTFBS dna (chr, s) (nm, (pwm, cutoff), (pwm', cutoff')) = toProducer+ ( (findTFBS bg pwm (dna :: DNA IUPAC) cutoff True =$=+ mapC (\i -> BED chr (s+i) (s+i+n) (Just nm) Nothing $ Just True)) >>+ (findTFBS bg pwm' dna cutoff' True =$=+ mapC (\i -> BED chr (s+i) (s+i+n) (Just nm) Nothing $ Just False)) )+ where+ n = Bio.Motif.size pwm+ motifs' = flip map motifs $ \(Motif nm pwm) ->+ let cutoff = pValueToScore p bg pwm+ cutoff' = pValueToScore p bg pwm'+ pwm' = rcPWM pwm+ in (nm, (pwm, cutoff), (pwm', cutoff'))+{-# INLINE motifScan #-} - chrom (BED3 f1 _ _) = f1- chromStart (BED3 _ f2 _) = f2- chromEnd (BED3 _ _ f3) = f3+-- | Retrieve motif matching scores+getMotifScore :: MonadIO m+ => Genome -> [Motif] -> Bkgd -> Conduit BED m BED+getMotifScore g motifs bg = awaitForever $ \(BED chr s e (Just nm) _ isForward) -> do+ r <- liftIO $ getSeq g (chr, s, e)+ let r' = case isForward of+ Just False -> rc <$> r+ _ -> r+ case r' of+ Left _ -> return ()+ Right dna -> do+ let pwm = M.lookupDefault (error "can't find motif with given name")+ nm motifMap+ sc = score bg pwm (dna :: DNA IUPAC)+ yield $ BED chr s e (Just nm) (Just sc) isForward+ where+ motifMap = M.fromListWith (error "found motif with same name") $+ map (\(Motif nm pwm) -> (nm, pwm)) motifs+{-# INLINE getMotifScore #-} -convert :: (BEDLike b1, BEDLike b2) => b1 -> b2-convert b = asBed (chrom b) (chromStart b) (chromEnd b)-{-# INLINE convert #-}+getMotifPValue :: MonadIO m+ => [Motif] -> Bkgd -> Conduit BED m BED+getMotifPValue motifs bg = awaitForever $ \bed -> do+ let sc = fromJust $ _score bed+ nm = fromJust $ _name bed+ d = M.lookupDefault (error "can't find motif with given name")+ nm motifMap+ p = 1 - cdf d sc+ yield bed{_score = Just p}+ where+ motifMap = M.fromListWith (error "found motif with same name") $+ map (\(Motif nm pwm) -> (nm, scoreCDF bg pwm)) motifs+{-# INLINE getMotifPValue #-}
src/Bio/GO/GREAT.hs view
@@ -5,22 +5,22 @@ , enrichedTerms ) where -import Control.Monad.Primitive-import qualified Data.ByteString.Char8 as B-import Conduit-import Data.Default.Class-import qualified Data.HashMap.Strict as M-import qualified Data.IntervalMap as IM-import qualified Data.Vector as V-import Data.Vector.Algorithms.Intro (sortBy)-import Data.List (sort, group)-import Data.Ord (comparing)-import Data.IntervalMap.Interval (lowerBound, upperBound)-import Data.Maybe+import Conduit+import Control.Monad.Primitive+import qualified Data.ByteString.Char8 as B+import Data.Default.Class+import Data.Function (on)+import qualified Data.HashMap.Strict as M+import qualified Data.IntervalMap as IM+import Data.List (foldl', group, sort, sortBy)+import Data.Maybe+import Data.Ord (comparing)+import qualified Data.Vector as V+import qualified Data.Vector.Algorithms.Intro as I -import Bio.Data.Bed-import Bio.GO-import Bio.Utils.Functions+import Bio.Data.Bed+import Bio.GO+import Bio.Utils.Functions -- | how to associate genomic regions with genes data AssocRule = BasalPlusExtension Int Int Int@@ -28,33 +28,31 @@ | OneNearest instance Default AssocRule where- def = BasalPlusExtension 5000 1000 50000+ def = BasalPlusExtension 5000 1000 1000000 -type Gene = ( B.ByteString -- ^ chromosome- , Int -- ^ tss- , Bool -- ^ is forward stranded- , [GOId]- )+type Gene a = ( ( B.ByteString -- ^ chromosome+ , Int -- ^ tss+ , Bool -- ^ is forward stranded+ ), a) -- | given a gene list and the rule, compute the rulatory domain for each gene-getRegulatoryDomains :: AssocRule -> [Gene] -> BEDTree [GOId]-getRegulatoryDomains (BasalPlusExtension up dw ext) gs =- bedToTree undefined $ flip map basal $ \(BED3 chr s e, go) ->- let intervals = fromJust . M.lookup chr $ basalTree- s' = let im = IM.intersecting intervals $ IM.OpenInterval (s-ext) s- in if IM.null im- then s - ext- else min s $ maximum $ map upperBound $ IM.keys im- e' = let im = IM.intersecting intervals $ IM.OpenInterval e (e+ext)- in if IM.null im- then e + ext- else max e $ minimum $ map lowerBound $ IM.keys im- in (BED3 chr s' e', go)+getRegulatoryDomains :: AssocRule -> [Gene a] -> [(BED3, a)]+getRegulatoryDomains (BasalPlusExtension up dw ext) genes = (extendTail r ext, a) : rs where- basalTree = bedToTree (error "encounter redundant regions") basal- basal = flip map gs $ \(chr,tss,str,go) ->- if str then (BED3 chr (tss - up) (tss + dw), go)- else (BED3 chr (tss - dw) (tss + up), go)+ (rs, Just (r,a)) = foldl' f ([], Nothing) $ sortBy (compareBed `on` fst) basal+ f (acc, Nothing) (b,x) = (acc, Just (extendHead b ext, x))+ f (acc, Just (b', x')) (b,x)+ | chrom b' /= chrom b = ( (extendTail b' ext, x') : acc+ , Just (extendHead b ext, x) )+ | chromEnd b' >= chromStart b = ((b',x') : acc, Just (b,x))+ | otherwise = let ext' = min ext $ (chromStart b - chromEnd b') `div` 2+ in ((extendTail b' ext', x') : acc, Just (extendHead b ext', x))+ extendHead (BED3 chr s e) l | s - l >= 0 = BED3 chr (s-l) e+ | otherwise = BED3 chr 0 e+ extendTail (BED3 chr s e) l = BED3 chr s (e+l)+ basal = flip map genes $ \((chr, tss, str), x) ->+ if str then (BED3 chr (tss - up) (tss + dw), x)+ else (BED3 chr (tss - dw) (tss + up), x) getRegulatoryDomains _ _ = undefined {-# INLINE getRegulatoryDomains #-} @@ -130,5 +128,5 @@ v <- V.unsafeThaw $ V.fromList $ M.toList $ flip M.mapWithKey table1 $ \t c -> let k = fromMaybe (error "x") $ M.lookup t table2 in (1 - hyperquick c k n1 n2, fromIntegral (c*n2) / fromIntegral (n1*k))- sortBy (comparing snd) v+ I.sortBy (comparing snd) v V.unsafeFreeze v
src/Bio/Motif.hs view
@@ -267,7 +267,7 @@ loop (prev,scFn) i | i < n = let (lo,hi) = minMax (1/0,-1/0) 0- nBin' = min 100000 $ ceiling $ (hi - lo) / precision+ nBin' = min 200000 $ ceiling $ (hi - lo) / precision step = (hi - lo) / fromIntegral nBin' idx x = let j = truncate $ (x - lo) / step in if j >= nBin' then nBin' - 1 else j@@ -299,7 +299,7 @@ in minMax (foldr min l [s1,s2,s3,s4],foldr max h [s1,s2,s3,s4]) (x+1) | otherwise = minMax (l,h) (x+1) toCDF (v, scFn) = CDF $ V.imap (\i x -> (scFn i, x)) $ V.scanl1 (+) v- precision = 0.002+ precision = 1e-4 n = size pwm log' x | x == 0 = log 0.001 | otherwise = log x@@ -350,7 +350,7 @@ 1 -> do when (startOfPwm x) $ put (2, str ++ [B.words x !! 7]) go xs 2 -> let x' = B.dropWhile (== ' ') x- in if B.null x'+ in if B.null x' || "URL" `B.isPrefixOf` x' then do put (0, []) r <- go xs return (toMotif str : r)
src/Bio/Seq/IO.hs view
@@ -2,13 +2,10 @@ module Bio.Seq.IO ( Genome- , GenomeH- , gHOpen- , gHClose- , pack- , getSeqs+ , openGenome+ , closeGenome+ , withGenome , getSeq- , readIndex , getChrom , getChrSizes , mkIndex@@ -16,6 +13,7 @@ import Bio.Seq import Bio.Utils.Misc (readInt)+import Control.Exception (bracket) import qualified Data.ByteString.Char8 as B import qualified Data.HashMap.Lazy as M import Data.List.Split@@ -24,9 +22,9 @@ -- | The first 2048 bytes are header. Header consists of a magic string, -- followed by chromosome information. Example: -- <HASKELLBIOINFORMATICS>\nCHR1 START SIZE-newtype Genome = G FilePath+data Genome = Genome !Handle !IndexTable -newtype GenomeH = GH Handle+type IndexTable = M.HashMap B.ByteString (Int, Int) headerSize :: Int headerSize = 2048@@ -34,84 +32,70 @@ magic :: String magic = "<HASKELLBIOINFORMATICS>" -pack :: FilePath -> IO Genome-pack fl = withFile fl ReadMode f- where f h = do l <- hGetLine h- if l == magic- then return $ G fl- else error "Bio.Seq.Query.pack: Incorrect format"--gHOpen :: Genome -> IO GenomeH-gHOpen (G fl) = do h <- openFile fl ReadMode- return $ GH h+openGenome :: FilePath -> IO Genome+openGenome fl = do+ h <- openFile fl ReadMode+ sig <- hGetLine h+ if sig == magic+ then Genome h <$> readIndex h+ else error "Bio.Seq.Query.openGenome: Incorrect format"+ where+ readIndex h = do+ header <- B.hGetLine h+ return $ M.fromList . map f . chunksOf 3 . B.words $ header+ where+ f [k, v, l] = (k, (readInt v, readInt l))+ f _ = error "error"+{-# INLINE openGenome #-} -gHClose :: GenomeH -> IO ()-gHClose (GH h) = hClose h+closeGenome :: Genome -> IO ()+closeGenome (Genome h _) = hClose h+{-# INLINE closeGenome #-} -type IndexTable = M.HashMap B.ByteString (Int, Int)+withGenome :: FilePath -> (Genome -> IO a) -> IO a+withGenome fl fn = bracket (openGenome fl) closeGenome fn+{-# INLINE withGenome #-} type Query = (B.ByteString, Int, Int) -- (chr, start, end), zero-based index, half-close-half-open -getSeqs :: BioSeq s a => Genome -> [Query] -> IO [Either String (s a)]-getSeqs g querys = do gH <- gHOpen g- index <- readIndex gH- r <- mapM (getSeq gH index) querys- gHClose gH- return r-{-# INLINE getSeqs #-}--getSeq :: BioSeq s a => GenomeH -> IndexTable -> Query -> IO (Either String (s a))-getSeq (GH h) index (chr, start, end) = case M.lookup chr index of- Just (chrStart, chrSize) -> if end > chrSize- then return $ Left $ "Bio.Seq.getSeq: out of index: " ++- show end ++ ">" ++ show chrSize- else do- hSeek h AbsoluteSeek $ fromIntegral $ headerSize + chrStart + start- (Right . fromBS) <$> B.hGet h (end - start)+getSeq :: BioSeq s a => Genome -> Query -> IO (Either String (s a))+getSeq (Genome h index) (chr, start, end) = case M.lookup chr index of+ Just (chrStart, chrSize) ->+ if end > chrSize+ then return $ Left $ "Bio.Seq.getSeq: out of index: " ++ show end +++ ">" ++ show chrSize+ else do+ hSeek h AbsoluteSeek $ fromIntegral $ headerSize + chrStart + start+ (Right . fromBS) <$> B.hGet h (end - start) _ -> return $ Left $ "Bio.Seq.getSeq: Cannot find " ++ show chr {-# INLINE getSeq #-} -getChrom :: Genome -> B.ByteString -> IO (Maybe (DNA IUPAC))-getChrom g chr = do- chrSize <- getChrSizes g- case lookup chr chrSize of- Just s -> do [Right dna] <- getSeqs g [(chr, 0, s)]- return $ Just dna- _ -> return Nothing+getChrom :: Genome -> B.ByteString -> IO (Either String (DNA IUPAC))+getChrom g chr = case lookup chr chrSize of+ Just s -> getSeq g (chr, 0, s)+ _ -> return $ Left "Unknown chromosome"+ where+ chrSize = getChrSizes g {-# INLINE getChrom #-} -getChrSizes :: Genome -> IO [(B.ByteString, Int)]-getChrSizes g = do gh <- gHOpen g- table <- readIndex gh- gHClose gh- return . map (\(k, (_, l)) -> (k, l)) . M.toList $ table+getChrSizes :: Genome -> [(B.ByteString, Int)]+getChrSizes (Genome _ table) = map (\(k, (_, l)) -> (k, l)) . M.toList $ table {-# INLINE getChrSizes #-} -readIndex :: GenomeH -> IO IndexTable-readIndex (GH h) = do header <- B.hGetLine h >> B.hGetLine h- return $ M.fromList . map f . chunksOf 3 . B.words $ header- where- f [k, v, l] = (k, (readInt v, readInt l))- f _ = error "error"-{-# INLINE readIndex #-}- -- | indexing a genome. mkIndex :: [FilePath] -- ^ fasta files representing individual chromosomes -> FilePath -- ^ output file -> IO ()-mkIndex fls outFl = do- outH <- openFile outFl WriteMode+mkIndex fls outFl = withFile outFl WriteMode $ \outH -> do hPutStr outH $ magic ++ "\n" ++ replicate 2024 '#' -- header size: 1024 chrs <- mapM (write outH) fls hSeek outH AbsoluteSeek 24 B.hPutStrLn outH $ mkHeader chrs- hClose outH where- write handle fl = do h <- openFile fl ReadMode- fastaHeader <- B.hGetLine h- n <- loop 0 h- hClose h- return (B.tail fastaHeader, n)+ write handle fl = withFile fl ReadMode $ \h -> do+ fastaHeader <- B.hGetLine h+ n <- loop 0 h+ return (B.tail fastaHeader, n) where loop !n h' = do eof <- hIsEOF h' if eof then return n
src/Bio/Utils/Functions.hs view
@@ -33,7 +33,7 @@ import qualified Data.Vector as V import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U-import Statistics.Sample (meanVarianceUnb, mean)+import Statistics.Sample (meanVarianceUnb) import Statistics.Function (sortBy) -- | inverse hyperbolic sine transformation
tests/Tests/Bed.hs view
@@ -3,6 +3,8 @@ module Tests.Bed (tests) where import Bio.Data.Bed+import Data.List (sortBy)+import Data.Function (on) import Test.Tasty import Test.Tasty.HUnit import qualified Data.Vector as V@@ -11,6 +13,7 @@ tests = testGroup "Test: Bio.Data.Bed" [ testCase "sortBed" sortBedTest , testCase "split" splitBedTest+ , testCase "splitOverlapped" splitOverlappedTest ] sortBedTest :: Assertion@@ -31,3 +34,28 @@ s3' = map f [ ( 0, 20), (10, 30), (20, 40), (30, 50), (40, 60) , (50, 70), (60, 80), (70, 90), (80, 100) ] f (a,b) = asBed "chr1" a b++splitOverlappedTest :: Assertion+splitOverlappedTest = expect @=? result+ where+ input = map (\(a,b) -> BED3 "chr1" a b)+ [ (0, 100)+ , (10, 20)+ , (50, 150)+ , (120, 160)+ , (155, 200)+ , (155, 220)+ ]+ expect = map (\((a,b), x) -> (BED3 "chr1" a b, x))+ [ ((0, 10), 1)+ , ((10, 20), 2)+ , ((20, 50), 1)+ , ((50, 100), 2)+ , ((100, 120), 1)+ , ((120, 150), 2)+ , ((150, 155), 1)+ , ((155, 160), 3)+ , ((160, 200), 2)+ , ((200, 220), 1)+ ]+ result = sortBy (compareBed `on` fst) $ splitOverlapped length input
+ tests/Tests/GREAT.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}+module Tests.GREAT (tests) where++import Bio.Data.Bed+import Bio.GO.GREAT+import Data.List+import Data.Ord++import Test.Tasty+import Test.Tasty.HUnit++testData = (genes, results)+ where+ genes = [ (("chr1", 1000, True), 0)+ , (("chr1", 6000, True), 1)+ , (("chr2", 6000, True), 2)+ , (("chr2", 2000000, False), 3)+ ]+ results = [ (BED3 "chr1" 0 2000, 0)+ , (BED3 "chr1" 1000 1007000, 1)+ , (BED3 "chr2" 0 1003000, 2)+ , (BED3 "chr2" 1003000 3005000, 3)+ ]++tests :: TestTree+tests = testGroup "Test: Bio.GO.GREAT"+ [ testCase "getRegulatoryDomains" testAssoc+ ]++testAssoc :: Assertion+testAssoc = do+ let result = sortBy (comparing snd) $ getRegulatoryDomains+ (BasalPlusExtension 5000 1000 1000000) $ fst testData+ snd testData @=? result
tests/Tests/Motif.hs view
@@ -50,7 +50,7 @@ findTFBSTest = do ms <- motifs let (Motif _ pwm) = head ms- expect <- findTFBS def pwm dna (0.6 * optimalScore def pwm) $$ CL.consume+ expect <- findTFBS def pwm dna (0.6 * optimalScore def pwm) True $$ CL.consume actual <- findTFBSSlow def pwm dna (0.6 * optimalScore def pwm) $$ CL.consume assertEqual "findTFBS" expect actual
tests/test.hs view
@@ -3,6 +3,7 @@ import qualified Tests.Motif as Motif import qualified Tests.ChIPSeq as ChIPSeq import qualified Tests.Seq as Seq+import qualified Tests.GREAT as GREAT import Test.Tasty main :: IO ()@@ -12,4 +13,5 @@ , Seq.tests , ChIPSeq.tests , Motif.tests+ , GREAT.tests ]