예제 #1
0
    def fit_responses(self, latency=None):
        if latency is None:
            latency_window = [0.5e-3, 10e-3]
        else:
            latency_window = [latency - 100e-6, latency + 100e-6]

        with pg.ProgressDialog("curve fitting..",
                               maximum=len(modes) * len(holdings)) as dlg:
            self.last_fit = {}
            for mode in modes:
                for holding in holdings:
                    self.fit_pass = False
                    sign = self.signs[mode][holding].get(
                        self.ctrl_panel.user_params['Synapse call'],
                        self.signs[mode][holding].get(
                            self.ctrl_panel.user_params['Polysynaptic call'],
                            0))

                    # ofp, x_offset, best_fit = fit_avg_response(self.traces, mode, holding, latency, sign)
                    prs = self.sorted_responses[mode, holding]['qc_pass']
                    if len(prs) == 0:
                        dlg += 1
                        continue

                    fit, avg = fit_avg_pulse_response(
                        prs, latency_window=latency_window, sign=sign)
                    fit_ts = avg.copy(data=fit.best_fit)
                    self.last_fit[mode, holding] = fit

                    self.initial_fit_parameters[mode][str(
                        holding)]['xoffset'] = latency
                    self.output_fit_parameters[mode][str(
                        holding)]['nrmse'] = fit.nrmse()
                    self.output_fit_parameters[mode][str(holding)].update(
                        fit.best_values)
                    self.fit_pass = fit.nrmse() < self.nrmse_thresh
                    self.ctrl_panel.output_params.child(
                        'Fit parameters',
                        str(holding) + ' ' + mode.upper(),
                        'Fit Pass').setValue(self.fit_pass)
                    if mode == 'vc':
                        self.vc_plot.plot_fit(fit_ts, holding, self.fit_pass)
                    elif mode == 'ic':
                        self.ic_plot.plot_fit(fit_ts, holding, self.fit_pass)
                    dlg += 1
                    if dlg.wasCanceled():
                        raise Exception("User canceled fit")
        self.fit_params['initial'] = self.initial_fit_parameters
        self.fit_params['fit'] = self.output_fit_parameters

        self.ctrl_panel.update_fit_params(self.fit_params['fit'])
        self.generate_warnings()
예제 #2
0
def get_pair_avg_fits(pair, session, notes_session=None, ui=None, max_ind_freq=50):
    """Return PSP fits to averaged responses for this pair.

    Fits are performed against average PSPs in 4 different categories: 
    IC -70mV, IC -55mV, VC -70mV, and VC -55mV. All PSPs in these categories are averaged together
    regardless of their position in a pulse train, so we expect the amplitudes of these averages to
    be affected by any short-term depression/facilitation present at the synapse. As such, these fits
    are not ideal for measuring the amplitude of the synapse; however, they do provide good estimates
    of rise time and decay tau.
    
    Operations are:
    
    - Query all pulse responses for this pair, where the pulse train frequency was 
      no faster than max_ind_freq
    - Sort responses by clamp mode and holding potential, with the latter in two bins: -80 to -60 mV and -60 to -45 mV.
      Responses are further separated into qc pass/fail for each bin. QC pass criteria:
        - PR must have exactly one presynaptic spike with detectable latency
        - Either PR.ex_qc_pass or .in_qc_pass must be True, depending on clamp mode / holding
    - Generate average response for qc-passed pulses responses in each mode/holding combination
    - Fit averages to PSP curve. If the latency was manually annotated for this synapse, then the curve
      fit will have its latency constrained within ±100 μs.
    - Compare to manually verified fit parameters; if these are not a close match OR if the 
      manual fits were already failed, then *fit_qc_pass* will be False.
      
    
    Returns
    -------
    results : dict
        {(mode, holding): {
            'responses': ..,
            'average': ..,
            'initial_latency': ..,
            'fit_result': ..,
            'fit_qc_pass': ..,
            'fit_qc_pass_reasons': ..,
            'expected_fit_params': ..,
            'expected_fit_pass': ..,
            'avg_baseline_noise': ..,
        }, ...}
    
    """
    prof = pg.debug.Profiler(disabled=True, delayed=False)
    prof(str(pair))
    results = {}
    
    # query and sort pulse responses with induction frequency 50Hz or slower
    records = response_query(session=session, pair=pair, max_ind_freq=max_ind_freq).all()
    prof('query prs')
    pulse_responses = [rec[0] for rec in records]

    # sort into clamp mode / holding bins
    sorted_responses = sort_responses(pulse_responses)
    prof('sort prs')

    # load expected PSP curve fit parameters from notes DB
    notes_rec = notes_db.get_pair_notes_record(pair.experiment.ext_id, pair.pre_cell.ext_id, pair.post_cell.ext_id, session=notes_session)
    prof('get pair notes')

    if ui is not None:
        ui.show_pulse_responses(sorted_responses)
        ui.show_data_notes(notes_rec)
        prof('update ui')

    for (clamp_mode, holding), responses in sorted_responses.items():
        if len(responses['qc_pass']) == 0:
            results[clamp_mode, holding] = None
            continue
            
        if notes_rec is None:
            notes = None
            sign = 0
            init_latency = None
            latency_window = (0.5e-3, 10e-3)
        else:
            notes = notes_rec.notes
            if notes.get('fit_parameters') is None:
                init_latency = None
                latency_window = (0.5e-3, 10e-3)
            else:
                init_latency = notes['fit_parameters']['initial'][clamp_mode][str(holding)]['xoffset']
                latency_window = (init_latency - 100e-6, init_latency + 100e-6)
            
            # Expected response sign depends on synapse type, clamp mode, and holding:
            sign = 0
            if notes['synapse_type'] == 'ex':
                sign = -1 if clamp_mode == 'vc' else 1
            elif notes['synapse_type'] == 'in' and holding == -55:
                sign = 1 if clamp_mode == 'vc' else -1

        prof('prepare %s %s' % (clamp_mode, holding))
        fit_result, avg_response = fit_avg_pulse_response(responses['qc_pass'], latency_window, sign)
        prof('fit avg')

        # measure baseline noise
        avg_baseline_noise = avg_response.time_slice(avg_response.t0, avg_response.t0+7e-3).data.std()

        # compare to manually-verified results
        if notes is None:
            qc_pass = False
            reasons = ['no data notes entry']
            expected_fit_params = None
            expected_fit_pass = None
        elif notes['fit_pass'][clamp_mode][str(holding)] is not True:
            qc_pass = False
            reasons = ['data notes fit failed qc']
            expected_fit_params = None
            expected_fit_pass = False
        else:
            expected_fit_params = notes['fit_parameters']['fit'][clamp_mode][str(holding)]
            expected_fit_pass = True
            qc_pass, reasons = check_fit_qc_pass(fit_result, expected_fit_params, clamp_mode)
            if not qc_pass:
                print("%s %s %s: %s" % (str(pair), clamp_mode, holding,  '; '.join(reasons)))

        if ui is not None:
            ui.show_fit_results(clamp_mode, holding, fit_result, avg_response, qc_pass)

        results[clamp_mode, holding] = {
            'responses': responses,
            'average': avg_response,
            'initial_latency': init_latency,
            'fit_result': fit_result,
            'fit_qc_pass': qc_pass,
            'fit_qc_pass_reasons': reasons,
            'expected_fit_params': expected_fit_params,
            'expected_fit_pass': expected_fit_pass,
            'avg_baseline_noise': avg_baseline_noise,
        }

    return results
