rbr (empty) → 0.8.3
raw patch · 14 files changed
+1418/−0 lines, 14 filesdep +basedep +biodep +bytestringsetup-changed
Dependencies added: base, bio, bytestring, containers
Files
- LICENSE +340/−0
- RBR/FreqTable.lhs +94/−0
- RBR/MCT.lhs +105/−0
- RBR/Options.lhs +74/−0
- RBR/RBR.lhs +385/−0
- RBR/Stats.hs +136/−0
- RBR/TextFormat.lhs +24/−0
- RBR/Util.hs +18/−0
- RBR/hooks.c +15/−0
- README +105/−0
- Setup.hs +5/−0
- TODO +26/−0
- idea.txt +47/−0
- rbr.cabal +44/−0
+ LICENSE view
@@ -0,0 +1,340 @@+ GNU GENERAL PUBLIC LICENSE+ Version 2, June 1991++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The licenses for most software are designed to take away your+freedom to share and change it. By contrast, the GNU General Public+License is intended to guarantee your freedom to share and change free+software--to make sure the software is free for all its users. This+General Public License applies to most of the Free Software+Foundation's software and to any other program whose authors commit to+using it. (Some other Free Software Foundation software is covered by+the GNU Library General Public License instead.) You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+this service if you wish), that you receive source code or can get it+if you want it, that you can change the software or use pieces of it+in new free programs; and that you know you can do these things.++ To protect your rights, we need to make restrictions that forbid+anyone to deny you these rights or to ask you to surrender the rights.+These restrictions translate to certain responsibilities for you if you+distribute copies of the software, or if you modify it.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must give the recipients all the rights that+you have. You must make sure that they, too, receive or can get the+source code. And you must show them these terms so they know their+rights.++ We protect your rights with two steps: (1) copyright the software, and+(2) offer you this license which gives you legal permission to copy,+distribute and/or modify the software.++ Also, for each author's protection and ours, we want to make certain+that everyone understands that there is no warranty for this free+software. If the software is modified by someone else and passed on, we+want its recipients to know that what they have is not the original, so+that any problems introduced by others will not reflect on the original+authors' reputations.++ Finally, any free program is threatened constantly by software+patents. We wish to avoid the danger that redistributors of a free+program will individually obtain patent licenses, in effect making the+program proprietary. To prevent this, we have made it clear that any+patent must be licensed for everyone's free use or not licensed at all.++ The precise terms and conditions for copying, distribution and+modification follow.++ GNU GENERAL PUBLIC LICENSE+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++ 0. This License applies to any program or other work which contains+a notice placed by the copyright holder saying it may be distributed+under the terms of this General Public License. The "Program", below,+refers to any such program or work, and a "work based on the Program"+means either the Program or any derivative work under copyright law:+that is to say, a work containing the Program or a portion of it,+either verbatim or with modifications and/or translated into another+language. (Hereinafter, translation is included without limitation in+the term "modification".) Each licensee is addressed as "you".++Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope. The act of+running the Program is not restricted, and the output from the Program+is covered only if its contents constitute a work based on the+Program (independent of having been made by running the Program).+Whether that is true depends on what the Program does.++ 1. You may copy and distribute verbatim copies of the Program's+source code as you receive it, in any medium, provided that you+conspicuously and appropriately publish on each copy an appropriate+copyright notice and disclaimer of warranty; keep intact all the+notices that refer to this License and to the absence of any warranty;+and give any other recipients of the Program a copy of this License+along with the Program.++You may charge a fee for the physical act of transferring a copy, and+you may at your option offer warranty protection in exchange for a fee.++ 2. You may modify your copy or copies of the Program or any portion+of it, thus forming a work based on the Program, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++ a) You must cause the modified files to carry prominent notices+ stating that you changed the files and the date of any change.++ b) You must cause any work that you distribute or publish, that in+ whole or in part contains or is derived from the Program or any+ part thereof, to be licensed as a whole at no charge to all third+ parties under the terms of this License.++ c) If the modified program normally reads commands interactively+ when run, you must cause it, when started running for such+ interactive use in the most ordinary way, to print or display an+ announcement including an appropriate copyright notice and a+ notice that there is no warranty (or else, saying that you provide+ a warranty) and that users may redistribute the program under+ these conditions, and telling the user how to view a copy of this+ License. (Exception: if the Program itself is interactive but+ does not normally print such an announcement, your work based on+ the Program is not required to print an announcement.)++These requirements apply to the modified work as a whole. If+identifiable sections of that work are not derived from the Program,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works. But when you+distribute the same sections as part of a whole which is a work based+on the Program, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Program.++In addition, mere aggregation of another work not based on the Program+with the Program (or with a work based on the Program) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++ 3. You may copy and distribute the Program (or a work based on it,+under Section 2) in object code or executable form under the terms of+Sections 1 and 2 above provided that you also do one of the following:++ a) Accompany it with the complete corresponding machine-readable+ source code, which must be distributed under the terms of Sections+ 1 and 2 above on a medium customarily used for software interchange; or,++ b) Accompany it with a written offer, valid for at least three+ years, to give any third party, for a charge no more than your+ cost of physically performing source distribution, a complete+ machine-readable copy of the corresponding source code, to be+ distributed under the terms of Sections 1 and 2 above on a medium+ customarily used for software interchange; or,++ c) Accompany it with the information you received as to the offer+ to distribute corresponding source code. (This alternative is+ allowed only for noncommercial distribution and only if you+ received the program in object code or executable form with such+ an offer, in accord with Subsection b above.)++The source code for a work means the preferred form of the work for+making modifications to it. For an executable work, complete source+code means all the source code for all modules it contains, plus any+associated interface definition files, plus the scripts used to+control compilation and installation of the executable. However, as a+special exception, the source code distributed need not include+anything that is normally distributed (in either source or binary+form) with the major components (compiler, kernel, and so on) of the+operating system on which the executable runs, unless that component+itself accompanies the executable.++If distribution of executable or object code is made by offering+access to copy from a designated place, then offering equivalent+access to copy the source code from the same place counts as+distribution of the source code, even though third parties are not+compelled to copy the source along with the object code.++ 4. You may not copy, modify, sublicense, or distribute the Program+except as expressly provided under this License. Any attempt+otherwise to copy, modify, sublicense or distribute the Program is+void, and will automatically terminate your rights under this License.+However, parties who have received copies, or rights, from you under+this License will not have their licenses terminated so long as such+parties remain in full compliance.++ 5. You are not required to accept this License, since you have not+signed it. However, nothing else grants you permission to modify or+distribute the Program or its derivative works. These actions are+prohibited by law if you do not accept this License. Therefore, by+modifying or distributing the Program (or any work based on the+Program), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Program or works based on it.++ 6. Each time you redistribute the Program (or any work based on the+Program), the recipient automatically receives a license from the+original licensor to copy, distribute or modify the Program subject to+these terms and conditions. You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties to+this License.++ 7. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Program at all. For example, if a patent+license would not permit royalty-free redistribution of the Program by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Program.++If any portion of this section is held invalid or unenforceable under+any particular circumstance, the balance of the section is intended to+apply and the section as a whole is intended to apply in other+circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system, which is+implemented by public license practices. Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++ 8. If the distribution and/or use of the Program is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Program under this License+may add an explicit geographical distribution limitation excluding+those countries, so that distribution is permitted only in or among+countries not thus excluded. In such case, this License incorporates+the limitation as if written in the body of this License.++ 9. The Free Software Foundation may publish revised and/or new versions+of the General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++Each version is given a distinguishing version number. If the Program+specifies a version number of this License which applies to it and "any+later version", you have the option of following the terms and conditions+either of that version or of any later version published by the Free+Software Foundation. If the Program does not specify a version number of+this License, you may choose any version ever published by the Free Software+Foundation.++ 10. If you wish to incorporate parts of the Program into other free+programs whose distribution conditions are different, write to the author+to ask for permission. For software which is copyrighted by the Free+Software Foundation, write to the Free Software Foundation; we sometimes+make exceptions for this. Our decision will be guided by the two goals+of preserving the free status of all derivatives of our free software and+of promoting the sharing and reuse of software generally.++ NO WARRANTY++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,+REPAIR OR CORRECTION.++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGES.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software; you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation; either version 2 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program; if not, write to the Free Software+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+++Also add information on how to contact you by electronic and paper mail.++If the program is interactive, make it output a short notice like this+when it starts in an interactive mode:++ Gnomovision version 69, Copyright (C) year name of author+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, the commands you use may+be called something other than `show w' and `show c'; they could even be+mouse-clicks or menu items--whatever suits your program.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the program, if+necessary. Here is a sample; alter the names:++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program+ `Gnomovision' (which makes passes at compilers) written by James Hacker.++ <signature of Ty Coon>, 1 April 1989+ Ty Coon, President of Vice++This General Public License does not permit incorporating your program into+proprietary programs. If your program is a subroutine library, you may+consider it more useful to permit linking proprietary applications with the+library. If this is what you want to do, use the GNU Library General+Public License instead of this License.
+ RBR/FreqTable.lhs view
@@ -0,0 +1,94 @@+\section{FreqTable}++Building a Frequency Table for keys.++Constructs the table of all possible keys, so be careful.++\begin{code}++module RBR.FreqTable (FreqTable(..)+ ,freqtable_int,freqtable_integer -- constructors+ ,sparsetable_int, sparsetable_integer+ ) where++import Bio.Sequence++import Data.IntMap as IM+import Data.Map as M+import Data.List as L++data FreqTable a = FT { count :: a -> Int+ , counts :: [(a,Int)]+ }++\end{code}++\subsection{Constructing the frequency table}++Frequency table, counting occurences.++\begin{code}++freqtable_int :: HashF Int -> [Sequence] -> FreqTable Int+freqtable_int kf ss = FT (\k -> IM.findWithDefault 0 k idx) (IM.toList idx)+ where+ idx = foldl' myupdate IM.empty $ concatMap (L.map fst . hashes kf . seqdata) ss+ myupdate m k = let x = 1 + IM.findWithDefault 0 k m+ in x `seq` IM.insert k x m++freqtable_integer :: HashF Integer -> [Sequence] -> FreqTable Integer+freqtable_integer kf ss = FT (\k -> M.findWithDefault 0 k idx) (M.toList idx)+ where+ idx = foldl' myupdate M.empty $ concatMap (L.map fst . hashes kf . seqdata) ss+ myupdate m k = let x = 1 + M.findWithDefault 0 k m+ in x `seq` M.insert k x m+++-- alternative: use+-- accumArray (+) 0 (minBound, maxBound) . (map (\x->(x,1)))++\end{code}++\subsection{Sparse maps}++For parameter k, increment all existing keys, insert new keys only+when a gap larger than k would otherwise result. Keys are added+weight according to distance to next inserted key, so that the sum+weight of a sequence in the map is independent of k.++For this to be useful, masking must be performed against a sliding+average. (How will this work against libraries?)++Invariant: sparsetable_int 1 == freqtable_int++\begin{code}++sparsetable_int :: Int -> HashF Int -> [Sequence] -> FreqTable Int+sparsetable_int step kf ss = FT (\k -> find k idx) (IM.toList idx)+ where+ find = IM.findWithDefault 0+ idx = foldl' ins1 IM.empty ss+ ins1 ix s = let ks = L.map fst $ hashes kf $ seqdata s+ in go ix (step-1) ks+ -- insert keys that already exist, or when the skip-counter is zero+ go ix' n (k:ks) | n==0 || find k ix' /= 0+ || L.null ks = let x = find k ix'+step-n+ in x `seq` go (IM.insert k x ix') (step-1) ks+ | True = go ix' (n-1) ks+ go ix' _n [] = ix'++sparsetable_integer :: Int -> HashF Integer -> [Sequence] -> FreqTable Integer+sparsetable_integer step kf ss = FT (\k -> find k idx) (M.toList idx)+ where+ find = M.findWithDefault 0+ idx = foldl' ins1 M.empty ss+ ins1 ix s = let ks = L.map fst $ hashes kf $ seqdata s+ in go ix (step-1) ks+ -- insert keys that already exist, or when the skip-counter is zero+ go ix' n (k:ks) | n==0 || find k ix' /= 0+ || L.null ks = let x = find k ix'+step-n+ in x `seq` go (M.insert k x ix') (step-1) ks+ | True = go ix' (n-1) ks+ go ix' _n [] = ix'++\end{code}
+ RBR/MCT.lhs view
@@ -0,0 +1,105 @@+\section{MaskCount}++Examine each nucleotide from two data sets, and count the Ns (or Xs?).+Separate into A AB B 0. Also (optionally?) count masked region sizes+to generate gnuplot'able histograms.++TODO:+ - parameter to specify lower case/N/X as masked+ - justify columns in output+ - output percentages++\begin{code}+{-# LANGUAGE BangPatterns, FlexibleContexts #-}+module Main where++import Bio.Sequence++import RBR.TextFormat++import Control.Monad (when)+import Data.Char (isLower)+import Data.List (foldl',intersperse,partition)+import System.Environment (getArgs)+import System.Exit (exitWith,ExitCode(..))+import Text.Printf (printf, PrintfType)++main :: IO ()+main = do+ (opts,[a,b]) <- parseArgs+ as <- readFasta a+ bs <- readFasta b+ let res = zipComp as bs+ when (elem 'l' opts)+ (do+ putStrLn ("# fraction of masked positions: "++a++" both "++b++"none")+ putStrLn $ unlines $ map printres res)+ when (elem 's' opts)+ (do+ putStrLn "# Summary:"+ putStrLn $ printSum a b $ foldl' add (CS 0 0 0 0) (map snd res))++parseArgs :: IO (String, [String])+parseArgs = do+ as <- getArgs+ let (opts,files) = partition ((=='-').head) as+ when (length files /= 2) usage+ let short = concat $ map tail opts+ return (short,files)++usage :: IO a+usage = do putStrLn "mc: compare two FASTA files for masked nucleotides"+ putStrLn "usage: mc [-s|-l] file_1 file_2"+ exitWith $ ExitFailure 1++data CompStruct = CS !Int !Int !Int !Int++add :: CompStruct -> CompStruct -> CompStruct+add (CS a ab b o) (CS a' ab' b' o') = CS (a+a') (ab+ab') (b+b') (o+o')++-- construct the percentage similar nucleotides+printres :: (String, CompStruct) -> String+printres (s,CS a ab b o) = let tot = a+ab+b+o+ in concat $ intersperse " "+ $ s : map (percent tot) [a,ab,b,o]++printSum :: String -> String -> CompStruct -> String+printSum a b (CS ca cab cb co) = unlines (["Summary for "++a++" vs. "++b++":\n"]+ ++ columns " " [ map (" "++) ["unmasked in both:","masked in both:"+ ,"masked in "++a++":","masked in "++b++":"]+ , map show [co, cab, ca, cb]+ , map (paren . percent tot) [co,cab,ca,cb ]])+ ++ "Pearson's phi: "++printf "%.3f" (phi :: Double)+ where tot = co+cab+ca+cb+ phi = let [a,b,ab,o] = map ((/(fromIntegral tot)) . fromIntegral) [ca,cb,cab,co]+ in (ab*o-a*b)/sqrt((ab+a)*(ab+b)*(a+o)*(b+o))++paren :: String -> String+paren s = "("++s++"%)"++percent :: (PrintfType (Double -> t), Integral a, Integral a1) => a1 -> a -> t+percent tot x = printf "%.2f" (100*fromIntegral x/fromIntegral tot :: Double)++zipComp :: [Sequence] -> [Sequence] -> [(String,CompStruct)]+zipComp as bs = map (z1 (CS 0 0 0 0)) $ zipSeqs as bs++z1 :: CompStruct -> (Sequence, Sequence) -> (String, CompStruct)+z1 cs (a,b) = (toStr $ seqlabel a, foldl' collect cs $ zip (get a) (get b))+ where get s = map isLower $ map (s!) [0..seqlength s-1]+ -- s/isLower/isN/ for N detect.++zipSeqs :: [Sequence] -> [Sequence] -> [(Sequence,Sequence)]+zipSeqs [] [] = []+zipSeqs (a:as) (b:bs) =+ if seqlabel a == seqlabel b then (a,b):zipSeqs as bs+ else error ("sequence sets differ (offending sequences: '"+ ++ toStr (seqlabel a) ++ "' and '" ++ toStr (seqlabel b) ++"'.")+zipSeqs _ _ = error "extraneous sequences"++collect :: CompStruct -> (Bool, Bool) -> CompStruct+collect (CS a ab b o) (True,True) = (CS a (ab+1) b o)+collect (CS a ab b o) (True,False) = (CS (a+1) ab b o)+collect (CS a ab b o) (False,True) = (CS a ab (b+1) o)+collect (CS a ab b o) (False,False) = (CS a ab b (o+1))++\end{code}
+ RBR/Options.lhs view
@@ -0,0 +1,74 @@+\subsection{Option handling}++You know the drill...++\begin{code}++module RBR.Options (version,usage,getOptions,Opts(..),parseargs,MaskWith(..)) where++import System.Environment (getArgs)+import System.Console.GetOpt+import System.Exit++version, usagemsg :: String+version = "0.8.4"+usagemsg = "Usage: rbr [-k <k>] [options] FILE.."++getOptions :: IO ([Opts -> IO Opts], [String], [String])+getOptions = getArgs >>= (return . getOpt Permute options)++usage :: [String] -> String+usage errs = usageInfo (concat errs ++ usagemsg) options++data MaskWith = Lower | Ns | Xs++options :: [OptDescr (Opts -> IO Opts)]+options = [Option ['k'] ["word-size"] (ReqArg (\arg opt -> return opt { kval = read arg }) "Int") "Word size"+ ,Option [] ["sparse-keys"] (ReqArg (\arg opt -> return opt { skip = read arg }) "Int") "Skip length"+-- ,Option [] ["shape"] (ReqArg ...) "Key shape"+ ,Option ['N'] ["no-stats"] (NoArg (\opt -> return opt { stats = False })) "Omit statistical masking"+ ,Option ['g'] ["gapclosing"] (ReqArg (\arg opt -> return opt { gap = read arg }) "Int") "Close gaps"+ ,Option ['s'] ["stringency"] (ReqArg (\arg opt -> return opt { string = read arg }) "Float") "Mask stringecy"+ ,Option ['t'] ["threshold"] (ReqArg (\arg opt -> return opt { thresh = read arg }) "Float") "Mask threshold"+ ,Option [] ["preserve-lower-case"] (NoArg (\opt -> return opt { preserve_lower = True })) "Preserve lower case in input sequences"++ ,Option ['D'] ["distribution"] (NoArg (\opt -> return opt { distr = True })) "Distribution output"+ ,Option ['l'] ["library"] (ReqArg (\arg opt -> return opt {lib = Just arg}) "file") "Mask words from library"+ ,Option ['L'] ["lower-case"] (NoArg (\opt -> return opt { lower = Lower })) "Mask using lower case letters"+ ,Option ['n'] ["n-mask"] (NoArg (\opt -> return opt { lower = Ns })) "Mask using 'n's"+ ,Option ['x'] ["x-mask"] (NoArg (\opt -> return opt { lower = Xs })) "Mask using 'X's"+ ,Option [] ["server"] (NoArg (\opt -> return opt { server = True })) "Run in server mode"+ ,Option ['o'] ["output-file"] (ReqArg (\arg opt -> return opt { ofile = Just arg }) "file") "Output file"+ ,Option ['v'] ["verbose"] (NoArg (\opt -> return opt { verb = True })) "Display progress"++ ,Option ['h'] ["help"] (NoArg (\_ -> do {putStrLn $ usage []; exitWith ExitSuccess })) "Display help"+ ,Option ['V'] ["version"] (NoArg (\_ -> do {putStrLn $ "rbr version "++version; exitWith ExitSuccess })) "Display version"+ ]++data Opts = Opts { kval :: Int, ofile :: Maybe FilePath+ , stats, preserve_lower :: Bool+ , thresh, string :: Double+ , gap :: Int+ , lower :: MaskWith+ , distr, verb, server :: Bool+ , lib :: Maybe FilePath+ , skip :: Int+ }++defaultopts :: Opts+defaultopts = Opts 16 Nothing True False 0 0 0 Lower False False False Nothing 0++parseargs :: [Opts -> IO Opts] -> IO Opts+parseargs args = foldl (>>=) (return defaultopts) args >>= fixS >>= fixT+ where fixS opts = return $ case (string opts,lower opts) of -- sensible(?) defaults+ (0,Ns) -> opts { string = 2.0 }+ (0,Xs) -> opts { string = 2.0 }+ (0,Lower) -> opts { string = 1.1 }+ _ -> opts+ fixT opts = return $ case (thresh opts,lower opts) of -- sensible(?) defaults+ (0,Ns) -> opts { thresh = 8.0 }+ (0,Xs) -> opts { thresh = 8.0 }+ (0,Lower) -> opts { thresh = 5.0 }+ _ -> opts++\end{code}
+ RBR/RBR.lhs view
@@ -0,0 +1,385 @@+\section{RBR - RepeatBeater revisited}++RBR works by examining word frequencies in EST data, and+based on the assumption that expression of positions within each gene+is relatively uniform, identifies extremely common words as potential+repeats.++A predecessor to RBR was implemented as some scripts parsing+\texttt{xsact} output, this is a revised, standalone implmentation.++\begin{code}+{-# LANGUAGE CPP #-}++module Main (main) where++import Bio.Sequence++import RBR.FreqTable+import RBR.Stats hiding (median,uv_add)+import RBR.Util (interactl)+import RBR.Options++-- import Text.Printf+import qualified Data.ByteString.Lazy.Char8 as B (map)++import System.IO+import System.IO.Unsafe+import Control.Monad+import Data.List as List+import Data.Char as Char++import Data.Map hiding (null,filter,map,elems,keys)++#define perror (\s->error ("Program error in function \""++s++"\" at "++__FILE__++":"++show (__LINE__::Int)++\+"\nPlease report this incident to <ketil@ii.uib.no>."))++main :: IO ()+main = do+ (opt1,non,err) <- getOptions+ -- print (opt1,non,err)+ when ((null non && null opt1) || not (null err)) (error $ usage err)+ opts <- parseargs opt1+ case non of [] -> do ss <- hReadFasta stdin+ main_real opts ("<stdin>", return ss)+ [f1] -> main_real opts (f1, readFasta f1)+ fs -> error ("Only supply one input file (you supplied: "++show fs++")")++header :: Opts -> [FilePath] -> String+header opts _inputs = "# rbr version "++version++", mask="++(case (lower opts) of Lower -> "L"; Ns -> "n"; Xs -> "X")+++ " k="++show (kval opts)++" stringency="++show (string opts)+++ " thresh="++show (thresh opts)++" g="++show (gap opts) ++ " skip="++ show (skip opts)++\end{code}++\subsection{The real main}++\begin{code}++debug :: Bool -> String -> IO ()+debug p m = when p $ hPutStr stderr m++countIO :: String -> String -> Int -> [a] -> IO [a]+countIO msg post step xs = sequence $ map unsafeInterleaveIO ((blank >> outmsg (0::Int) >> c):cs)+ where (c:cs) = ct 0 xs+ output = hPutStr stderr+ blank = output ('\r':take 70 (repeat ' '))+ outmsg x = output ('\r':msg++show x) >> hFlush stderr+ ct s ys = let (a,b) = splitAt (step-1) ys+ next = s+step+ in case b of [b1] -> map return a ++ [outmsg (s+step) >> hPutStr stderr post >> return b1]+ [] -> map return (init a) ++ [outmsg (s+length a) >> hPutStr stderr post >> return (last a)]+ _ -> map return a ++ [outmsg s >> return (head b)] ++ ct next (tail b)++main_real :: Opts -> (String, IO [Sequence]) -> IO ()+main_real opts (ifile,ssio) = do+ when (verb opts || distr opts) $ putStrLn $ header opts [ifile]+ when (verb opts) $ do+ debug (verb opts) ("Reading file: "++ifile++"\n")+ ss <- ssio+ debug (verb opts) ("Number of sequences: "++show (length ss)++"\n")+ if kval opts <= 16 then main_cont make_ft_int opts ssio+ else main_cont make_ft_integer opts ssio++main_cont :: Integral a => MkFT a -> Opts -> IO [Sequence] -> IO ()+main_cont make_ft opts ssio = do+ mask1 <- mask_func make_ft opts ssio+ ss'' <- if (verb opts) then countIO " ...masking sequences: " ", done.\n" 100 =<< ssio+ else ssio+ if (server opts) then do+ ft <- make_ft "Building index array" (verb opts) (skip opts) (kval opts) ssio+ interactl "READY: " (concatMap (main_server opts ft ss'') . words)+ else do if (distr opts) then main_coverage make_ft opts ssio+ else main_masked opts mask1 ss''++type MkFT a = String -> Bool -> Int -> Int -> IO [Sequence] -> IO (FreqTable a)++make_ft_int :: MkFT Int+make_ft_int msg vb skp k ssio = do+ ss' <- if vb then countIO (" "++msg++": ") ", done.\n" 100 =<< ssio+ else ssio+ if skp > 0 then return (sparsetable_int skp (rcontig k) ss')+ else return (freqtable_int (rcontig k) ss')++make_ft_integer :: MkFT Integer+make_ft_integer msg vb skp k ssio = do+ ss' <- if vb then countIO (" "++msg++": ") ", done.\n" 100 =<< ssio+ else ssio+ if skp>0 then return (sparsetable_integer skp (rcontig k) ss')+ else return (freqtable_integer (rcontig k) ss')++main_coverage :: Integral a => MkFT a -> Opts -> IO [Sequence] -> IO ()+main_coverage make_ft opts ssio = do+ ft <- make_ft "Building index array" (verb opts) (skip opts) (kval opts) ssio+ let k = kval opts+ sk = skip opts+ (stdevs,b2,strg) = (thresh opts, 5, string opts)+ seq_cov s = let cv = coverage k sk (rcontig k) ft s+ ls = sortv $ blunt b2+ $ map (\l->(fromIntegral $ head l,length l)) $ group $ sort cv+ (mu,stdv) = distrib strg ls+ t = mu + max b2 (stdevs*stdv)+ in (seqheader s, if null cv || null ls then (0,0,0) else (mu,stdv,t),cv) -- todo: round!+ write = case ofile opts of Nothing -> putStrLn; Just f -> writeFile f+ debug (verb opts) "Calculating coverage\n"+ ss <- ssio+ write $ unlines $ map show $ map seq_cov ss++main_masked :: Opts -> (Sequence->Sequence) -> [Sequence] -> IO ()+main_masked opts mf ss = do+ let ms = if kval opts == 0 then ss else map mf ss+ write = case ofile opts of Nothing -> hWriteFasta stdout+ Just f -> writeFasta f+ write ms++mask_func :: Integral a => MkFT a -> Opts -> IO [Sequence] -> IO (Sequence -> Sequence)+mask_func make_ft opts ssio =+ do let k = kval opts+ g = gap opts+ sk = skip opts+ gc = if g > 0 then closegaps (g+k-1) else id+ sparm = (thresh opts, 5, string opts)+ upcase = if preserve_lower opts then id else upcaseSequence+ ms <- do case stats opts of+ False -> return []+ True -> do ft <- make_ft "Indexing input" (verb opts) sk k ssio+ return [mask_stat k sk sparm ft]+ mt <- case lib opts of+ Nothing -> return []+ Just f -> do lft <- make_ft "Indexing repeat library" (verb opts) sk k $ readFasta f+ return [mask_table k sk lft]+ return ((case ms++mt of [] -> id+ xs -> mask_generic (lower opts) (k+if sk==0 then 0 else sk-1)+ (gc . min_mask ((sk+2) `div` 2) . combine xs)) . upcase)++upcaseSequence :: Sequence -> Sequence+upcaseSequence (Seq l d q) = (Seq l (B.map toUpper d) q)++-- sparse keys can give aberrant counts, so require more than one consequtive+-- todo: really really convert to RLL-encoded whitelist+min_mask :: Int -> [Bool] -> [Bool]+min_mask _ [] = []+min_mask sk xs@(True:_) = let (t,f) = span id xs in t ++ min_mask sk f+min_mask sk xs@(False:_) = let (f,t) = span not xs in (if length f >= sk then f+ else take (length f) (repeat True)) ++ min_mask sk t++combine :: [a -> [Bool]] -> (a -> [Bool])+combine [] = perror "combine"+combine [f] = f+combine (f1:f2:fs) = let f = \s -> zipWith (&&) (f1 s) (f2 s) in combine (f:fs)+++main_server :: Integral a => Opts -> FreqTable a -> [Sequence] -> (String -> String)+main_server opts ft ss =+ let sidx = fromList $ map (\s->(seqlabel s,s)) ss+ (k,sk,gc,ps) = (kval opts, skip opts,if gap opts > 0 then closegaps (k+gap opts-1) else id, (thresh opts,5,string opts))+ in (\s -> case Data.Map.lookup (fromStr s) sidx of+ Nothing -> ("input error: "++s++" not found\n")+ Just sq -> let cover = show $ coverage k sk (rcontig k) ft sq+ masked_ = show $ mask_generic (lower opts) (if sk==0 then k else k+sk-1) (gc . mask_stat k (skip opts) ps ft) sq+ -- mask_stat (lower opts) k g ps ft sq+ unmasked = show $ seqdata sq+ in unlines [masked_, cover,unmasked])++\end{code}++Generic masking function. The list of Bool is a whitelist, so that+True corresponds to retained nucleotides, while False indicates+nucleotides to be masked.++(TODO: Run-length encode the boolean list?)++\begin{code}++mask_generic :: MaskWith -> Int -> (Sequence -> [Bool]) -> Sequence -> Sequence+mask_generic b sz fn s@(Seq l sd mq) = Seq l (fromStr . masked b sz (fn s) . toStr $ sd) mq++\end{code}++Mask all words from a dictionary (i.e. a library of known repeats)++\begin{code}++mask_table :: Integral a => Int -> Int -> FreqTable a -> Sequence -> [Bool]+mask_table k skp rlib s = let+ cv = coverage k skp (rcontig k) rlib s -- slight abuse of 'coverage'+ in map (==0) cv++\end{code}++Masking sequences from frequency counts.++\begin{code}++type MPars = (Double,Double,Double) -- stdevs thres, 1-elim, stringency++-- calculate the 'whitelist'+mask_stat :: Integral a => Int -> Int -> MPars -> FreqTable a -> Sequence -> [Bool]+mask_stat k sk (stdevs,b2,strg) ft s =+ let cv = coverage k sk (rcontig k) ft s+ ls = sortv $ blunt b2+ $ map (\l->(fromIntegral $ head l,length l)) $ group $ sort cv+ (mu,stdv) = distrib strg ls+ t = mu + max b2 (stdevs*stdv)+ in if null cv || null ls then take (1-k+fromIntegral (seqlength s)) $ repeat True+ else map ((<=t) . fromIntegral) cv++-- distrib calculates the base distribution+distrib :: Double -> [(Double,Int)] -> (Double,Double)+distrib strg ls = distr' ls (uv_mk [])+ where+ distr' [] _+ = perror "distrib"+ distr' ((mag,cnt):ls') ustd =+ let ustd' = uv_add mag cnt ustd+ u = uniVar ustd'+ score = variance u / stdev u+ in if score >= strg || null ls'+ then (mean u,stdev u)+ else distr' ls' ustd'++-- s {seqannot = seqannot s+-- ++[UKV "Threshold" $ printf "%.2f" t]+-- ,seqdata = listArray (bounds $ seqdata s) ms}++-- compensate for 1% error rate by reducing number of ones by k%+-- this is inaccurate, but possibly a workable estimate nonetheless+blunt :: Double -> [(Double,Int)] -> [(Double,Int)]+blunt k = reduce1s . dropWhile ((==0).fst)+ where reduce1s all_@((a,x):ls) = if a==1+ then (1,max 0 (x-floor k*(sum $ map snd all_) `div` 100)):ls+ else all_+ reduce1s [] = [] -- sequence contains no words++-- sort ls around the centre or the modal interval+sortv :: [(Double,Int)] -> [(Double,Int)]+sortv ls = let m = scaled_modal ls+ in sortBy (\(a,_) (c,_) -> compare (abs (a-m)) (abs (c-m))) ls++-- NB: 1 = error - chose mode ignoring 1s? (see C.e. seq. 24)++-- for each window of occurrence frequencies (n..sqrt n), find the one with most positions+scaled_modal :: [(Double,Int)] -> Double+scaled_modal = modal . map window . tails'++window :: [(Double,Int)] -> (Double,Int)+window [] = perror "window"+window ((mag,cnt):ls) =+ let top = mag + sqrt mag+ xs = (mag,cnt) : takeWhile ((<top).fst) ls+ in ((mag+(fst . last) xs)/2, sum (map snd xs))++-- tails without the final [[]]+tails' :: [a] -> [[a]]+tails' [] = []+tails' x@(_:xs) = x : tails' xs++{-+windowize ((mag,count):ls) =+ let (this,rest) = break ((> mag+sqrt mag) . fst) ls+ in (mag+sqrt mag,count+sum (map snd this)) : windowize rest+windowize [] = []+-}++modal :: Ord b => [(a,b)] -> a+modal = fst . maximumBy (\x y -> compare (snd x) (snd y))++median :: [(Double,Int)] -> Double+median ls = let n = (`div` 2) $ sum $ map snd ls+ in nquant n ls++nquant :: Int -> [(Double,Int)] -> Double+nquant n ((v,c):vs) | n < c = v+ | otherwise = nquant (n-c) vs+nquant _ [] = perror "median of an empty list is impossible!"+++-- faster(?) replacements for Stats.uv and uv_del+type UV = (Int,Double,Double,Double,Double)++uv_mk :: [(Double,Int)] -> UV+uv_mk = foldr uv1 (0,0,0,0,0)+ where uv1 (m,cnt) (n,x,x2,x3,x4) = let c = fromIntegral cnt in+ (n+cnt,x+m*c,x2+m*m*c,x3+m*m*m*c,x4+m*m*m*m*c)++uv_sub, uv_add :: Double -> Int -> UV -> UV+uv_sub m cnt (n,x,x2,x3,x4) = let c = fromIntegral cnt in+ (n-cnt,x-m*c,x2-m*m*c,x3-m*m*m*c,x4-m*m*m*m*c)++uv_add m cnt (n,x,x2,x3,x4) = let c = fromIntegral cnt in+ (n+cnt,x+m*c,x2+m*m*c,x3+m*m*m*c,x4+m*m*m*m*c)++-- masked takes mask type, kval, threshold, a whitelist ('False' means mask)+-- and the (nucleotides in the) sequence+masked :: MaskWith -> Int -> [Bool] -> String -> String+masked Lower = masked' toLower 0+masked Ns = masked' (const 'n') 0+masked Xs = masked' (const 'X') 0++-- mask using an arbitrary masking function+-- the 'ns' parameter keeps track of any remaining masked word, so that+-- we finish masking out the whole over-represented word+masked' :: (Char->Char) -> Int -> Int -> [Bool] -> String -> String+masked' _ _ _ (_:_) [] =+ perror "masked' (1)"+masked' mf ns _ [] es = map mf (take ns es) ++ drop ns es+masked' mf ns k (b:bs) (e:es)+ | ns == 0 && b = e : masked' mf 0 k bs es+ | ns > 0 && b = mf e : masked' mf (ns-1) k bs es+ | not b = mf e : masked' mf k k bs es+ | otherwise = perror "masked' (2)"++-- close gaps of less than 'g' unmasked words between groups of masked characters+-- 'False' corresponds to words to mask (whitelist)+closegaps :: Int -> [Bool] -> [Bool]+closegaps g = close g True . partitions++-- always starts with a (possibly empty) unmasked group+partitions :: [Bool] -> [Int]+partitions es = map List.length $ if head es == False then []:partitions' es else partitions' es+ where partitions' = List.groupBy (\x y -> x == False && y == False || x /= False && y /= False)++-- convert runs of <=g Trues to Falses+close :: Int -> Bool -> [Int] -> [Bool]+close _ _ [] = []+close g True (c:cs) = take c (repeat (if c>g then True else False)) ++ close g False cs+close g False (c:cs) = (take c $ repeat False) ++ close g True cs++\end{code}++\subsection{Coverage}++Calculating the coverage over a sequence++For sparse indices, this should be a sliding average.++\begin{code}++coverage :: Integral a => Int -> Int -> HashF a -> FreqTable a -> Sequence -> [Int]+coverage k skp kf tb s = if skp > 0 then sliding_avg skp raw_counts else raw_counts+ where+ raw_counts = map mcount $ padkeys (seqlength s-fromIntegral k+1) 0 $ hashes kf $ seqdata s+ mcount (Just k1) = count tb k1+ mcount Nothing = 0++padkeys :: Offset -> Offset -> [(k,Offset)] -> [Maybe k]+padkeys top cur [] = take (fromIntegral (top - cur)) $ repeat Nothing+padkeys top cur ((k,p):ks) = if cur == p then Just k : padkeys top (cur+1) ks+ else Nothing : padkeys top (cur+1) ((k,p):ks)+++-- or just:+-- sliding_avg k = map ((`div` k) . sum . take k) . tails++sliding_avg :: Int -> [Int] -> [Int]+sliding_avg s xs = let x1 = take s xs+ in if length x1 == s then slav s (sum x1) xs (drop s xs)+ else [sum x1 `div` length x1]++-- todo: use floats?+slav :: Int -> Int -> [Int] -> [Int] -> [Int]+slav s tmp hd tl@(_:_) = tmp `div` s : slav s (tmp-head hd+head tl) (tail hd) (tail tl)+slav s tmp _ [] = [tmp `div` s]++\end{code}+
+ RBR/Stats.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE CPP, Rank2Types #-}+module RBR.Stats (UniVar(..),uniVar,uv,uv_add,uv_del+ ,Quantiles(..),quantiles) where -- ,histogram,display) where++import Data.List (sort, sortBy, group)+-- import FiniteMap++class Statistic s where+ samples :: s -> Int++data UniVar = UV { uv_samples :: Int+ , mean :: Double+ , stdev :: Double+ , variance :: Double+ , skewness :: Double+ , kurtosis :: Double+ , sumSq :: Double+ , coeffVar :: Double+ , stdErrMn :: Double+ }++instance Statistic UniVar where samples = uv_samples++instance Show UniVar where+ show u = adjust+ [["Samples", (show $ samples u)]+ , ["Mean", (show $ mean u)]+ , ["Standard dev", (show $ stdev u)]+ , ["Variance", (show $ variance u)]+ , ["Skewness", (show $ skewness u)]+ , ["Kurtosis", (show $ kurtosis u)]+ , ["Sum of squares", (show $ sumSq u)]+ , ["Coeff. of var", (show $ coeffVar u)]+ , ["Std err mean", (show $ stdErrMn u)]+ ]++adjust :: [[String]] -> String+adjust [] = []+adjust ([a,b]:xs) = (a++":"++take (15-length a) (repeat ' ')++b) ++ "\n" ++ adjust xs+adjust _ = error "Pattern matching error in 'adjust'"++type UVTMP = (Int,Double,Double,Double,Double)++-- | more or less the univariate function from SAS+-- calculate by tracking n, sum of x, of x², x^3, x^4+uniVar :: (Int, Double, Double, Double, Double) -> UniVar+uniVar (n',x,x2,x3,x4) =+ let n = fromIntegral n'+ m = x/n+ m2 = m*m+ m3 = m*m2+ var = (x2-m2*n)/(n-1)+ s = sqrt(var)+ skew = (x3 - 3*m*x2 + 2*m3*n)/(s*s*s*n)+ kurt = (x4 - 4*m*x3 + 6*m2*x2 - 4*m3*x + n*m*m3)/(s*s*s*s*n) - 3+ cv = s/m+ in UV n' m s var skew kurt x2 cv (s/sqrt n)++uv_add, uv_del :: Real a => a -> UVTMP -> UVTMP+uv_add = uv_upd (+)+uv_del = uv_upd (-)++-- requires -fglasgow-exts+uv_upd :: (Real a) => (forall b . Real b => b -> b -> b)+ -> a -> UVTMP -> UVTMP+uv_upd f d' (n,x,x2,x3,x4) = let d = toFloat d' in n `seq` x `seq` x2+ `seq` x3 `seq` x4 `seq` (f n 1,f x d,f x2 (d*d),f x3 (d*d*d),f x4 (d*d*d*d))+ where toFloat = fromRational . toRational+++uv :: Real a => [a] -> UVTMP+uv ds = foldr uv_add (0,0,0,0,0) ds++data Quantiles = Qs { wsamples :: Int+ , smallest :: Double+ , quartile1 :: Double+ , median :: Double+ , mode :: [Double]+ , quartile3 :: Double+ , greatest :: Double+ }++instance Statistic Quantiles where samples = wsamples++instance Show Quantiles where+ show w = adjust+ [["Samples", (show $ samples w)]+ , ["Smallest", (show $ smallest w)]+ , ["Q1", (show $ quartile1 w)]+ , ["Median", (show $ median w)]+ , ["Modes", (show $ mode w)]+ , ["Q3", (show $ quartile3 w)]+ , ["Greatest", (show $ greatest w)]]++quantiles :: [Double] -> Quantiles+quantiles ds = let+ n = length ds+ sorted = sort ds+ q1 = case n `quotRem` 4 of (q,0) -> ((sorted!!(q-1))+(sorted!!q))/2.0+ (q,_) -> sorted!!q+ q2 = case n `quotRem` 2 of (q,0) -> ((sorted!!(q-1))+(sorted!!q))/2.0+ (q,_) -> sorted!!q+ q3 = case (3*n) `quotRem` 4 of (q,0) -> ((sorted!!(q-1))+(sorted!!q))/2.0+ (q,_) -> sorted!!q+ modes = let+ ms = sortOn (negate.fst) $ map (\x->(length x,head x))$ group sorted+ in (snd $ head ms) : map snd+ (takeWhile (\x->fst x==fst (head ms)) (tail ms))+ in+ Qs n (head sorted) q1 q2 modes q3 (last sorted)++sortOn :: Ord b => (a->b) -> [a] -> [a]+sortOn f = sortBy (\x y -> compare (f x) (f y))++{-+type Histogram = FiniteMap Double Int++-- | histogram builds a histogram given the list of midpoints+histogram :: [Double] -> [Double] -> Histogram+histogram ms xs = foldl (insert ms) emptyFM' xs+ where+ emptyFM' = foldl (\s v -> addToFM s v 0) emptyFM ms+ insert :: [Double] -> Histogram -> Double -> Histogram+ insert (m1:m2:ms) s x = if abs (m1-x) <= abs (m2-x)+ then addToFM_C (+) s m1 1 else insert (m2:ms) s x+ insert [m1] s x = addToFM_C (+) s m1 1+ insert [] _ _ = error "Must provide at least one midpoint"++-- todo: speed up with strict foldl'++display :: Histogram -> String+display h = unlines $ map disp1 $ fmToList h+ where disp1 (v,n) = show v ++ (take (7-(length $ show v)) (repeat ' ')) +++ ": " ++ (take n $ repeat '*')++-}
+ RBR/TextFormat.lhs view
@@ -0,0 +1,24 @@+\begin{code}++-- todo: support uneven column lengths++module RBR.TextFormat where++import Data.List++columns :: String -> [[String]] -> [String]+columns delim = map (concat . intersperse delim) . transpose . extend++extend :: [[String]] -> [[String]]+extend cs = zipWith pad (sizes cs) cs++-- mkrow rsizes = concat . intersperse "^" . zipWith pad rsizes++pad :: Int -> [String] -> [String]+pad n = map (pad1 n)+ where pad1 o text = text ++ take (o - length text) (repeat ' ')++sizes :: [[String]] -> [Int]+sizes = map (maximum . map length)++\end{code}
+ RBR/Util.hs view
@@ -0,0 +1,18 @@+module RBR.Util where++import System.IO++interactl :: String -> (String -> String) -> IO ()+interactl p f = do+ putStr p+ hFlush stdout+ ls <- getContents+ i' p f (lines ls)++i' :: String -> (t -> String) -> [t] -> IO ()+i' _ _ [] = return ()+i' p f (l:ls) = do+ putStr (f l)+ putStr p+ hFlush stdout+ i' p f ls
+ RBR/hooks.c view
@@ -0,0 +1,15 @@+/* after a tip from David Roundy */+#include <Rts.h>+#include <RtsFlags.h>+#include <unistd.h>++void defaultsHook (void) {+ RtsFlags.GcFlags.maxStkSize = 8*10000002 / sizeof(W_); /* 80M */++#ifdef _SC_PHYS_PAGES+ unsigned long long pagesize = sysconf(_SC_PAGESIZE);+ unsigned long long numpages = sysconf(_SC_PHYS_PAGES);+ unsigned long long mhs = numpages*pagesize*8/10;+ RtsFlags.GcFlags.maxHeapSize = 1ULL+mhs/BLOCK_SIZE_W;+#endif+}
+ README view
@@ -0,0 +1,105 @@+ New in this version (0.8)+ -------------------------++Mostly a maintenance release, but at least we have++* Cabalized install++* Sparse mode completed and optimized++* More reasonable default parameters++ Acquiring RBR+ -------------++I'll try to keep a selection of source and binaries at:++ http://www.ii.uib.no/~ketil/bioinformatics/downloads/software++If you need binaries for other architectures, drop me a mail at+<ketil@ii.uib.no>. The latest version should always be available from+my darcs repo: ++ darcs get http://www.ii.uib.no/~ketil/bioinformatics/repos/rbr+++ Installation instructions+ -------------------------++I'm working on a smoother installation process, but this is how it+currently works.++You need GHC (http://haskell.org/ghc). Everything is tested against+version 6.6, but version 6.4.x might also work. Expect to do some+modifications in the code for earlier versions than that.++You first need to get my 'bio' library, it is available from the same+website, and install that. You can use cabal to install the binary,+at the top level, do++ chmod +x Setup.hs+ ./Setup.hs configure (add --prefix=$HOME if you don't have root access)+ ./Setup.hs build+ ./Setup.hs install++If you want to go the more manual route, cd to the src subdirectory, and++ make rbr -- builds a dynamically linked executable+or: make rbr_s -- builds a statically linked executable++The main development platform is Linux/x86, so expect that to be most+well supported. In order to build on an aging Sun with an old gcc+(2.95), I had to comment out 'hooks.o' from the Makefile, and static+build didn't work either. I'm investigating this, but perhaps it+suffices to have a current GCC available, and/or a newer Solaris.+++ Usage+ -----++I've no real manual page yet, but 'rbr --help' should list the+available options. Basically, masking is determined by examining word+frequencies of a certain word length (-k), estimating a distribution+around the "modal interval" of the word frequencies with a certain+stringency (-s), and masking words with frequencies exceeding the mean+of this distribution by a certain standard deviations (-t). Defaults+are -k 16 -s 1.1 -t 5.0 if -L (lower case masking) is specified and+-k 16 -s 2.0 -t 8.0 if -n (masking with 'n') is specified. Lower case+is now the default.++To mask more agressively, you can either try to reduce stringency, or+a lower deviation, or both. Conversely if you want more conservative+masking. In general, the differences are small, and typically you can+compensate for a decrease in one parameter with an increase in the+other. ++Shorter word length will be more tolerant against SNPs and read+errors, but increase the variance. Longer will be less tolarant, but+have less variance. In addition, word lengths beyond 16 will be+slower.++There's also a --sparse=X option that will store a fraction (but at+least every X'th) of the words. This will reduce memory consumption proportionally.++RBR's memory usage can be limited with options to the run time+system's garbage collector. I good rule of thumb may be to limit it+to 80-90% of available physcal memory, which will avoid paging to+disk. If RBR is compiled with hooks.o linked in, this will be the+default, but if other behaviour is desired, you can use "+RTS -MxxxM+-RTS" to limit heap use to xxxMB¹. See the GHC documentation+(http://haskell.org/ghc) for more on this. Usually, you'll get better+performance by supplying -HxxxM as well (this will reduce GC time,+again see the GHC docs).++The -v option gives some feedback while RBR runs, which is nice if+you're using it interactively.++There is also a server mode, where RBR will index a data set, and+listen on stdin for sequence names, and answer on stdout with the+original sequence, the masked sequence, and the distribution of word+frequencies along the sequence.+++¹) GHC version 6.6 and earlier had a bug that would cause memory+consumption to be measured incorrectly if the system allocated it in+an unusual order. The fix will be in subsequent releases of GHC.
+ Setup.hs view
@@ -0,0 +1,5 @@+#!/usr/bin/env runhaskell+ +import Distribution.Simple+main = defaultMain+
+ TODO view
@@ -0,0 +1,26 @@+TODO: Check out options for freqtable data structure.+ Things to try out: fmindex/afi, hashtable, accumArray, HsJudy+ And perhaps combining key and count into an Int(|eger|64)?++----------------------------------------++TODO: support shaped keys+TODO: Repeat ID:+ output report (.tbl, .out)+ * calculate 1..k'th order entropy+ * other?++TODO: Mask against library++TODO: three-pass: build FT, build library, mask against it++TODO: calculate distrib and mask over windows (w=200? 400?)+ avoids different treatment of different length sequences++- Clustering++- Clustering with (SG/Lee) assembly++ -> statistics to use when clustering:+ 1. mode of word counts distribution (= coverage)+ 2. estimated p value (1-var/mu) (= avg. overlap)
+ idea.txt view
@@ -0,0 +1,47 @@+A comprehensive pipeline for EST analysis (?)++IDEA: based on a word map,+ - identify repeats (RB-style)+ - mask RB-repeats and against libraries+ - cluster+ - assemble++Stages:++ 1. FASTA parser (Parsec)+ 2. word map builder (using arrays)+ 3 a) cluster all-against-all+ b) detect repeats (word frequency aberrances)+ c) position sequence against a reference+ 4. linear assembly (xtract replacement(?))+ - and feature output++(thought on RB:+alt splicing may cause different expression levels along the EST --+but each exon should give a region of constant expression level,+possibly tapering off.++ * identify exons and relative expression level?+ * use region-based "background expression level" to find repeats+)+(thought: determine sequence quality based on low match number?)++(thoughts on visualization: align sequences (and curves)?)++(thought: use the length of the sequence to get an idea about+quality? mail Stephen Rudd, ask what happens if he turns sequences+around in the quality experiment)++How to handle unkeyable words (i.e. with Ns in them)?+ - zero (current tack)+ - average of prec/succ+ - average of possible interpretations++Call mc something else - mc is midnight commander (or something)++Current status (check darcs!):++f3 - does a bit of debugging output+rb - does masking based on contiguous words,+ and can output freqs along a sequence+mc - compares maskings
+ rbr.cabal view
@@ -0,0 +1,44 @@+Name: rbr+Version: 0.8.3+License: GPL+License-File: LICENSE+Stability: Beta+Author: Ketil Malde+Maintainer: Ketil Malde <ketil@malde.org>+Category: Bioinformatics+Synopsis: Mask nucleotide (EST) sequences in Fasta format+Description: RBR is a tool for masking EST sequences. It uses a+ statistical model to identify suspicious sequence parts,+ and masks them. The README has more details.+ .+ The Darcs repository is at <http://malde.org/~ketil/rbr>.+Homepage: http://malde.org/~ketil/++Cabal-Version: >= 1.2+Tested-With: GHC==6.8.2+-- add fps for ghc-6.4.2+Build-Depends: base, bio >= 0.3.1, QuickCheck, binary, bytestring, containers+Build-Type: Simple++data-files: idea.txt, README, TODO++Library+ Exposed-Modules: RBR.Options, RBR.Util, RBR.Stats, RBR.FreqTable, RBR.TextFormat+ C-sources: RBR/hooks.c+ Build-Depends: base>3, containers, bio+ Extensions:+ Ghc-Options: -Wall -funbox-strict-fields++Executable rbr+ Executable: rbr+ Main-Is: RBR/RBR.lhs+ build-depends: bytestring+ Extensions: CPP+ Ghc-Options: -Wall -funbox-strict-fields++Executable mct+ Executable: mct+ Main-Is: RBR/MCT.lhs+ build-depends: bytestring+ Extensions: BangPatterns, FlexibleContexts+ Ghc-Options: -Wall -funbox-strict-fields