Updated FlatPeaksCommand to ColumnAddingCommand.
authorW. Trevor King <wking@drexel.edu>
Wed, 11 Aug 2010 21:11:18 +0000 (17:11 -0400)
committerW. Trevor King <wking@drexel.edu>
Wed, 11 Aug 2010 21:11:18 +0000 (17:11 -0400)
hooke/plugin/flatfilt.py
test/flat_filter_peaks.py [new file with mode: 0644]
test/flat_filter_playlist.py [moved from test/flat_filter.py with 100% similarity]

index ed08a34..7c0b534 100644 (file)
@@ -35,15 +35,15 @@ See Also
 import copy
 from Queue import Queue
 
-from numpy import diff
+from numpy import absolute, diff
 from scipy.signal.signaltools import medfilt
 
-from ..command import Command, Argument, Success, Failure, UncaughtException
+from ..command import Argument, Success, Failure, UncaughtException
 from ..config import Setting
 from ..curve import Data
 from ..experiment import VelocityClamp
 from ..plugin import Plugin, argument_to_setting
-from ..plugin.curve import CurveArgument
+from ..plugin.curve import ColumnAddingCommand
 from ..plugin.playlist import FilterCommand
 from ..util.fit import PoorFit
 from ..util.peak import (find_peaks, peaks_to_mask,
@@ -84,33 +84,6 @@ Minimum number of peaks for curve acceptance.
             self._settings.append(argument_to_setting(
                     self.setting_section, argument))
             argument.default = None # if argument isn't given, use the config.
-        self._arguments.extend([ # Non-configurable arguments
-                Argument(name='retract block', type='string',
-                         default='retract',
-                         help="""
-Name of the retracting data block.
-""".strip()),
-                Argument(name='input distance column', type='string',
-                         default='surface distance (m)',
-                         help="""
-Name of the column to use as the surface position input.
-""".strip()),
-                Argument(name='input deflection column', type='string',
-                         default='deflection (N)',
-                         help="""
-Name of the column to use as the deflection input.
-""".strip()),
-                Argument(name='output peak column', type='string',
-                         default='peak deflection',
-                         help="""
-Name of the column (without units) to use as the peak-maske deflection output.
-""".strip()),
-                Argument(name='peak info name', type='string',
-                         default='flat filter peaks',
-                         help="""
-Name for storing the list of peaks in the `.info` dictionary.
-""".strip()),
-                ])
         self._commands = [FlatPeaksCommand(self), FlatFilterCommand(self)]
 
     def dependencies(self):
@@ -120,7 +93,7 @@ Name for storing the list of peaks in the `.info` dictionary.
         return self._settings
 
 
-class FlatPeaksCommand (Command):
+class FlatPeaksCommand (ColumnAddingCommand):
     """Detect peaks in velocity clamp data using noise statistics.
 
     Notes
@@ -144,63 +117,73 @@ class FlatPeaksCommand (Command):
         # function, just detecting peaks.
         super(FlatPeaksCommand, self).__init__(
             name='flat filter peaks',
+            columns=[
+                ('distance column', 'surface distance (m)', """
+Name of the column to use as the surface position input.
+""".strip()),
+                ('deflection column', 'surface deflection (m)', """
+Name of the column to use as the deflection input.
+""".strip()),
+                ],
+            new_columns=[
+                ('output peak column', 'flat filter peaks', """
+Name of the column (without units) to use as the peak output.
+""".split()),
+                ],
             arguments=[
-                CurveArgument,
+                Argument(name='peak info name', type='string',
+                         default='flat filter peaks',
+                         help="""
+Name (without units) for storing the list of peaks in the `.info`
+dictionary.
+""".strip()),
                 ] + plugin_arguments,
             help=self.__doc__, plugin=plugin)
 
     def _run(self, hooke, inqueue, outqueue, params):
-        data,block_index,z_data,d_data,params = self._setup(hooke, params)
-        start_index = 0
-        while (start_index < len(z_data)
-               and z_data[start_index] < params['blind window']):
-            start_index += 1
-        median = medfilt(d_data[start_index:], params['median window'])
+        params = self.__setup_params(hooke=hooke, params=params)
+        block = self._block(hooke=hooke, params=params)
+        dist_data = self._get_column(hooke=hooke, params=params,
+                                     column_name='distance column')
+        def_data = self._get_column(hooke=hooke, params=params,
+                                     column_name='deflection column')
+        start_index = absolute(dist_data-params['blind window']).argmin()
+        median = medfilt(def_data[start_index:], params['median window'])
         deriv = diff(median)
         peaks = find_peaks(deriv, **_kwargs(params, find_peaks_arguments,
                                             argument_input_keys=True))
-        d_name,d_unit = split_data_label(params['input deflection column'])
         for i,peak in enumerate(peaks):
-            peak.name = 'flat filter peak %d of %s' % (i, d_name)
+            peak.name = self._peak_name(params, i)
             peak.index += start_index
-        data.info[params['peak info name']] = peaks
-
-        # Add a peak-masked deflection column.
-        new = Data((data.shape[0], data.shape[1]+1), dtype=data.dtype)
-        new.info = copy.deepcopy(data.info)
-        new[:,:-1] = data
-        d_name,d_unit = split_data_label(params['input deflection column'])
-        new.info['columns'].append(
-            join_data_label(params['output peak column'], d_unit))
-        new[:,-1] = peaks_to_mask(d_data, peaks) * d_data
+        block.info[params['peak info name']] = peaks
+
+        self._set_column(hooke=hooke, params=params,
+                         column_name='output peak column',
+                         values=peaks_to_mask(def_data, peaks) * def_data)
         outqueue.put(peaks)
-        params['curve'].data[block_index] = new
 
-    def _setup(self, hooke, params):
+    def __setup_params(self, hooke, params):
         """Setup `params` from config and return the z piezo and
         deflection arrays.
         """
-        curve = params['curve']
+        curve = self._curve(hooke=hooke, params=params)
         if curve.info['experiment'] != VelocityClamp:
             raise Failure('%s operates on VelocityClamp experiments, not %s'
                           % (self.name, curve.info['experiment']))
-        data = None
-        for i,block in enumerate(curve.data):
-            if block.info['name'].startswith(params['retract block']):
-                data = block
-                block_index = i
-                break
-        if data == None:
-            raise Failure('No retraction blocks in %s.' % curve)
-        z_data = data[:,data.info['columns'].index(
-                params['input distance column'])]
-        d_data = data[:,data.info['columns'].index(
-                params['input deflection column'])]
         for key,value in params.items():
             if value == None: # Use configured default value.
                 params[key] = self.plugin.config[key]
         # TODO: convert 'see double' from nm to points
-        return (data, block_index, z_data, d_data, params)
+        name,def_unit = split_data_label(params['deflection column'])
+        params['output peak column'] = join_data_label(
+            params['output peak column'], def_unit)
+        params['peak info name'] = join_data_label(
+            params['peak info name'], def_unit)
+        return params
+
+    def _peak_name(self, params, index):
+        d_name,d_unit = split_data_label(params['deflection column'])
+        return 'flat filter peak %d of %s' % (index, d_name)
 
 
 class FlatFilterCommand (FilterCommand):
@@ -228,7 +211,7 @@ class FlatFilterCommand (FilterCommand):
     def __init__(self, plugin):
         super(FlatFilterCommand, self).__init__(
             plugin, name='flat filter playlist')
-        self.arguments.extend(plugin._arguments)
+        self.arguments.extend(plugin._arguments)  # TODO: curve & block arguments
 
     def filter(self, curve, hooke, inqueue, outqueue, params):
         params['curve'] = curve
diff --git a/test/flat_filter_peaks.py b/test/flat_filter_peaks.py
new file mode 100644 (file)
index 0000000..fd8a578
--- /dev/null
@@ -0,0 +1,51 @@
+# Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of Hooke.
+#
+# Hooke is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Hooke 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 Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Hooke.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""
+>>> from hooke.hooke import Hooke, HookeRunner
+>>> h = Hooke()
+>>> r = HookeRunner()
+>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS
+<FilePlaylist test.hkp>
+Success
+<BLANKLINE>
+>>> h = r.run_lines(h,
+...     ['zero_surface_contact_point --block retract']
+...     ) # doctest: +ELLIPSIS, +REPORT_UDIFF
+{'info':...}
+Success
+<BLANKLINE>
+>>> h = r.run_lines(h, ['flat_filter_peaks --block retract']
+...     ) # doctest: +ELLIPSIS
+[<Peak flat filter peak 0 of surface deflection 610 [ -1.156...e-09  -8.840...e-10  -3.173...e-10  -7.480...e-10]>]
+Success
+<BLANKLINE>
+>>> curve = h.playlists.current().current()
+>>> retract = curve.data[-1]
+>>> retract.info['flat filter peaks (m)']  # doctest: +ELLIPSIS
+[<Peak flat filter peak 0 of surface deflection 610 [ -1.156...e-09  -8.840...e-10  -3.173...e-10  -7.480...e-10]>]
+>>> retract.info['columns']
+['z piezo (m)', 'deflection (m)', 'surface distance (m)', 'surface deflection (m)', 'flat filter peaks (m)']
+>>> retract[:5,-1]  # doctest: +ELLIPSIS
+Data([-0., -0., -0., -0., -0.])
+>>> retract[609:615,-1]  # doctest: +ELLIPSIS
+Data([  0.000...e+00,   2.380...e-09,   9.747...e-10,
+        -2.266...e-10,   9.071...e-11,  -0.000...e+00])
+>>> retract[-5:,-1]  # doctest: +ELLIPSIS
+Data([-0., -0., -0., -0., -0.])
+"""