def compute(self, qlwell, qlwell_channel, well_channel_metric): # only return for >= 1000 peaks if len(qlwell.peaks) < 1000: return well_channel_metric data = polydisperse_peaks(qlwell, qlwell_channel.channel_num, threshold=qlwell_channel.statistics.threshold) peaksets, rain_gates, width_gates = data poly_peaks = sum([len(p) for p in peaksets]) # have to worry about min amplitude peaks here # found in well_metric but we don't have access in superclass above_min_amp_peaks = above_min_amplitude_peaks(qlwell) if len(above_min_amp_peaks) == 0: well_channel_metric.polydispersity = 0 else: well_channel_metric.polydispersity = float(poly_peaks)/len(above_min_amp_peaks) if hasattr(qlwell, 'sum_amplitude_bins') and len(qlwell.sum_amplitude_bins) > 0: data = revb_polydisperse_peaks(qlwell, qlwell_channel.channel_num, threshold=qlwell_channel.statistics.threshold) peaksets, rain_gates, min_amps = data poly_peaks = sum([len(p) for p in peaksets]) if len(above_min_amp_peaks) == 0: well_channel_metric.revb_polydispersity = 0 else: well_channel_metric.revb_polydispersity = float(poly_peaks)/len(above_min_amp_peaks) else: well_channel_metric.revb_polydispersity = None return well_channel_metric
def galaxy_disperse_revb(self, id=None, *args, **kwargs): from pyqlb.nstats.well import above_min_amplitude_peaks from qtools.lib.mplot import galaxy_polydisperse_revb, cleanup, render as plt_render, empty_fig from qtools.lib.nstats.peaks import revb_polydisperse_peaks response.content_type = 'image/png' qlwell = self.__qlwell_from_threshold_form(id) self.__set_threshold_context(qlwell) channel_idx = int(request.params.get("channel", 0)) peaks = above_min_amplitude_peaks(qlwell) threshold = c.vic_threshold if channel_idx == 1 else c.fam_threshold title = 'GalaxyPDB - %s, %s, %s' % (c.well.plate.plate.name, c.well.well_name, 'VIC' if channel_idx == 1 else 'FAM') if hasattr(qlwell, 'sum_amplitude_bins') and len(qlwell.sum_amplitude_bins) > 0: polydisperse_data = revb_polydisperse_peaks(qlwell, channel_idx, threshold=threshold) poly_peaks, rain_boundaries, mean_amplitudes = polydisperse_data pos_peaks, midhigh_peaks, midlow_peaks, neg_peaks = poly_peaks pos, midhigh, midlow, neg = rain_boundaries fam_mean, vic_mean = mean_amplitudes fig = galaxy_polydisperse_revb(title, peaks, channel_idx, threshold, pos, midhigh, midlow, neg, pos_peaks, midhigh_peaks, midlow_peaks, neg_peaks, min_amplitude_excluded=True, sum_amplitude_bins=qlwell.sum_amplitude_bins, other_channel_mean=fam_mean if channel_idx == 1 else vic_mean) else: fig = empty_fig() ax = fig.add_subplot(111, title=title) ax.text(0.33,0.5, "No amplitude bins for this well.") imgdata = plt_render(fig, dpi=72) cleanup(fig) return imgdata
def galaxy_extra(self, id=None, *args, **kwargs): from pyqlb.nstats.well import above_min_amplitude_peaks from qtools.lib.mplot import galaxy_extracluster, cleanup, render as plt_render from qtools.lib.nstats.peaks import extracluster_peaks response.content_type = 'image/png' qlwell = self.__qlwell_from_threshold_form(id) self.__set_threshold_context(qlwell) channel_idx = int(request.params.get("channel", 0)) peaks = above_min_amplitude_peaks(qlwell) threshold = c.vic_threshold if channel_idx == 1 else c.fam_threshold extra_data = extracluster_peaks(qlwell, channel_idx, threshold=threshold) extra_peaks, rain_boundaries, width_gates = extra_data pos, midhigh, midlow, neg = rain_boundaries min_gate, max_gate = width_gates title = 'GalaxyEX - %s, %s, %s' % (c.well.plate.plate.name, c.well.well_name, 'VIC' if channel_idx == 1 else 'FAM') fig = galaxy_extracluster(title, peaks, channel_idx, threshold, min_gate, max_gate, pos, midhigh, midlow, neg, extra_peaks, min_amplitude_excluded=True) imgdata = plt_render(fig, dpi=72) cleanup(fig) return imgdata
def compute(self, qlwell, qlwell_channel, well_channel_metric): # only return for >= 1000 peaks if len(qlwell.peaks) < 1000: return well_channel_metric data = extracluster_peaks(qlwell, qlwell_channel.channel_num, threshold=qlwell_channel.statistics.threshold) peaks, rain_gates, width_gates = data # have to worry about min amplitude peaks here # found in well_metric but don't have access to wm in compute() above_min_amp_peaks = above_min_amplitude_peaks(qlwell) if len(above_min_amp_peaks) == 0: well_channel_metric.extracluster = 0 else: well_channel_metric.extracluster = float(len(peaks))/len(above_min_amp_peaks) if hasattr(qlwell, 'sum_amplitude_bins') and len(qlwell.sum_amplitude_bins) > 0: data = revb_extracluster_peaks(qlwell, qlwell_channel.channel_num, threshold=qlwell_channel.statistics.threshold) peaks, rain_gates, mean_amps = data if len(above_min_amp_peaks) == 0: well_channel_metric.revb_extracluster = 0 else: well_channel_metric.revb_extracluster = float(len(peaks))/len(above_min_amp_peaks) else: well_channel_metric.revb_extracluster = None return well_channel_metric
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 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 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) min_gate, max_gate = well_static_width_gates(well) if middle_high and middle_low: extracluster_peaks = np.extract(np.logical_not( np.logical_or( reduce(np.logical_and, (channel_widths(peaks, channel_num) > min_gate, channel_widths(peaks, channel_num) < max_gate, channel_amplitudes(peaks, channel_num) > middle_high, channel_amplitudes(peaks, channel_num) < pos)), reduce(np.logical_and, (channel_widths(peaks, channel_num) > min_gate, channel_widths(peaks, channel_num) < max_gate, channel_amplitudes(peaks, channel_num) > neg, channel_amplitudes(peaks, channel_num) < middle_low)) ) ), peaks) else: extracluster_peaks = np.extract(np.logical_not( reduce(np.logical_and, (channel_widths(peaks, channel_num) > min_gate, channel_widths(peaks, channel_num) < max_gate, channel_amplitudes(peaks, channel_num) > neg, channel_amplitudes(peaks, channel_num) < pos) ) ), peaks) return (extracluster_peaks, (pos, middle_high, middle_low, neg), (min_gate, max_gate))
def nds(self, id=None, *args, **kwargs): from pyqlb.nstats.well import above_min_amplitude_peaks, NARROW_NORMALIZED_DROPLET_SPACING from qtools.lib.mplot import nds, cleanup, render as plt_render qlwell = self.__qlwell_from_threshold_form(id) title = 'NDS Histogram - %s - %s' % (c.well.plate.plate.name, c.well.well_name) ok_peaks = above_min_amplitude_peaks(qlwell) fig = nds(title, ok_peaks, NARROW_NORMALIZED_DROPLET_SPACING) response.content_type = 'image/png' imgdata = plt_render(fig, dpi=72) cleanup(fig) return imgdata
def temporal_galaxy(self, id=None, channel_num=0, *args, **kwargs): from qtools.lib.nstats.peaks import above_min_amplitude_peaks from pyqlb.nstats.peaks import peak_times, channel_amplitudes, channel_widths qlwell = self.__qlwell_from_threshold_form(id) self.__set_threshold_context(qlwell) c.channel_num = int(channel_num) ok_peaks = above_min_amplitude_peaks(qlwell) c.taw = zip(peak_times(ok_peaks), channel_amplitudes(ok_peaks, c.channel_num), channel_widths(ok_peaks, c.channel_num)) if c.channel_num == 0: c.channel_name = 'FAM' else: c.channel_name = 'VIC' return render('/well/temporal_galaxy.html')
def _compute_well_metrics(qlwell, well_metric): """ Populate the well_metric object with statistics derived from the QLWell object. This computes well-specific metrics, which are channel-agnostic, such as width, number of accepted peaks, and B-score. :param qlwell: QLWell record read from QLP. :param well_metric: WellMetric object meant to be stored. :return: The WellMetric object, though it gets side-effected. """ # assume that width, events are same in both channels; so the width # and quality gates should be OK accepted_events = accepted_peaks(qlwell) wm = well_metric # for convenience wm.accepted_event_count = len(accepted_events) wm.total_event_count = len(qlwell.peaks) # TODO: double check on desired values for these (width of accepted, or overall width?) # current hunch is to use the width of all events width_peaks = above_min_amplitude_peaks(qlwell) wm.width_mean = np.mean(fam_widths(width_peaks)) wm.width_variance = np.std(fam_widths(width_peaks)) wm.accepted_width_mean = np.mean(fam_widths(accepted_events)) wm.accepted_width_stdev = np.std(fam_widths(accepted_events)) wm.rejected_peaks = qlwell.statistics.rejected_peaks wm.min_amplitude_peaks = len(min_amplitude_peaks(qlwell)) wm.vertical_streak_events = qlwell.statistics.vertical_streak_peaks wm.sum_baseline_mean = qlwell.statistics.sum_baseline_mean wm.sum_baseline_stdev = qlwell.statistics.sum_baseline_stdev wm.short_interval_count = narrow_droplet_spacing_count(qlwell, threshold=NARROW_NORMALIZED_DROPLET_SPACING) wm.short_interval_threshold = NARROW_NORMALIZED_DROPLET_SPACING wm.balance_score = well_balance_score_2d(qlwell)[0] wm.fragmentation_probability = well_fragmentation_probability(qlwell)[0] #new droplet metrics -> Diagonal scatter.... NEW_DROPLET_CLUSTER_WELL_METRICS_CALCULATOR.compute(qlwell, wm) global DEFAULT_CNV_CALC DEFAULT_CNV_CALC.compute(qlwell, wm) global DEFAULT_NULL_LINKAGE_CALC DEFAULT_NULL_LINKAGE_CALC.compute(qlwell, wm) return wm
def widthbin(self, id=None, *args, **kwargs): from qtools.lib.nstats.peaks import above_min_amplitude_peaks from qtools.lib.mplot import plot_cluster_widthbins, cleanup, render as plt_render qlwell = self.__qlwell_from_threshold_form(id) title = 'Width Bins - %s, %s' % (c.well.plate.plate.name, c.well.well_name) min_gate = qlwell.channels[0].statistics.min_width_gate max_gate = qlwell.channels[0].statistics.max_width_gate peaks = above_min_amplitude_peaks(qlwell) fam_threshold = qlwell.channels[0].statistics.threshold vic_threshold = qlwell.channels[1].statistics.threshold fig = plot_cluster_widthbins(title, peaks, qlwell.sum_amplitude_bins, min_gate, max_gate, fam_threshold=fam_threshold, vic_threshold=vic_threshold) response.content_type = 'image/png' imgdata = plt_render(fig, dpi=72) cleanup(fig) return imgdata
def galaxy(self, id=None, channel_num=0, *args, **kwargs): from qtools.lib.nstats.peaks import above_min_amplitude_peaks from pyqlb.nstats.well import well_static_width_gates qlwell = self.__qlwell_from_threshold_form(id) self.__set_threshold_context(qlwell) channel_idx = int(request.params.get("channel", 0)) from qtools.lib.mplot import galaxy, cleanup, render as plt_render peaks = above_min_amplitude_peaks(qlwell) title = 'Galaxy Lite - %s - %s, %s' % (c.well.plate.plate.name, c.well.well_name, 'VIC' if channel_idx == 1 else 'FAM') threshold = c.vic_threshold if channel_idx == 1 else c.fam_threshold min_width_gate, max_width_gate = well_static_width_gates(qlwell) fig = galaxy(title, peaks, threshold, min_width_gate, max_width_gate, channel_idx) response.content_type = 'image/png' imgdata = plt_render(fig, dpi=72) cleanup(fig) return imgdata
def galaxy_extra_region(self, id=None, *args, **kwargs): from pyqlb.nstats.well import above_min_amplitude_peaks from qtools.lib.mplot import graph_extracluster_by_region, cleanup, render as plt_render from qtools.lib.nstats.peaks import revb_extracluster_peaks_by_region response.content_type = 'image/png' qlwell = self.__qlwell_from_threshold_form(id) self.__set_threshold_context(qlwell) channel_idx = int(request.params.get("channel", 0)) peaks = above_min_amplitude_peaks(qlwell) threshold = c.vic_threshold if channel_idx == 1 else c.fam_threshold extra_data = revb_extracluster_peaks_by_region(qlwell, channel_idx, threshold=threshold) peaks_by_region, rain_gates, means = extra_data title = "GalaxyEXR - %s, %s, %s" % (c.well.plate.plate.name, c.well.well_name, 'VIC' if channel_idx == 1 else 'FAM') fig = graph_extracluster_by_region(title, peaks, channel_idx, threshold, peaks_by_region, rain_gates, sum_amplitude_bins=qlwell.sum_amplitude_bins, other_channel_mean=means[channel_idx]) imgdata = plt_render(fig, dpi=72) cleanup(fig) return imgdata
def stats_for_qlp_well(well, compute_clusters=False, override_thresholds=None): """ Return statistics about a QLWell object read from a QLP file. The QLWell object should have a populated `peaks` attribute (reading from QLBs won't work) For parameter explanations and return values, see :func:`stats_for_qlp_well`. """ from pyqlb.nstats.peaks import cluster_1d, channel_amplitudes from pyqlb.nstats.well import accepted_peaks, above_min_amplitude_peaks, well_channel_sp_values, well_cluster_peaks from pyqlb.nstats.well import well_observed_positives_negatives, well_s2d_values, getClusters from pyqlb.nstats.well import high_flier_droplets, low_flier_droplets, singleRain_droplets, doubleRain_droplets, diagonal_scatter from numpy import mean as np_mean, std as np_std if not override_thresholds: override_thresholds = (None, None) statistics = well_statistics(well, override_thresholds=override_thresholds) accepted = len(accepted_peaks(well)) num_above_min = len(above_min_amplitude_peaks(well)) if num_above_min > 0 and accepted > 0: if well.sum_amplitude_bins: peaksets, boundaries, amps = revb_polydisperse_peaks(well, 0, threshold=override_thresholds[0]) poly_peaks = sum([len(p) for p in peaksets]) statistics[0].revb_polydispersity_pct = 100*float(poly_peaks)/num_above_min else: peaksets, boundaries, width_gates = polydisperse_peaks(well, 0, threshold=override_thresholds[0]) poly_peaks = sum([len(p) for p in peaksets]) statistics[0].revb_polydispersity_pct = 100*float(poly_peaks)/num_above_min else: statistics[0].revb_polydispersity_pct = 0 s, p_plus, p, p_minus = well_channel_sp_values(well, 0, override_threshold=override_thresholds[0]) statistics[0].s_value = s statistics[0].p_plus = p_plus statistics[0].p_plus_drops = int(p_plus*accepted) if p_plus is not None else None statistics[0].p = p statistics[0].p_drops = int(p*accepted) if p is not None else None statistics[0].p_minus = p_minus statistics[0].p_minus_drops = int(p_minus*accepted) if p_minus is not None else None if num_above_min > 0 and accepted > 0: if well.sum_amplitude_bins: peaksets, boundaries, amps = revb_polydisperse_peaks(well, 1, threshold=override_thresholds[1]) poly_peaks = sum([len(p) for p in peaksets]) statistics[1].revb_polydispersity_pct = 100*float(poly_peaks)/num_above_min else: peaksets, boundaries, width_gates = polydisperse_peaks(well, 1, threshold=override_thresholds[1]) poly_peaks = sum([len(p) for p in peaksets]) statistics[1].revb_polydispersity_pct = 100*float(poly_peaks)/num_above_min else: statistics[1].revb_polydispersity_pct = 0 s, p_plus, p, p_minus = well_channel_sp_values(well, 1, override_threshold=override_thresholds[1]) statistics[1].s_value = s statistics[1].p_plus = p_plus statistics[1].p_plus_drops = int(p_plus*accepted) if p_plus is not None else None statistics[1].p = p statistics[1].p_drops = int(p*accepted) if p is not None else None statistics[1].p_minus = p_minus statistics[1].p_minus_drops = int(p_minus*accepted) if p_minus is not None else None ## compute s2d plots s2d_vals = well_s2d_values( well, thresholds=override_thresholds) statistics[0].s2d_value = s2d_vals[0] if s2d_vals is not None else None statistics[1].s2d_value = s2d_vals[1] if s2d_vals is not None else None ## compute extra cluster metrics clusters = getClusters( well, override_thresholds ) dscatter = diagonal_scatter( clusters ) statistics.diagonal_scatter = dscatter[1] if dscatter is not None else None statistics.diagonal_scatter_pct = dscatter[2] *100 if dscatter is not None else None for channel in [0,1]: high_fliers = high_flier_droplets( clusters, channel ) statistics[channel].high_flier_value = high_fliers[1] if high_fliers is not None else None statistics[channel].high_flier_pct = high_fliers[2] * 100 if high_fliers is not None else None low_fliers = low_flier_droplets( clusters, channel ) statistics[channel].low_flier_value = low_fliers[1] if low_fliers is not None else None statistics[channel].low_flier_pct = low_fliers[2] * 100 if low_fliers is not None else None singleRain = singleRain_droplets( clusters, channel ) statistics[channel].single_rain_value = singleRain[1] if singleRain is not None else None statistics[channel].single_rain_pct = singleRain[2] * 100 if singleRain is not None else None doubleRain = doubleRain_droplets( clusters, channel ) statistics[channel].double_rain_value = doubleRain[1] if doubleRain is not None else None statistics[channel].double_rain_pct = doubleRain[2] * 100 if doubleRain is not None else None if compute_clusters: clusters = well_cluster_peaks(well, override_thresholds) else: clusters = {'positive_peaks': {'positive_peaks': [], 'negative_peaks': []}, 'negative_peaks': {'positive_peaks': [], 'negative_peaks': []}} # cheap hack statistics.alg_version = "%s.%s/%s.%s" % (well.statistics.peak_alg_major_version, well.statistics.peak_alg_minor_version, well.statistics.quant_alg_major_version, well.statistics.quant_alg_minor_version) statistics.ref_copy_num = well.ref_copy_num statistics[0].decision_tree = well.channels[0].decision_tree_verbose statistics[1].decision_tree = well.channels[1].decision_tree_verbose # end cheap hack # SNR for chan in (0,1): if override_thresholds[chan]: # TODO add this to pyqlb.nstats.well instead pos, neg = cluster_1d(accepted_peaks(well), chan, override_thresholds[chan]) else: pos, neg, unknown = well_observed_positives_negatives(well, chan) for attr, coll in (('positive_snr', pos),('negative_snr',neg)): if len(pos) > 0: amps = channel_amplitudes(coll, chan) amp_mean = np_mean(amps) amp_std = np_std(amps) if amp_std > 0: setattr(statistics[chan], attr, amp_mean/amp_std) else: setattr(statistics[chan], attr, 10000) else: setattr(statistics[chan], attr, 0) for channel in [0,1]: means,stds = total_events_amplitude_vals(well,channel) statistics[channel].total_events_amplitude_mean = means if means is not None else None statistics[channel].total_events_amplitude_stdev = stds if stds is not None else None return statistics, clusters
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 _compute_well_channel_metrics(qlwell, well_channel_metric, channel_num): """ Internal method for populating a WellChannelMetric object from statistics determined by analyzing the QLWell's channel object. This will populate the well_channel_metric object with information such as positives and negatives, concentration, polydispersity, and other metrics that have separate values for FAM and VIC channels. :param qlwell: The QLWell object created by reading a QLP file. :param well_channel_metric: The WellChannelMetric object to populate. :param channel_num: Which channel to analyze. """ # for convenience wcm = well_channel_metric stats = qlwell.channels[channel_num].statistics wcm.min_quality_gating = stats.min_quality_gate wcm.min_quality_gating_conf = stats.min_quality_gate_conf wcm.min_width_gate = stats.min_width_gate wcm.min_width_gate_conf = stats.min_width_gate_conf wcm.max_width_gate = stats.max_width_gate wcm.max_width_gate_conf = stats.max_width_gate_conf wcm.width_gating_sigma = stats.width_gating_sigma # TODO: try to guarantee automatic threshold here? wcm.threshold = stats.threshold wcm.threshold_conf = stats.threshold_conf wcm.auto_threshold_expected = False wcm.concentration = stats.concentration wcm.conc_lower_bound = stats.concentration_lower_bound wcm.conc_upper_bound = stats.concentration_upper_bound wcm.conc_calc_mode = stats.concentration_calc_mode wcm.clusters_automatic = well_channel_automatic_classification(qlwell, channel_num) wcm.decision_tree_flags = qlwell.channels[channel_num].decision_tree_flags ap = accepted_peaks(qlwell) if channel_num == 0: ampfunc = fam_amplitudes elif channel_num == 1: ampfunc = vic_amplitudes else: raise ValueError, "Incompatible channel number: %s" % channel_num wcm.amplitude_mean = np.mean(ampfunc(ap)) wcm.amplitude_stdev = np.std(ampfunc(ap)) allpeaks = qlwell.peaks wcm.total_events_amplitude_mean = np.mean(ampfunc(allpeaks)) wcm.total_events_amplitude_stdev = np.std(ampfunc(allpeaks)) above_min_peaks = above_min_amplitude_peaks(qlwell) quality_gated_peaks = quality_rejected(above_min_peaks, wcm.min_quality_gating, channel_num, include_vertical_streak_flagged=False) wcm.quality_gated_peaks = len(quality_gated_peaks) width_gated_peaks = width_rejected(above_min_peaks, wcm.min_width_gate, wcm.max_width_gate, channel_num) wcm.width_gated_peaks = len(width_gated_peaks) # TODO add min amplitude, vertical streak wcm.s_value = separation_value(ap, channel_num, wcm.threshold) p_plus, p, p_minus = rain_pvalues(ap, channel_num, wcm.threshold) wcm.rain_p_plus = p_plus wcm.rain_p = p wcm.rain_p_minus = p_minus ## extra cluster events... NEW_DROPLET_CLUSTER_METRICS_CALCULATOR.compute(qlwell, qlwell.channels[channel_num], wcm) # DEFAULT_TEAVALS.compute(qlwell, qlwell.channels[channel_num], wcm) DEFAULT_POLYD_CALC.compute(qlwell, qlwell.channels[channel_num], wcm) DEFAULT_EXTRAC_CALC.compute(qlwell, qlwell.channels[channel_num], wcm) DEFAULT_NTC_POSITIVE_CALCULATOR.compute(qlwell, qlwell.channels[channel_num], wcm) wcm.baseline_mean = qlwell.channels[channel_num].statistics.baseline_mean wcm.baseline_stdev = qlwell.channels[channel_num].statistics.baseline_stdev wcm.cluster_conf = qlwell.channels[channel_num].statistics.cluster_conf positive_peaks, negative_peaks, unclassified_peaks = well_observed_positives_negatives(qlwell, channel_num) if len(positive_peaks) > 0 or len(negative_peaks) > 0: wcm.positive_peaks = len(positive_peaks) wcm.negative_peaks = len(negative_peaks) wcm.positive_mean = np.mean(ampfunc(positive_peaks)) wcm.positive_stdev = np.std(ampfunc(positive_peaks)) wcm.negative_mean = np.mean(ampfunc(negative_peaks)) wcm.negative_stdev = np.std(ampfunc(negative_peaks)) wcm.positive_skew = skew(positive_peaks, channel_num) wcm.positive_kurtosis = kurtosis(positive_peaks, channel_num) wcm.nonpositive_skew = skew(negative_peaks, channel_num) wcm.nonpositive_kurtosis = kurtosis(negative_peaks, channel_num) # CLUSTER-TODO: this is not going to be right for clusters. Consider # changing this. wcm.concentration_rise_ratio = quartile_concentration_ratio(qlwell, peaks=ap, channel_num=channel_num, threshold=wcm.threshold, min_events=4000) del positive_peaks del negative_peaks del unclassified_peaks else: wcm.nonpositive_skew = skew(ap, channel_num) wcm.nonpositive_kurtosis = kurtosis(ap, channel_num) del ap # daisy chain just in case return wcm
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))
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