Coverage for /home/kale/research/software/libraries/fcmcmp/fcmcmp/experiments.py : 56%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python3
else:
else:
# Find the *.fcs data files relevant to this experiment. If there is a # document with a mapping called "plates:", treat the values as paths to # data directories and the keys as names that can refer to the directories # in the rest of the file. If there is a document with an assignment # called "plate:", treat it as the path to the only data directory that # will be used in the rest of the file. If no data directory is specified # by either of these two mechanisms, try to infer a path from the name of # the YAML file itself.
else:
# Construct and fill in a list of experiments. Well names are converted # into paths based on the user-given glob pattern, then parsed and stored # as pandas data frames. Note that if a well is referenced more than once, # it will also be parsed more than once. This guarantees that each well # can be processed independently, which is important for many workflows.
# Short-circuit the case where the well has already been loaded, which # is triggered by the "from" external reference machinery.
# Parse well and plate names from the given label. The plate name is # optional, because often there is only one.
# Find the *.fcs file referenced by the given label.
"Plate '{}' not defined.".format(plate) if plate is not None else "No default plate defined.")
# Load the cell data for the given well.
# Reference experiments from other files if the special "from" keyword # is present.
config_path.parent / experiment['from'], experiment['label'])
# Make sure each document has a label and a list of wells. Other key- # value pairs can be present but are not required.
# Set the well data for the comparison. This requires converting the # well names we were given into paths and parsing those files.
plate=None, plates=None, plate_order=None, layout='col/row/plate', **extra_params):
header = [] script_name = Path(sys.argv[0]).stem
# If the user didn't specify any plates: if plate is None and plates is None: plates = {None: ''}
# If the user specified a single plate: elif plate is not None and plates is None: header.append({'plate': plate.replace('$', script_name)}) plates = {None: plate}
# If the user specifies multiple plates: elif plate is None and plates is not None: header.append({'plates': { k: v.replace('$', script_name) for k, v in plates.items()}})
else: raise UsageError("cannot specify both 'plate' and 'plates'")
# Set the order in which the plates should be considered. The default # order is alphabetical.
if plate_order is None: plate_order = natsorted(plates)
# Understand how the plates are indexed, i.e. do the indices increment by # column then row then plate, or by column then plate then row, etc.
steps = layout.split('/')
if len(steps) == 2: steps.append('plate') if set(steps) != {'row', 'col', 'plate'}: raise UsageError("invalid layout: '{}'".format(layout))
strides = { 'col': 12, 'row': 8, 'plate': len(plates), } divisors = { steps[0]: 1, steps[1]: strides[steps[0]], steps[2]: strides[steps[0]] * strides[steps[1]], }
# Create experiments by iterating through each well and associating a # labels and a condition with each one.
experiments = []
for i in range(96 * len(plates)):
# Figure out which row, column, and plate this index refers to.
row = (i // divisors['row']) % strides['row'] col = (i // divisors['col']) % strides['col'] plate = plate_order[(i // divisors['plate']) % strides['plate']]
# Get the experiment and condition to associate with this well from the # user. Skip this well if define_well() returns None.
well = WellCursor96(i, row, col, plate) definition = define_well(well)
if definition is None: continue
label, condition = definition
# If an experiment with this label already exists, find it. # Otherwise create an empty experiment data structure and add # it to the list of experiments.
try: experiment = next( expt for expt in experiments if expt['label'] == label)
except StopIteration: experiment = extra_params.copy() experiment['label'] = label experiment['wells'] = {} experiments.append(experiment)
# Associate this well with the given condition.
experiment['wells'].setdefault(condition, []).append(str(well))
# Allow the user to add custom parameters to each experiment.
for experiment in experiments: define_experiment(experiment)
# Export the experiments to YAML and either print them to stdout or save # them to a file, depending on the command-line arguments.
output_path = script_name + '.yml'
import docopt args = docopt.docopt("""\ Usage: {script_name}.py [-o]
Options: -o --output Save the experimental layout to ``{output_path}`` """.format(**locals()))
# I wanted to use yaml.dump_all() here, but yaml.dump() has a better # default indentation algorithm.
dump_config = lambda **kwargs: '---\n'.join( yaml.dump(x, **kwargs) for x in header + experiments)
if args['--output']: with open(output_path, 'w') as file: file.write(dump_config()) else: print(dump_config())
self._index = index self._row = row self._col = col self._plate = plate
return self.label
try: return (self.index, self.row, self.col, self.plate) == \ (other.index, other.row, other.col, other.plate)
except AttributeError: other_plate, other_well = parse_well_label(str(other)) other_well_match = re.match('([A-H])([0-9]{1,2})', other_well)
if not other_well_match: raise UsageError("can't compare {} to {}".format(other, self))
other_row = list('ABCDEFGH').index(other_well_match.group(1)) other_col = int(other_well_match.group(2)) - 1
return (self.row, self.col, self.plate) == \ (other_row, other_col, other_plate)
def index(self): return self._index
def row(self): return self._row
def col(self): return self._col
def plate(self): return self._plate
def label(self): row = 'ABCDEFGH'[self.row] col = self.col + 1 label = '{}{:02d}'.format(row, col)
if self.plate: label = '{}/{}'.format(self.plate, label)
return label
""" Indicate errors caused by invalid user input. """
|