From d7618402f2a83572b122715796f20cb471bae5f3 Mon Sep 17 00:00:00 2001 From: "Michael D. M. Dryden" Date: Fri, 1 Apr 2016 17:49:56 -0400 Subject: [PATCH] Added new analysis options. --- dstat_interface/analysis.py | 166 +++++++++++++++++ .../interface/analysis_options.glade | 167 ++++++++++++++++++ .../interface/dstatinterface.glade | 28 +++ dstat_interface/interface/save.py | 19 +- dstat_interface/main.py | 25 +++ dstat_interface/params.py | 2 + 6 files changed, 406 insertions(+), 1 deletion(-) create mode 100755 dstat_interface/analysis.py create mode 100644 dstat_interface/interface/analysis_options.glade diff --git a/dstat_interface/analysis.py b/dstat_interface/analysis.py new file mode 100755 index 0000000..bdfc9d2 --- /dev/null +++ b/dstat_interface/analysis.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# DStat Interface - An interface for the open hardware DStat potentiostat +# Copyright (C) 2014 Michael D. M. Dryden - +# Wheeler Microfluidics Laboratory +# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Functions for analyzing data. +""" +import logging + +import pygtk +import gtk +from numpy import mean + +logger = logging.getLogger(__name__) + +class AnalysisOptions(object): + """Analysis options window.""" + def __init__(self, builder): + self.builder = builder + self.builder.add_from_file('interface/analysis_options.glade') + self.builder.connect_signals(self) + + self.window = self.builder.get_object('analysis_dialog') + self.stats_button = self.builder.get_object('stats_button') + self.stats_start = self.builder.get_object('stats_start_spin') + self.stats_start_button = self.builder.get_object('stats_start_button') + self.stats_stop = self.builder.get_object('stats_stop_spin') + self.stats_stop_button = self.builder.get_object('stats_stop_button') + + self.stats_button.connect('toggled', + self.on_button_toggled_hide, + [self.stats_stop, + self.stats_stop_button, + self.stats_start, + self.stats_start_button + ] + ) + + self._params = {'stats_true':False, + 'stats_start_true':False, + 'stats_stop':0, + 'stats_stop_true':False, + 'stats_start':0 + } + + def show(self): + """Show options window.""" + self.window.run() + self.window.hide() + + def on_button_toggled_hide(self, control, widgets): + """Hide unchecked fields""" + active = control.get_active() + + for widget in widgets: + widget.set_sensitive(active) + + @property + def params(self): + """Getter for analysis params""" + self._params['stats_true'] = self.stats_button.get_active() + + self._params['stats_start_true'] = self.stats_start_button.get_active() + self._params['stats_start'] = self.stats_start.get_value() + + self._params['stats_stop_true'] = self.stats_stop_button.get_active() + self._params['stats_stop'] = self.stats_stop.get_value() + + return self._params + + @params.setter + def params(self, params): + try: + for key in self._params: + self._params[key] = params[key] + except KeyError as e: + logger.warning("Missing parameter - %s" % e) + + self.stats_button.set_active(self._params['stats_true']) + self.stats_start_button.set_active(self._params['stats_start_true']) + self.stats_start.set_value(self._params['stats_start']) + self.stats_stop_button.set_active(self._params['stats_stop_true']) + self.stats_stop.set_value(self._params['stats_stop']) + +def do_analysis(experiment): + """Takes an experiment class instance and runs selected analysis.""" + + experiment.analysis = {} + + if experiment.parameters['stats_true']: + if (experiment.parameters['stats_start_true'] or + experiment.parameters['stats_stop_true']): + + if experiment.parameters['stats_start_true']: + start = experiment.parameters['stats_start'] + else: + start = min(experiment.data[0][0]) + + if experiment.parameters['stats_stop_true']: + stop = experiment.parameters['stats_stop'] + else: + stop = min(experiment.data[0][0]) + + data = _data_slice(experiment.data, + start, + stop + ) + else: + data = experiment.data + + experiment.analysis.update(_summary_stats(data)) + +def _data_slice(data, start, stop): + """Accepts data (as list of tuples of lists) and returns copy of data + between start and stop (in whatever x-axis units for the experiment type). + """ + output = [] + + for scan in range(len(data)): + t = [] + for i in range(len(data[scan])): + t.append([]) + output.append(tuple(t)) + + for i in range(len(data[scan][0])): # x-axis column + if data[scan][0][i] >= start or data[scan][0][i] <= stop: + for d in range(len(output[scan])): + output[scan][d].append(data[scan][d][i]) + + return output + +def _summary_stats(data): + """Takes data and returns summary statistics of first y variable as dict of + name, (scan, values). + """ + + stats = {'min':[],'max':[], 'mean':[]} + + for scan in range(len(data)): + stats['min'].append( + (scan, min(data[scan][1])) + ) + stats['max'].append( + (scan, max(data[scan][1])) + ) + stats['mean'].append( + (scan, mean(data[scan][1])) + ) + return stats + + diff --git a/dstat_interface/interface/analysis_options.glade b/dstat_interface/interface/analysis_options.glade new file mode 100644 index 0000000..be8262f --- /dev/null +++ b/dstat_interface/interface/analysis_options.glade @@ -0,0 +1,167 @@ + + + + + + -3000 + 9999 + 0.5 + 10 + + + -3000 + 9999 + 0.5 + 10 + + + False + 5 + Analysis Options… + dialog + + + True + False + 2 + + + True + False + end + + + + + + gtk-ok + True + True + True + True + + + False + False + 1 + + + + + True + True + 0 + + + + + True + False + 4 + 2 + + + + + + + + + True + False + True + + False + False + True + True + stats_start_adj + 0.050000000000000003 + 2 + True + + + 1 + 2 + 1 + 2 + + + + + True + False + True + + False + False + True + True + stats_stop_adj + 0.050000000000000003 + 2 + True + if-valid + + + 1 + 2 + 2 + 3 + + + + + Generate Summary Stats + True + True + False + bottom + True + + + 2 + + + + + Stat Start (mV or s) + True + False + True + False + True + + + 1 + 2 + + + + + Stat Stop (mV or s) + True + False + True + False + True + + + 2 + 3 + + + + + True + True + 1 + + + + + + ok_button + + + diff --git a/dstat_interface/interface/dstatinterface.glade b/dstat_interface/interface/dstatinterface.glade index dc598ce..8ae7a00 100644 --- a/dstat_interface/interface/dstatinterface.glade +++ b/dstat_interface/interface/dstatinterface.glade @@ -744,6 +744,11 @@ Thanks to Christian Fobel for help with Dropbot Plugin False gtk-open + + True + False + gtk-preferences + @@ -864,6 +869,29 @@ Thanks to Christian Fobel for help with Dropbot Plugin + + + True + False + Analysis + + + True + False + + + Analysis options… + True + False + image6 + False + + + + + + + True diff --git a/dstat_interface/interface/save.py b/dstat_interface/interface/save.py index d443194..3f8917b 100755 --- a/dstat_interface/interface/save.py +++ b/dstat_interface/interface/save.py @@ -240,7 +240,9 @@ def text(exp, data, path, auto=False): time = exp.time - header = "".join(['#', time.isoformat(), "\n#"]) + header = "".join(['# TIME ', time.isoformat(), "\n"]) + + header += "# DSTAT COMMANDS\n# " for i in exp.commands: header += i @@ -262,6 +264,21 @@ def text(exp, data, path, auto=False): pass file.write("".join([header, '\n'])) + + analysis_buffer = [] + + if exp.analysis != {}: + analysis_buffer.append("# ANALYSIS") + for key, value in exp.analysis.iteritems(): + analysis_buffer.append("# %s:" % key) + for scan in value: + number, result = scan + analysis_buffer.append( + "# Scan %s -- %s" % (number, result) + ) + + for i in analysis_buffer: + file.write("%s\n" % i) # Write out actual data line_buffer = [] diff --git a/dstat_interface/main.py b/dstat_interface/main.py index e84ed0a..c7526f4 100755 --- a/dstat_interface/main.py +++ b/dstat_interface/main.py @@ -57,6 +57,7 @@ import plot import microdrop import params import parameter_test +import analysis from errors import InputError, VarError, ErrorLogger _logger = ErrorLogger(sender="dstat-interface-main") @@ -87,6 +88,7 @@ class Main(object): self.period_window = self.builder.get_object('period_box') self.exp_window = exp_window.Experiments(self.builder) + self.analysis_opt_window = analysis.AnalysisOptions(self.builder) # Setup Autosave self.autosave_checkbox = self.builder.get_object('autosave_checkbutton') @@ -183,6 +185,9 @@ class Main(object): """Display the about window.""" self.response = self.aboutdialog.run() # waits for user to click close self.aboutdialog.hide() + + def on_menu_analysis_options_activate(self, menuitem, data=None): + self.analysis_opt_window.show() def on_expcombobox_changed(self, data=None): """Change the experiment window when experiment box changed.""" @@ -448,6 +453,7 @@ class Main(object): parameters['shutter_true'] = False try: parameters.update(self.adc_pot.params) + parameters.update(self.analysis_opt_window.params) self.line = 0 self.lastline = 0 @@ -668,6 +674,10 @@ class Main(object): gobject.source_remove(self.plot_proc) # stop automatic plot update self.experiment_running_plot() # make sure all data updated on plot + # Run Analysis + + analysis.do_analysis(self.current_exp) + self.databuffer.set_text("") self.databuffer.place_cursor(self.databuffer.get_start_iter()) self.rawbuffer.insert_at_cursor("\n") @@ -697,6 +707,21 @@ class Main(object): self.rawbuffer.insert_at_cursor("\n") # Data Output + analysis_buffer = [] + + if self.current_exp.analysis != {}: + analysis_buffer.append("# ANALYSIS") + for key, value in self.current_exp.analysis.iteritems(): + analysis_buffer.append("# %s:" % key) + for scan in value: + number, result = scan + analysis_buffer.append( + "# Scan %s -- %s" % (number, result) + ) + + for i in analysis_buffer: + self.rawbuffer.insert_at_cursor("%s\n" % i) + line_buffer = [] for scan in self.current_exp.data: diff --git a/dstat_interface/params.py b/dstat_interface/params.py index 565fe4b..91be380 100755 --- a/dstat_interface/params.py +++ b/dstat_interface/params.py @@ -41,6 +41,7 @@ def get_params(window): except InputError: _logger.error("No gain selected.", 'INFO') parameters.update(window.exp_window.get_params(selection)) + parameters.update(window.analysis_opt_window.params) return parameters @@ -71,5 +72,6 @@ def load_params(window, path): window.expcombobox.set_active( window.exp_window.classes[params['experiment_index']][0]) window.exp_window.set_params(params['experiment_index'], params) + window.analysis_opt_window.params = params window.params_loaded = True \ No newline at end of file -- GitLab