diff bin/pact_wrapper.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/pact_wrapper.py	Mon Oct 12 17:43:33 2015 -0400
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+
+import argparse
+from os import path
+import re
+import os
+import csv
+import shutil
+import subprocess
+
+
+setting_template = """
+# TREE MANIPULATION
+{prune}
+
+# TREE OUTPUT
+print rule tree             # print out the tree...
+
+# SUMMARY STATS
+summary tmrca               # time to mrca
+summary proportions         # proportions on trunk of geneology
+summary coal rates          # coalescent rates; separate for each label
+summary mig rates           # migration rates; separate for each label pair
+#summary fst                 # diversity between vs within labels
+
+skyline settings -{sky_start} -{sky_end} {sky_interval}
+skyline proportions
+"""
+
+
+def tips_from_label(meta_reader, label, deme_col="deme"):
+    return [row['sequence'] for row in meta_reader if row[deme_col] == label]
+
+def tips_from_labels(meta_reader, labels, deme_col="deme"):
+    return [row['sequence'] for row in meta_reader if row[deme_col] in labels]
+
+def translate_tips(tips, translation):
+    result = list()
+    for tip in tips:
+        try:
+            result.append(translation[tip])
+        except KeyError:
+            pass
+    return result
+
+
+def translation_from_nexus(handle):
+    """This reads the seqname -> id translation from the trees file. Should probably eventually move this out
+    and into a separate file so that we can use the results in plotting. Not now though..."""
+    in_trans = False
+    trans = dict()
+    for line in handle:
+        if re.search("Translate", line):
+            # This is the demarkator for the translation section of the nexus file
+            in_trans = True
+            continue
+        if in_trans:
+            if re.match(";", line):
+                # If we hit this, we've reached the end of the translation region
+                break
+            else:
+                # Otherwise we're still in the Translate region, so populate trans
+                index, name = line.strip().strip(',').split()
+                trans[name] = index
+    return trans
+
+
+def prune_tips_string(tips, args):
+    if args.translate_trees:
+        trans = translation_from_nexus(file(args.trees_in))
+        tips = translate_tips(tips, trans)
+    return "prune to tips " + " ".join(tips)
+
+
+def prune_string(args):
+    if args.labels:
+        if args.metadata:
+            labels = args.labels.replace("\"", "").replace("'", "").split()
+            print "Pruning to labels:", labels
+            tips = tips_from_labels(csv.DictReader(args.metadata), labels, deme_col=args.deme_col)
+            print "Tips for labels are:", tips
+            return prune_tips_string(tips, args)
+        else:
+            return "prune to label " + args.labels
+
+    elif args.tip_file or args.tips:
+        if args.tip_file:
+            tips = args.tip_file.read().split()
+        else:
+            tips = args.tips.replace("'", "").replace("\"", "").split()
+        print "Tips specified are:", tips
+        return prune_tips_string(tips, args)
+    else:
+        return ""
+
+
+def get_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('trees_in')
+    parser.add_argument('-t', '--tips', help="List of tips in quotes")
+    parser.add_argument('-T', '--tip-file', help="File of tips sep by space", type=argparse.FileType('r'))
+    parser.add_argument('-r', '--translate-trees', action="store_true", default=False,
+        help="""If flagged, the trees_in file will be used for translating tip names from indices to actual
+        tip names; this is necessary for BEAST runs only""")
+    parser.add_argument('-m', '--metadata', type=argparse.FileType('r'),
+        help="""Required for filtering by deme/label with the beast method""")
+    parser.add_argument('-l', '--labels')
+    parser.add_argument('-s', '--sky-start', default=2.0, type=float,
+        help="How far back to compute skyline statistics (should be a potive number)")
+    parser.add_argument('-S', '--trim-start', type=float,
+        help="""Trim the tree from 0 back to the specified time; overrides sky-start for skyline plots.
+        (should be a positive number)""")
+    parser.add_argument('-e', '--trim-end', default=0.0, type=float,
+        help="Most recent time to trim to in time interval; will also be used for sky (should be non-negative)")
+    parser.add_argument('-o', '--out-dir')
+    parser.add_argument('-p', '--prune-to-trunk', action="store_true", default=False)
+    parser.add_argument('-d', '--deme-col', default="deme", help="Deme column in metadata file")
+    return parser.parse_args()
+
+
+def main():
+    args = get_args()
+
+    # Create the param file in the proper directory
+    outfile = file(path.join(args.out_dir, 'in.param'), 'w')
+
+    prune = prune_string(args)
+    if args.prune_to_trunk:
+        prune += "\nprune to trunk"
+
+    if args.trim_start:
+        prune += "\ntrim ends -%s -%s" % (args.trim_start, args.trim_end)
+
+    sky_start = args.trim_start if args.trim_start else args.sky_start
+    sky_end = args.trim_end
+    sky_interval =  abs(sky_end - sky_start) / 100
+    outfile.write(setting_template.format(
+            prune=prune,
+            sky_end=sky_end,
+            sky_start=sky_start,
+            sky_interval=sky_interval))
+    outfile.close()
+
+    # Copy the tree file over to the directory
+    shutil.copyfile(args.trees_in, path.join(args.out_dir, 'in.trees'))
+
+    # Actually run PACT
+    os.chdir(args.out_dir)
+    subprocess.check_call(['pact'])
+
+
+if __name__ == '__main__':
+    main()
+
+