def contamination_carryover_peaks(self, qlplate, exclude_min_amplitude_peaks=True): """ Returns (aggregate contamination peaks, aggregate gated contamination peaks, aggregate carryover peaks, number of carryover wells, well name->#carryover well peaks per well) """ well_pairs = [] eventful_well = None stealth_well = None # FIXFIX relax when QuantaSoft bug is resolved #for well in qlplate.in_run_order: wells = sorted(qlplate.analyzed_wells.values(), cmp=QLWell.row_order_comparator) for well in sorted(qlplate.analyzed_wells.values(), cmp=QLWell.row_order_comparator): if well.original_sample_name not in self.empty_sample_names: stealth_well = None eventful_well = well elif well.original_sample_name in self.empty_sample_names: stealth_well = well if eventful_well: well_pairs.append((eventful_well, stealth_well)) eventful_well = None stealth_well = None contamination_peaks = np.ndarray([0], dtype=peak_dtype(2)) gated_contamination_peaks = np.ndarray([0], dtype=peak_dtype(2)) carryover_peaks = np.ndarray([0], dtype=peak_dtype(2)) num_wells = 0 carryover_well_peak_dict = dict() for e, s in well_pairs: num_wells = num_wells + 1 stats = e.channels[self.channel_num].statistics threshold = stats.threshold min_width_gate, max_width_gate = well_static_width_gates(e) # TODO: what to do about quality gating? # get all contamination first above 750 RFU (assume threshold above 750?) if exclude_min_amplitude_peaks: peaks = above_min_amplitude_peaks(s) else: peaks = s.peaks well_contamination_peaks, too_low = cluster_1d(peaks, self.channel_num, 750) well_gated_contamination_peaks = width_gated(well_contamination_peaks, min_width_gate=min_width_gate, max_width_gate=max_width_gate, on_channel_num=self.channel_num, ignore_gating_flags=True) if threshold: well_carryover_peaks, under_threshold = cluster_1d(well_gated_contamination_peaks, self.channel_num, threshold) else: well_carryover_peaks = np.ndarray([0], dtype=peak_dtype(2)) contamination_peaks = np.hstack([contamination_peaks, well_contamination_peaks]) gated_contamination_peaks = np.hstack([gated_contamination_peaks, well_gated_contamination_peaks]) carryover_peaks = np.hstack([carryover_peaks, well_carryover_peaks]) carryover_well_peak_dict[s.name] = len(well_carryover_peaks) return contamination_peaks, gated_contamination_peaks, carryover_peaks, num_wells, carryover_well_peak_dict
def gap_rain(qlwell, channel_num=0, threshold=None, pct_boundary=0.3, gap_size=10000): """ Return the rain in the gaps between non-rain droplets. """ rain, nonrain = rain_split(qlwell, channel_num=channel_num, threshold=threshold, pct_boundary=pct_boundary) # ok, now identify the gaps in the gates. times = peak_times(nonrain) if nonrain is None or len(nonrain) < 2: return np.ndarray([0],dtype=peak_dtype(2)) intervals = np.ediff1d(times, to_begin=0, to_end=0) big_intervals = intervals > gap_size # find beginning of gaps with extract beginnings = np.extract(big_intervals[1:], times) ends = np.extract(big_intervals[:-1], times) gap_intervals = zip(beginnings, ends) gap_intervals.insert(0, (0, times[0])) gap_intervals.append((times[-1], times[-1]*100)) # count the rain in the intervals gap_drops = np.extract(reduce(np.logical_or, [np.logical_and(peak_times(rain) > b, peak_times(rain) < e) for b, e in gap_intervals]), rain) return gap_drops
def revb_extracluster_peaks(well, channel_num, threshold=None, pct_boundary=0.3, exclude_min_amplitude_peaks=True): """ Return the peaks that are outside the clusters. A superset of polydispersity peaks, meant primarily for dye wells, where there should be no biological basis for rain. Returns a 3-tuple: peaks, rain gates, width gates """ if not threshold: threshold = well.channels[channel_num].statistics.threshold if not threshold: threshold = None if exclude_min_amplitude_peaks: peaks = above_min_amplitude_peaks(well) else: peaks = well.peaks # get rain_pvalues p_plus, p, p_minus, pos, middle_high, middle_low, neg = \ rain_pvalues_thresholds(peaks, channel_num=channel_num, threshold=threshold, pct_boundary=pct_boundary) binned_peaks = bin_peaks_by_amplitude(peaks, well.sum_amplitude_bins) extra_peaks = np.ndarray([0], dtype=peak_dtype(2)) for bin, (min_gate, max_gate, boundary) in zip(binned_peaks, well.sum_amplitude_bins): if middle_high and middle_low: extra_peaks = np.hstack([extra_peaks, np.extract(np.logical_not( np.logical_or( reduce(np.logical_and, (channel_widths(bin, channel_num) > min_gate, channel_widths(bin, channel_num) < max_gate, channel_amplitudes(bin, channel_num) > middle_high, channel_amplitudes(bin, channel_num) < pos)), reduce(np.logical_and, (channel_widths(bin, channel_num) > min_gate, channel_widths(bin, channel_num) < max_gate, channel_amplitudes(bin, channel_num) > neg, channel_amplitudes(bin, channel_num) < middle_low)) ) ), bin)]) else: extra_peaks = np.hstack([extra_peaks, np.extract(np.logical_not( reduce(np.logical_and, (channel_widths(bin, channel_num) > min_gate, channel_widths(bin, channel_num) < max_gate, channel_amplitudes(bin, channel_num) > neg, channel_amplitudes(bin, channel_num) < pos) ) ), bin)]) return (extra_peaks, (pos, middle_high, middle_low, neg), (np.mean(fam_amplitudes(peaks)), np.mean(vic_amplitudes(peaks))))
def gap_air(qlwell, channel_num=0, threshold=None, pct_boundary=0.3, gap_size=10000, gap_buffer=250, max_amp=1000): """ Return the air (gap rain < max_amp) in the gaps between non-rain droplets. :param channel_num: The channel on which to detect air droplets. :param threshold: The threshold dividing positives and negatives (used to detect 'rain') :param pct_boundary: The percentage outside of which a droplet is classified as rain. :param gap_size: The minimum size (in samples) of a gap. Default 0.1s. :param gap_buffer: The distance a droplet must be from the main population to be considered an air droplet, in samples. Default 0.0025s. :param max_amp: The maximum color-corrected amplitude of an air droplet. Default 1000 RFU. """ rain, nonrain = rain_split(qlwell, channel_num=channel_num, threshold=threshold, pct_boundary=pct_boundary, split_all_peaks=True) low_amp = np.extract(channel_amplitudes(rain, channel_num) < max_amp, rain) times = peak_times(nonrain) if nonrain is None or len(nonrain) < 2: return np.ndarray([0], dtype=peak_dtype(2)) intervals = np.ediff1d(times, to_begin=0, to_end=0) big_intervals = intervals > gap_size # find beginning of gaps with extract beginnings = [b+gap_buffer for b in np.extract(big_intervals[1:], times)] ends = [e-gap_buffer for e in np.extract(big_intervals[:-1], times)] gap_intervals = zip(beginnings, ends) gap_intervals.insert(0, (0, times[0]-gap_buffer)) gap_intervals.append((times[-1]+gap_buffer, times[-1]*100)) # count the rain in the intervals gap_drops = np.extract(reduce(np.logical_or, [np.logical_and(peak_times(low_amp) > b, peak_times(low_amp) < e) for b, e in gap_intervals]), low_amp) return gap_drops
def contamination_carryover_peaks(self, qlplate, exclude_min_amplitude_peaks=True): """ For now, just count FAM HI carryover """ carryover_upper_bound = None carryover_lower_bound = None min_width_gate = None max_width_gate = None contamination_peaks = np.ndarray([0], dtype=peak_dtype(2)) gated_contamination_peaks = np.ndarray([0], dtype=peak_dtype(2)) carryover_peaks = np.ndarray([0], dtype=peak_dtype(2)) num_wells = 0 carryover_well_peak_dict = dict() fam_bounds = [] vic_bounds = [] # TODO fixfix when QS bug resolved #for idx, well in enumerate(qlplate.in_run_order): for idx, well in enumerate(sorted(qlplate.analyzed_wells.values(), cmp=QLWell.row_order_comparator)): if exclude_min_amplitude_peaks: peaks = above_min_amplitude_peaks(well) else: peaks = well.peaks if well.original_sample_name not in ('FAM HI', 'FAM 350nM'): num_wells = num_wells+1 for bounds, channel in zip((fam_bounds, vic_bounds),(0,1)): for lower_bound, upper_bound, min_width_gate, max_width_gate in bounds: # TODO: this will double-count contamination if the bounds overlap. But if the bounds # overlap, you have much bigger problems than carryover. OK for now. argument_bounds = [(None, None),(None, None)] argument_bounds[channel] = (lower_bound, upper_bound) well_contamination_peaks = filter_amplitude_range(peaks, argument_bounds) well_carryover_peaks = width_gated(well_contamination_peaks, min_width_gate=min_width_gate, max_width_gate=max_width_gate, on_channel_num=channel, ignore_gating_flags=True) if well.name not in carryover_well_peak_dict: carryover_well_peak_dict[well.name] = len(well_carryover_peaks) else: carryover_well_peak_dict[well.name] = carryover_well_peak_dict[well.name] + len(well_carryover_peaks) contamination_peaks = np.hstack([contamination_peaks, well_contamination_peaks]) carryover_peaks = np.hstack([carryover_peaks, well_carryover_peaks]) if well.original_sample_name in ('FAM HI', 'FAM 350nM', 'FAM LO', 'FAM 40nM', 'VIC HI', 'VIC 350nM'): if well.original_sample_name.startswith('VIC'): add_to = vic_bounds amps = vic_amplitudes(peaks) else: add_to = fam_bounds amps = fam_amplitudes(peaks) min_width_gate, max_width_gate = well_static_width_gates(well) mean = np.mean(amps) std = np.std(amps) lower_bound = mean - 3*std upper_bound = mean + 3*std add_to.append((lower_bound, upper_bound, min_width_gate, max_width_gate)) return contamination_peaks, gated_contamination_peaks, carryover_peaks, num_wells, carryover_well_peak_dict
def revb_polydisperse_peaks(well, channel_num, threshold=None, pct_boundary=0.3, exclude_min_amplitude_peaks=True): """ Computes polydispersity for a well which has amplitude bins defined. Returns a 3-tuple (4-tuple, 4-tuple, 2-tuple). The first 4-tuple is: * positive droplets, with widths above the width gate set for that droplet's amplitude bin. * middle rain, with widths above the bin width gate. * middle rain, with width below the bin width gate. * negative rain, with width below the bin width gate. The second 4-tuple is: * positive rain boundary * middle rain upper boundary (can be None) * middle rain lower boundary (can be None) * negative rain boundary The third 2-tuple is: * mean FAM amplitude * mean VIC amplitude This is for being able to draw approximate single-channel polydispersity graphs down the line (this does beg the question, is there a better 2D definition of polydispersity?) Will raise an error if amplitude bins are not defined on the well. """ if not hasattr(well, 'sum_amplitude_bins') or len(well.sum_amplitude_bins) == 0: raise ValueError("No amplitude bins for this well.") if not threshold: threshold = well.channels[channel_num].statistics.threshold if not threshold: threshold = None if exclude_min_amplitude_peaks: peaks = above_min_amplitude_peaks(well) else: peaks = well.peaks p_plus, p, p_minus, pos, middle_high, middle_low, neg = \ rain_pvalues_thresholds(peaks, channel_num=channel_num, threshold=threshold, pct_boundary=pct_boundary) binned_peaks = bin_peaks_by_amplitude(peaks, well.sum_amplitude_bins) pos_peaks = np.ndarray([0], dtype=peak_dtype(2)) midhigh_peaks = np.ndarray([0], dtype=peak_dtype(2)) midlow_peaks = np.ndarray([0], dtype=peak_dtype(2)) neg_peaks = np.ndarray([0], dtype=peak_dtype(2)) for bin, (min_gate, max_gate, boundary) in zip(binned_peaks, well.sum_amplitude_bins): pos_peaks = np.hstack([pos_peaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) > pos)), bin)]) if middle_high and middle_low: midhigh_peaks = np.hstack([midhigh_peaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, reduce(np.logical_and, (channel_amplitudes(bin, channel_num) < middle_high, channel_amplitudes(bin, channel_num) > middle_low)))), bin)]) midlow_peaks = np.hstack([midlow_peaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, reduce(np.logical_and, (channel_amplitudes(bin, channel_num) < middle_high, channel_amplitudes(bin, channel_num) > middle_low)))), bin)]) neg_peaks = np.hstack([neg_peaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) < neg)), bin)]) return ((pos_peaks, midhigh_peaks, midlow_peaks, neg_peaks), (pos, middle_high, middle_low, neg), (np.mean(fam_amplitudes(peaks)), np.mean(vic_amplitudes(peaks))))
def revb_extracluster_peaks_by_region(well, channel_num, threshold=None, pct_boundary=0.3, exclude_min_amplitude_peaks=True): """ Return the peaks that are not desired (outside clusters) and separate them by region. The region order is: -- positive large peaks -- positive rain -- positive small peaks -- positive wide peaks (directly above positive cluster) -- positive narrow peaks (directly below positive cluster) -- middle large peaks -- middle rain -- middle small peaks -- negative large peaks -- negative rain -- negative small peaks -- negative wide peaks (directly above positive cluster) -- negative narrow peaks (directly below positive cluster) Returns this 9-tuple, then rain gates, then mean of FAM and VIC. """ extra_peaks, rain_gates, means = \ revb_extracluster_peaks(well, channel_num, threshold=threshold, pct_boundary=pct_boundary, exclude_min_amplitude_peaks=exclude_min_amplitude_peaks) pos_gate, midhigh_gate, midlow_gate, neg_gate = rain_gates binned_peaks = bin_peaks_by_amplitude(extra_peaks, well.sum_amplitude_bins) plpeaks = np.ndarray([0], dtype=peak_dtype(2)) prpeaks = np.ndarray([0], dtype=peak_dtype(2)) pspeaks = np.ndarray([0], dtype=peak_dtype(2)) pwpeaks = np.ndarray([0], dtype=peak_dtype(2)) pnpeaks = np.ndarray([0], dtype=peak_dtype(2)) mlpeaks = np.ndarray([0], dtype=peak_dtype(2)) mrpeaks = np.ndarray([0], dtype=peak_dtype(2)) mspeaks = np.ndarray([0], dtype=peak_dtype(2)) nlpeaks = np.ndarray([0], dtype=peak_dtype(2)) nrpeaks = np.ndarray([0], dtype=peak_dtype(2)) nspeaks = np.ndarray([0], dtype=peak_dtype(2)) nwpeaks = np.ndarray([0], dtype=peak_dtype(2)) nnpeaks = np.ndarray([0], dtype=peak_dtype(2)) for bin, (min_gate, max_gate, boundary) in zip(binned_peaks, well.sum_amplitude_bins): plpeaks = np.hstack([plpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) > pos_gate) ), bin)]) prpeaks = np.hstack([prpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) >= min_gate, channel_widths(bin, channel_num) <= max_gate, channel_amplitudes(bin, channel_num) > pos_gate) ), bin)]) pspeaks = np.hstack([pspeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) > pos_gate) ), bin)]) if midhigh_gate and midlow_gate: mlpeaks = np.hstack([mlpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) < midhigh_gate, channel_amplitudes(bin, channel_num) > midlow_gate) ), bin)]) mrpeaks = np.hstack([mrpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) >= min_gate, channel_widths(bin, channel_num) <= max_gate, channel_amplitudes(bin, channel_num) < midhigh_gate, channel_amplitudes(bin, channel_num) > midlow_gate) ), bin)]) mspeaks = np.hstack([mspeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) < midhigh_gate, channel_amplitudes(bin, channel_num) > midlow_gate) ), bin)]) # this means there are positives pwpeaks = np.hstack([pwpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) >= midhigh_gate, channel_amplitudes(bin, channel_num) <= pos_gate) ), bin)]) pnpeaks = np.hstack([pnpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) >= midhigh_gate, channel_amplitudes(bin, channel_num) <= pos_gate) ), bin)]) nwpeaks = np.hstack([nwpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) >= neg_gate, channel_amplitudes(bin, channel_num) <= midlow_gate) ), bin)]) nnpeaks = np.hstack([nnpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) >= neg_gate, channel_amplitudes(bin, channel_num) <= midlow_gate) ), bin)]) else: nwpeaks = np.hstack([nwpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) >= neg_gate, channel_amplitudes(bin, channel_num) <= pos_gate) ), bin)]) nnpeaks = np.hstack([nnpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) >= neg_gate, channel_amplitudes(bin, channel_num) <= pos_gate) ), bin)]) nlpeaks = np.hstack([nlpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) > max_gate, channel_amplitudes(bin, channel_num) < neg_gate) ), bin)]) nrpeaks = np.hstack([nrpeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) >= min_gate, channel_widths(bin, channel_num) <= max_gate, channel_amplitudes(bin, channel_num) < neg_gate) ), bin)]) pbpeaks = np.hstack([pspeaks, np.extract( reduce(np.logical_and, (channel_widths(bin, channel_num) < min_gate, channel_amplitudes(bin, channel_num) >= midhigh_gate, channel_amplitudes(bin, channel_num) <= pos_gate) ), bin)]) return ((plpeaks, prpeaks, pspeaks, pwpeaks, pnpeaks, mlpeaks, mrpeaks, mspeaks, nlpeaks, nrpeaks, nspeaks, nwpeaks, nnpeaks), rain_gates, means)
def polydisperse_peaks(well, channel_num, threshold=None, pct_boundary=0.3, exclude_min_amplitude_peaks=True): """ Returns a 3-tuple (4-tuple, 4-tuple, 2-tuple). The first 4-tuple is: * positive rain above the width gates. * middle rain above the width gates. * middle rain below the width gates. * negative rain below the width gates. The second 4-tuple is: * positive rain boundary * middle rain upper boundary (can be None) * middle rain lower boundary (can be None) * negative rain boundary The last 2-tuple is: * computed min width gate * computed max width gate Positives & negatives are computed on the specified channel number. """ if not threshold: threshold = well.channels[channel_num].statistics.threshold if not threshold: threshold = None # filter out min_amplitude_peaks if exclude_min_amplitude_peaks: peaks = above_min_amplitude_peaks(well) else: peaks = well.peaks p_plus, p, p_minus, pos, middle_high, middle_low, neg = \ rain_pvalues_thresholds(peaks, channel_num=channel_num, threshold=threshold, pct_boundary=pct_boundary) min_gate, max_gate = well_static_width_gates(well) pos_peaks = np.extract( reduce(np.logical_and, (channel_widths(peaks, channel_num) > max_gate, channel_amplitudes(peaks, channel_num) > pos)), peaks) if middle_high and middle_low: midhigh_peaks = np.extract( reduce(np.logical_and, (channel_widths(peaks, channel_num) > max_gate, reduce(np.logical_and, (channel_amplitudes(peaks, channel_num) < middle_high, channel_amplitudes(peaks, channel_num) > middle_low)))), peaks) midlow_peaks = np.extract( reduce(np.logical_and, (channel_widths(peaks, channel_num) < min_gate, reduce(np.logical_and, (channel_amplitudes(peaks, channel_num) < middle_high, channel_amplitudes(peaks, channel_num) > middle_low)))), peaks) else: midhigh_peaks = np.ndarray([0],dtype=peak_dtype(2)) midlow_peaks = np.ndarray([0],dtype=peak_dtype(2)) neg_peaks = np.extract( reduce(np.logical_and, (channel_widths(peaks, channel_num) < min_gate, channel_amplitudes(peaks, channel_num) < neg)), peaks) return ((pos_peaks, midhigh_peaks, midlow_peaks, neg_peaks), (pos, middle_high, middle_low, neg), (min_gate, max_gate))