partis-bcr 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bin/build.sh CHANGED
@@ -21,4 +21,4 @@ if [ "$*" == "with-simulation" ]; then
21
21
  fi
22
22
 
23
23
  echo -e "\n--> test"
24
- ./test/test.py --quick
24
+ test.py --quick # may need to specify full path if not using pip[x] install
bin/partis CHANGED
@@ -41,6 +41,15 @@ from python.parametercounter import ParameterCounter
41
41
  from python.corrcounter import CorrCounter
42
42
  from python.waterer import Waterer
43
43
 
44
+ # ----------------------------------------------------------------------------------------
45
+ def find_cmd(cmd):
46
+ if shutil.which(os.path.basename(cmd)): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
47
+ return os.path.basename(cmd)
48
+ elif os.path.exists(cmd):
49
+ return cmd
50
+ else:
51
+ return '%s/bin/%s' % (partis_dir, os.path.basename(cmd))
52
+
44
53
  # ----------------------------------------------------------------------------------------
45
54
  def run_simulation(args):
46
55
  # ----------------------------------------------------------------------------------------
@@ -88,8 +97,7 @@ def run_simulation(args):
88
97
  for iproc in range(args.n_procs):
89
98
  n_sub_events = get_sub_events(iproc)
90
99
  clist = copy.deepcopy(sys.argv)
91
- if shutil.which('partis'): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
92
- clist[0] = 'partis'
100
+ clist[0] = find_cmd(clist[0])
93
101
  utils.replace_in_arglist(clist, '--n-procs', '1')
94
102
  clist.append('--im-a-subproc')
95
103
  utils.replace_in_arglist(clist, '--random-seed', str(args.random_seed + iproc))
@@ -107,7 +115,7 @@ def run_simulation(args):
107
115
  cmdstr = ' '.join(clist)
108
116
  if args.debug:
109
117
  print(' %s %s' % (utils.color('red', 'run'), cmdstr))
110
- cmdfos.append({'cmd_str' : cmdstr, 'workdir' : get_workdir(iproc), 'logdir' : args.workdir+'/log-'+str(iproc), 'outfname' : get_outfname(iproc)}) # logdirs have to be different than <workdirs> since ./bin/partis (rightfully) barfs if its workdir already exists
118
+ cmdfos.append({'cmd_str' : cmdstr, 'workdir' : get_workdir(iproc), 'logdir' : args.workdir+'/log-'+str(iproc), 'outfname' : get_outfname(iproc)}) # logdirs have to be different than <workdirs> since partis (rightfully) barfs if its workdir already exists
111
119
 
112
120
  utils.run_cmds(cmdfos, batch_system=args.batch_system, batch_options=args.batch_options, batch_config_fname=args.batch_config_fname, debug='print')
113
121
  file_list = [cmdfos[i]['outfname'] for i in range(args.n_procs)]
@@ -330,8 +338,7 @@ def subset_partition(args):
330
338
  # ----------------------------------------------------------------------------------------
331
339
  def get_cmd(infname, outdir, merged_odir=None):
332
340
  clist = copy.deepcopy(sys.argv)
333
- if shutil.which('partis'): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
334
- clist[0] = 'partis'
341
+ clist[0] = find_cmd(clist[0])
335
342
  assert is_subset_action(clist[1]) # ugh, but utils.replace_in_arglist() only handles -- style arg strs
336
343
  clist[1] = args.action.split('-')[1]
337
344
  utils.remove_from_arglist(clist, '--n-subsets', has_arg=True)
@@ -628,13 +635,13 @@ def run_all_loci(args, ig_or_tr='ig'):
628
635
  mfname = getofn(None, joint=True, input_meta=True)
629
636
  if is_subset_action(args.action): # UGH
630
637
  mfname = '%s/input/%s' % (os.path.dirname(mfname), os.path.basename(mfname))
631
- if not args.guess_pairing_info and not args.no_pairing_info and not has_input_meta_pair_info:
638
+ if True: #not args.guess_pairing_info and not args.no_pairing_info and not has_input_meta_pair_info:
632
639
  if utils.getsuffix(args.infname) == '.yaml':
633
640
  new_fn = '%s/input-seqs.fa' % utils.non_none([args.paired_outdir, args.workdir])
634
641
  print(' note: converting input .yaml file to .fa so we can extract pairing info + split loci (new --infname: %s)' % new_fn)
635
- utils.simplerun('%s/bin/parse-output.py %s %s' % (utils.get_partis_dir(), args.infname, new_fn), extra_str=' ')
642
+ utils.simplerun('%s %s %s' % (find_cmd('parse-output.py'), args.infname, new_fn), extra_str=' ')
636
643
  args.infname = new_fn
637
- cmd = '%s%s/bin/extract-pairing-info.py %s %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', args.partis_dir, args.infname, mfname)
644
+ cmd = '%s%s %s %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', find_cmd('extract-pairing-info.py'), args.infname, mfname)
638
645
  if args.n_max_queries > 0:
639
646
  cmd += ' --n-max-queries %d' % args.n_max_queries
640
647
  if args.n_random_queries is not None:
@@ -651,7 +658,7 @@ def run_all_loci(args, ig_or_tr='ig'):
651
658
  print(' note: couldn\'t extract 10x-style droplet ids from sequence ids (see above), continuing without pairing info')
652
659
  if is_subset_action(args.action): # don't want loci to be split, since we pass all loci to each subset process in one file
653
660
  return
654
- cmd = '%s%s/bin/split-loci.py %s --outdir %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', args.partis_dir, args.infname, getodir())
661
+ cmd = '%s%s %s --outdir %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', find_cmd('split-loci.py'), args.infname, getodir())
655
662
  if args.reverse_negative_strands:
656
663
  cmd += ' --reverse-negative-strands'
657
664
  if os.path.exists(mfname): # NOTE this won't show up up in dry_run since it won't exist yet
@@ -718,8 +725,7 @@ def run_all_loci(args, ig_or_tr='ig'):
718
725
  # ----------------------------------------------------------------------------------------
719
726
  def prep_args(ltmp):
720
727
  clist = copy.deepcopy(sys.argv)
721
- if shutil.which('partis'): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
722
- clist[0] = 'partis'
728
+ clist[0] = find_cmd(clist[0])
723
729
  if any('=' in a for a in clist): # arg (have to handle --arg=val style syntax)
724
730
  new_clist = []
725
731
  for cstr in clist:
@@ -1325,7 +1331,7 @@ def run_all_loci(args, ig_or_tr='ig'):
1325
1331
  titlestr = ltmp
1326
1332
  # if ccfs is not None and ltmp in ccfs:
1327
1333
  # titlestr += ':@specif.%.2f'%ccfs[ltmp][0]