예제 #3
0
def get_pair_avg_fits(pair, session, notes_session=None, ui=None):
    """Return PSP fits to averaged responses for this pair.
    
    Operations are:
    - query all pulse responses for this pair
    - sort responses by clamp mode and holding potential
    - generate average response for each mode/holding combination
    - fit averages to PSP curve
    
    Returns
    -------
    results : dict
        {(mode, holding): {
            'traces': , 
            'average', 
            'fit_params',
            'initial_latency',
            'fit_qc_pass',
            'expected_fit_params',
            'avg_baseline_noise',
            }, 
        }
    
    """
    prof = pg.debug.Profiler(disabled=True, delayed=False)
    prof(str(pair))
    results = {}
    
    # query and sort pulse responses
    records = response_query(session=session, pair=pair).all()
    prof('query prs')
    pulse_responses = [rec[0] for rec in records]
    sorted_responses = sort_responses(pulse_responses)
    prof('sort prs')

    notes_rec = notes_db.get_pair_notes_record(pair.experiment.ext_id, pair.pre_cell.ext_id, pair.post_cell.ext_id, session=notes_session)
    prof('get pair notes')

    if ui is not None:
        ui.show_pulse_responses(sorted_responses)
        ui.show_data_notes(notes_rec)
        prof('update ui')

    for (clamp_mode, holding), responses in sorted_responses.items():
        if len(responses['qc_pass']) == 0:
            results[clamp_mode, holding] = None
            continue
            
        if notes_rec is None:
            notes = None
            sign = 0
            init_latency = None
            latency_window = (0.5e-3, 8e-3)
        else:
            notes = notes_rec.notes
            if notes.get('fit_parameters') is None:
                init_latency = None
                latency_window = (0.5e-3, 8e-3)
            else:
                init_latency = notes['fit_parameters']['initial'][clamp_mode][str(holding)]['xoffset']
                latency_window = (init_latency - 100e-6, init_latency + 100e-6)
            
            # Expected response sign depends on synapse type, clamp mode, and holding:
            sign = 0
            if notes['synapse_type'] == 'ex':
                sign = -1 if clamp_mode == 'vc' else 1
            elif notes['synapse_type'] == 'in' and holding == -55:
                sign = 1 if clamp_mode == 'vc' else -1

        prof('prepare %s %s' % (clamp_mode, holding))
        fit_result, avg_response = fit_avg_pulse_response(responses['qc_pass'], latency_window, sign)
        prof('fit avg')

        # measure baseline noise
        avg_baseline_noise = avg_response.time_slice(avg_response.t0, avg_response.t0+7e-3).data.std()

        # load up expected fit results and compare to manually-verified
        # results
        if notes is None:
            qc_pass = False
            reasons = ['no data notes entry']
            expected_fit_params = None
            expected_fit_pass = None
        elif notes['fit_pass'][clamp_mode][str(holding)] is not True:
            qc_pass = False
            reasons = ['data notes fit failed qc']
            expected_fit_params = None
            expected_fit_pass = False
        else:
            expected_fit_params = notes['fit_parameters']['fit'][clamp_mode][str(holding)]
            expected_fit_pass = True
            qc_pass, reasons = check_fit_qc_pass(fit_result, expected_fit_params, clamp_mode)
            if not qc_pass:
                print("%s %s %s: %s" % (str(pair), clamp_mode, holding,  '; '.join(reasons)))

        if ui is not None:
            ui.show_fit_results(clamp_mode, holding, fit_result, avg_response, qc_pass)

        results[clamp_mode, holding] = {
            'responses': responses,
            'average': avg_response,
            'initial_latency': init_latency,
            'fit_result': fit_result,
            'fit_qc_pass': qc_pass,
            'fit_qc_pass_reasons': reasons,
            'expected_fit_params': expected_fit_params,
            'expected_fit_pass': expected_fit_pass,
            'avg_baseline_noise': avg_baseline_noise,
        }

    return results