1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2010 Alberto Gomez-Casado
5 # Massimo Sandal <devicerandom@gmail.com>
6 # W. Trevor King <wking@drexel.edu>
8 # This file is part of Hooke.
10 # Hooke is free software: you can redistribute it and/or
11 # modify it under the terms of the GNU Lesser General Public
12 # License as published by the Free Software Foundation, either
13 # version 3 of the License, or (at your option) any later version.
15 # Hooke is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU Lesser General Public License for more details.
20 # You should have received a copy of the GNU Lesser General Public
21 # License along with Hooke. If not, see
22 # <http://www.gnu.org/licenses/>.
24 """The ``flatfilt`` module provides :class:`~FlatFiltPlugin` and
25 several associated :class:`~hooke.command.Command`\s for removing flat
26 (featureless) :mod:`~hooke.curve.Curve`\s from
27 :class:`~hooke.playlist.Playlist`\s.
31 :mod:`~hooke.plugin.convfilt` for a convolution-based filter for
32 :class:`~hooke.experiment.VelocityClamp` experiments.
36 from multiprocessing import Queue
38 from numpy import diff
39 from scipy.signal.signaltools import medfilt
41 from ..command import Command, Argument, Failure
42 from ..config import Setting
43 from ..experiment import VelocityClamp
44 from ..plugin import Plugin, argument_to_setting
45 from ..plugin.curve import CurveArgument
46 from ..plugin.playlist import FilterCommand
47 from ..plugin.vclamp import scale
48 from ..util.peak import find_peaks, find_peaks_arguments, Peak
51 class FlatFiltPlugin (Plugin):
52 """Standard-devitiation-based peak recognition and filtering.
55 super(FlatFiltPlugin, self).__init__(name='flatfilt')
56 self._arguments = [ # For Command initialization
57 Argument('median filter', type='int', default=7, help="""
58 Median window filter size (in points).
60 ] + copy.deepcopy(find_peaks_arguments)
61 # Set flat-filter-specific defaults for the fit_peak_arguments.
62 for key,value in [('cut side', 'both'),
65 ('min deviation', 9.0),
67 ('see double', 10e-9),
69 argument = [a for a in self._arguments if a.name == key][0]
70 argument.default = value
72 Setting(section=self.setting_section, help=self.__doc__)]
73 for argument in self._arguments:
74 self._settings.append(argument_to_setting(
75 self.setting_section, argument))
76 argument.default = None # if argument isn't given, use the config.
77 self._commands = [FlatPeaksCommand(self), FlatFilterCommand(self)]
79 def dependencies(self):
82 def default_settings(self):
86 class FlatPeaksCommand (Command):
87 """Detect peaks in velocity clamp data using noise statistics.
92 Noise analysis on the retraction curve:
94 1) A median window filter (using
95 :func:`scipy.signal.signaltools.medfilt`) smooths the
97 2) The deflection derivative is calculated (using
98 :func:`numpy.diff` which uses forward differencing).
99 3) Peaks in the derivative curve are extracted with
100 :func:`~hooke.plugins.peak.find_peaks`.
102 The algorithm was originally Francesco Musiani's idea.
104 def __init__(self, plugin):
105 config_arguments = [a for a in plugin._arguments
106 if a.name != 'min peaks']
107 # Drop min peaks, since we're not filtering with this
108 # function, just detecting peaks.
109 super(FlatPeaksCommand, self).__init__(
110 name='flat filter peaks',
111 arguments=[CurveArgument] + config_arguments,
112 help=self.__doc__, plugin=plugin)
114 def _run(self, hooke, inqueue, outqueue, params):
115 z_data,d_data,params = self._setup(params)
117 while z_data[start_index] < params['bind window']:
119 median = medfilt(d_data[start_index:], params['median window']),
121 kwargs = dict([(a.name, params[a.name]) for a in find_peaks_arguments])
122 peaks = find_peaks(deriv, **kwargs)
124 peak.name = 'flat filter of %s with %s' \
125 % (params['deflection column name'], params['convolution'])
126 peak.index += start_index
129 def _setup(self, params):
130 """Setup `params` from config and return the z piezo and
133 curve = params['curve']
134 if curve.info['experiment'] != VelocityClamp:
135 raise Failure('%s operates on VelocityClamp experiments, not %s'
136 % (self.name, curve.info['experiment']))
137 for col in ['surface z piezo (m)', 'deflection (N)']:
138 if col not in curve.data[0].info['columns']:
141 for block in curve.data:
142 if block.info['name'].startswith('retract'):
146 raise Failure('No retraction blocks in %s.' % curve)
147 z_data = data[:,data.info['columns'].index('surface z piezo (m)')]
148 if 'flattened deflection (N)' in data.info['columns']:
149 params['deflection column name'] = 'flattened deflection (N)'
151 params['deflection column name'] = 'deflection (N)'
152 d_data = data[:,data.info['columns'].index(
153 params['deflection column name'])]
154 for key,value in params.items():
155 if value == None: # Use configured default value.
156 params[key] = self.plugin.config[key]
157 return z_data,d_data,params
159 class FlatFilterCommand (FilterCommand):
160 u"""Create a subset playlist of curves with enough flat peaks.
164 This type of filter is of course very raw, and requires relatively
165 conservative settings to safely avoid false negatives (that is, to
166 avoid discarding interesting curves). Using it on the protein
167 unfolding experiments described by Sandal [#sandal2008] it has
168 been found to reduce the data set to analyze by hand by 60-80%.
170 .. [#sandal2008] M. Sandal, F. Valle, I. Tessari, S. Mammi, E. Bergantino,
171 F. Musiani, M. Brucale, L. Bubacco, B. Samorì.
172 "Conformational equilibria in monomeric α-Synuclein at the
173 single molecule level."
175 doi: `10.1371/journal.pbio.0060006 <http://dx.doi.org/10.1371/journal.pbio.0060006>`_
179 FlatCommand : Underlying flat-based peak detection.
181 def __init__(self, plugin):
182 super(FlatFilterCommand, self).__init__(
183 plugin, name='flat filter playlist')
184 self.arguments.extend(plugin._arguments)
186 def filter(self, curve, hooke, inqueue, outqueue, params):
189 conv_command = [c for c in hooke.commands
190 if c.name=='flat filter peaks'][0]
191 conv_command.run(hooke, inq, outq, **params)
193 if not isinstance(peaks[0], Peak):
194 raise Failure('Expected a list of Peaks, not %s' % peaks)
196 if not isinstance(ret, Success):
198 return len(peaks) >= params['min peaks']