1328
- cmdstr = '%s/bin/compare-plotdirs.py --outdir %s/%s --names single:joint --plotdirs %s/%s:%s/%s' % (args.partis_dir, cfpdir, ltmp, getplotdir(ltmp, single_chain=True), subd, getplotdir(ltmp), subd)
1334
+ cmdstr = '%s --outdir %s/%s --names single:joint --plotdirs %s/%s:%s/%s' % (find_cmd('compare-plotdirs.py'), cfpdir, ltmp, getplotdir(ltmp, single_chain=True), subd, getplotdir(ltmp), subd)
1329
1335
  cmdstr += ' --make-parent-html --add-to-title %s --log xy --translegend=0.07:0.07' % titlestr
1330
1336
  utils.simplerun(cmdstr, dryrun=args.dry_run)
1331
1337
 
bin/partis.py CHANGED
@@ -41,6 +41,15 @@ from python.parametercounter import ParameterCounter
41
41
  from python.corrcounter import CorrCounter
42
42
  from python.waterer import Waterer
43
43
 
44
+ # ----------------------------------------------------------------------------------------
45
+ def find_cmd(cmd):
46
+ if shutil.which(os.path.basename(cmd)): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
47
+ return os.path.basename(cmd)
48
+ elif os.path.exists(cmd):
49
+ return cmd
50
+ else:
51
+ return '%s/bin/%s' % (partis_dir, os.path.basename(cmd))
52
+
44
53
  # ----------------------------------------------------------------------------------------
45
54
  def run_simulation(args):
46
55
  # ----------------------------------------------------------------------------------------
@@ -88,8 +97,7 @@ def run_simulation(args):
88
97
  for iproc in range(args.n_procs):
89
98
  n_sub_events = get_sub_events(iproc)
90
99
  clist = copy.deepcopy(sys.argv)
91
- if shutil.which('partis'): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
92
- clist[0] = 'partis'
100
+ clist[0] = find_cmd(clist[0])
93
101
  utils.replace_in_arglist(clist, '--n-procs', '1')
94
102
  clist.append('--im-a-subproc')
95
103
  utils.replace_in_arglist(clist, '--random-seed', str(args.random_seed + iproc))
@@ -107,7 +115,7 @@ def run_simulation(args):
107
115
  cmdstr = ' '.join(clist)
108
116
  if args.debug:
109
117
  print(' %s %s' % (utils.color('red', 'run'), cmdstr))
110
- cmdfos.append({'cmd_str' : cmdstr, 'workdir' : get_workdir(iproc), 'logdir' : args.workdir+'/log-'+str(iproc), 'outfname' : get_outfname(iproc)}) # logdirs have to be different than <workdirs> since ./bin/partis (rightfully) barfs if its workdir already exists
118
+ cmdfos.append({'cmd_str' : cmdstr, 'workdir' : get_workdir(iproc), 'logdir' : args.workdir+'/log-'+str(iproc), 'outfname' : get_outfname(iproc)}) # logdirs have to be different than <workdirs> since partis (rightfully) barfs if its workdir already exists
111
119
 
112
120
  utils.run_cmds(cmdfos, batch_system=args.batch_system, batch_options=args.batch_options, batch_config_fname=args.batch_config_fname, debug='print')
113
121
  file_list = [cmdfos[i]['outfname'] for i in range(args.n_procs)]
@@ -330,8 +338,7 @@ def subset_partition(args):
330
338
  # ----------------------------------------------------------------------------------------
331
339
  def get_cmd(infname, outdir, merged_odir=None):
332
340
  clist = copy.deepcopy(sys.argv)
333
- if shutil.which('partis'): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
334
- clist[0] = 'partis'
341
+ clist[0] = find_cmd(clist[0])
335
342
  assert is_subset_action(clist[1]) # ugh, but utils.replace_in_arglist() only handles -- style arg strs
336
343
  clist[1] = args.action.split('-')[1]
337
344
  utils.remove_from_arglist(clist, '--n-subsets', has_arg=True)
@@ -628,13 +635,13 @@ def run_all_loci(args, ig_or_tr='ig'):
628
635
  mfname = getofn(None, joint=True, input_meta=True)
629
636
  if is_subset_action(args.action): # UGH
630
637
  mfname = '%s/input/%s' % (os.path.dirname(mfname), os.path.basename(mfname))
631
- if not args.guess_pairing_info and not args.no_pairing_info and not has_input_meta_pair_info:
638
+ if True: #not args.guess_pairing_info and not args.no_pairing_info and not has_input_meta_pair_info:
632
639
  if utils.getsuffix(args.infname) == '.yaml':
633
640
  new_fn = '%s/input-seqs.fa' % utils.non_none([args.paired_outdir, args.workdir])
634
641
  print(' note: converting input .yaml file to .fa so we can extract pairing info + split loci (new --infname: %s)' % new_fn)
635
- utils.simplerun('%s/bin/parse-output.py %s %s' % (utils.get_partis_dir(), args.infname, new_fn), extra_str=' ')
642
+ utils.simplerun('%s %s %s' % (find_cmd('parse-output.py'), args.infname, new_fn), extra_str=' ')
636
643
  args.infname = new_fn
637
- cmd = '%s%s/bin/extract-pairing-info.py %s %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', args.partis_dir, args.infname, mfname)
644
+ cmd = '%s%s %s %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', find_cmd('extract-pairing-info.py'), args.infname, mfname)
638
645
  if args.n_max_queries > 0:
639
646
  cmd += ' --n-max-queries %d' % args.n_max_queries
640
647
  if args.n_random_queries is not None:
@@ -651,7 +658,7 @@ def run_all_loci(args, ig_or_tr='ig'):
651
658
  print(' note: couldn\'t extract 10x-style droplet ids from sequence ids (see above), continuing without pairing info')
652
659
  if is_subset_action(args.action): # don't want loci to be split, since we pass all loci to each subset process in one file
653
660
  return
654
- cmd = '%s%s/bin/split-loci.py %s --outdir %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', args.partis_dir, args.infname, getodir())
661
+ cmd = '%s%s %s --outdir %s' % (cov_cmd()+' ' if args.prepend_coverage_command else '', find_cmd('split-loci.py'), args.infname, getodir())
655
662
  if args.reverse_negative_strands:
656
663
  cmd += ' --reverse-negative-strands'
657
664
  if os.path.exists(mfname): # NOTE this won't show up up in dry_run since it won't exist yet
@@ -718,8 +725,7 @@ def run_all_loci(args, ig_or_tr='ig'):
718
725
  # ----------------------------------------------------------------------------------------
719
726
  def prep_args(ltmp):
720
727
  clist = copy.deepcopy(sys.argv)
