def test_onregulararray(self): "Test cmov_average on a basic ndarray." data = self.data for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) assert_equal(ravg, data) assert_equal(ravg._mask, [1] * k + [0] * (len(data) - 2 * k) + [1] * k)
def test_onregulararray(self): "Test cmov_average on a basic ndarray." data = self.data for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) assert_equal(ravg, data) assert_equal(ravg._mask, [1] * k + [0] * (len(data) - 2 * k) + [1] * k)
def test_onmaskedarray(self): "Test cmov_average on a MaskedArray." data = self.maskeddata for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) assert_equal(ravg, data) m = np.zeros(len(data), bool) m[:k] = m[-k:] = m[10 - k:10 + k + 1] = True assert_equal(ravg._mask, m)
def test_onmaskedarray(self): "Test cmov_average on a MaskedArray." data = self.maskeddata for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) assert_equal(ravg, data) m = np.zeros(len(data), bool) m[:k] = m[-k:] = m[10 - k : 10 + k + 1] = True assert_equal(ravg._mask, m)
def test_ontimeseries(self): "Test cmov_average on a 1D TimeSeries." data = ts.time_series(self.maskeddata, start_date=ts.now('D')) for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) assert_equal(ravg, data) m = np.zeros(len(data), bool) m[:k] = m[-k:] = m[10 - k:10 + k + 1] = True assert_equal(ravg._mask, m) assert_equal(ravg._dates, data._dates)
def test_ontimeseries(self): "Test cmov_average on a 1D TimeSeries." data = ts.time_series(self.maskeddata, start_date=ts.now("D")) for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) assert_equal(ravg, data) m = np.zeros(len(data), bool) m[:k] = m[-k:] = m[10 - k : 10 + k + 1] = True assert_equal(ravg._mask, m) assert_equal(ravg._dates, data._dates)
def tests_onmultitimeseries(self): "Test cmov_average on a nD TimeSeries." maskeddata = MaskedArray(np.random.random(75).reshape(25, 3)) maskeddata[10] = masked data = ts.time_series(maskeddata, start_date=ts.now("D")) for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) #!!!: __getitem__ used to return a TimeSeries, now returns an array # assert_almost_equal(ravg[18]._series.squeeze(), # data[18-k:18+k+1]._series.mean(0)) assert_almost_equal(ravg[18].squeeze(), data[18 - k : 18 + k + 1]._series.mean(0)) m = np.zeros(data.shape, bool) m[:k] = m[-k:] = m[10 - k : 10 + k + 1] = True assert_equal(ravg._mask, m) assert_equal(ravg._dates, data._dates)
def tests_onmultitimeseries(self): "Test cmov_average on a nD TimeSeries." maskeddata = MaskedArray(np.random.random(75).reshape(25, 3)) maskeddata[10] = masked data = ts.time_series(maskeddata, start_date=ts.now('D')) for width in [3, 5, 7]: k = (width - 1) / 2 ravg = mf.cmov_average(data, width) self.failUnless(isinstance(ravg, MaskedArray)) #!!!: __getitem__ used to return a TimeSeries, now returns an array # assert_almost_equal(ravg[18]._series.squeeze(), # data[18-k:18+k+1]._series.mean(0)) assert_almost_equal(ravg[18].squeeze(), data[18 - k:18 + k + 1]._series.mean(0)) m = np.zeros(data.shape, bool) m[:k] = m[-k:] = m[10 - k:10 + k + 1] = True assert_equal(ravg._mask, m) assert_equal(ravg._dates, data._dates)
def main(options, args): """The main procedure of physionoise""" initialize_logger() if (options.verbosity == 1): logger.setLevel(logging.INFO) if (options.verbosity == 2): logger.setLevel(logging.DEBUG) if (options.verbosity > 2): logger.setLevel(0) respiratory_signal = mean_centered( load_signal(options.sigfile) ) respiratory_triggers = load_signal(options.ctrigfile).astype('int') cardiac_signal = mean_centered( load_signal(options.csigfile) ) cardiac_triggers = load_signal(options.ctrigfile).astype('int') respiratory_xaxis = range(len(respiratory_signal)) cardiac_xaxis = range(len(cardiac_signal)) input_sample_rate = options.OrigSamp output_sample_rate = options.NewSamp sample_ratio = float(input_sample_rate) / output_sample_rate nyquist = input_sample_rate / 2.0 passband_frequency = options.fpass / nyquist stopband_frequency = options.fstop / nyquist tr_time = options.TR num_tr = options.NumTR input_signal_length = int(num_tr * tr_time * input_sample_rate) output_signal_length = int(num_tr * tr_time * output_sample_rate) # these options are for tuning the peakdet algorithm respiratory_dm = options.RMagThresh respiratory_dt = options.RTimeThresh # in seconds! cardiac_dm = options.CMagThresh cardiac_dt = options.CTimeThresh # in seconds! plots_requested = options.PlotAll use_fast_spline = options.Speed prefix = options.Prefix truncate = options.truncate # converting absolute event times from the trigger file to a boolean array CPttl = (numpy.zeros(len(cardiac_signal)) != 0) # array of Falses same size as signal CPttl[cardiac_triggers] = True ### NOTE on encoding events ### # in all cases where we want to denote a series of events in a signal, we will # use an array of booleans. For example, if we want to know where signal # peaks are we create an array of the same size as the signal with True entries # where each of the peaks occured and False everywhere else. # These boolean arrays can be used conveniently to index into the signals # e.g. signal[peaks] => produces the magnitude of the signal at the peaks. # #### Butterworth Filter Data ### filter_b, filter_a, butter_response_magnitude, butter_sample_frequences = build_butterworth_filter( passband_frequency, stopband_frequency ) filtered_respiratory = filtfilt(filter_b, filter_a, respiratory_signal) filtered_cardiac = filtfilt(filter_b, filter_a, cardiac_signal) # the spectral analyses are just for plotting, they are currently not saved # to file at the end pxx, freqs, maxfreq, PeakPower, TPeriod = spectral_analysis( filtered_respiratory, input_sample_rate, "Respiratory" ) Cpxx, Cfreqs, Cmaxfreq, CPeakPower, CTPeriod = spectral_analysis( filtered_cardiac, input_sample_rate, "Cardiac" ) #### Spline interpolation #### respiratory_residual = respiratory_signal - filtered_respiratory cardiac_residual = cardiac_signal - filtered_cardiac if use_fast_spline: logger.info( "Speedy Spline Filtering" ) respiratory_spline = scipy.ndimage.spline_filter1d(filtered_respiratory, order=3) cardiac_spline = scipy.ndimage.spline_filter1d(filtered_cardiac, order=3) else: logger.info( "Slower (better?) Spline Filtering" ) logger.info( "Censoring regions of high variance" ) censored_resp, resp_included, resp_included_indexes = censor_signal( respiratory_signal, filtered_respiratory, options.FoldSTD, options.TrimWindow, options.FwdRm ) censored_cardiac, card_included, card_included_indexes = censor_signal( cardiac_signal, filtered_cardiac, options.FoldSTD, options.TrimWindow, options.FwdRm ) logger.debug("resp len(%d) censored resp len(%d)" % (len(respiratory_signal), len(censored_resp))) logger.debug("card len(%d) censored card len(%d)" % (len(cardiac_signal), len(censored_cardiac))) logger.info( "Spline interpolation" ) # Generate cubic spline....Too slow when not on Mac OS X but looks nice resp_spline_estimate = scipy.interpolate.splrep(resp_included_indexes, resp_included, k=3) respiratory_spline = scipy.interpolate.splev(respiratory_xaxis, resp_spline_estimate) card_spline_estimate = scipy.interpolate.splrep(card_included_indexes, card_included, k=3) cardiac_spline = scipy.interpolate.splev(cardiac_xaxis, card_spline_estimate) #---------------------------------------------------------- Finding peaks respiratory_extrema = extrema(respiratory_spline, respiratory_dm, respiratory_dt) respiratory_maxima = maxima(respiratory_spline, respiratory_dm, respiratory_dt) respiratory_minima = minima(respiratory_spline, respiratory_dm, respiratory_dt) cardiac_extrema = extrema(cardiac_spline, cardiac_dm, cardiac_dt) cardiac_maxima = maxima(cardiac_spline, cardiac_dm, cardiac_dt) cardiac_minima = minima(cardiac_spline, cardiac_dm, cardiac_dt) #------------------------------------------------------- Find Volume over Time logger.info( "Calculating Envelopes" ) logger.info( "Respiratory" ) resp_top_envelope = envelope(respiratory_spline, respiratory_maxima) resp_bottom_envelope = envelope(respiratory_spline, respiratory_minima) resp_abs_volume = resp_top_envelope - resp_bottom_envelope resp_rate = event_rate(respiratory_maxima, input_sample_rate) rvt = resp_abs_volume * resp_rate logger.info( "Cardiac" ) card_top_envelope = envelope(cardiac_spline, cardiac_maxima) card_bottom_envelope = envelope(cardiac_spline, cardiac_minima) card_abs_volume = card_top_envelope - card_bottom_envelope card_rate = event_rate(cardiac_maxima, input_sample_rate) cvt = card_abs_volume * card_rate #------ Find third derivatives of Cardiac signal to create new trigger file logger.info( "Finding Cardiac Derivatives" ) cardiac_d1 = slopes(cardiac_xaxis, cardiac_spline) #* input_sample_rate cardiac_d2 = slopes(cardiac_xaxis, cardiac_d1) #* input_sample_rate cardiac_d3 = slopes(cardiac_xaxis, cardiac_d2) #* input_sample_rate smoothed_caradiac_d3 = cmov_average(cardiac_d3, input_sample_rate / 2) # caution: using 1.0 for the amplitude threshold for peak finding, this might # not always work well. card_d3_maxima = maxima(smoothed_caradiac_d3, 1.0, cardiac_dt) card_d3_minima = minima(smoothed_caradiac_d3, 1.0, cardiac_dt) logger.info( "Thresholding CPd3" ) CPd3 = card_d3_minima card_d3_bottom_envelope = envelope(smoothed_caradiac_d3, card_d3_minima) card_d3_top_envelope = envelope(smoothed_caradiac_d3, card_d3_maxima) # There have been several approaches to filtering acceptable d3 minima # to include in CPd3: # 1. the min must be in a region where d1 is negative # 2. the min must be less than the top envelope at that point # 3. the min must be less than 0.5 * the mean of the top envelope # # The current approach, calculate the bottom envelope and compute a moving # average of it at 1.5 * the sampling rate, accept any minima that # are <= the moving average. The motivation for this is that in most cases # there are two classes of minima that are relatively seperable locally but # not globally. The moving average provides a consistent margin between the # classes at the local level. CPd3 &= (card_d3_bottom_envelope <= cmov_average(card_d3_bottom_envelope, input_sample_rate*1.5)) # Older minima filtering approaches # # CPd3 &= (cardiac_d1 >= -0.5) # CPd3 &= (card_d3_bottom_envelope <= mean(card_d3_bottom_envelope)) # CPd3 &= (abs(card_d3_bottom_envelope) >= abs(card_d3_top_envelope)) # CPd3 &= (abs(card_d3_bottom_envelope) >= 0.5*mean(abs(card_d3_top_envelope))) # CPd3 = -1.0 * spikewave(smoothed_caradiac_d3, CPd3) # R waves are d3 peaks that immediately precede CPd3 blips CPd3Rwave = (numpy.zeros(len(CPd3)) != 0) # boolean array of all falses. for i in indices_of(CPd3): previous_d3_maxima = card_d3_maxima[0:i] if (len(indices_of(previous_d3_maxima)) > 0): index_of_prior_d3_maximum = indices_of(previous_d3_maxima)[-1] CPd3Rwave[index_of_prior_d3_maximum] = True #----------- Calculate respiratory (RRT) and cardiac rate per time (CRT) logger.info( "Calculating Rates over Time" ) RRT = event_rate(respiratory_maxima, input_sample_rate) CRTttl = event_rate(CPttl, input_sample_rate) CRTd3 = event_rate(CPd3, input_sample_rate) CRTd3R = event_rate(CPd3Rwave, input_sample_rate) #---------------------------------------------------------------- DownSampling logger.info( "Downsampling" ) downsampled_respiratory = downsample(respiratory_spline, sample_ratio) downsampled_cardiac = downsample(cardiac_spline, sample_ratio) #-------------------------------------------------------------------- Plotting if plots_requested: # Butterworth filter analysis summary plt.figure(1) plt.title("Butterworth filter spectral analysis") plt.subplot(311) plt.semilogy(butter_sample_frequences * nyquist, butter_response_magnitude) plt.ylabel('Magnitude') plt.title('Bode Plot') plt.ylim((10e-4, 10e0)) plt.xlim((0.0, options.fstop)) plt.figure(1) plt.subplot(312) plt.plot(freqs, pxx) plt.ylabel('Respiratory Power') plt.xlim((0.0, options.fstop)) plt.figure(1) plt.subplot(313) plt.plot(Cfreqs, Cpxx) plt.xlabel('frequency(Hz)') plt.ylabel('Cardiac Power') plt.xlim((0.0, options.fstop)) # overview of the filter and spline interpolated signals plt.figure(2) plt.subplot(211) plt.plot(respiratory_xaxis, filtered_respiratory, 'b-', respiratory_xaxis, respiratory_spline, 'go', respiratory_xaxis, respiratory_signal, 'k', respiratory_xaxis, respiratory_residual, 'r' ) plt.legend(('Filtered', 'Spline', 'Raw', 'Residual'), shadow=False, loc=1) ltext = plt.gca().get_legend().get_texts() plt.setp(ltext[0], fontsize = 8, color = 'b') plt.setp(ltext[1], fontsize = 8, color = 'g') plt.setp(ltext[2], fontsize = 8, color = 'k') plt.setp(ltext[3], fontsize = 8, color = 'r') plt.title('Respiratory') plt.subplot(212) plt.plot(cardiac_xaxis, filtered_cardiac, 'b-', cardiac_xaxis, cardiac_spline, 'go', cardiac_xaxis, cardiac_signal, 'k', cardiac_xaxis, cardiac_residual, 'r' ) plt.legend(('Filtered','Spline','Raw','Residual'), shadow=False, loc=1) ltext = plt.gca().get_legend().get_texts() plt.setp(ltext[0], fontsize = 8, color = 'b') plt.setp(ltext[1], fontsize = 8, color = 'g') plt.setp(ltext[2], fontsize = 8, color = 'k') plt.setp(ltext[3], fontsize = 8, color = 'r') plt.title('Cardiac') # top and bottom peak finding and envelopes chart_envelopes(respiratory_spline, respiratory_maxima, resp_top_envelope, respiratory_minima, resp_bottom_envelope, rvt, 'Respiratory Data', ('Respiratory Spline','Top Envelope','Bottom Envelope','Maxima','Minima','RVT'), figure=3, row=1 ) chart_envelopes(cardiac_spline, cardiac_maxima, card_top_envelope, cardiac_minima, card_bottom_envelope, cvt, 'Cardiac Data', ('Cardiac Spline','Top Envelope','Bottom Envelope','Maxima','Minima','CVT'), figure=3, row=2 ) # summary of the volume over time waves and cardiac wave event finding results signals_to_plot = (CRTd3R, CRTttl, RRT, CRTd3, rvt, cvt) labels = ('CRTd3R', 'CRTttl', 'RRT', 'CRTd3', 'RVT', 'CVT') chart_summary_of_signals(signals_to_plot, labels, figure=5) # cardiac wave events viewed on the spline # make cardiac_d3 in roughly the same range as the spline adjustment_ratio = numpy.nanmax(cardiac_spline) / numpy.nanmax(smoothed_caradiac_d3) adjusted_d3 = smoothed_caradiac_d3 * 1.5 * adjustment_ratio plt.figure(6) plt.title('Event Locations on the Cardiac Waveform') plt.hold() plt.plot(cardiac_signal, '#aaaaaa') plt.plot(cardiac_spline, 'k') plt.plot(adjusted_d3, '#ff5555') plt.plot(indices_of(CPttl), cardiac_spline[CPttl], 'yo') plt.plot(indices_of(CPd3), cardiac_spline[CPd3], 'bo') plt.plot(indices_of(CPd3Rwave), cardiac_spline[CPd3Rwave], 'go') plt.legend(('Raw Cardiac','Cardiac Spline','Cardiac d3','CPttl','CPd3','CPd3Rwave'), shadow=False, loc=1) ltext = plt.gca().get_legend().get_texts() plt.setp(ltext[0], fontsize = 8, color = '#aaaaaa') plt.setp(ltext[1], fontsize = 8, color = 'k') plt.setp(ltext[2], fontsize = 8, color = '#ff5555') plt.setp(ltext[3], fontsize = 8, color = 'y') plt.setp(ltext[4], fontsize = 8, color = 'b') plt.setp(ltext[5], fontsize = 8, color = 'g') #--------------------------------------------------- Trim length for retroicor logger.info( "Trim length" ) # expected length at input sample rate ei = tr_time * num_tr * input_sample_rate # expected length at downsample rate ed = tr_time * num_tr * output_sample_rate if (truncate == 'end'): logger.info( "Truncating at end of signal" ) respiratory_spline = respiratory_spline[0:ei] cardiac_spline = cardiac_spline[0:ei] CPd3 = CPd3[0:ei] CPd3Rwave = CPd3Rwave[0:ei] CPttl = CPttl[0:ei] RRT = RRT[0:ei] CRTd3 = CRTd3[0:ei] CRTttl = CRTttl[0:ei] CRTd3R = CRTd3R[0:ei] resp_top_envelope = resp_top_envelope[0:ei] card_top_envelope = card_top_envelope[0:ei] downsampled_cardiac = downsampled_cardiac[0:ed] downsampled_respiratory = downsampled_respiratory[0:ed] elif (truncate == 'front'): logger.info( "Truncating at front of signal" ) ei_front = len(respiratory_spline) - ei ed_front = len(downsampled_respiratory) - ed respiratory_spline = respiratory_spline[ei_front:] cardiac_spline = cardiac_spline[ei_front:] CPd3 = CPd3[ei_front:] CPd3Rwave = CPd3Rwave[ei_front:] CPttl = CPttl[ei_front:] RRT = RRT[ei_front:] CRTd3 = CRTd3[ei_front:] CRTttl = CRTttl[ei_front:] CRTd3R = CRTd3R[ei_front:] resp_top_envelope = resp_top_envelope[ei_front:] card_top_envelope = card_top_envelope[ei_front:] downsampled_cardiac = downsampled_cardiac[ed_front:] downsampled_respiratory = downsampled_respiratory[ei_front:] else: logger.info( "Not truncating signal to expected TR size." ) #---------------------------------------------------------------- Reporting peaktypes = { "CPttl": CPttl, "CPd3": CPd3, "CPd3Rwave": CPd3Rwave } rpt = report_value # just aliasing the report_value function tblhdr_fmt = "%-14s%-14s%-14s" tblrow_fmt = "%-14d%-14.4f%-14.4f" lbl_fmt = "%-14s%-14s" divider_width = 90 divider = "=" * divider_width print rpt( lbl_fmt % ("Peak type","Aspect"), tblhdr_fmt % ('N', 'mean', 'std') ) print divider for peak_label, peakseries in peaktypes.iteritems(): mags = cardiac_spline[peakseries] times = indices_of(peakseries) dt = diff(times) gt3std = mags[mags > mean(mags) + 3*std(mags)] gt4std = mags[mags > mean(mags) + 4*std(mags)] gtmean = mags[mags > mean(mags) + 1.0] gtbelow = mags[mags > mean(mags) - 0.4] reports = { "all timepoints": peakseries, "peak magnitudes": mags, "peak times": times, "interpeak times": dt, "peaks > 3std": gt3std, "peaks > 4std": gt4std, "peaks > mean + 1": gtmean, "peaks > mean - 0.4": gtbelow } for report_label, report_series in reports.iteritems(): n, m, s = stat_summary(report_series) rpt(lbl_fmt % (peak_label, report_label), tblrow_fmt % (n,m,s)) # Dumping results to files if (options.saveFiles): logger.info( "Saving files" ) signals = { 'resp_spline': respiratory_spline, 'card_spline': cardiac_spline, 'RVT' : rvt, 'RRT' : RRT, 'CRTd3' : CRTd3, 'CRTd3R' : CRTd3R, 'CRTttl': CRTttl } for fbase, signal in signals.iteritems(): dump(zeromin(signal), fbase, prefix, input_sample_rate) for tr_label, tr_multiple in { 'TR_': 1.0, 'HalfTR_': 0.5 }.iteritems(): resampled_signal = resample_to_tr(signal, input_sample_rate, num_tr, tr_time, tr_multiple) dump(zeromin(resampled_signal), tr_label + fbase, prefix, input_sample_rate) peaksigs = { 'CPd3': CPd3, 'CPd3R': CPd3Rwave, 'CPttl': CPttl } for fbase, peak in peaksigs.iteritems(): dump(peak, fbase, prefix, input_sample_rate) # probably not needed, left over from original physionoise.py dump(zeromin(downsampled_respiratory), 'resp_spline_downsampled', prefix, output_sample_rate) if plots_requested: plt.show() sys.exit(0)
def main(options, args): """The main procedure of physionoise""" initialize_logger() if (options.verbosity == 1): logger.setLevel(logging.INFO) if (options.verbosity == 2): logger.setLevel(logging.DEBUG) if (options.verbosity > 2): logger.setLevel(0) respiratory_signal = mean_centered(load_signal(options.sigfile)) respiratory_triggers = load_signal(options.ctrigfile).astype('int') cardiac_signal = mean_centered(load_signal(options.csigfile)) cardiac_triggers = load_signal(options.ctrigfile).astype('int') respiratory_xaxis = range(len(respiratory_signal)) cardiac_xaxis = range(len(cardiac_signal)) input_sample_rate = options.OrigSamp output_sample_rate = options.NewSamp sample_ratio = float(input_sample_rate) / output_sample_rate nyquist = input_sample_rate / 2.0 passband_frequency = options.fpass / nyquist stopband_frequency = options.fstop / nyquist tr_time = options.TR num_tr = options.NumTR input_signal_length = int(num_tr * tr_time * input_sample_rate) output_signal_length = int(num_tr * tr_time * output_sample_rate) # these options are for tuning the peakdet algorithm respiratory_dm = options.RMagThresh respiratory_dt = options.RTimeThresh # in seconds! cardiac_dm = options.CMagThresh cardiac_dt = options.CTimeThresh # in seconds! plots_requested = options.PlotAll use_fast_spline = options.Speed prefix = options.Prefix truncate = options.truncate # converting absolute event times from the trigger file to a boolean array CPttl = (numpy.zeros(len(cardiac_signal)) != 0 ) # array of Falses same size as signal CPttl[cardiac_triggers] = True ### NOTE on encoding events ### # in all cases where we want to denote a series of events in a signal, we will # use an array of booleans. For example, if we want to know where signal # peaks are we create an array of the same size as the signal with True entries # where each of the peaks occured and False everywhere else. # These boolean arrays can be used conveniently to index into the signals # e.g. signal[peaks] => produces the magnitude of the signal at the peaks. # #### Butterworth Filter Data ### filter_b, filter_a, butter_response_magnitude, butter_sample_frequences = build_butterworth_filter( passband_frequency, stopband_frequency) filtered_respiratory = filtfilt(filter_b, filter_a, respiratory_signal) filtered_cardiac = filtfilt(filter_b, filter_a, cardiac_signal) # the spectral analyses are just for plotting, they are currently not saved # to file at the end pxx, freqs, maxfreq, PeakPower, TPeriod = spectral_analysis( filtered_respiratory, input_sample_rate, "Respiratory") Cpxx, Cfreqs, Cmaxfreq, CPeakPower, CTPeriod = spectral_analysis( filtered_cardiac, input_sample_rate, "Cardiac") #### Spline interpolation #### respiratory_residual = respiratory_signal - filtered_respiratory cardiac_residual = cardiac_signal - filtered_cardiac if use_fast_spline: logger.info("Speedy Spline Filtering") respiratory_spline = scipy.ndimage.spline_filter1d( filtered_respiratory, order=3) cardiac_spline = scipy.ndimage.spline_filter1d(filtered_cardiac, order=3) else: logger.info("Slower (better?) Spline Filtering") logger.info("Censoring regions of high variance") censored_resp, resp_included, resp_included_indexes = censor_signal( respiratory_signal, filtered_respiratory, options.FoldSTD, options.TrimWindow, options.FwdRm) censored_cardiac, card_included, card_included_indexes = censor_signal( cardiac_signal, filtered_cardiac, options.FoldSTD, options.TrimWindow, options.FwdRm) logger.debug("resp len(%d) censored resp len(%d)" % (len(respiratory_signal), len(censored_resp))) logger.debug("card len(%d) censored card len(%d)" % (len(cardiac_signal), len(censored_cardiac))) logger.info("Spline interpolation") # Generate cubic spline....Too slow when not on Mac OS X but looks nice resp_spline_estimate = scipy.interpolate.splrep(resp_included_indexes, resp_included, k=3) respiratory_spline = scipy.interpolate.splev(respiratory_xaxis, resp_spline_estimate) card_spline_estimate = scipy.interpolate.splrep(card_included_indexes, card_included, k=3) cardiac_spline = scipy.interpolate.splev(cardiac_xaxis, card_spline_estimate) #---------------------------------------------------------- Finding peaks respiratory_extrema = extrema(respiratory_spline, respiratory_dm, respiratory_dt) respiratory_maxima = maxima(respiratory_spline, respiratory_dm, respiratory_dt) respiratory_minima = minima(respiratory_spline, respiratory_dm, respiratory_dt) cardiac_extrema = extrema(cardiac_spline, cardiac_dm, cardiac_dt) cardiac_maxima = maxima(cardiac_spline, cardiac_dm, cardiac_dt) cardiac_minima = minima(cardiac_spline, cardiac_dm, cardiac_dt) #------------------------------------------------------- Find Volume over Time logger.info("Calculating Envelopes") logger.info("Respiratory") resp_top_envelope = envelope(respiratory_spline, respiratory_maxima) resp_bottom_envelope = envelope(respiratory_spline, respiratory_minima) resp_abs_volume = resp_top_envelope - resp_bottom_envelope resp_rate = event_rate(respiratory_maxima, input_sample_rate) rvt = resp_abs_volume * resp_rate logger.info("Cardiac") card_top_envelope = envelope(cardiac_spline, cardiac_maxima) card_bottom_envelope = envelope(cardiac_spline, cardiac_minima) card_abs_volume = card_top_envelope - card_bottom_envelope card_rate = event_rate(cardiac_maxima, input_sample_rate) cvt = card_abs_volume * card_rate #------ Find third derivatives of Cardiac signal to create new trigger file logger.info("Finding Cardiac Derivatives") cardiac_d1 = slopes(cardiac_xaxis, cardiac_spline) #* input_sample_rate cardiac_d2 = slopes(cardiac_xaxis, cardiac_d1) #* input_sample_rate cardiac_d3 = slopes(cardiac_xaxis, cardiac_d2) #* input_sample_rate smoothed_caradiac_d3 = cmov_average(cardiac_d3, input_sample_rate / 2) # caution: using 1.0 for the amplitude threshold for peak finding, this might # not always work well. card_d3_maxima = maxima(smoothed_caradiac_d3, 1.0, cardiac_dt) card_d3_minima = minima(smoothed_caradiac_d3, 1.0, cardiac_dt) logger.info("Thresholding CPd3") CPd3 = card_d3_minima card_d3_bottom_envelope = envelope(smoothed_caradiac_d3, card_d3_minima) card_d3_top_envelope = envelope(smoothed_caradiac_d3, card_d3_maxima) # There have been several approaches to filtering acceptable d3 minima # to include in CPd3: # 1. the min must be in a region where d1 is negative # 2. the min must be less than the top envelope at that point # 3. the min must be less than 0.5 * the mean of the top envelope # # The current approach, calculate the bottom envelope and compute a moving # average of it at 1.5 * the sampling rate, accept any minima that # are <= the moving average. The motivation for this is that in most cases # there are two classes of minima that are relatively seperable locally but # not globally. The moving average provides a consistent margin between the # classes at the local level. CPd3 &= (card_d3_bottom_envelope <= cmov_average(card_d3_bottom_envelope, input_sample_rate * 1.5)) # Older minima filtering approaches # # CPd3 &= (cardiac_d1 >= -0.5) # CPd3 &= (card_d3_bottom_envelope <= mean(card_d3_bottom_envelope)) # CPd3 &= (abs(card_d3_bottom_envelope) >= abs(card_d3_top_envelope)) # CPd3 &= (abs(card_d3_bottom_envelope) >= 0.5*mean(abs(card_d3_top_envelope))) # CPd3 = -1.0 * spikewave(smoothed_caradiac_d3, CPd3) # R waves are d3 peaks that immediately precede CPd3 blips CPd3Rwave = (numpy.zeros(len(CPd3)) != 0) # boolean array of all falses. for i in indices_of(CPd3): previous_d3_maxima = card_d3_maxima[0:i] if (len(indices_of(previous_d3_maxima)) > 0): index_of_prior_d3_maximum = indices_of(previous_d3_maxima)[-1] CPd3Rwave[index_of_prior_d3_maximum] = True #----------- Calculate respiratory (RRT) and cardiac rate per time (CRT) logger.info("Calculating Rates over Time") RRT = event_rate(respiratory_maxima, input_sample_rate) CRTttl = event_rate(CPttl, input_sample_rate) CRTd3 = event_rate(CPd3, input_sample_rate) CRTd3R = event_rate(CPd3Rwave, input_sample_rate) #---------------------------------------------------------------- DownSampling logger.info("Downsampling") downsampled_respiratory = downsample(respiratory_spline, sample_ratio) downsampled_cardiac = downsample(cardiac_spline, sample_ratio) #-------------------------------------------------------------------- Plotting if plots_requested: # Butterworth filter analysis summary plt.figure(1) plt.title("Butterworth filter spectral analysis") plt.subplot(311) plt.semilogy(butter_sample_frequences * nyquist, butter_response_magnitude) plt.ylabel('Magnitude') plt.title('Bode Plot') plt.ylim((10e-4, 10e0)) plt.xlim((0.0, options.fstop)) plt.figure(1) plt.subplot(312) plt.plot(freqs, pxx) plt.ylabel('Respiratory Power') plt.xlim((0.0, options.fstop)) plt.figure(1) plt.subplot(313) plt.plot(Cfreqs, Cpxx) plt.xlabel('frequency(Hz)') plt.ylabel('Cardiac Power') plt.xlim((0.0, options.fstop)) # overview of the filter and spline interpolated signals plt.figure(2) plt.subplot(211) plt.plot(respiratory_xaxis, filtered_respiratory, 'b-', respiratory_xaxis, respiratory_spline, 'go', respiratory_xaxis, respiratory_signal, 'k', respiratory_xaxis, respiratory_residual, 'r') plt.legend(('Filtered', 'Spline', 'Raw', 'Residual'), shadow=False, loc=1) ltext = plt.gca().get_legend().get_texts() plt.setp(ltext[0], fontsize=8, color='b') plt.setp(ltext[1], fontsize=8, color='g') plt.setp(ltext[2], fontsize=8, color='k') plt.setp(ltext[3], fontsize=8, color='r') plt.title('Respiratory') plt.subplot(212) plt.plot(cardiac_xaxis, filtered_cardiac, 'b-', cardiac_xaxis, cardiac_spline, 'go', cardiac_xaxis, cardiac_signal, 'k', cardiac_xaxis, cardiac_residual, 'r') plt.legend(('Filtered', 'Spline', 'Raw', 'Residual'), shadow=False, loc=1) ltext = plt.gca().get_legend().get_texts() plt.setp(ltext[0], fontsize=8, color='b') plt.setp(ltext[1], fontsize=8, color='g') plt.setp(ltext[2], fontsize=8, color='k') plt.setp(ltext[3], fontsize=8, color='r') plt.title('Cardiac') # top and bottom peak finding and envelopes chart_envelopes(respiratory_spline, respiratory_maxima, resp_top_envelope, respiratory_minima, resp_bottom_envelope, rvt, 'Respiratory Data', ('Respiratory Spline', 'Top Envelope', 'Bottom Envelope', 'Maxima', 'Minima', 'RVT'), figure=3, row=1) chart_envelopes(cardiac_spline, cardiac_maxima, card_top_envelope, cardiac_minima, card_bottom_envelope, cvt, 'Cardiac Data', ('Cardiac Spline', 'Top Envelope', 'Bottom Envelope', 'Maxima', 'Minima', 'CVT'), figure=3, row=2) # summary of the volume over time waves and cardiac wave event finding results signals_to_plot = (CRTd3R, CRTttl, RRT, CRTd3, rvt, cvt) labels = ('CRTd3R', 'CRTttl', 'RRT', 'CRTd3', 'RVT', 'CVT') chart_summary_of_signals(signals_to_plot, labels, figure=5) # cardiac wave events viewed on the spline # make cardiac_d3 in roughly the same range as the spline adjustment_ratio = numpy.nanmax(cardiac_spline) / numpy.nanmax( smoothed_caradiac_d3) adjusted_d3 = smoothed_caradiac_d3 * 1.5 * adjustment_ratio plt.figure(6) plt.title('Event Locations on the Cardiac Waveform') plt.hold() plt.plot(cardiac_signal, '#aaaaaa') plt.plot(cardiac_spline, 'k') plt.plot(adjusted_d3, '#ff5555') plt.plot(indices_of(CPttl), cardiac_spline[CPttl], 'yo') plt.plot(indices_of(CPd3), cardiac_spline[CPd3], 'bo') plt.plot(indices_of(CPd3Rwave), cardiac_spline[CPd3Rwave], 'go') plt.legend(('Raw Cardiac', 'Cardiac Spline', 'Cardiac d3', 'CPttl', 'CPd3', 'CPd3Rwave'), shadow=False, loc=1) ltext = plt.gca().get_legend().get_texts() plt.setp(ltext[0], fontsize=8, color='#aaaaaa') plt.setp(ltext[1], fontsize=8, color='k') plt.setp(ltext[2], fontsize=8, color='#ff5555') plt.setp(ltext[3], fontsize=8, color='y') plt.setp(ltext[4], fontsize=8, color='b') plt.setp(ltext[5], fontsize=8, color='g') #--------------------------------------------------- Trim length for retroicor logger.info("Trim length") # expected length at input sample rate ei = tr_time * num_tr * input_sample_rate # expected length at downsample rate ed = tr_time * num_tr * output_sample_rate if (truncate == 'end'): logger.info("Truncating at end of signal") respiratory_spline = respiratory_spline[0:ei] cardiac_spline = cardiac_spline[0:ei] CPd3 = CPd3[0:ei] CPd3Rwave = CPd3Rwave[0:ei] CPttl = CPttl[0:ei] RRT = RRT[0:ei] CRTd3 = CRTd3[0:ei] CRTttl = CRTttl[0:ei] CRTd3R = CRTd3R[0:ei] resp_top_envelope = resp_top_envelope[0:ei] card_top_envelope = card_top_envelope[0:ei] downsampled_cardiac = downsampled_cardiac[0:ed] downsampled_respiratory = downsampled_respiratory[0:ed] elif (truncate == 'front'): logger.info("Truncating at front of signal") ei_front = len(respiratory_spline) - ei ed_front = len(downsampled_respiratory) - ed respiratory_spline = respiratory_spline[ei_front:] cardiac_spline = cardiac_spline[ei_front:] CPd3 = CPd3[ei_front:] CPd3Rwave = CPd3Rwave[ei_front:] CPttl = CPttl[ei_front:] RRT = RRT[ei_front:] CRTd3 = CRTd3[ei_front:] CRTttl = CRTttl[ei_front:] CRTd3R = CRTd3R[ei_front:] resp_top_envelope = resp_top_envelope[ei_front:] card_top_envelope = card_top_envelope[ei_front:] downsampled_cardiac = downsampled_cardiac[ed_front:] downsampled_respiratory = downsampled_respiratory[ei_front:] else: logger.info("Not truncating signal to expected TR size.") #---------------------------------------------------------------- Reporting peaktypes = {"CPttl": CPttl, "CPd3": CPd3, "CPd3Rwave": CPd3Rwave} rpt = report_value # just aliasing the report_value function tblhdr_fmt = "%-14s%-14s%-14s" tblrow_fmt = "%-14d%-14.4f%-14.4f" lbl_fmt = "%-14s%-14s" divider_width = 90 divider = "=" * divider_width print rpt(lbl_fmt % ("Peak type", "Aspect"), tblhdr_fmt % ('N', 'mean', 'std')) print divider for peak_label, peakseries in peaktypes.iteritems(): mags = cardiac_spline[peakseries] times = indices_of(peakseries) dt = diff(times) gt3std = mags[mags > mean(mags) + 3 * std(mags)] gt4std = mags[mags > mean(mags) + 4 * std(mags)] gtmean = mags[mags > mean(mags) + 1.0] gtbelow = mags[mags > mean(mags) - 0.4] reports = { "all timepoints": peakseries, "peak magnitudes": mags, "peak times": times, "interpeak times": dt, "peaks > 3std": gt3std, "peaks > 4std": gt4std, "peaks > mean + 1": gtmean, "peaks > mean - 0.4": gtbelow } for report_label, report_series in reports.iteritems(): n, m, s = stat_summary(report_series) rpt(lbl_fmt % (peak_label, report_label), tblrow_fmt % (n, m, s)) # Dumping results to files if (options.saveFiles): logger.info("Saving files") signals = { 'resp_spline': respiratory_spline, 'card_spline': cardiac_spline, 'RVT': rvt, 'RRT': RRT, 'CRTd3': CRTd3, 'CRTd3R': CRTd3R, 'CRTttl': CRTttl } for fbase, signal in signals.iteritems(): dump(zeromin(signal), fbase, prefix, input_sample_rate) for tr_label, tr_multiple in { 'TR_': 1.0, 'HalfTR_': 0.5 }.iteritems(): resampled_signal = resample_to_tr(signal, input_sample_rate, num_tr, tr_time, tr_multiple) dump(zeromin(resampled_signal), tr_label + fbase, prefix, input_sample_rate) peaksigs = {'CPd3': CPd3, 'CPd3R': CPd3Rwave, 'CPttl': CPttl} for fbase, peak in peaksigs.iteritems(): dump(peak, fbase, prefix, input_sample_rate) # probably not needed, left over from original physionoise.py dump(zeromin(downsampled_respiratory), 'resp_spline_downsampled', prefix, output_sample_rate) if plots_requested: plt.show() sys.exit(0)