elynx-seq 0.3.0 → 0.3.1
raw patch · 21 files changed
+207/−159 lines, 21 filesdep +attoparsecdep −hspec-megaparsecdep −megaparsec
Dependencies added: attoparsec
Dependencies removed: hspec-megaparsec, megaparsec
Files
- ChangeLog.md +8/−1
- README.md +27/−27
- elynx-seq.cabal +3/−5
- src/ELynx/Data/Alphabet/Character.hs +1/−1
- src/ELynx/Data/Alphabet/DistributionDiversity.hs +13/−4
- src/ELynx/Data/Character/AminoAcid.hs +1/−1
- src/ELynx/Data/Character/AminoAcidI.hs +1/−1
- src/ELynx/Data/Character/AminoAcidS.hs +1/−1
- src/ELynx/Data/Character/AminoAcidX.hs +1/−1
- src/ELynx/Data/Character/Character.hs +1/−1
- src/ELynx/Data/Character/Nucleotide.hs +1/−1
- src/ELynx/Data/Character/NucleotideI.hs +1/−1
- src/ELynx/Data/Character/NucleotideX.hs +1/−1
- src/ELynx/Data/Sequence/Alignment.hs +48/−17
- src/ELynx/Data/Sequence/Sequence.hs +50/−30
- src/ELynx/Data/Sequence/Translate.hs +7/−1
- src/ELynx/Export/Sequence/Fasta.hs +7/−7
- src/ELynx/Import/Sequence/Fasta.hs +26/−41
- test/ELynx/Data/Sequence/AlignmentSpec.hs +3/−3
- test/ELynx/Data/Sequence/SequenceSpec.hs +5/−9
- test/ELynx/Export/Sequence/FastaSpec.hs +1/−5
ChangeLog.md view
@@ -2,7 +2,14 @@ # Changelog for ELynx -## Version 0.3.1; in development+## Version 0.3.2; in development+++## Version 0.3.1++- Use Attoparsec.+- Use ByteString consistently.+- Remove elynx-tools dependency from libaries. ## Version 0.3.0
README.md view
@@ -2,7 +2,7 @@ # The ELynx Suite -Version: 0.3.0.+Version: 0.3.1. Reproducible evolution made easy. <p align="center"><img src="https://travis-ci.org/dschrempf/elynx.svg?branch=master"/></p>@@ -90,9 +90,9 @@ slynx --help | head -n -16 - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME] [-f|--force] COMMAND@@ -136,9 +136,9 @@ slynx concatenate --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx concatenate (-a|--alphabet NAME) INPUT-FILE Concatenate sequences found in input files.@@ -157,9 +157,9 @@ slynx examine --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx examine (-a|--alphabet NAME) INPUT-FILE [--per-site] Examine sequences. If data is a multi sequence alignment, additionally analyze columns.@@ -179,9 +179,9 @@ slynx filter-rows --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx filter-rows (-a|--alphabet NAME) INPUT-FILE [--longer-than LENGTH] [--shorter-than LENGTH] [--standard-characters]@@ -202,9 +202,9 @@ slynx filter-columns --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx filter-columns (-a|--alphabet NAME) INPUT-FILE [--standard-chars DOUBLE]@@ -226,9 +226,9 @@ slynx simulate --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx simulate (-t|--tree-file Name) [-s|--substitution-model MODEL] [-m|--mixture-model MODEL] [-e|--edm-file NAME] @@ -305,9 +305,9 @@ slynx sub-sample --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx sub-sample (-a|--alphabet NAME) INPUT-FILE (-n|--number-of-sites INT)@@ -335,9 +335,9 @@ slynx translate --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: slynx translate (-a|--alphabet NAME) INPUT-FILE (-r|--reading-frame INT) (-u|--universal-code CODE)@@ -360,9 +360,9 @@ tlynx --help | head -n -16 - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: tlynx [-v|--verbosity VALUE] [-o|--output-file-basename NAME] [-f|--force] COMMAND@@ -399,9 +399,9 @@ tlynx compare --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: tlynx compare [-n|--normalize] [-b|--bipartitions] [-t|--intersect] [-f|--newick-format FORMAT] NAMES@@ -428,9 +428,9 @@ tlynx examine --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: tlynx examine INPUT-FILE [-f|--newick-format FORMAT] Compute summary statistics of phylogenetic trees.@@ -452,9 +452,9 @@ tlynx simulate --help - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: tlynx simulate [-t|--nTrees INT] [-n|--nLeaves INT] [-H|--height DOUBLE] [-M|--condition-on-mrca] [-l|--lambda DOUBLE] @@ -491,9 +491,9 @@ elynx --help | head -n -16 - ELynx Suite version 0.3.0.+ ELynx Suite version 0.3.1. Developed by Dominik Schrempf.- Compiled on July 30, 2020, at 12:40 pm, UTC.+ Compiled on August 3, 2020, at 12:12 pm, UTC. Usage: elynx COMMAND Validate and redo past ELynx analyses
elynx-seq.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: afdff7b344d4b63f734a6f18db88ef39b1f45ed84c64e63045b57edfb839684f+-- hash: 656a2c088bc0e776325be75f926d33618cc4a11940ba39f35f50bb90e6636b7d name: elynx-seq-version: 0.3.0+version: 0.3.1 synopsis: Handle molecular sequences description: Examine, modify, and simulate molecular sequences in a reproducible way. Please see the README on GitHub at <https://github.com/dschrempf/elynx>. category: Bioinformatics@@ -56,12 +56,11 @@ ghc-options: -Wall -fllvm build-depends: aeson+ , attoparsec , base >=4.7 && <5 , bytestring , containers- , elynx-tools , matrices- , megaparsec , mwc-random , parallel , primitive@@ -90,7 +89,6 @@ , elynx-seq , elynx-tools , hspec- , hspec-megaparsec , matrices , vector default-language: Haskell2010
src/ELynx/Data/Alphabet/Character.hs view
@@ -26,11 +26,11 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import qualified Data.Vector.Unboxed as V import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Alphabet characters; abstracted so that representation can be changed at -- some point.
src/ELynx/Data/Alphabet/DistributionDiversity.hs view
@@ -33,8 +33,17 @@ import qualified Data.Vector.Generic as V import ELynx.Data.Alphabet.Alphabet import ELynx.Data.Alphabet.Character-import ELynx.Tools +eps :: Double+eps = 1e-12++-- Calculate x*log(x) but set to 0.0 when x is smaller than 'eps'.+xLogX :: Double -> Double+xLogX x+ | x < 0.0 = error "Argument lower than zero."+ | eps > x = 0.0+ | otherwise = x * log x+ -- | Entropy of vector. entropy :: (Vector v Double) => v Double -> Double entropy v =@@ -44,7 +53,7 @@ ("entropy: Sesult of following vector is NaN: " ++ show (toList v) ++ ".") else res where- res = negate $ sumVec $ V.map xLogX v+ res = negate $ V.sum $ V.map xLogX v -- | Effective number of used characters measured using 'entropy'. The result -- only makes sense when the sum of the array is 1.0.@@ -55,7 +64,7 @@ -- binomially sampling the same character twice and only makes sense when the -- sum of the array is 1.0. homoplasy :: Vector v Double => v Double -> Double-homoplasy v = sumVec $ V.map (\x -> x * x) v+homoplasy v = V.sum $ V.map (\x -> x * x) v -- | Effective number of used characters measured using 'homoplasy'. The result -- only makes sense when the sum of the array is 1.0.@@ -98,4 +107,4 @@ frequencyCharacters alph d = V.map (`saveDivision` s) counts where counts = countCharacters alph d- s = sumVec counts+ s = V.sum counts
src/ELynx/Data/Character/AminoAcid.hs view
@@ -47,10 +47,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Amino acids. data AminoAcid = A | C | D | E | F | G | H | I | K | L | M | N | P | Q | R | S | T | V | W | Y
src/ELynx/Data/Character/AminoAcidI.hs view
@@ -68,10 +68,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Amino acids. data AminoAcidI
src/ELynx/Data/Character/AminoAcidS.hs view
@@ -52,10 +52,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Amino acids. data AminoAcidS
src/ELynx/Data/Character/AminoAcidX.hs view
@@ -52,10 +52,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Amino acids. data AminoAcidX
src/ELynx/Data/Character/Character.hs view
@@ -27,10 +27,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import qualified Data.Set as S import Data.Vector.Unboxed.Base (Unbox) import Data.Word8 (Word8)-import ELynx.Tools -- XXX: Remove name clash with ELynx.Data.Alphabet.Alphabet.Character?
src/ELynx/Data/Character/Nucleotide.hs view
@@ -29,10 +29,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Nucleotides. data Nucleotide = A | C | G | T
src/ELynx/Data/Character/NucleotideI.hs view
@@ -58,10 +58,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | NucleotideIs. data NucleotideI
src/ELynx/Data/Character/NucleotideX.hs view
@@ -34,10 +34,10 @@ ) where +import Data.ByteString.Internal (c2w, w2c) import Data.Vector.Unboxed.Deriving import Data.Word8 import qualified ELynx.Data.Character.Character as C-import ELynx.Tools -- | Extended nucleotides. data NucleotideX
src/ELynx/Data/Sequence/Alignment.hs view
@@ -44,7 +44,8 @@ import Control.Monad hiding (join) import Control.Monad.Primitive-import qualified Data.ByteString.Lazy.Char8 as L+import Control.Parallel.Strategies+import qualified Data.ByteString.Lazy.Char8 as BL import Data.List hiding ( concat, length,@@ -56,7 +57,6 @@ import qualified ELynx.Data.Alphabet.DistributionDiversity as D import ELynx.Data.Sequence.Defaults import qualified ELynx.Data.Sequence.Sequence as S-import ELynx.Tools import System.Random.MWC import Prelude hiding ( concat,@@ -94,6 +94,8 @@ a = S.alphabet $ head ss bss = map S.characters ss d = M.fromRows bss+ allEqual [] = True+ allEqual xs = all (== head xs) $ tail xs -- | Conversion to list of 'S.Sequence's. toSequences :: Alignment -> [S.Sequence]@@ -106,32 +108,25 @@ where rows = M.toRows da --- -- | Show a 'Alignment', untrimmed.--- summarizeSeq :: Alignment -> Int -> L.ByteString--- summarizeSeq m i =--- L.unwords [ alignLeft nameWidth $ names m !! i--- , summarizeByteString summaryLength $--- L.pack $ V.toList $ V.map toChar $ M.takeRow (matrix m) i ]--header :: Alignment -> L.ByteString+header :: Alignment -> BL.ByteString header a =- L.unlines $- [ L.pack "Multi sequence alignment.",- L.pack $ "Code: " ++ A.alphabetDescription (alphabet a) ++ ".",- L.pack $ "Length: " ++ show (length a) ++ "."+ BL.unlines $+ [ BL.pack "Multi sequence alignment.",+ BL.pack $ "Code: " ++ A.alphabetDescription (alphabet a) ++ ".",+ BL.pack $ "Length: " ++ show (length a) ++ "." ] ++ reportLengthSummary ++ reportNumberSummary where reportLengthSummary =- [ L.pack $+ [ BL.pack $ "For each sequence, the " ++ show summaryLength ++ " first bases are shown." | length a > summaryLength ] reportNumberSummary =- [ L.pack $+ [ BL.pack $ show summaryNSequences ++ " out of " ++ show (nSequences a)@@ -140,9 +135,23 @@ ] -- | Similar to 'S.summarizeSequenceList' but with different Header.-summarize :: Alignment -> L.ByteString+summarize :: Alignment -> BL.ByteString summarize a = header a <> S.body (toSequences a) +-- Vertical concatenation.+(===) :: V.Unbox a => M.Matrix a -> M.Matrix a -> M.Matrix a+(===) l r = M.fromRows $ lRs ++ rRs+ where+ lRs = M.toRows l+ rRs = M.toRows r++-- Horizontal concatenation.+(|||) :: V.Unbox a => M.Matrix a -> M.Matrix a -> M.Matrix a+(|||) l r = M.fromColumns $ lCs ++ rCs+ where+ lCs = M.toColumns l+ rCs = M.toColumns r+ -- | Join two 'Alignment's vertically. That is, add more sequences -- to an alignment. See also 'concat'. join :: Alignment -> Alignment -> Alignment@@ -219,6 +228,16 @@ -- the number of characters. type FrequencyData = M.Matrix Double +-- Map a function on each row of a DIM2 array; parallel version with given chunk size.+fMapColParChunk ::+ (V.Unbox a, V.Unbox b) =>+ Int ->+ (V.Vector a -> V.Vector b) ->+ M.Matrix a ->+ M.Matrix b+fMapColParChunk n f m =+ M.fromColumns (map f (M.toColumns m) `using` parListChunk n rseq)+ -- | Calculcate frequency of characters at each site of a multi sequence alignment. toFrequencyData :: Alignment -> FrequencyData toFrequencyData a = fMapColParChunk 100 (D.frequencyCharacters spec) (matrix a)@@ -236,6 +255,13 @@ where nSites = M.cols fd +-- Parallel map with given chunk size.+parMapChunk :: Int -> (a -> b) -> [a] -> [b]+parMapChunk n f as = map f as `using` parListChunk n rseq++chunksize :: Int+chunksize = 500+ -- | Diversity analysis. See 'kEffEntropy'. kEffEntropy :: FrequencyData -> [Double] kEffEntropy fd = parMapChunk chunksize D.kEffEntropy (M.toColumns fd)@@ -262,6 +288,11 @@ countUnknowns a = V.length . V.filter (A.isUnknown (alphabet a)) $ allChars where allChars = M.flatten $ matrix a++-- Sample the given sites from a matrix.+subSampleMatrix :: V.Unbox a => [Int] -> M.Matrix a -> M.Matrix a+subSampleMatrix is m =+ M.fromColumns $ foldl' (\a i -> M.takeColumn m i : a) [] is -- | Sample the given sites from a multi sequence alignment. subSample :: [Int] -> Alignment -> Alignment
src/ELynx/Data/Sequence/Sequence.hs view
@@ -48,14 +48,13 @@ where import Control.Parallel.Strategies-import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import Data.List (maximumBy) import Data.Ord (comparing) import qualified Data.Vector.Unboxed as V import qualified ELynx.Data.Alphabet.Alphabet as A import ELynx.Data.Alphabet.Character import ELynx.Data.Sequence.Defaults-import ELynx.Tools import qualified Text.Printf as P import Prelude hiding ( concat,@@ -65,22 +64,22 @@ ( length, ) --- | For now, 'Name's are just 'L.ByteString's.-type Name = L.ByteString+-- | For now, 'Name's are just 'BL.ByteString's.+type Name = BL.ByteString -- | The description of a sequence.-type Description = L.ByteString+type Description = BL.ByteString -- | The vector of characters of a sequence. type Characters = V.Vector Character -- | Convert byte string to sequence characters.-fromByteString :: L.ByteString -> Characters-fromByteString = V.fromList . map fromChar . L.unpack+fromByteString :: BL.ByteString -> Characters+fromByteString = V.fromList . map fromChar . BL.unpack -- | Convert sequence characters to byte string.-toByteString :: Characters -> L.ByteString-toByteString = L.pack . map toChar . V.toList+toByteString :: Characters -> BL.ByteString+toByteString = BL.pack . map toChar . V.toList -- | Sequences have a name, a possibly empty description, a code and hopefully a -- lot of data.@@ -92,33 +91,51 @@ } deriving (Show, Eq) -getInfo :: Sequence -> L.ByteString+alignRight :: Int -> BL.ByteString -> BL.ByteString+alignRight n s =+ BL.replicate (fromIntegral n - l) ' ' <> BL.take (fromIntegral n) s+ where+ l = BL.length s++alignLeft :: Int -> BL.ByteString -> BL.ByteString+alignLeft n s =+ BL.take (fromIntegral n) s <> BL.replicate (fromIntegral n - l) ' '+ where+ l = BL.length s++getInfo :: Sequence -> BL.ByteString getInfo s =- L.unwords+ BL.unwords [ alignLeft nameWidth (name s),- alignRight fieldWidth (L.pack $ show $ alphabet s),- alignRight fieldWidth (L.pack . show $ len),- alignRight fieldWidth (L.pack $ P.printf "%2.2f" pGaps)+ alignRight fieldWidth (BL.pack $ show $ alphabet s),+ alignRight fieldWidth (BL.pack . show $ len),+ alignRight fieldWidth (BL.pack $ P.printf "%2.2f" pGaps) ] where len = length s nGaps = countGaps s pGaps = 100 * fromIntegral nGaps / fromIntegral len :: Double +-- If a string is longer than a given value, trim it and add some dots.+summarizeByteString :: Int -> BL.ByteString -> BL.ByteString+summarizeByteString l s+ | BL.length s >= fromIntegral l = BL.take (fromIntegral l) s <> BL.pack "..."+ | otherwise = s+ -- | Trim and show a 'Sequence'.-summarize :: Sequence -> L.ByteString+summarize :: Sequence -> BL.ByteString summarize s =- L.unwords+ BL.unwords [getInfo s, summarizeByteString summaryLength $ toByteString (characters s)] -- | Trim and show a list of 'Sequence's.-summarizeSequences :: [Sequence] -> L.ByteString+summarizeSequences :: [Sequence] -> BL.ByteString summarizeSequences ss = header ss <> body (take summaryNSequences ss) -- | Header printed before 'Sequence' list.-tableHeader :: L.ByteString+tableHeader :: BL.ByteString tableHeader =- L.unwords+ BL.unwords [ alignLeft nameWidth "Name", alignRight fieldWidth "Code", alignRight fieldWidth "Length",@@ -127,15 +144,15 @@ ] -- | A short description of the sequence.-header :: [Sequence] -> L.ByteString+header :: [Sequence] -> BL.ByteString header ss =- L.unlines $+ BL.unlines $ reportIfSubsetIsShown- ++ [ L.pack $+ ++ [ BL.pack $ "For each sequence, the " ++ show summaryLength ++ " first bases are shown.",- L.pack $ "List contains " ++ show (Pr.length ss) ++ " sequences.",+ BL.pack $ "List contains " ++ show (Pr.length ss) ++ " sequences.", "", tableHeader ]@@ -147,12 +164,12 @@ ++ show (Pr.length ss) ++ " sequences are shown." reportIfSubsetIsShown- | l > summaryNSequences = [L.pack s]+ | l > summaryNSequences = [BL.pack s] | otherwise = [] -- | Trim and show a list of 'Sequence's.-body :: [Sequence] -> L.ByteString-body ss = L.unlines (map summarize ss `using` parListChunk 5 rdeepseq)+body :: [Sequence] -> BL.ByteString+body ss = BL.unlines (map summarize ss `using` parListChunk 5 rdeepseq) -- | Calculate length of 'Sequence'. length :: Sequence -> Int@@ -161,6 +178,9 @@ -- | Check if all 'Sequence's have equal length. equalLength :: [Sequence] -> Bool equalLength = allEqual . map length+ where+ allEqual [] = True+ allEqual xs = all (== head xs) $ tail xs -- | Find the longest 'Sequence' in a list. longest :: [Sequence] -> Sequence@@ -180,16 +200,16 @@ | i /= j = error $ "concatenate: Sequences do not have equal names: "- ++ L.unpack i+ ++ BL.unpack i ++ ", "- ++ L.unpack j+ ++ BL.unpack j ++ "." | d /= f = error $ "concatenate: Sequences do not have equal descriptions: "- ++ L.unpack d+ ++ BL.unpack d ++ ", "- ++ L.unpack f+ ++ BL.unpack f ++ "." | c /= k = error $
src/ELynx/Data/Sequence/Translate.hs view
@@ -19,7 +19,13 @@ import qualified ELynx.Data.Alphabet.Character as C import ELynx.Data.Character.Codon import ELynx.Data.Sequence.Sequence-import ELynx.Tools++-- Chop list into chunks of given length. If the last chop is shorter than+-- length, it is dropped.+chopVec :: V.Unbox a => Int -> V.Vector a -> [V.Vector a]+chopVec n xs+ | V.length xs < n = []+ | otherwise = V.take n xs : chopVec n (V.drop n xs) -- | Translate a sequence from 'DNA' or 'DNAX' to 'ProteinS'. translateSeq :: UniversalCode -> Int -> Sequence -> Sequence
src/ELynx/Export/Sequence/Fasta.hs view
@@ -18,19 +18,19 @@ ) where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import ELynx.Data.Sequence.Sequence -fastaHeader :: L.ByteString -> L.ByteString -> L.ByteString+fastaHeader :: BL.ByteString -> BL.ByteString -> BL.ByteString fastaHeader n d =- L.singleton '>' <> n <> if L.null d then L.empty else L.pack " " <> d+ BL.singleton '>' <> n <> if BL.null d then BL.empty else BL.pack " " <> d -- | Convert a 'Sequence' to Fasta format.-sequenceToFasta :: Sequence -> L.ByteString+sequenceToFasta :: Sequence -> BL.ByteString sequenceToFasta s =- L.unlines [fastaHeader (name s) (description s), toByteString $ characters s]+ BL.unlines [fastaHeader (name s) (description s), toByteString $ characters s] -- | Convert a list 'Sequence's to Fasta format. A newline is added between any -- two 'Sequence's.-sequencesToFasta :: [Sequence] -> L.ByteString-sequencesToFasta ss = L.concat $ map sequenceToFasta ss+sequencesToFasta :: [Sequence] -> BL.ByteString+sequencesToFasta ss = BL.concat $ map sequenceToFasta ss
src/ELynx/Import/Sequence/Fasta.hs view
@@ -14,72 +14,57 @@ -- Parse FASTA files. -- -- [NCBI file specifications](https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=BlastHelp).------ For more complicated parsers, try to use a [lexer](https://hackage.haskell.org/package/megaparsec-7.0.1/docs/Text-Megaparsec-Byte-Lexer.html). module ELynx.Import.Sequence.Fasta- ( Parser,- fastaSequence,+ ( fastaSequence, fasta, ) where -import Control.Monad-import qualified Data.ByteString.Lazy.Char8 as L+import Control.Applicative+import qualified Data.Attoparsec.ByteString as AS+import qualified Data.Attoparsec.ByteString.Char8 as AC+import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Set as S-import Data.Void-import Data.Word8- ( Word8,- isAlphaNum,- )+import Data.Word8 (Word8) import ELynx.Data.Alphabet.Alphabet as A import ELynx.Data.Alphabet.Character import ELynx.Data.Sequence.Sequence-import ELynx.Tools-import Text.Megaparsec-import Text.Megaparsec.Byte --- | Shortcut.-type Parser = Parsec Void L.ByteString--isSpecial :: Word8 -> Bool-isSpecial w = w `elem` map c2w ['_', '|', '.', '-']--isHeader :: Word8 -> Bool-isHeader w = isAlphaNum w || isSpecial w+isSpecial :: Char -> Bool+isSpecial w = w `elem` ['_', '|', '.', '-'] -isHorizontalSpace :: Word8 -> Bool-isHorizontalSpace w = (w == c2w ' ') || (w == c2w '\t')+isHeader :: Char -> Bool+isHeader w = AC.isAlpha_ascii w || AC.isDigit w || isSpecial w -sequenceHeader :: Parser (L.ByteString, L.ByteString)+sequenceHeader :: AS.Parser (BL.ByteString, BL.ByteString) sequenceHeader = do- _ <- char (c2w '>')- n <- takeWhile1P (Just "Name") isHeader- _ <- takeWhileP (Just "Horizontal space") isHorizontalSpace- d <- takeWhileP (Just "Description") isHeader- _ <- eol- return (n, d)+ _ <- AC.char '>'+ n <- AC.takeWhile1 isHeader+ _ <- AS.takeWhile AC.isHorizontalSpace+ d <- AC.takeWhile isHeader+ _ <- AC.endOfLine+ return (BL.fromStrict n, BL.fromStrict d) -- It is a little faster to directly pass the set of allowed characters. Then, -- this set only has to be calculcated once per sequence in 'fastaSequence'.-sequenceLine :: S.Set Word8 -> Parser L.ByteString+sequenceLine :: S.Set Word8 -> AS.Parser BL.ByteString sequenceLine s = do -- XXX: Will fail for non-capital letters.- !xs <- takeWhile1P (Just "Alphabet character") (`S.member` s)- _ <- void eol <|> eof- return xs+ !xs <- AS.takeWhile1 (`S.member` s)+ return (BL.fromStrict xs) -- XXX: If sequences are parsed line by line, the lines have to be copied when -- forming the complete sequence. This is not memory efficient. -- | Parse a sequence of characters.-fastaSequence :: Alphabet -> Parser Sequence+fastaSequence :: Alphabet -> AS.Parser Sequence fastaSequence a = do (n, d) <- sequenceHeader let !alph = S.map toWord (A.all . alphabetSpec $ a)- lns <- some (sequenceLine alph)- _ <- many eol- return $ Sequence n d a (fromByteString $ L.concat lns)+ lns <- sequenceLine alph `AS.sepBy1` AC.endOfLine+ _ <- many AC.endOfLine+ return $ Sequence n d a (fromByteString $ BL.concat lns) -- | Parse a Fasta file with given 'Alphabet'.-fasta :: Alphabet -> Parser [Sequence]-fasta a = some (fastaSequence a) <* eof+fasta :: Alphabet -> AS.Parser [Sequence]+fasta a = some (fastaSequence a) <* AS.endOfInput
test/ELynx/Data/Sequence/AlignmentSpec.hs view
@@ -13,7 +13,7 @@ ) where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Matrix.Unboxed as M import ELynx.Data.Alphabet.Alphabet import ELynx.Data.Alphabet.Character@@ -31,8 +31,8 @@ ssA :: Alignment ssA = Alignment- (map L.pack ["SEQUENCE_1", "SEQUENCE_2", "SEQUENCE_3"])- (replicate 3 L.empty)+ (map BL.pack ["SEQUENCE_1", "SEQUENCE_2", "SEQUENCE_3"])+ (replicate 3 BL.empty) DNAI ssData
test/ELynx/Data/Sequence/SequenceSpec.hs view
@@ -13,7 +13,7 @@ ) where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Lazy.Char8 as BL import ELynx.Data.Alphabet.Alphabet import ELynx.Data.Sequence.Sequence import ELynx.Import.Sequence.Fasta@@ -26,17 +26,13 @@ fastaDifferentLengthTrimmedFN :: FilePath fastaDifferentLengthTrimmedFN = "data/NucleotideDifferentLengthTrimmed.fasta" -longestSequenceInFileBS :: L.ByteString+longestSequenceInFileBS :: BL.ByteString longestSequenceInFileBS =- L.unlines $- map L.pack [">SEQUENCE_3", "ATTTAAAAAAACCCAAAACCCGGGCCCCGGGTTTTTTTA"]+ BL.unlines $+ map BL.pack [">SEQUENCE_3", "ATTTAAAAAAACCCAAAACCCGGGCCCCGGGTTTTTTTA"] longestSequenceInFile :: Sequence-longestSequenceInFile =- parseByteStringWith- "Fasta byte string"- (fastaSequence DNA)- longestSequenceInFileBS+longestSequenceInFile = parseByteStringWith (fastaSequence DNA) longestSequenceInFileBS spec :: Spec spec = do
test/ELynx/Export/Sequence/FastaSpec.hs view
@@ -30,9 +30,5 @@ $ do ss <- parseFileWith (fasta DNAI) fastaNucleotideIUPACFN let f = sequencesToFasta ss- ss' =- parseByteStringWith- "Fasta byte string created from sequence object"- (fasta DNAI)- f+ ss' = parseByteStringWith (fasta DNAI) f ss `shouldBe` ss'