721
- if shutil.which('partis'): # use version in PATH if it's there (pipx seems to leave two incompatible versions lying around)
722
- clist[0] = 'partis'
728
+ clist[0] = find_cmd(clist[0])
723
729
  if any('=' in a for a in clist): # arg (have to handle --arg=val style syntax)
724
730
  new_clist = []
725
731
  for cstr in clist:
@@ -1325,7 +1331,7 @@ def run_all_loci(args, ig_or_tr='ig'):
1325
1331
  titlestr = ltmp
1326
1332
  # if ccfs is not None and ltmp in ccfs:
1327
1333
  # titlestr += ':@specif.%.2f'%ccfs[ltmp][0]
1328
- cmdstr = '%s/bin/compare-plotdirs.py --outdir %s/%s --names single:joint --plotdirs %s/%s:%s/%s' % (args.partis_dir, cfpdir, ltmp, getplotdir(ltmp, single_chain=True), subd, getplotdir(ltmp), subd)
1334
+ cmdstr = '%s --outdir %s/%s --names single:joint --plotdirs %s/%s:%s/%s' % (find_cmd('compare-plotdirs.py'), cfpdir, ltmp, getplotdir(ltmp, single_chain=True), subd, getplotdir(ltmp), subd)
1329
1335
  cmdstr += ' --make-parent-html --add-to-title %s --log xy --translegend=0.07:0.07' % titlestr
1330
1336
  utils.simplerun(cmdstr, dryrun=args.dry_run)
1331
1337
 
