From 3f5c82e13b70184b350e10a3d6300b085a2d60a5 Mon Sep 17 00:00:00 2001 From: Alexander Minges Date: Fri, 15 Apr 2016 15:09:18 +0200 Subject: [PATCH] improve color handling; fix crash if using log scaled plots and zero values --- analyze-cli.py | 52 +++++++++++++++++++++++++++++++++++------ libkinetics/__init__.py | 43 +++++++++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/analyze-cli.py b/analyze-cli.py index 5a0988d..a3d9a4f 100755 --- a/analyze-cli.py +++ b/analyze-cli.py @@ -49,6 +49,8 @@ class ExperimentHelper(): self.logx = logx self.logy = logy self.unit = unit + + self.colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k'] def linear_regression_function(self, slope, x, intercept): y = slope * x + intercept @@ -64,19 +66,28 @@ class ExperimentHelper(): ax_title = 'Linear regression {} {}'.format(m.concentration, m.concentration_unit) ax.set_title(ax_title) - + + color_index = 0 + for r in m.replicates: + # TODO: If number of colors exceeds predefined colors, calculate a proper palette + if color_index < len(self.colors): + color = self.colors[color_index] + else: + color = 'k' ax.plot(r.x, r.y, + '{}o'.format(color), linestyle='None', - marker='o', ms=3, fillstyle='none', label='replicate #{}'.format(r.num)) y = self.linear_regression_function(r.fitresult['slope'], r.x, r.fitresult['intercept']) - ax.plot(r.x, y, 'k-') + ax.plot(r.x, y, '{}-'.format(color), label='linear fit #{}'.format(r.num)) ax.axvspan(m.xlim[0], m.xlim[1], facecolor='0.8', alpha=0.5) + + color_index += 1 plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.savefig('{}/fit_{}_{}.png'.format(outpath, m.concentration, @@ -92,9 +103,24 @@ class ExperimentHelper(): ax.set_title('Kinetics') if self.logx: + if 0 in exp.raw_kinetic_data['x']: + index = exp.raw_kinetic_data['x'].index(0) + exp.raw_kinetic_data['x'].pop(index) + exp.raw_kinetic_data['y'].pop(index) + exp.raw_kinetic_data['yerr'].pop(index) + self.logger.warn(' Data in x contains zero values.') ax.set_xscale('log') + if self.logy: + if 0 in exp.raw_kinetic_data['y']: + + index = exp.raw_kinetic_data['y'].index(0) + exp.raw_kinetic_data['x'].pop(index) + exp.raw_kinetic_data['y'].pop(index) + exp.raw_kinetic_data['yerr'](index) + self.logger.warn(' Data in y contains zero values.') ax.set_yscale('log') + ax.errorbar(exp.raw_kinetic_data['x'], exp.raw_kinetic_data['y'], @@ -168,6 +194,10 @@ def parse_arguments(): '--replicates', action='store_true', help='fit kinetics to individual replicates') + parser.add_argument('-ep', + '--end-point', + action='store_true', + help='use end point determination instead of linear fit') parser.add_argument('-nm', '--no-michaelis', action='store_true', @@ -268,6 +298,12 @@ def main(): # grab fitting window from provided arguments fitting_window = (args.start, args.end) + # use end point determination + if args.end_point: + end_point = args.end_point + else: + end_point = False + # scaling of axes in kinetics plot if args.log_x: logx = args.log_x @@ -321,17 +357,19 @@ def main(): logger.info('{}'.format(msg)) exp = libkinetics.Experiment(data_files, fitting_window, + end_point=end_point, do_hill=do_hill, no_mm=no_mm, logger=logger, fit_to_replicates=fit_to_replicates) ehlp = ExperimentHelper(exp, logger, args.unit, logx=logx, logy=logy) - logger.info('Plotting linear fits') - ehlp.plot_data(str(output_path)) - logger.info('Plotting kinetic fit(s)') - ehlp.plot_kinetics(str(output_path)) + if not end_point: + logger.info('Plotting linear fits') + ehlp.plot_data(str(output_path)) logger.info('Writing results to results.csv') ehlp.write_data(str(output_path)) + logger.info('Plotting kinetic fit(s)') + ehlp.plot_kinetics(str(output_path)) logger.info('Finished!') else: msg = '{} is not a directory!'.format(input_path) diff --git a/libkinetics/__init__.py b/libkinetics/__init__.py index ce2bcbc..012c8d5 100644 --- a/libkinetics/__init__.py +++ b/libkinetics/__init__.py @@ -64,7 +64,38 @@ class Replicate(): self.x, self.y = xy self.owner = owner self.xlim = owner.xlim - self.fitresult = self.fit() + self.conc = '{} {}'.format(self.owner.concentration, + self.owner.concentration_unit) + if not self.owner.owner.end_point: + self.fitresult = self.fit() + else: + self.fitresult = self.end_point_determination() + + + def end_point_determination(self): + ind_min_max = np.where((self.x >= self.xlim[0]) & (self.x <= + self.xlim[1]))[0] + x_start = self.x[ind_min_max[0]] + x_end = self.x[ind_min_max[-1]] + + y_start = self.y[ind_min_max[0]] + y_end = self.y[ind_min_max[-1]] + + slope = (y_end - y_start) / (x_end - x_start) + + self.logger.info('End point determination for {} #{}:'.format(self.conc, self.num)) + self.logger.info(' start x/y: {}/{}'.format(x_start, float(y_start))) + self.logger.info(' end x/y: {}/{}'.format(x_end, float(y_end))) + self.logger.info(' slope: {}'.format(float(slope))) + + return { + 'slope': slope, + 'intercept': None, + 'r_value': None, + 'r_squared': None, + 'p_value': None, + 'std_err': None + } def fit(self): ind_min_max = np.where((self.x >= self.xlim[0]) & (self.x <= @@ -88,11 +119,8 @@ class Replicate(): # calculcate adjusted R² adj_r_squared = r_squared - (1 - r_squared) * k/(n - k - 1) - - conc = '{} {}'.format(self.owner.concentration, - self.owner.concentration_unit) - self.logger.info('Linear fit for {} #{}:'.format(conc, self.num)) + self.logger.info('Linear fit for {} #{}:'.format(self.conc, self.num)) if adj_r_squared < 0.9 and adj_r_squared > 0.7: msg = ' adjusted R² < 0.9; Check fit manually!' self.logger.warning(msg.format(round(adj_r_squared, 4))) @@ -232,7 +260,7 @@ class Experiment(): """ def __init__(self, data_files, xlim, do_hill=False, no_mm=False, - fit_to_replicates=False, logger=None): + fit_to_replicates=False, logger=None, end_point=False): """ Inits Experiment class with experimental parameters @@ -267,6 +295,8 @@ class Experiment(): # dictionary to store data for the kinetics calculation self.raw_kinetic_data = {'x': [], 'y': [], 'yerr': []} self.xlim = xlim + + self.end_point = end_point # parse data files and generate measurements for csvfile in data_files: @@ -317,6 +347,7 @@ class Experiment(): self.raw_kinetic_data['x'].append(m.concentration) self.raw_kinetic_data['y'].append(np.absolute(m.avg_slope)) self.raw_kinetic_data['yerr'].append(m.avg_slope_err) + # calculate kinetics if not no_mm: