packages feed

a50 (empty) → 0.2

raw patch · 7 files changed

+321/−0 lines, 7 filesdep +basedep +biodep +cmdargssetup-changed

Dependencies added: base, bio, cmdargs, containers, directory, process

Files

+ README view
@@ -0,0 +1,62 @@+a50 is a tool for comparing genome assemblies, providing a bit more+information than the usual numeric statistics, like N50.  For a quick+overview of the options, use 'a50 --help'.+++  General usage+  -------------++To compare assemblies, you need two or more fasta-formatted files+containing contigs.  Running 'a50' with the files as arguments+produces a plot with one curve per input.  On the x-axis are the+contigs, ordered by size, and on the y-axis is the corresponding+cumulative size.  Generally, a better assembly has a steeper curve+(big contigs) which ends early (few contigs) near the y-value+corresponding to the expected genome size.++The plot is generated using 'gnuplot', so the gnuplot executable must+be in your $PATH.  You can pass various information to gnuplot on the+commandline, specifically you can use -f to specify output format+('terminal' in gnuplot lingo), -o to specify an output file, and -e to+produce horizontal lines, e.g. at the expected genome size.++For example:++        a50 -t pdf -o asm.pdf asm1.fasta asm2.fasta -e 8e+8++will plot the assemblies given in asm1.fasta and asm2.fasta against an+expected genome size of 800Mb in PDF format to the file asm.pdf.  If+no output or format is specified, gnuplot will display the graph in a+window.  If an output file, but no format is specified, a50 will try+to determine the format from the file name extension.+++  Using EST or transcripts as a reference+  ---------------------------------------++A similar way to compare assemblies is to measure the gene coverage of+each contig.  To do this, you need to have the genes available,+typically in the form of raw or assembled ESTs.  By specifying a set+of transcripts using the -E option, e.g:++        a50 asm.fasta asm2.fasta -E ests.fasta++will plot curves with contigs of the assemblies ordered by size along+the x-axis as before, but the y-value will be the total amount of+transcript data mapped to the contigs.++The mapping process runs BLAT, and stores the resulting PSL files in a+tempdir, typically /tmp, but overriden with the $TMPDIR variable or+specified with -T.  Only the best hit for each EST is retained, so+exons in other contigs are not counted.+ ++  Comparison to other measures+  ----------------------------++You can read various other measures off the graph fairly easily.+Total assembly size is the y-value at which the curve ends, and the+number of contigs in the assembly is the corresponding x-value.  An+assembly with greater average contig size will end to the left and/or+above of an assembly with shorter average contigs.  N50 is the slope+of the graph at the y-value corresponding to half the genome size.
+ Setup.hs view
@@ -0,0 +1,5 @@+#!/usr/bin/env runhaskell+ +import Distribution.Simple+main = defaultMain+
+ a50.cabal view
@@ -0,0 +1,35 @@+Name:           a50+Version:        0.2+License:        GPL++Author:         Ketil Malde+Maintainer:     Ketil Malde <ketil@malde.org>++Category:       Bioinformatics+Synopsis:       Compare genome assemblies+Description:    a50 - a simple tool for graphing genome coverage and fragmentation.+                .+                Reads files of contigs, and compares them by plotting each as a line in a graph. +		The x-axis represents contig number, the y-axis represents total (cumulative) size.+		An ideal assembly contains a few, large contigs, thus this curve should rise steeply, and +		stop early (but at the expected genome size).  Conversely, a poor assembly consisting of+		many small fragments will have a less steep curve extending far to the right.+		.+		The graphs produced by a50 gives a simple and easy to grasp comparison between assemblies,+		and yet produces a more detailed and informative view than the usual metrics like largest+		contig size or N50.+		.+                The Darcs repository is at <http://malde.org/~ketil/biohaskell/a50>.++-- HomePage:	http://blog.malde.org/area50+Build-Depends:  base>=3 && <5, bio > 0.4, process, containers, cmdargs >= 0.5, directory+Build-Type:     Simple+Cabal-Version:	>= 1.2.3+Tested-with:    GHC==6.12.1++Data-files:     README+Executable:     a50+Main-Is:        A50.hs+Other-Modules:	Gnuplot, Options, Blat+Hs-Source-Dirs: src+Ghc-Options:    -Wall
+ src/A50.hs view
@@ -0,0 +1,60 @@+module Main where++import Bio.Sequence+import Data.List (intersperse, foldl')++import qualified Data.IntMap as M++import Gnuplot+import Blat+import Options++main :: IO ()+main = do +  opts <- getArgs+  ss <- mapM sizes $ inputs opts+  case estref opts of +    "" -> do+      mkplot opts $ map (each 10) $ map ((scanl1 (+)) . sort) ss+    est -> do  +      ps <- mapM (\asm -> fmap gen_result $ runBlat (tmpdir opts) asm est) (inputs opts)+      mkplot opts $ map (each 10) $ map (scanl1 (+)) $ zipWith interleave (map sort ss) ps+  -- putStr $ zipLists fs (map ((scanl1 (+)) . sort) ss)++each :: Int -> [a] -> [a]+each _ [] = []+each n (x:xs) = x : each n (drop (n-1) xs)++zipLists :: [String] -> [[Int]] -> String+zipLists fs ss = unlines ((concat $ map ("#\t"++) fs) : go (map (++repeat 0) ([1..]:ss)))+  where go :: [[Int]] -> [String]+        go xs = let hs = map head xs+                    ts = map tail xs+                in if all (==0) $ tail hs then []+                   else (concat $ intersperse "\t" $ map myshow hs) : go ts++myshow :: Integral i => i -> String+myshow 0 = ""+myshow n = show n++mkplot :: Opt -> [[Int]] -> IO ()+mkplot o ns = gnuplot [conf,outp,labels,tics] (zip (inputs o) ns) es+  where labels = "set ylabel 'cumulative size'; set xlabel 'contig number'"+        tics  = "set format y '%.0s%c'; set format x '%.0f0'"+        conf = if null $ format o then "" else "set terminal "++format o+        outp = if null $ outfile o then "" else "set out '"++outfile o++"'"+        es   = map read $ expect o+                  +sizes :: FilePath -> IO [Int]+sizes f = map (fromIntegral . seqlength) `fmap` readFasta f++sort :: [Int] -> [Int]+sort = concatMap (\(x,c) -> replicate c (fromIntegral $ negate x)) . M.toAscList . freqs++-- equivalent to  'M.fromList . map (\x->(fromIntegral $ negate x,1))', +-- except for not blowing the stack+freqs :: [Int] -> M.IntMap Int+freqs = foldl' ins M.empty . map fromIntegral . map negate+  where ins m x = case M.lookup x m of +          Just v -> v `seq` M.insert x (v+1) m+          Nothing -> M.insert x 1 m
+ src/Blat.hs view
@@ -0,0 +1,63 @@+--  blat DB QRY OUT -t= -d= -minScore= -extendThroughN+--  blat P_2010_10_11_10_15_26_runAssembly/454AllContigs.fna ../EST/lakselus.fasta -t=dna -q=rna tmp.psl -minScore=50++module Blat (module Bio.Alignment.PSL, runBlat, gen_result, interleave) where++import Bio.Alignment.PSL+import Control.Arrow (second)+import Data.List (sortBy)+import System.Directory (doesFileExist, findExecutable)+import Control.Monad (when)+import System.Process (runCommand, waitForProcess) +import System.Exit (ExitCode(..))++blat_cmd :: String+blat_cmd = "blat -t=dna -q=rna -minScore=50 -extendThroughN "++-- run blat+runBlat :: FilePath -> FilePath -> FilePath -> IO [PSL]+runBlat tmpdir asm ests = do+  let pslfile = tmpdir++"/"++basename asm++"_vs_"++basename ests++".psl"+      basename = reverse . takeWhile (/='/') . reverse+  dfe <- doesFileExist pslfile+  when (not dfe) $ do+    fe <- findExecutable "blat"+    when (fe == Nothing) $ error "Couldn't find the 'blat' executable - aborting"+    blat <- runCommand $ unwords [blat_cmd,asm,ests,pslfile]+    e <- waitForProcess blat+    case e of ExitSuccess -> return ()+              _ -> error $ show e+  readPSL pslfile++-- this will only get you the sequences that have a match!+gen_result :: [PSL] -> [(Int,Int)]+gen_result = coverage . order . pslbest++interleave :: Integral i => [i] -> [(i,i)] -> [i]+interleave sz [] = map (const 0) sz+interleave (sz:szs) covs@((s1,c1):cs) +  | sz > s1   = 0 : interleave szs covs+  | sz == s1  = c1 : interleave szs cs++-- if this fails, something is wrong with my assumptions!+  | otherwise = error ("interleave failed: "++show (take 10 (sz:szs)) ++" "++show (take 10 covs))+interleave [] rs = error ("interleave failed - leftover contigs: "++show (take 10 rs)++"...")++coverage :: [PSL] -> [(Int,Int)]+coverage = map (second (sum . map (sum . blocksizes))) . tgroup+  where tgroup [] = []+        tgroup (p1:ps) = let (this,rest) = span ((tname p1 ==) . tname) ps+                         in (tsize p1, p1:this) : tgroup rest++order :: [PSL] -> [PSL]+order = sortBy (comparing (\c -> (negate $ tsize c,tname c)))++pslbest :: [PSL] -> [PSL]+pslbest = map (last . sortBy (comparing match)) . qgroup+  where +    qgroup [] = []+    qgroup (p1:ps) = let (this,rest) = span ((qname p1 ==) . qname) ps+                     in (p1:this) : qgroup rest++comparing :: (Ord a) => (t -> a) -> t -> t -> Ordering+comparing f a b = compare (f a) (f b)
+ src/Gnuplot.hs view
@@ -0,0 +1,44 @@+module Gnuplot where    ++import System.Process+import System.Exit+import System.IO+import Data.List (intersperse)++import Control.Monad (when)+import System.Directory (findExecutable)+import Control.Concurrent (forkIO)++gnuplot :: Num i => [String] -> [(String,[i])] -> [Int] -> IO ()+gnuplot preamble cols hlines = do+  fe <- findExecutable "gnuplot"+  when (fe == Nothing) $ error "Couldn't find the 'gnuplot' executable - aborting"+  (i,o,e,p) <- runInteractiveCommand "gnuplot -persist"+  _ <- forkIO (hGetContents o >>= hPutStr stdout)+  -- ugly hack to limit error output+  _ <- forkIO $ do +    let go ~(l1:l2:ls) =do putStrLn l1+                           if ('^' `elem` l1) +                             then do +                               putStrLn l2+                             else go (l2:ls)+    go =<< fmap lines (hGetContents e)++  +  hPutStr i $ unlines $ preamble+  hPutStrLn i (mkplots cols ++ concatMap ((',':) . show) hlines)+  mapM_ (\col -> do {hPutStr i . unlines . map show . snd $ col; hPutStrLn i "e"}) cols++  hClose i++  x <- waitForProcess p+  case x of ExitSuccess ->  return ()+            ExitFailure j -> hPutStrLn stderr (errmsg++show j) >> return ()+    where errmsg = "'gnuplot' failed with exit code "++-- | generate the plot command+mkplots :: [(String,[a])] -> String+mkplots cs = "plot " ++ (concat . intersperse "," . map mkp $ cs)+  where mkp :: (String,a) -> String+        mkp (s,_) = "'-' with lines title '"++s++"'"+
+ src/Options.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Options where++import System.Console.CmdArgs+import Control.Monad (when)+import System.Directory (getTemporaryDirectory)+import Foreign (unsafePerformIO)++tmpdefault :: FilePath+tmpdefault = unsafePerformIO getTemporaryDirectory++data Opt = Opt { outfile  :: FilePath+               , format :: String+               , expect   :: [String]+               , inputs   :: [FilePath]+               , estref   :: FilePath+               , tmpdir   :: FilePath+               } deriving (Typeable, Data, Show, Eq)++myopt :: Opt+myopt = Opt +  { outfile = def &= help "Output file, if applicable" &= typFile+  , format  = def &= help "Gnuplot output format (a.k.a. 'terminal')"+  , expect  = []  &= help "Expected genome size"+  , inputs  = def &= args &= typFile+  , estref  = def &= help "Reference transcripts" &= typFile &= name "E"+  , tmpdir  = tmpdefault &= help "Set temporary directory" &= typDir &= name "T"+  } &= summary "a50 - compare genome assemblies" &= program "a50"++getArgs :: IO Opt+getArgs = do+  o <- setOutputFormat `fmap` cmdArgs myopt+  when (null $ inputs o) $ error "Please specify one or more input files!"+  return o+  +setOutputFormat :: Opt -> Opt+setOutputFormat o +  | null (format o) && null (outfile o) = o+  | null (format o)                     = o { format = determineFormat $ outfile o }+  | null (outfile o)                    = error "Please specify an output file (-o) when you specify format."+  | otherwise       = o+    where +      determineFormat fp =+        let ext = reverse . takeWhile (/='.') . reverse+        in case ext fp of +          "png" -> "png"+          "jpg" -> "jpg"+          "ps"  -> "postscript eps"+          "svg" -> "svg"+          "pdf" -> "pdf"+          _ -> error "Couldn't determine format from output file name.\nPlease specify format with -f."