packages/ham/bcrham ADDED
Binary file
@@ -0,0 +1,298 @@
1
+ #!python
2
+ from __future__ import absolute_import, division, unicode_literals
3
+ from __future__ import print_function
4
+ import argparse
5
+ from collections import OrderedDict
6
+ import os
7
+ import glob
8
+ import sys
9
+ import colored_traceback.always
10
+ import copy
11
+ from pathlib import Path
12
+ partis_dir = str(Path(__file__).parent.parent)
13
+ sys.path.insert(1, partis_dir) # + '/python')
14
+
15
+ import python.plotconfig as plotconfig
16
+ import python.plotting as plotting
17
+ import python.utils as utils
18
+ import python.glutils as glutils
19
+ from python.hist import Hist
20
+ import python.treeutils as treeutils
21
+
22
+ xtitledict = copy.deepcopy(plotting.legends)
23
+ xtitledict.update(plotconfig.xtitles)
24
+ xtitledict.update(treeutils.legtexts)
25
+
26
+ ptitledict = copy.deepcopy(plotting.legends)
27
+ ptitledict.update(plotconfig.plot_titles)
28
+ ptitledict.update(treeutils.legtexts)
29
+
30
+ # ----------------------------------------------------------------------------------------
31
+ def get_hists_from_dir(dirname, histname, string_to_ignore=None):
32
+ hists = {}
33
+ for fname in glob.glob('%s/%s' % (dirname, args.file_glob_str)):
34
+ varname = os.path.basename(fname)
35
+ for rstr in args.file_replace_strs:
36
+ varname = varname.replace(rstr, '')
37
+ if string_to_ignore is not None:
38
+ varname = varname.replace(string_to_ignore, '')
39
+ hists[varname] = Hist(fname=fname, title=histname)
40
+ if len(hists) == 0:
41
+ print(' no csvs found%s in %s' % ('' if args.file_glob_str is None else ' with --file-glob-str \'%s\''%args.file_glob_str, dirname))
42
+ return hists
43
+
44
+
45
+ # ----------------------------------------------------------------------------------------
46
+ def compare_directories(args, plotdirlist, outdir):
47
+ utils.prep_dir(outdir, wildlings=['*.png', '*.svg', '*.csv'])
48
+
49
+ # read hists from <plotdirlist>
50
+ allhists = OrderedDict()
51
+ allvars = set() # all variables that appeared in any dir
52
+ for idir in range(len(plotdirlist)):
53
+ dirhists = get_hists_from_dir(plotdirlist[idir], args.names[idir])
54
+ allvars |= set(dirhists.keys())
55
+ allhists[args.names[idir]] = dirhists
56
+ # then loop over all the <varname>s we found
57
+ for varname in allvars:
58
+ hlist = [allhists[dname].get(varname, Hist(1, 0, 1, title='null')) for dname in allhists]
59
+ plot_single_variable(args, varname, hlist, outdir, pathnameclues=plotdirlist[0])
60
+
61
+ plotting.make_html(outdir, n_columns=4)
62
+
63
+ # ----------------------------------------------------------------------------------------
64
+ def plot_single_variable(args, varname, hlist, outdir, pathnameclues):
65
+ if varname in plotconfig.gene_usage_columns:
66
+ hlist = plotting.add_bin_labels_not_in_all_hists(hlist)
67
+
68
+ no_labels = False
69
+ xline, bounds, figsize = None, None, None
70
+ stats = args.extra_stats
71
+ translegend = [0.0, -0.2]
72
+ xtitle, ytitle = hlist[0].xtitle, hlist[0].ytitle
73
+ bounds, xticks, xticklabels = args.xbounds, args.xticks, None
74
+ if xtitle == '': # arg, plotting.py thinks default should be None, hist.py thinks it's ''
75
+ xtitle = None
76
+ if '-mean-bins' in varname:
77
+ raise Exception('darn, I was hoping I wasn\'t making these plots any more')
78
+ plottitle = args.plottitle
79
+ if plottitle is None:
80
+ plottitle = ptitledict.get(varname, varname)
81
+
82
+ ytitle = 'fraction of total' if args.normalize else 'counts'
83
+
84
+ if 'mute-freqs/v' in pathnameclues or 'mute-freqs/d' in pathnameclues or 'mute-freqs/j' in pathnameclues:
85
+ assert not args.normalize
86
+ ytitle = 'mutation freq'
87
+
88
+ if varname in plotconfig.gene_usage_columns:
89
+ xtitle = 'allele'
90
+ if hlist[0].n_bins == 2:
91
+ stats = '0-bin' # print the fraction of entries in the zero bin into the legend (i.e. the fraction correct)
92
+ xtitle = None
93
+ # elif hlist[0].bin_labels.count('') == hlist[0].n_bins + 2:
94
+ # xtitle = '???'
95
+
96
+ line_width_override = None
97
+ if args.performance_plots:
98
+ if 'hamming_to_true_naive' in varname:
99
+ xtitle = 'hamming distance'
100
+ if '_normed' in varname:
101
+ xtitle = 'fractional ' + xtitle
102
+ elif '_vs_mute_freq' in varname:
103
+ xtitle = 'mutation freq'
104
+ ytitle = 'fraction correct'
105
+ if varname[0] == 'v' or varname[0] == 'j':
106
+ translegend = [-0.4, -0.4]
107
+ elif varname.find('_gene') == 1:
108
+ xtitle = ''
109
+ ytitle = 'fraction correct'
110
+ else:
111
+ xtitle = 'inferred - true'
112
+ bounds = plotconfig.true_vs_inferred_hard_bounds.setdefault(varname, None)
113
+ else:
114
+ if bounds is None:
115
+ bounds = plotconfig.default_hard_bounds.setdefault(varname, None)
116
+ if bounds is None and 'insertion' in varname:
117
+ bounds = plotconfig.default_hard_bounds.setdefault('all_insertions', None)
118
+ if varname in plotconfig.gene_usage_columns:
119
+ # no_labels = True # not sure why i wanted these labels turned off?
120
+ if 'j_' not in varname:
121
+ figsize = (10, 5)
122
+ line_width_override = 1
123
+ elif 'per-gene-per-position/v' in pathnameclues:
124
+ figsize = (20, 5)
125
+ if bounds is None:
126
+ bounds = plotconfig.default_hard_bounds.setdefault(utils.unsanitize_name(varname), None)
127
+
128
+ if 'IG' in varname or 'TR' in varname:
129
+ if 'mute-freqs' in pathnameclues:
130
+ gene = utils.unsanitize_name(varname)
131
+ plottitle = gene # + ' -- mutation frequency'
132
+ xtitle = 'position'
133
+ if utils.get_region(gene) == 'j':
134
+ translegend = [0.1, 0.] #(-0.35, -0.02)
135
+ else:
136
+ translegend = [0.15, -0.02]
137
+ xline = None
138
+ if args.glfo is not None:
139
+ if utils.get_region(gene) in utils.conserved_codons[args.locus]:
140
+ xline = args.glfo[utils.conserved_codons[args.locus][utils.get_region(gene)] + '-positions'][gene]
141
+ else:
142
+ ilastdash = varname.rfind('-')
143
+ gene = utils.unsanitize_name(varname[:ilastdash])
144
+ base_varname = varname[ilastdash + 1 :]
145
+ base_plottitle = plotconfig.plot_titles[base_varname] if base_varname in plotconfig.plot_titles else ''
146
+ plottitle = gene + ' -- ' + base_plottitle
147
+
148
+ if varname == 'cluster-sizes':
149
+ xtitle = 'cluster size'
150
+ ytitle = 'fraction of clusters' if args.normalize else 'N clusters'
151
+ plottitle = ''
152
+ xticks, xticklabels = plotting.get_cluster_size_xticks(hlist=hlist) # it would be better to use all the hists, but i think it'll just screw up the ticks
153
+ import matplotlib.pyplot as plt
154
+ if varname in ['func-per-drop', 'nonfunc-per-drop']:
155
+ bounds = (-0.5, 15.5)
156
+ if 'subtree-purity' in varname:
157
+ if 'size' in varname:
158
+ if args.log == '':
159
+ args.log = 'xy'
160
+ xticks = [1, 2, 3, 5, 10, 15, 20]
161
+ xticklabels = ['1', '2', '3', '5', '10', '15', '20']
162
+
163
+ if xtitle is None:
164
+ xtitle = xtitledict.get(varname)
165
+
166
+ if args.add_to_title is not None:
167
+ plottitle += args.add_to_title
168
+
169
+ if len(hlist) > 9: # skootch it down so they (maybe) all fit
170
+ translegend[1] -= 0.5
171
+ if args.translegend is not None: # override with the command line
172
+ translegend = args.translegend
173
+ if varname == 'paired-uids-per-uid':
174
+ translegend = [translegend[0] + 0.15, translegend[1] - 0.3]
175
+ if args.extra_stats == 'auto': # kind of hackey
176
+ if xtitle == 'inferred - true':
177
+ stats = 'absmean'
178
+ else:
179
+ stats = 'mean'
180
+ # draw that little #$*(!
181
+ linewidths = [line_width_override, ] if line_width_override is not None else args.linewidths
182
+ if args.alphas is None or len(args.alphas) != len(hlist):
183
+ if args.alphas is not None and len(args.alphas) != len(hlist):
184
+ print(' %s --alphas wrong length, using first entry for all' % utils.wrnstr())
185
+ args.alphas = [0.6 if args.alphas is None else args.alphas[0] for _ in range(len(hlist))]
186
+ shift_overflows = os.path.basename(outdir) != 'gene-call' and 'func-per-drop' not in varname
187
+ plotting.draw_no_root(hlist[0], plotname=varname, plotdir=outdir, more_hists=hlist[1:], write_csv=False, stats=stats, bounds=bounds, ybounds=args.ybounds,
188
+ shift_overflows=shift_overflows, plottitle=plottitle, colors=args.colors,
189
+ xtitle=xtitle if args.xtitle is None else args.xtitle, ytitle=ytitle if args.ytitle is None else args.ytitle, xline=xline, normalize=(args.normalize and '_vs_mute_freq' not in varname),
190
+ linewidths=linewidths, markersizes=args.markersizes, alphas=args.alphas, errors=not args.no_errors, remove_empty_bins=True, #='y' in args.log,
191
+ figsize=figsize, no_labels=no_labels, log=args.log, translegend=translegend, xticks=xticks, xticklabels=xticklabels, square_bins=args.square_bins)
192
+
193
+ if args.swarm_meta_key is not None:
194
+ plotvals = {h.title : [h.get_bin_centers()[i] for i in h.ibiniter(True) for _ in range(int(h.bin_contents[i]))] for h in hlist}
195
+ plotting.stack_meta_hists(varname, outdir, args.swarm_meta_key, plotvals, colors={h.title : c for h, c in zip(hlist, args.colors)}, xtitle=xtitle, swarm_plots=True, no_hist=True, xticks=xticks)
196
+
197
+ # ----------------------------------------------------------------------------------------
198
+ helpstr = """
199
+ Compare csv histogram plot files across multiple directories
200
+ ./bin/compare-plotdirs.py --outdir _output/tmp-plots --plotdirs docs/example-plots/sw/mute-freqs/overall:docs/example-plots/hmm/mute-freqs/overall:docs/example-plots/multi-hmm/mute-freqs/overall --names sw:hmm:multi-hmm --normalize
201
+ """
202
+ class MultiplyInheritedFormatter(argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHelpFormatter):
203
+ pass
204
+ formatter_class = MultiplyInheritedFormatter
205
+ parser = argparse.ArgumentParser(formatter_class=MultiplyInheritedFormatter, description=helpstr)
206
+ parser.add_argument('--outdir', required=True, help='Output directory to which to write the resulting comparison plots. A summary .html file is also written to <outdir>.html')
207
+ parser.add_argument('--plotdirs', required=True, help='Colon-separated list of input plot directories, each of which must have identical structure. Looks for svgs first in each dir, but then also in the subdirs of each dir (so e.g. if each of them have a/, b/, and c/ subdirs, this script will make a separate comparison of a/, b/, and c/)')
208
+ parser.add_argument('--names', required=True, help='colon-separated list of names/labels corresponding to --plotdirs (use @ as space)')
209
+ parser.add_argument('--performance-plots', action='store_true', help='set to true if these are annotation performance plots, i.e. made with --plot-annotation-performance (this makes the axis labels more sensible)')
210
+ parser.add_argument('--colors', default=':'.join(plotting.default_colors), help='color-separated list of colors to cycle through for the plotdirs')
211
+ parser.add_argument('--alphas')
212
+ parser.add_argument('--linewidths', default=':'.join(plotting.default_linewidths), help='colon-separated list of linewidths to cycle through')
213
+ parser.add_argument('--markersizes', default=':'.join(plotting.default_markersizes), help='colon-separated list of linewidths to cycle through')
214
+ parser.add_argument('--gldirs', help='On plots showing mutation vs individual gene positions, if you\'d like a dashed veritcal line showing conserved codon positions, set this as a colon-separated list of germline info dirs corresponding to each plotdir') #, default=['data/germlines/human'])
215
+ parser.add_argument('--locus', default='igh')
216
+ parser.add_argument('--normalize', action='store_true', help='If set, the histograms from each plotdir are normalized (each bin contents divided by the integral) before making the comparison (e.g. for comparing different size samples).')
217
+ parser.add_argument('--extra-stats', help='if set, adds extra stat to legend, e.g. \'mean\', \'absmean\', \'auto\'')
218
+ parser.add_argument('--translegend', help='colon-separated list of x, y values with which to translate all the legends')
219
+ parser.add_argument('--log', default='', help='Display these axes on a log scale, set to either \'x\', \'y\', or \'xy\'')
220
+ parser.add_argument('--make-parent-html', action='store_true', help='after doing everything within subdirs, make a single html in the main/parent dir with all plots from subdirs')
221
+ parser.add_argument('--add-to-title', help='string to append to existing title (use @ as space)')
222
+ parser.add_argument('--file-glob-str', default='*.csv', help='shell glob style regex for matching plot files')
223
+ parser.add_argument('--file-replace-strs', default='.csv', help='colon-separated list of strings to remove frome file base name to get variable name')
224
+ parser.add_argument('--xbounds')
225
+ parser.add_argument('--ybounds')
226
+ parser.add_argument('--xticks')
227
+ parser.add_argument('--plottitle')
228
+ parser.add_argument('--xtitle')
229
+ parser.add_argument('--ytitle')
230
+ parser.add_argument('--no-errors', action='store_true')
231
+ parser.add_argument('--single-plotdir', action='store_true')
232
+ parser.add_argument('--square-bins', action='store_true')
233
+ parser.add_argument('--swarm-meta-key', help='if set, also make swarm plots, pretending that each hist\'s title is the value for this "fake" meta info key, and treat each bin\'s entries as observations at the bin center\'s value')
234
+
235
+ args = parser.parse_args()
236
+ args.plotdirs = utils.get_arg_list(args.plotdirs)
237
+ args.names = utils.get_arg_list(args.names)
238
+ args.alphas = utils.get_arg_list(args.alphas, floatify=True)
239
+ args.colors = utils.get_arg_list(args.colors)
240
+ args.linewidths = utils.get_arg_list(args.linewidths, intify=True)
241
+ args.markersizes = utils.get_arg_list(args.markersizes, intify=True)
242
+ args.gldirs = utils.get_arg_list(args.gldirs)
243
+ args.translegend = utils.get_arg_list(args.translegend, floatify=True)
244
+ args.xbounds = utils.get_arg_list(args.xbounds, floatify=True)
245
+ args.ybounds = utils.get_arg_list(args.ybounds, floatify=True)
246
+ args.xticks = utils.get_arg_list(args.xticks, floatify=True)
247
+ args.file_replace_strs = utils.get_arg_list(args.file_replace_strs)
248
+ for iname in range(len(args.names)):
249
+ args.names[iname] = args.names[iname].replace('@', ' ')
250
+ if args.add_to_title is not None:
251
+ args.add_to_title = args.add_to_title.replace('@', ' ')
252
+
253
+ if len(args.plotdirs) == 1 and not args.single_plotdir:
254
+ print(' --plotdirs is length 1 (and --single-plotdir wasn\'t set), so assuming --names has the desired subdirs')
255
+ parentdir = args.plotdirs[0]
256
+ args.plotdirs = [parentdir + '/' + n for n in args.names]
257
+
258
+ if len(args.plotdirs) != len(args.names):
259
+ raise Exception('poorly formatted args:\n %s\n %s' % (' '.join(args.plotdirs), ' '.join(args.names)))
260
+
261
+ # make a merged glfo from all the gldirs
262
+ args.glfo = None
263
+ if args.gldirs is not None:
264
+ for gldir in [gd for gd in args.gldirs if os.path.exists(gd)]:
265
+ tmpglfo = glutils.read_glfo(gldir, args.locus)
266
+ if args.glfo is None:
267
+ args.glfo = tmpglfo
268
+ else:
269
+ args.glfo = glutils.get_merged_glfo(args.glfo, tmpglfo)
270
+
271
+ if any(not os.path.isdir(d) for d in args.plotdirs):
272
+ print(' at least one of --plotdirs doesn\'t exist: %s' % ' '.join(d for d in args.plotdirs if not os.path.isdir(d)))
273
+ sys.exit(0)
274
+
275
+ listof_plotdirlists, listof_outdirs = [], []
276
+ # first add the main/parent dir, if it has csvs
277
+ firstdir = args.plotdirs[0]
278
+ if len(glob.glob(firstdir + '/*.csv')) > 0:
279
+ listof_plotdirlists.append(args.plotdirs)
280
+ listof_outdirs.append(args.outdir)
281
+ else:
282
+ print(' no csvs in main/parent dir %s' % firstdir)
283
+ # then figure out if there's subdirs we need to deal with
284
+ added_subds = []
285
+
286
+ for subdir in [d for d in os.listdir(firstdir) if os.path.isdir(firstdir + '/' + d)]:
287
+ listof_plotdirlists.append([d + '/' + subdir for d in args.plotdirs])
288
+ listof_outdirs.append(args.outdir + '/' + subdir)
289
+ added_subds.append(subdir)
290
+ if len(added_subds) > 0:
291
+ print(' added %d subdirs: %s' % (len(added_subds), ' '.join(added_subds)))
292
+
293
+ for dlist, outdir in zip(listof_plotdirlists, listof_outdirs):
294
+ compare_directories(args, dlist, outdir)
295
+
296
+ if args.make_parent_html: # didn't really test this very well
297
+ fnoutstr, _ = utils.simplerun('find %s -type f -name *.svg' % args.outdir, return_out_err=True)
298
+ plotting.make_html(args.outdir, fnames=[fnoutstr.strip().split('\n')])
@@ -0,0 +1,126 @@
1
+ #!python
2
+ from __future__ import absolute_import, division, unicode_literals
3
+ from __future__ import print_function
4
+ import os
5
+ import sys
6
+ import csv
7
+ import argparse
8
+ import operator
9
+ import argparse
10
+ import yaml
11
+ import colored_traceback.always
12
+ from io import open
13
+
14
+ # if you move this script, you'll need to change this method of getting the imports
15
+ from pathlib import Path
16
+ partis_dir = str(Path(__file__).parent.parent)
17
+ sys.path.insert(1, partis_dir) # + '/python')
18
+
19
+ import python.utils as utils
20
+
21
+ # ----------------------------------------------------------------------------------------
22
+ def is_acceptable(scol, acceptable_values, lval):
23
+ if lval in acceptable_values:
24
+ return True
25
+ if args.any_allele and '_gene' in scol and any(utils.are_alleles(g, lval) for g in acceptable_values):
26
+ return True
27
+ return False
28
+
29
+ class MultiplyInheritedFormatter(argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHelpFormatter):
30
+ pass
31
+ formatter_class = MultiplyInheritedFormatter
32
+ parser = argparse.ArgumentParser(formatter_class=MultiplyInheritedFormatter)
33
+ parser.add_argument('--infname', default='test/ref-results/test/parameters/data/hmm/all-probs.csv', help='input all-probs.csv file from a previously-inferred partis parameter directory, for instance: test/reference-results/test/parameters/data/hmm/all-probs.csv')
34
+ parser.add_argument('--config-fname', help='yaml file with info on columns for which we want to specify particular values (and skip others). See default/example set below. To create a yaml config file to start from, uncomment the yaml.dump() line below and rerun with no arguments.')
35
+ parser.add_argument('--outfname')
36
+ parser.add_argument('--any-allele', action='store_true', help='if set, also include any other alleles of any of the genes specified in \'skip_column_vals\' (note: can also set it in the cfg file).')
37
+ parser.add_argument('--debug', action='store_true', default=True) # it's kind of confusing without the debug printout
38
+ args = parser.parse_args()
39
+
40
+ non_summed_column = None
41
+ if args.config_fname is None:
42
+ non_summed_column = 'v_gene'
43
+ skip_column_vals = { # to input your own dict on the command line, just convert with str() and quote it
44
+ # 'cdr3_length' : ['33', '36', '39', '42', '45', '48'], # <value> is list of acceptable values NOTE need to all be strings, otherwise you have to worry about converting the values in the csv file
45
+ 'v_gene' : ['IGHV1-2*02+G35A', 'IGHV1-2*02+T147C', 'IGHV1-2*02'],
46
+ # 'd_gene' : ['IGHD3-22*01'],
47
+ 'j_gene' : ['IGHJ4*02'],
48
+ 'cdr3_length' : ['66',],
49
+ }
50
+ print('%s using default skip column/non-summed column values (which probably don\'t correspond to what you\'re actually interested in)' % utils.color('red', 'note'))
51
+ # # uncomment to create a yaml file to start from:
52
+ # with open('tmp.yaml', 'w') as tfile:
53
+ # yaml.dump({'non_summed_column' : non_summed_column, 'skip_column_vals' : skip_column_vals}, tfile)
54
+ else:
55
+ with open(args.config_fname) as yamlfile:
56
+ yamlfo = yaml.load(yamlfile, Loader=yaml.Loader)
57
+ if 'non_summed_column' in yamlfo:
58
+ non_summed_column = yamlfo['non_summed_column']
59
+ skip_column_vals = yamlfo['skip_column_vals']
60
+ for scol in skip_column_vals:
61
+ skip_column_vals[scol] = [str(v) for v in skip_column_vals[scol]] # yaml.load() converts to integers, which is usually nice, but here we don't want it to since we're not converting when reading all-probs.csv (I think there's options to yaml.load to change this, I just don't want to figure it out now)
62
+ if 'any_allele' in yamlfo:
63
+ if args.any_allele and not yamlfo['any_allele']: # if it's set to true on the command line, but false in the file
64
+ print(' %s overwriting --any-allele with value from cfg file %s' % (utils.color('red', 'warning'), args.config_fname))
65
+ args.any_allele = yamlfo['any_allele']
66
+
67
+ info = {}
68
+ lines_skipped, lines_used = 0, 0
69
+ counts_skipped, counts_used = 0, 0
70
+ print(' reading probs from %s' % args.infname)
71
+ with open(args.infname) as csvfile:
72
+ reader = csv.DictReader(csvfile)
73
+ # if args.debug:
74
+ # print ' all columns in file: %s' % ' '.join(reader.fieldnames)
75
+ if len(set(skip_column_vals) - set(reader.fieldnames)) > 0:
76
+ raise Exception('keys in --skip-column-fname not in file: %s' % ' '.join(set(skip_column_vals) - set(reader.fieldnames)))
77
+ for line in reader:
78
+ skip_this_line = False
79
+ for scol, acceptable_values in skip_column_vals.items():
80
+ if not is_acceptable(scol, acceptable_values, line[scol]):
81
+ skip_this_line = True
82
+ lines_skipped += 1
83
+ counts_skipped += int(line['count'])
84
+ break
85
+ if skip_this_line:
86
+ continue
87
+
88
+ if non_summed_column is not None:
89
+ if line[non_summed_column] not in info:
90
+ info[line[non_summed_column]] = 0
91
+ info[line[non_summed_column]] += int(line['count'])
92
+
93
+ lines_used += 1
94
+ counts_used += int(line['count'])
95
+
96
+ # ----------------------------------------------------------------------------------------
97
+ import python.fraction_uncertainty as fraction_uncertainty
98
+ def frac_err(obs, total):
99
+ lo, hi = fraction_uncertainty.err(obs, total)
100
+ return 0.5 * (hi - lo)
101
+ count_fraction = counts_used / float(counts_used + counts_skipped)
102
+
103
+ if args.debug:
104
+ print(' applied restrictions:%s' % (' (including all alleles of these genes)' if args.any_allele else ''))
105
+ for scol, acceptable_values in skip_column_vals.items():
106
+ print(' %15s in %s' % (scol, acceptable_values))
107
+ print(' used:')
108
+ print(' %6d / %-6d = %.3f lines' % (lines_used, lines_used + lines_skipped, lines_used / float(lines_used + lines_skipped)))
109
+ print(' %6d / %-6d = %.3f +/- %.3f counts' % (counts_used, counts_used + counts_skipped, count_fraction, frac_err(counts_used, counts_used + counts_skipped)))
110
+
111
+ if non_summed_column is not None:
112
+ print(' %18s count / %d = fraction' % (non_summed_column, counts_used))
113
+ for val, count in sorted(list(info.items()), key=operator.itemgetter(1), reverse=True): # sort by counts
114
+ # for val, count in sorted(info.items()): # sort by column value (e.g. cdr3 length)
115
+ print(' %18s %6d %.3f +/- %.3f' % (val, count, count / float(counts_used), frac_err(count, counts_used)))
116
+
117
+ if args.outfname is not None:
118
+ if args.debug:
119
+ print(' writing total counts (plus %d info entries) to %s' % (len(info), args.outfname))
120
+ with open(args.outfname, 'w') as outfile:
121
+ yamlfo = {'counts' : counts_used,
122
+ 'total' : counts_used + counts_skipped,
123
+ 'fraction' : count_fraction,
124
+ 'frac_err' : frac_err(counts_used, counts_used + counts_skipped),
125
+ 'info' : info}
126
+ yaml.dump(yamlfo, outfile, width=150)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: partis-bcr
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: B- and T-cell receptor sequence annotation, simulation, clonal family and germline inference
5
5
  Home-page: https://github.com/psathyrella/partis
