BiobaseBlast 0.0.0.1 → 0.2.0.0
raw patch · 10 files changed
+443/−66 lines, 10 filesdep +BiobaseBlastdep +BiobaseTypesdep +PrimitiveArraydep −arraydep ~BiobaseXNAdep ~base
Dependencies added: BiobaseBlast, BiobaseTypes, PrimitiveArray, aeson, attoparsec, binary, bytestring, cereal, deepseq, directory, filepath, split, tasty, tasty-quickcheck, tasty-silver, tasty-th, vector, vector-th-unbox
Dependencies removed: array
Dependency ranges changed: BiobaseXNA, base
Files
- Biobase/BLAST.hs +11/−0
- Biobase/BLAST/Import.hs +130/−0
- Biobase/BLAST/Types.hs +49/−0
- Biobase/SubstMatrix.hs +111/−38
- Biobase/SubstMatrix/Import.hs +17/−6
- BiobaseBlast.cabal +81/−20
- README.md +15/−0
- changelog +0/−2
- changelog.md +16/−0
- tests/properties.hs +13/−0
+ Biobase/BLAST.hs view
@@ -0,0 +1,11 @@+-- | Types and functions for NCBI BLAST++--++module Biobase.BLAST+ ( module Biobase.BLAST.Types+ , module Biobase.BLAST.Import+ ) where++import Biobase.BLAST.Import (blastFromFile)+import Biobase.BLAST.Types+
+ Biobase/BLAST/Import.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}++-- | Parses NCBI BLAST+ tabular output++module Biobase.BLAST.Import where++import Prelude hiding (takeWhile)+import Data.Attoparsec.ByteString.Char8 hiding (isSpace)+import qualified Data.Attoparsec.ByteString.Lazy as L+import qualified Data.ByteString.Char8 as C+import qualified Data.ByteString.Builder as S+import qualified Data.ByteString.Lazy.Char8 as B+import qualified Data.Vector as V+import System.Directory+import Data.Char+import Control.Monad+import Debug.Trace+import Text.Printf+import Biobase.BLAST.Types++-- | reads and parses tabular Blast result from provided filePath+blastFromFile :: String -> IO [BlastTabularResult]+blastFromFile filePath = do+ printf "# reading tabular blast input from file %s\n" filePath+ blastFileExists <- doesFileExist filePath+ if blastFileExists+ then parseTabularBlasts <$> B.readFile filePath+ else fail "# tabular blast file \"%s\" does not exist\n" filePath++-- | Read a lazy bytestring and stream out a lsit of @BlastTabularResult@'s.+-- In case, there is a parse error "late" in the file, we might have+-- already streamed out some (or many!) of these results.++parseTabularBlasts :: B.ByteString -> [BlastTabularResult]+parseTabularBlasts = go+ where go xs = case L.parse genParseTabularBlast xs of+ L.Fail remainingInput ctxts err -> error $ "parseTabularBlasts failed! " ++ err ++ " ctxt: " ++ show ctxts ++ " head of remaining input: " ++ B.unpack (B.take 1000 remainingInput)+ L.Done remainingInput btr+ | B.null remainingInput -> [btr]+ | otherwise -> btr : go remainingInput++genParseBlastProgram :: Parser BlastProgram+genParseBlastProgram = do+ choice [string "# BLAST",string "# blast"]+ (toLower <$> anyChar) >>= return . \case + 'x' -> BlastX+ 'p' -> BlastP+ 'n' -> BlastN++genParseTabularBlast :: Parser BlastTabularResult+genParseTabularBlast = do+ --choice [string "# BLAST",string "# blast"]+ --_blastProgram <- choice [string "p",string "P",string "x",string "X"] <?> "Program"+ _blastProgram <- genParseBlastProgram <?> "Program"+ many1 (notChar '\n')+ endOfLine+ string "# Query: " <?> "Query"+ --_blastQueryId <- many1 (notChar ' ') <* skipWhile (not (string "\n")) <?> "QueryId"+ _blastQueryId <- takeWhile (not . isSpace) <* manyTill anyChar endOfLine <?> "QueryId"+ --_blastQueryName <- many' (notChar '\n') <?> "QueryName"+ string "# Database: " <?> "Database"+ _blastDatabase <- many1 (notChar '\n') <?> "Db"+ string "\n# " <?> "header linebreak"+ --fields line+ skipMany (try genParseFieldLine) <?> "Fields"+ _blastHitNumber <- decimal <?> "Hit number"+ string " hits found\n" <?> "hits found"+ _tabularHit <- count _blastHitNumber (try genParseBlastTabularHit) <?> "Tabular hit"+ return $ BlastTabularResult _blastProgram (toLB _blastQueryId) (B.pack _blastDatabase) _blastHitNumber (V.fromList _tabularHit)++genParseFieldLine :: Parser ()+genParseFieldLine = do+ string "Fields:"+ skipMany (notChar '\n')+ string "\n# "+ return ()++genParseBlastTabularHit :: Parser BlastTabularHit+genParseBlastTabularHit = do+ --start <- peekChar'+ --when (start == '#') (fail "irgendwas")+ --_queryId <- takeWhile (\c -> c /= '#' || c /= '\t') <?> "hit qid"+ _queryId <- takeWhile1 ((/=9) . ord) <?> "hit qid"+ char '\t'+ _subjectId <- takeWhile1 ((/=9) . ord) <?> "hit sid"+ char '\t'+ _seqIdentity <- double <?> "hit seqid"+ char '\t'+ _alignmentLength <- decimal <?> "hit sid"+ char '\t'+ _misMatches <- decimal <?> "hit mmatch"+ char '\t'+ _gapOpenScore <- decimal <?> "hit gopen"+ char '\t'+ _queryStart <- decimal <?> "hit qstart"+ char '\t'+ _queryEnd <- decimal <?> "hit qend"+ char '\t'+ _hitSeqStart <- decimal <?> "hit sstart"+ char '\t'+ _hitSeqEnd <- decimal <?> "hit send"+ char '\t'+ _eValue <- double <?> "hit eval"+ char '\t'+ _bitScore <- double <?> "hit bs"+ char '\t'+ _subjectFrame <- decimal <?> "hit sF"+ char '\t'+ _querySeq <- takeWhile1 ((/=9) . ord) <?> "hit qseq" -- 9 == '\t'+ char '\t'+ _subjectSeq <- takeWhile1 ((/=10) . ord) <?> "hit subSeq" -- 10 == '\n'+ char '\n'+ return $ BlastTabularHit (B.fromStrict _queryId) (B.fromStrict _subjectId) _seqIdentity _alignmentLength _misMatches _gapOpenScore _queryStart _queryEnd _hitSeqStart _hitSeqEnd _eValue _bitScore _subjectFrame (B.fromStrict _querySeq) (B.fromStrict _subjectSeq)+ +--IUPAC amino acid with gap+--aminoacidLetters :: Char -> Bool+aminoacidLetters = inClass "ARNDCQEGHILMFPSTWYVBZX-"++--IUPAC nucleic acid characters with gap+--nucleotideLetters :: Char -> Bool+nucleotideLetters = inClass "AGTCURYSWKMBDHVN-."++--IUPAC nucleic acid characters with gap+--bioLetters :: Char -> Bool+bioLetters = inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZ.-"+++toLB :: C.ByteString -> B.ByteString+toLB = S.toLazyByteString . S.byteString
+ Biobase/BLAST/Types.hs view
@@ -0,0 +1,49 @@++-- | Encoding of tabular NCBI BLAST+ output++module Biobase.BLAST.Types where++import Prelude hiding (takeWhile)+import Data.Attoparsec.ByteString.Char8 hiding (isSpace)+import qualified Data.Attoparsec.ByteString.Lazy as L+import qualified Data.ByteString.Char8 as C+import qualified Data.ByteString.Builder as S+import qualified Data.ByteString.Lazy.Char8 as B+import qualified Data.Vector as V+import System.Directory+import Data.Char+import Control.Monad+import Debug.Trace+import Text.Printf++data BlastTabularResult = BlastTabularResult+ { blastProgram :: !BlastProgram,+ blastQueryId :: !B.ByteString,+-- blastQueryName :: !B.ByteString,+ blastDatabase :: !B.ByteString,+ blastHitNumber :: !Int,+ hitLines :: !(V.Vector BlastTabularHit)+ }+ deriving (Show, Eq)++data BlastProgram = BlastX | BlastP | BlastN+ deriving (Show, Eq)++data BlastTabularHit = BlastTabularHit+ { queryId :: !B.ByteString,+ subjectId :: !B.ByteString,+ seqIdentity :: !Double,+ alignmentLength :: !Int,+ misMatches :: !Int,+ gapOpenScore :: !Int,+ queryStart :: !Int,+ queryEnd :: !Int,+ hitSeqStart :: !Int,+ hitSeqEnd :: !Int,+ eValue :: !Double,+ bitScore :: !Double,+ subjectFrame :: !Int,+ querySeq :: !B.ByteString,+ subjectSeq :: !B.ByteString+ }+ deriving (Show, Eq)
Biobase/SubstMatrix.hs view
@@ -1,58 +1,131 @@ module Biobase.SubstMatrix where -import qualified Data.Array.IArray as A-import qualified Data.Array.Unboxed as U-import qualified Data.Map as M-import Data.Char (toLower)+import Control.DeepSeq (NFData(..))+import Data.Aeson (FromJSON,ToJSON)+import Data.Binary (Binary)+import Data.Serialize (Serialize)+import Data.Vector.Unboxed.Deriving+import GHC.Generics (Generic)+import qualified Data.Map.Strict as M+import qualified Data.Vector.Unboxed as VU -import Biobase.Primary-import Biobase.Codon+import Biobase.Primary.AA (AA,aaRange)+import Biobase.Primary.Letter+import Biobase.Primary.Nuc.DNA (DNA)+import Biobase.Primary.Trans (dnaAAmap)+import Biobase.Types.Odds+import Data.PrimitiveArray+import qualified Biobase.Primary.AA as AA+import qualified Biobase.Primary.Nuc.DNA as D --- | Substitution table for one amino acid with another+-- | Denotes that we are dealing with a similarity score. Higher is more+-- similar. -type SubstMatrix = U.Array (Char,Char) Int+data Similarity --- | Substitution from three DNA nucleotides to an amino acid.+-- | Denotes that we are dealing with a distance score. Lower is more+-- similar. -type Nuc3SubstMatrix = U.Array (Nuc,Nuc,Nuc,Char) Int+data Distance -type Nuc2SubstMatrix = U.Array (Nuc,Nuc,Char) Int+-- An amino-acid substitution matrix. Tagged with the type of scoring used. -type Nuc1SubstMatrix = U.Array (Nuc,Char) Int+newtype AASubstMat t = AASubstMat { aaSubstMat :: Unboxed (Z:.Letter AA:.Letter AA) DLO }+ deriving (Generic,Eq,Read,Show) +instance Binary (AASubstMat t)+instance Serialize (AASubstMat t)+--instance FromJSON (AASubstMat t)+--instance ToJSON (AASubstMat t) +instance NFData (AASubstMat t) --- *+-- | @PAM@ matrices are similarity matrices. -mkNuc3SubstMatrix :: SubstMatrix -> Nuc3SubstMatrix-mkNuc3SubstMatrix mat = A.accumArray (\_ z -> z) (-10) ((nN,nN,nN,'*'),(nT,nT,nT,'Z'))- [ ( (a,b,c,k), case l of Just l' -> mat A.! (l',k) ; Nothing -> -10 )- | a<-acgt, b<-acgt, c<-acgt- , k<-['*' .. 'Z']- , let abc = map (toLower . fromNuc) [a,b,c]- , let l = M.lookup abc codonTable- ]+type SubstPAM = AASubstMat Similarity -mkNuc2SubstMatrix :: (Int -> Int -> Int) -> (Int -> Int) -> SubstMatrix -> Nuc2SubstMatrix-mkNuc2SubstMatrix f g mat = A.accumArray f (-10) ((nN,nN,'*'),(nT,nT,'Z'))- [ ( (x,y,k), case l of Just l' -> mat A.! (l',k) ; Nothing -> -10 )- | a<-acgt, b<-acgt, c<-acgt- , k<-['*' .. 'Z']- , (x,y) <- [ (a,b), (a,c), (b,c) ]- , let abc = map (toLower . fromNuc) [a,b,c]- , let l = M.lookup abc codonTable- ]+-- | @BLOSUM@ matrices are distance matrices. -mkNuc1SubstMatrix :: (Int -> Int -> Int) -> (Int -> Int) -> SubstMatrix -> Nuc1SubstMatrix-mkNuc1SubstMatrix f g mat = A.accumArray f (-10) ((nN,'*'),(nT,'Z'))- [ ( (x,k), case l of Just l' -> mat A.! (l',k) ; Nothing -> -10 )- | a<-acgt, b<-acgt, c<-acgt- , k<-['*' .. 'Z']- , x <- [a,b,c]- , let abc = map (toLower . fromNuc) [a,b,c]- , let l = M.lookup abc codonTable+type SubstBLOSUM = AASubstMat Distance++-- | Substitution matrix from amino acids to nucleotide triplets.++newtype ANuc3SubstMat t = ANuc3SubstMat { anuc3SubstMat :: Unboxed (Z:.Letter AA:.Letter DNA:.Letter DNA:.Letter DNA) DLO }+ deriving (Generic,Eq,Read,Show)++instance Binary (ANuc3SubstMat t)+instance Serialize (ANuc3SubstMat t)+--instance FromJSON (ANuc3SubstMat t)+--instance ToJSON (ANuc3SubstMat t)++instance NFData (ANuc3SubstMat t)++-- | Substitution matrix from amino acids to degenerate nucleotide+-- 2-tuples. The third nucleotide letter is missing.++newtype ANuc2SubstMat t = ANuc2SubstMat { anuc2SubstMat :: Unboxed (Z:.Letter AA:.Letter DNA:.Letter DNA) DLO }+ deriving (Generic,Eq,Read,Show)++instance Binary (ANuc2SubstMat t)+instance Serialize (ANuc2SubstMat t)+--instance FromJSON (ANuc2SubstMat t)+--instance ToJSON (ANuc2SubstMat t)++instance NFData (ANuc2SubstMat t)++-- | Substitution matrix from amino acids to degenerate nucleotide+-- 1-tuples. Two out of three nucleotides in a triplet are missing.++newtype ANuc1SubstMat t = ANuc1SubstMat { anuc1SubstMat :: Unboxed (Z:.Letter AA:.Letter DNA) DLO }+ deriving (Generic,Eq,Read,Show)++instance Binary (ANuc1SubstMat t)+instance Serialize (ANuc1SubstMat t)+--instance FromJSON (ANuc1SubstMat t)+--instance ToJSON (ANuc1SubstMat t)++instance NFData (ANuc1SubstMat t)++-- | The usual substitution matrix, but here with a codon and an amino acid+-- to be compared.++mkANuc3SubstMat :: AASubstMat t -> ANuc3SubstMat t+mkANuc3SubstMat (AASubstMat m) = ANuc3SubstMat $ fromAssocs (Z:. AA.Stop :. D.A:.D.A:.D.A) (Z:. AA.Z :. D.N:.D.N:.D.N) (DLO $ -999)+ [ ( (Z:.a:.u:.v:.w) , maybe (DLO $ -999) (\b -> m!(Z:.a:.b)) $ M.lookup uvw dnaAAmap)+ | a <- aaRange+ , u <- [D.A .. D.N], v <- [D.A .. D.N], w <- [D.A .. D.N]+ , let uvw = VU.fromList [u,v,w] ]++-- | Create a 2-tuple to amino acid substitution matrix. Here, @f@ combines+-- all to entries that have the same 2-tuple index.++mkANuc2SubstMat :: (DLO -> DLO -> DLO) -> AASubstMat t -> ANuc2SubstMat t+mkANuc2SubstMat f (AASubstMat m) = ANuc2SubstMat $ fromAssocs (Z:. AA.Stop :. D.A:.D.A) (Z:. AA.Z :. D.N:.D.N) (DLO $ -999)+ . M.assocs+ . M.fromListWith f+ $ [ ((Z:.a:.x:.y), maybe (DLO $ -999) (\k -> m!(Z:.a:.k)) $ M.lookup uvw dnaAAmap)+ | a <- aaRange+ , u <- [D.A .. D.N], v <- [D.A .. D.N], w <- [D.A .. D.N]+ , (x,y) <- [ (u,v), (u,w), (v,w) ]+ , let uvw = VU.fromList [u,v,w]+ ]++-- | The most degenerate case, where just a single nucleotide remains in+-- the amino-acid / nucleotide substitution. Again, @f@ combines different+-- entries.++mkANuc1SubstMat :: (DLO -> DLO -> DLO) -> AASubstMat t -> ANuc1SubstMat t+mkANuc1SubstMat f (AASubstMat m) = ANuc1SubstMat $ fromAssocs (Z:. AA.Stop :. D.A) (Z:. AA.Z :. D.N) (DLO $ -999)+ . M.assocs+ . M.fromListWith f+ $ [ ((Z:.a:.x), maybe (DLO $ -999) (\k -> m!(Z:.a:.k)) $ M.lookup uvw dnaAAmap)+ | a <- aaRange+ , u <- [D.A .. D.N], v <- [D.A .. D.N], w <- [D.A .. D.N]+ , x <- [u,v,w]+ , let uvw = VU.fromList [u,v,w]+ ]
Biobase/SubstMatrix/Import.hs view
@@ -1,17 +1,28 @@ module Biobase.SubstMatrix.Import where -import qualified Data.Array.IArray as A-import Control.Applicative -import Biobase.SubstMatrix+import Control.Applicative+import Data.Char (toLower)+import qualified Data.Map as M +import Biobase.Primary.AA (charAA)+import Biobase.Types.Odds+import Data.PrimitiveArray hiding (map)+import qualified Biobase.Primary.AA as AA +import Biobase.SubstMatrix -fromFile :: FilePath -> IO SubstMatrix+++fromFile :: FilePath -> IO (AASubstMat t) fromFile fname = do (x:xs) <- dropWhile (("#"==).take 1) . lines <$> readFile fname let cs = map head . words $ x -- should give us the characters encoding an amino acid- let ss = map (map read . drop 1 . words) $ xs- return $ A.accumArray (\_ z -> z) (-10) (('*','*'),('Z','Z')) [ ((k1,k2),z) | (k1,s) <- zip cs ss, (k2,z) <- zip cs s ]+ let ss = map (map DLO . map read . drop 1 . words) $ xs+ let xs = [ ((Z:.charAA k1:.charAA k2),z)+ | (k1,s) <- zip cs ss+ , (k2,z) <- zip cs s+ ]+ return . AASubstMat $ fromAssocs (Z:.AA.Stop:.AA.Stop) (Z:.AA.Z:.AA.Z) (DLO $ -999) xs
BiobaseBlast.cabal view
@@ -1,50 +1,111 @@ name: BiobaseBlast-version: 0.0.0.1-author: Christian Hoener zu Siederdissen-maintainer: choener@tbi.univie.ac.at-homepage: http://www.tbi.univie.ac.at/~choener/-copyright: Christian Hoener zu Siederdissen, 2013+version: 0.2.0.0+author: Christian Hoener zu Siederdissen, Florian Eggenhofer+maintainer: choener@bioinf.uni-leipzig.de+homepage: https://github.com/choener/BiobaseBlast+bug-reports: https://github.com/choener/BiobaseBlast/issues+copyright: Christian Hoener zu Siederdissen, 2013 - 2017 category: Bioinformatics-synopsis: BLAST-related tools license: GPL-3 license-file: LICENSE build-type: Simple stability: experimental-cabal-version: >= 1.6.0+cabal-version: >= 1.10.0+tested-with: GHC == 7.10.3, GHC == 8.0.1, GHC == 8.0.2+synopsis: BLAST-related tools description:- This library contains BLAST-related functionality. For now,- this library is very limited (and Ketil Malde provides other- BLAST functionality anyway).+ This library contains BLAST-related functionality: .- We do provide parsers for BLOSUM and PAM matrices.+ - Parser for tabular NCBI BLAST+ output+ - Parsers for BLOSUM and PAM matrices.+ - Specialized substitution functions for (in)complete amino+ acid / nucleotide triplet substitution.+ - Incomplete nucleotide patterns map one or two nucleotides to+ an amino acid (need for indel editing in the mitochondria of+ certain species like /p.polycephalum/). .- The matrices can be found here:+ The matrices are currently not provided but can be found here: <ftp://ftp.ncbi.nih.gov/blast/matrices/> extra-source-files:- changelog+ changelog.md+ README.md library- build-depends:- base >3 && <5 ,- array ,- containers ,- BiobaseXNA >= 0.7.0.2+ build-depends: base >= 4.7 && < 5.0+ , aeson >= 1.0+ , attoparsec >= 0.13+ , binary >= 0.7+ , bytestring+ , cereal >= 0.4+ , containers+ , deepseq >= 1.3+ , directory+ , vector >= 0.10+ , vector-th-unbox >= 0.2+ --+ , BiobaseTypes == 0.1.2.*+ , BiobaseXNA == 0.9.3.*+ , PrimitiveArray == 0.8.0.* + default-language:+ Haskell2010+ default-extensions: DeriveGeneric+ , MultiParamTypeClasses+ , TemplateHaskell+ , TypeFamilies+ , TypeOperators exposed-modules: Biobase.SubstMatrix Biobase.SubstMatrix.Import+ Biobase.BLAST+ Biobase.BLAST.Import+ Biobase.BLAST.Types ghc-options: -O2 +test-suite properties+ type:+ exitcode-stdio-1.0+ main-is:+ properties.hs+ ghc-options:+ -O2 -threaded -rtsopts -with-rtsopts=-N+ hs-source-dirs:+ tests+ default-language:+ Haskell2010+ default-extensions: BangPatterns+ , OverloadedStrings+ , ScopedTypeVariables+ , TemplateHaskell+ build-depends: base+ , bytestring+ , containers+ , filepath+ , split >= 0.2.3+ , tasty >= 0.11+ , tasty-quickcheck >= 0.8+ , tasty-silver >= 3.1.9+ , tasty-th >= 0.1+ --+ , BiobaseBlast+++ source-repository head- type: git- location: git://github.com/choener/BiobaseBlast+ type: git+ location: https://github.com/choener/BiobaseBlast++source-repository this+ type: git+ location: https://github.com/choener/BiobaseBlast/tree/0.2.0.0+ tag: 0.2.0.0
+ README.md view
@@ -0,0 +1,15 @@+[](https://travis-ci.org/choener/BiobaseBlast)++# BiobaseBlast++This library contains BLAST-related functionality.++++#### Contact++Christian Hoener zu Siederdissen +Leipzig University, Leipzig, Germany +choener@bioinf.uni-leipzig.de +http://www.bioinf.uni-leipzig.de/~choener/ +
− changelog
@@ -1,2 +0,0 @@-0.0.0.1- * initial commit
+ changelog.md view
@@ -0,0 +1,16 @@+0.2.0.0+-------++- Added parsing functionality for tabular NCBI BLAST+ output++0.1.0.0+-------++- more efficient tables (using PrimitiveArray)+- updated to newest PrimitiveArray & BiobaseXNA+- travis.yml added++0.0.0.1+-------++- initial commit
+ tests/properties.hs view
@@ -0,0 +1,13 @@++-- |+--+-- TODO some unit tests to check if assumed-known values are in the right+-- table cells++module Main where++++main :: IO ()+main = return ()+