6
6
  Author: Duncan Ralph
@@ -2,7 +2,7 @@ bin/FastTree,sha256=ztIqHXzUJRUFLo-BEcE97UyityueIJmFPWXFOoJ0l4k,368264
2
2
  bin/add-chimeras.py,sha256=7Zug9D7qtVa2k81kcIGtMKH0BjCmeiq2_rRDjPoiZug,2351
3
3
  bin/add-seqs-to-outputs.py,sha256=O7Mkz7GbkJPNtJ4v1fI1d3XfuYLOxG0ZofG37U_Rjsg,5156
4
4
  bin/bcr-phylo-run.py,sha256=rzafOBrnLlOQWp3XnBHwYqnsVQVThrVLab1K_zgNi-k,54060
5
- bin/build.sh,sha256=s5xo8fe7VuvEh8l4myWdS03r84VDLanzIIDfaQc0BuA,637
5
+ bin/build.sh,sha256=W2U4VNyf3lHTSiwb4K8lI8kFRmiXiTn0EfKpB520_DY,691
6
6
  bin/cf-alleles.py,sha256=HEQzDvXGSIFoWysUQTF5MZwDWIYwfAeXTepOZj8UN6A,4737
7
7
  bin/cf-germlines.py,sha256=AA82Hvy53Qh9czl3gQK6IzZAb8BnqcoUWFbIFuBGyQw,3149
8
8
  bin/cf-linearham.py,sha256=mUp7aEUGLCifUv-jk3GL5IbeUiktJ9f8tFi5h7CRkYc,11603
@@ -21,9 +21,9 @@ bin/lonr.r,sha256=YzAsjyQ-NZ2shOPhzkFFppsgXJEhMTztFA-C6NVyEJ4,44212
21
21
  bin/makeHtml,sha256=SMMulEmYoU4ve4jlCRNaDuw9f4FS4t8g_FGE7z5Ndb4,1190
22
22
  bin/mds-run.py,sha256=rShy5b3oqvFZqhNdVxrtyZ4nw7whK-SKPAYl2thXooU,2003
23
23
  bin/parse-output.py,sha256=_vlx4ptHEoKiztm2RrCMqQZYlFriFmv4Sv17E6M6J9o,19533
24
- bin/partis,sha256=l_rAMqINhxVTAfopFjVWF6BeiYFtCpWEAFoZvtPGE2U,225082
24
+ bin/partis,sha256=sz5w4-MTttkbaerC8FYA7YMiJu_8VBcnXhVsNXitLJE,225069
25
25
  bin/partis-pip,sha256=STJkb_lm94kKxthpmIsMgFIhBWsoOJAtRJZbiFewaAU,5562
26
- bin/partis.py,sha256=l_rAMqINhxVTAfopFjVWF6BeiYFtCpWEAFoZvtPGE2U,225082
26
+ bin/partis.py,sha256=sz5w4-MTttkbaerC8FYA7YMiJu_8VBcnXhVsNXitLJE,225069
27
27
  bin/plot-gl-set-trees.py,sha256=bgWPBmzzlD_YgWl9nQetGdzmgosXDKyVyfvaHs0VvVc,25695
28
28
  bin/plot-hmms.py,sha256=rKhPyWxexMKBt8kmwuM5ebFfFONtREoDlRqs4J52rT8,6560
29
29
  bin/plot-lb-tree.py,sha256=EKvA2DhRL8oAJoUy5FYdQaLyZYL4Ez6_4OpwD3kVbVM,23674
@@ -296,15 +296,18 @@ data/substitution-profiles/GSSPs_for_VH_genes_with_at_least_300_lineages.txt,sha
296
296
  data/substitution-profiles/GSSPs_for_VK_genes_with_at_least_300_lineages.txt,sha256=dyMU1R_Izk_ciOVTNWmGL5SQBIKDCCKpxXM6eTGO4Fg,184138
297
297
  data/substitution-profiles/GSSPs_for_VL_genes_with_at_least_300_lineages.txt,sha256=CMFb1gk8MbfaMb6y0_qLLiKErrx866C2EafKRSPesB4,184979
298
298
  data/substitution-profiles/notes.txt,sha256=-DK9E31YovqOWaXR1Q-LL5KFhjVJcSLwXbriajUq1TA,389
299
+ packages/ham/bcrham,sha256=4NstZkQgqdF1l_tNemIwuzVyN2zOFsw248qPoPldYWg,1194368
299
300
  packages/ig-sw/src/ig_align/ig-sw,sha256=SNFYfMlvX9s9T64t9fYIB5ugD6cZ016SZVbRhrz5Pro,293464
300
- partis_bcr-1.0.2.data/scripts/cf-alleles.py,sha256=AgFLoRfcwuX1InVpE3zG6-unqoCBGOeCTpEl_RZftvU,4723
301
- partis_bcr-1.0.2.data/scripts/cf-germlines.py,sha256=8_bq0ue3ALFQ-a7-ntnhkbCaBdQ5mlWtQgNeQP_TvcQ,3135
302
- partis_bcr-1.0.2.data/scripts/extract-pairing-info.py,sha256=IGPd4jYpsW3IPgsrxTJrk9jAwUKlb45eX3lOpb8Maos,2941
303
- partis_bcr-1.0.2.data/scripts/gctree-run.py,sha256=BfSZCFgqdhUEqVsv2h20o6OkS6KbQgx5tfvBhySRx2U,13950
304
- partis_bcr-1.0.2.data/scripts/parse-output.py,sha256=8nrg0ip3XDhme02NCiI45BrSqCNvbRIbKYErD8STk8U,19519
305
- partis_bcr-1.0.2.data/scripts/split-loci.py,sha256=K0WXQYtloL5rs46mwvEXUecqT3iG8bbqwNOTNwfpvzY,20704
306
- partis_bcr-1.0.2.data/scripts/test.py,sha256=aI-2mLH5WXYVWoS4zateeSgDynDUC_DK9QF-UAEpWms,63609
307
- partis_bcr-1.0.2.dist-info/licenses/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
301
+ partis_bcr-1.0.4.data/scripts/cf-alleles.py,sha256=AgFLoRfcwuX1InVpE3zG6-unqoCBGOeCTpEl_RZftvU,4723
302
+ partis_bcr-1.0.4.data/scripts/cf-germlines.py,sha256=8_bq0ue3ALFQ-a7-ntnhkbCaBdQ5mlWtQgNeQP_TvcQ,3135
303
+ partis_bcr-1.0.4.data/scripts/compare-plotdirs.py,sha256=AABVGUOwlLCijjo-wwDLfLHiq80TrFVm8mE-4Zqn5Sg,16370
304
+ partis_bcr-1.0.4.data/scripts/extract-pairing-info.py,sha256=IGPd4jYpsW3IPgsrxTJrk9jAwUKlb45eX3lOpb8Maos,2941
305
+ partis_bcr-1.0.4.data/scripts/gctree-run.py,sha256=BfSZCFgqdhUEqVsv2h20o6OkS6KbQgx5tfvBhySRx2U,13950
306
+ partis_bcr-1.0.4.data/scripts/get-naive-probabilities.py,sha256=WfuglaedP3PF0wpksGiUSvfAg4wJoNlucTPa1Wl9MDo,6916
307
+ partis_bcr-1.0.4.data/scripts/parse-output.py,sha256=8nrg0ip3XDhme02NCiI45BrSqCNvbRIbKYErD8STk8U,19519
308
+ partis_bcr-1.0.4.data/scripts/split-loci.py,sha256=K0WXQYtloL5rs46mwvEXUecqT3iG8bbqwNOTNwfpvzY,20704
309
+ partis_bcr-1.0.4.data/scripts/test.py,sha256=aI-2mLH5WXYVWoS4zateeSgDynDUC_DK9QF-UAEpWms,63609
310
+ partis_bcr-1.0.4.dist-info/licenses/COPYING,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
308
311
  python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
309
312
  python/alleleclusterer.py,sha256=x50E_lJMlKPc-CPkdkM1qe4AvVi-mkDJ1cBDxs5L-ms,30551
310
313
  python/allelefinder.py,sha256=SgJjopk5ivLOcmTumsWq5PZ05aPZ6zQyjTd1fxeRjaM,80402
@@ -325,7 +328,7 @@ python/hmmwriter.py,sha256=lWC4oW7dvAFj0mmCYCmzV67kuM-zZPSzPXqu0d6o_Ro,55480
325
328
  python/hutils.py,sha256=o3z4LbFbemxiQdBoTJv8YPbnwul0SkdfBl9UXJXOHIE,15684
326
329
  python/indelutils.py,sha256=SlPFQnzPN27VHxvcUXHIgHZtBm6MGfookz6jHDavtm4,45001
327
330
  python/lbplotting.py,sha256=AKQsFJKi_gw7us7ARNwUhVNfHvWbxp1oo4JbJtWORaQ,126486
328
- python/main.py,sha256=HiNELhSRbXoA3qcePoU-BLoAh_aQ8BGLYdtbzvDbzmg,891
331
+ python/main.py,sha256=f_xBE3yxVo7k7CMWkYRD6kHyAeGt4QV75bD_rO-wOyc,994
329
332
  python/mds.py,sha256=58JdN3X11t8yMLeuhpYEqpC6doZQc9m4yLFj4Bm1hHc,15859
330
333
  python/mutefreqer.py,sha256=1xkbi0rnkKbPN-09VB0OXC4MLAMAzwVYsOlQ77NVB9Q,14097
331
334
  python/paircluster.py,sha256=jnIXPtGe2-Z_PP-bYztMh0BuKnoh7P55ixsEkls0xUg,154337
@@ -349,8 +352,8 @@ python/vrc01.py,sha256=f3PpRV9ZOU9kxJCKdoJlP9wvKB7FEgMM8WoNpevsEOE,10813
349
352
  python/waterer.py,sha256=sPBsJchmK2EO3M5UCwZq_S5mRV3eYFuIZoite4Js62g,109955
350
353
  python/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
351
354
  python/cache/cached_uncertainties.py,sha256=GNs18AG8ROdDg0OOrcArsU73Ev1U8k5Gsnf79WndHG8,4257821
352
- partis_bcr-1.0.2.dist-info/METADATA,sha256=OMZO1vX1KCG3OQvWJvnVbsMl_OBJ8aoG4QwTk1CFvxI,4976
353
- partis_bcr-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
354
- partis_bcr-1.0.2.dist-info/entry_points.txt,sha256=Q-AkAQ91YwiKNHruS32ACdY4_IQdpZhpij2EuVDmdnM,44
355
- partis_bcr-1.0.2.dist-info/top_level.txt,sha256=J-z0poNcsv31IHB413--iOY8LoHBKiTHeybHX3abokI,7
356
- partis_bcr-1.0.2.dist-info/RECORD,,
355
+ partis_bcr-1.0.4.dist-info/METADATA,sha256=NdUeCqs9F-GpKyvHaA2DCYr3IAKyQ01GI1qdmjGzqOk,4976
356
+ partis_bcr-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
357
+ partis_bcr-1.0.4.dist-info/entry_points.txt,sha256=Q-AkAQ91YwiKNHruS32ACdY4_IQdpZhpij2EuVDmdnM,44
358
+ partis_bcr-1.0.4.dist-info/top_level.txt,sha256=J-z0poNcsv31IHB413--iOY8LoHBKiTHeybHX3abokI,7
359
+ partis_bcr-1.0.4.dist-info/RECORD,,
python/main.py CHANGED
@@ -20,7 +20,9 @@ def main():
20
20
 
21
21
  if os.path.exists(bin_partis):
22
22
  import subprocess
23
- result = subprocess.run([sys.executable, bin_partis] + sys.argv[1:])
23
+ # If no arguments provided, show help
24
+ args = sys.argv[1:] if len(sys.argv) > 1 else ['--help']
25
+ result = subprocess.run([sys.executable, bin_partis] + args)
24
26
  sys.exit(result.returncode)
25
27
  else:
26
28
  print(f'Error: partis script not found at {bin_partis}')