From 4cf263aabeac8f1c45ce69d08c401cb54fa1eb34 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 11 Aug 2010 14:09:22 -0400 Subject: [PATCH] Add CurveCommand, BlockCommand, ColumnAccessCommand, and ColumnAddingCommand. Added to hooke.plugin.curve. These abstract out the boring bookkeeping from many commands (so far only the ones in plugin.curve). Also add test/derivative.py and rework hooke.util.calculus.derivative to be less block-centric. --- hooke/plugin/curve.py | 471 ++++++++++++++++++++++++++--------------- hooke/util/calculus.py | 33 +-- test/derivative.py | 42 ++++ test/difference.py | 12 +- 4 files changed, 363 insertions(+), 195 deletions(-) create mode 100644 test/derivative.py diff --git a/hooke/plugin/curve.py b/hooke/plugin/curve.py index 4877b5b..308124b 100644 --- a/hooke/plugin/curve.py +++ b/hooke/plugin/curve.py @@ -1,5 +1,5 @@ -# Copyright (C) 2008-2010 Alberto Gomez-Casado -# Fabrizio Benedetti +# Copyright (C) 2008-2010 Alberto Gomez-Kasai +# Fabiano's Benedetti # Massimo Sandal # W. Trevor King # @@ -37,16 +37,6 @@ from ..util.fft import unitary_avg_power_spectrum from ..util.si import ppSI, join_data_label, split_data_label - -class CurvePlugin (Builtin): - def __init__(self): - super(CurvePlugin, self).__init__(name='curve') - self._commands = [ - GetCommand(self), InfoCommand(self), DeltaCommand(self), - ExportCommand(self), DifferenceCommand(self), - DerivativeCommand(self), PowerSpectrumCommand(self)] - - # Define common or complicated arguments def current_curve_callback(hooke, command, argument, value): @@ -65,26 +55,185 @@ CurveArgument = Argument( of the current playlist. """.strip()) +def _name_argument(name, default, help): + """TODO + """ + return Argument(name=name, type='string', default=default, help=help) + +def block_argument(*args, **kwargs): + """TODO + """ + return _name_argument(*args, **kwargs) + +def column_argument(*args, **kwargs): + """TODO + """ + return _name_argument(*args, **kwargs) + + +# Define useful command subclasses + +class CurveCommand (Command): + """A :class:`~hooke.command.Command` operating on a + :class:`~hooke.curve.Curve`. + """ + def __init__(self, **kwargs): + if 'arguments' in kwargs: + kwargs['arguments'].insert(0, CurveArgument) + else: + kwargs['arguments'] = [CurveArgument] + super(CurveCommand, self).__init__(**kwargs) + + def _curve(self, hooke, params): + # HACK? rely on params['curve'] being bound to the local hooke + # playlist (i.e. not a copy, as you would get by passing a + # curve through the queue). Ugh. Stupid queues. As an + # alternative, we could pass lookup information through the + # queue... + return params['curve'] + + +class BlockCommand (CurveCommand): + """A :class:`CurveCommand` operating on a :class:`~hooke.curve.Data` block. + """ + def __init__(self, blocks=None, **kwargs): + if blocks == None: + blocks = [('block', None, 'Name of the data block to act on.')] + block_args = [] + for name,default,help in blocks: + block_args.append(block_argument(name, default, help)) + self._block_arguments = block_args + if 'arguments' not in kwargs: + kwargs['arguments'] = [] + kwargs['arguments'] = block_args + kwargs['arguments'] + super(BlockCommand, self).__init__(**kwargs) + + def _block_names(self, hooke, params): + curve = self._curve(hooke, params) + return [b.info['name'] for b in curve.data] + + def _block_index(self, hooke, params, name=None): + if name == None: + name = self._block_arguments[0].name + block_name = params[name] + if block_name == None: + curve = self._curve(hooke=hooke, params=params) + if len(curve.data) == 0: + raise Failure('no blocks in %s' % curve) + block_name = curve.data[0].info['name'] + names = self._block_names(hooke=hooke, params=params) + try: + return names.index(block_name) + except ValueError, e: + curve = self._curve(hooke, params) + raise Failure('no block named %s in %s (%s): %s' + % (block_name, curve, names, e)) + + def _block(self, hooke, params, name=None): + # HACK? rely on params['block'] being bound to the local hooke + # playlist (i.e. not a copy, as you would get by passing a + # block through the queue). Ugh. Stupid queues. As an + # alternative, we could pass lookup information through the + # queue... + curve = self._curve(hooke, params) + index = self._block_index(hooke, params, name) + return curve.data[index] + + +class ColumnAccessCommand (BlockCommand): + """A :class:`BlockCommand` accessing a :class:`~hooke.curve.Data` + block column. + """ + def __init__(self, columns=None, **kwargs): + if columns == None: + columns = [('column', None, 'Name of the data column to act on.')] + column_args = [] + for name,default,help in columns: + column_args.append(column_argument(name, default, help)) + self._column_arguments = column_args + if 'arguments' not in kwargs: + kwargs['arguments'] = [] + kwargs['arguments'] = column_args + kwargs['arguments'] + super(ColumnAccessCommand, self).__init__(**kwargs) + + def _get_column(self, hooke, params, block_name=None, column_name=None): + if column_name == None: + column_name = self._column_arguments[0].name + column_name = params[column_name] + block = self._block(hooke, params, block_name) + column_index = block.info['columns'].index(column_name) + return block[:,column_index] + + +class ColumnAddingCommand (ColumnAccessCommand): + """A :class:`ColumnAccessCommand` that also adds columns. + """ + def __init__(self, new_columns=None, **kwargs): + if new_columns == None: + new_columns = [] + column_args = [] + for name,default,help in new_columns: + column_args.append(column_argument(name, default, help)) + self._new_column_arguments = column_args + if 'arguments' not in kwargs: + kwargs['arguments'] = [] + kwargs['arguments'] = column_args + kwargs['arguments'] + super(ColumnAddingCommand, self).__init__(**kwargs) + + def _get_column(self, hooke, params, block_name=None, column_name=None): + if column_name == None and len(self._column_arguments) == 0: + column_name = self._new_column_arguments[0].name + return super(ColumnAddingCommand, self)._get_column( + hooke=hooke, params=params, block_name=block_name, + column_name=column_name) + + def _set_column(self, hooke, params, block_name=None, column_name=None, + values=None): + if column_name == None: + column_name = self._column_arguments[0].name + column_name = params[column_name] + block = self._block(hooke=hooke, params=params, name=block_name) + if column_name not in block.info['columns']: + new = Data((block.shape[0], block.shape[1]+1), dtype=block.dtype) + new.info = copy.deepcopy(block.info) + new[:,:-1] = block + new.info['columns'].append(column_name) + block = new + block_index = self._block_index(hooke, params, name=block_name) + self._curve(hooke, params).data[block_index] = block + column_index = block.info['columns'].index(column_name) + block[:,column_index] = values + + +# The plugin itself + +class CurvePlugin (Builtin): + def __init__(self): + super(CurvePlugin, self).__init__(name='curve') + self._commands = [ + GetCommand(self), InfoCommand(self), DeltaCommand(self), + ExportCommand(self), DifferenceCommand(self), + DerivativeCommand(self), PowerSpectrumCommand(self)] + # Define commands -class GetCommand (Command): +class GetCommand (CurveCommand): """Return a :class:`hooke.curve.Curve`. """ def __init__(self, plugin): super(GetCommand, self).__init__( - name='get curve', arguments=[CurveArgument], - help=self.__doc__, plugin=plugin) + name='get curve', help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - outqueue.put(params['curve']) + outqueue.put(self._curve(hooke, params)) -class InfoCommand (Command): + +class InfoCommand (CurveCommand): """Get selected information about a :class:`hooke.curve.Curve`. """ def __init__(self, plugin): args = [ - CurveArgument, Argument(name='all', type='bool', default=False, count=1, help='Get all curve information.'), ] @@ -99,6 +248,7 @@ class InfoCommand (Command): help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): + curve = self._curve(hooke, params) fields = {} for key in self.fields: fields[key] = params[key] @@ -111,7 +261,7 @@ class InfoCommand (Command): for key in self.fields: if fields[key] == True: get = getattr(self, '_get_%s' % key.replace(' ', '_')) - lines.append('%s: %s' % (key, get(params['curve']))) + lines.append('%s: %s' % (key, get(curve))) outqueue.put('\n'.join(lines)) def _get_name(self, curve): @@ -139,7 +289,7 @@ class InfoCommand (Command): return [block.shape for block in curve.data] -class DeltaCommand (Command): +class DeltaCommand (BlockCommand): """Get distance information between two points. With two points A and B, the returned distances are A-B. @@ -148,13 +298,6 @@ class DeltaCommand (Command): super(DeltaCommand, self).__init__( name='delta', arguments=[ - CurveArgument, - Argument(name='block', type='int', default=0, - help=""" -Data block that points are selected from. For an approach/retract -force curve, `0` selects the approaching curve and `1` selects the -retracting curve. -""".strip()), Argument(name='point', type='point', optional=False, count=2, help=""" Indicies of points bounding the selected data. @@ -167,7 +310,7 @@ Return distances in SI notation. help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] + data = self._block(hooke, params) As = data[params['point'][0],:] Bs = data[params['point'][1],:] ds = [A-B for A,B in zip(As, Bs)] @@ -182,7 +325,7 @@ Return distances in SI notation. outqueue.put(out) -class ExportCommand (Command): +class ExportCommand (BlockCommand): """Export a :class:`hooke.curve.Curve` data block as TAB-delimeted ASCII text. @@ -193,12 +336,6 @@ class ExportCommand (Command): super(ExportCommand, self).__init__( name='export block', arguments=[ - CurveArgument, - Argument(name='block', type='int', default=0, - help=""" -Data block to save. For an approach/retract force curve, `0` selects -the approaching curve and `1` selects the retracting curve. -""".strip()), Argument(name='output', type='file', default='curve.dat', help=""" File name for the output data. Defaults to 'curve.dat' @@ -211,15 +348,15 @@ True if you want the column-naming header line. help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] + data = self._block(hooke, params) + + with open(params['output'], 'w') as f: + if params['header'] == True: + f.write('# %s \n' % ('\t'.join(data.info['columns']))) + numpy.savetxt(f, data, delimiter='\t') - f = open(params['output'], 'w') - if params['header'] == True: - f.write('# %s \n' % ('\t'.join(data.info['columns']))) - numpy.savetxt(f, data, delimiter='\t') - f.close() -class DifferenceCommand (Command): +class DifferenceCommand (ColumnAddingCommand): """Calculate the difference between two columns of data. The difference is added to block A as a new column. @@ -231,28 +368,25 @@ class DifferenceCommand (Command): def __init__(self, plugin): super(DifferenceCommand, self).__init__( name='difference', - arguments=[ - CurveArgument, - Argument(name='block A', type='int', - help=""" -Block A in A-B. For an approach/retract force curve, `0` selects the -approaching curve and `1` selects the retracting curve. Defaults to -the first block. -""".strip()), - Argument(name='block B', type='int', - help=""" -Block B in A-B. Defaults to matching `block A`. -""".strip()), - Argument(name='column A', type='string', - help=""" + blocks=[ + ('block A', None, + 'Name of block A in A-B. Defaults to the first block'), + ('block B', None, + 'Name of block B in A-B. Defaults to matching `block A`.'), + ], + columns=[ + ('column A', None, + """ Column of data from block A to difference. Defaults to the first column. """.strip()), - Argument(name='column B', type='string', default=1, - help=""" + ('column B', None, + """ Column of data from block B to difference. Defaults to matching `column A`. """.strip()), - Argument(name='output column name', type='string', - help=""" + ], + new_columns=[ + ('output column', None, + """ Name of the new column for storing the difference (without units, defaults to `difference of and `). """.strip()), @@ -260,39 +394,50 @@ Name of the new column for storing the difference (without units, defaults to help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data_A = params['curve'].data[params['block A']] - data_B = params['curve'].data[params['block B']] - # HACK? rely on params['curve'] being bound to the local hooke - # playlist (i.e. not a copy, as you would get by passing a - # curve through the queue). Ugh. Stupid queues. As an - # alternative, we could pass lookup information through the - # queue... - new = Data((data_A.shape[0], data_A.shape[1]+1), dtype=data_A.dtype) - new.info = copy.deepcopy(data.info) - new[:,:-1] = data_A - - a_col = data_A.info['columns'].index(params['column A']) - b_col = data_A.info['columns'].index(params['column A']) - out = data_A[:,a_col] - data_B[:,b_col] - - a_name,a_units = split_data_label(params['column A']) - b_name,b_units = split_data_label(params['column B']) - assert a_units == b_units, ( - 'Unit missmatch: %s != %s' % (a_units, b_units)) - if params['output column name'] == None: - params['output column name'] = ( + params = self.__setup_params(hooke=hooke, params=params) + data_A = self._get_column(hooke=hooke, params=params, + block_name='block A', + column_name='column A') + data_B = self._get_column(hooke=hooke, params=params, + block_name='block B', + column_name='column B') + out = data_A - data_B + self._set_column(hooke=hooke, params=params, + block_name='block A', + column_name='output column', + values=out) + + def __setup_params(self, hooke, params): + curve = self._curve(hooke, params) + if params['block A'] == None: + params['block A'] = curve.data[0].info['name'] + if params['block B'] == None: + params['block B'] = params['block A'] + block_A = self._block(hooke, params=params, name='block A') + block_B = self._block(hooke, params=params, name='block B') + if params['column A'] == None: + params['column A'] = block.info['columns'][0] + if params['column B'] == None: + params['column B'] = params['column A'] + a_name,a_unit = split_data_label(params['column A']) + b_name,b_unit = split_data_label(params['column B']) + if a_unit != b_unit: + raise Failure('Unit missmatch: %s != %s' % (a_unit, b_unit)) + if params['output column'] == None: + params['output column'] = join_data_label( 'difference of %s %s and %s %s' % ( - block_A.info['name'], params['column A'], - block_B.info['name'], params['column B'])) - new.info['columns'].append( - join_data_label(params['output distance column'], a_units)) - new[:,-1] = out - params['curve'].data[params['block A']] = new + block_A.info['name'], a_name, + block_B.info['name'], b_name), + a_unit) + else: + params['output column'] = join_data_label( + params['output column'], a_unit) + return params -class DerivativeCommand (Command): +class DerivativeCommand (ColumnAddingCommand): """Calculate the derivative (actually, the discrete differentiation) - of a curve data block. + of a data column. See :func:`hooke.util.calculus.derivative` for implementation details. @@ -300,80 +445,67 @@ class DerivativeCommand (Command): def __init__(self, plugin): super(DerivativeCommand, self).__init__( name='derivative', - arguments=[ - CurveArgument, - Argument(name='block', type='int', default=0, - help=""" -Data block to differentiate. For an approach/retract force curve, `0` -selects the approaching curve and `1` selects the retracting curve. -""".strip()), - Argument(name='x column', type='string', - help=""" -Column of data block to differentiate with respect to. -""".strip()), - Argument(name='f column', type='string', - help=""" -Column of data block to differentiate. + columns=[ + ('x column', None, + 'Column of data block to differentiate with respect to.'), + ('f column', None, + 'Column of data block to differentiate.'), + ], + new_columns=[ + ('output column', None, + """ +Name of the new column for storing the derivative (without units, defaults to +`derivative of with respect to `). """.strip()), + ], + arguments=[ Argument(name='weights', type='dict', default={-1:-0.5, 1:0.5}, help=""" Weighting scheme dictionary for finite differencing. Defaults to central differencing. -""".strip()), - Argument(name='output column name', type='string', - help=""" -Name of the new column for storing the derivative (without units, defaults to -`derivative of with respect to `). """.strip()), ], help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] - # HACK? rely on params['curve'] being bound to the local hooke - # playlist (i.e. not a copy, as you would get by passing a - # curve through the queue). Ugh. Stupid queues. As an - # alternative, we could pass lookup information through the - # queue... - new = Data((data.shape[0], data.shape[1]+1), dtype=data.dtype) - new.info = copy.deepcopy(data.info) - new[:,:-1] = data - - x_col = data.info['columns'].index(params['x column']) - f_col = data.info['columns'].index(params['f column']) + params = self.__setup_params(hooke=hooke, params=params) + x_data = self._get_column(hooke=hooke, params=params, + column_name='x column') + f_data = self._get_column(hooke=hooke, params=params, + column_name='f column') d = derivative( - block, x_col=x_col, f_col=f_col, weights=params['weights']) - - x_name,x_units = split_data_label(params['x column']) - f_name,f_units = split_data_label(params['f column']) - if params['output column name'] == None: - params['output column name'] = ( + x_data=x_data, f_data=f_data, weights=params['weights']) + self._set_column(hooke=hooke, params=params, + column_name='output column', + values=d) + + def __setup_params(self, hooke, params): + curve = self._curve(hooke, params) + x_name,x_unit = split_data_label(params['x column']) + f_name,f_unit = split_data_label(params['f column']) + d_unit = '%s/%s' % (f_unit, x_unit) + if params['output column'] == None: + params['output column'] = join_data_label( 'derivative of %s with respect to %s' % ( - params['f column'], params['x column'])) - - new.info['columns'].append( - join_data_label(params['output distance column'], - '%s/%s' % (f_units/x_units))) - new[:,-1] = d[:,1] - params['curve'].data[params['block']] = new + f_name, x_name), + d_unit) + else: + params['output column'] = join_data_label( + params['output column'], d_unit) + return params -class PowerSpectrumCommand (Command): - """Calculate the power spectrum of a data block. +class PowerSpectrumCommand (ColumnAddingCommand): + """Calculate the power spectrum of a data column. """ def __init__(self, plugin): super(PowerSpectrumCommand, self).__init__( name='power spectrum', arguments=[ - CurveArgument, - Argument(name='block', type='int', default=0, - help=""" -Data block to act on. For an approach/retract force curve, `0` -selects the approaching curve and `1` selects the retracting curve. -""".strip()), - Argument(name='column', type='string', optional=False, + Argument(name='output block', type='string', help=""" -Name of the data block column containing to-be-transformed data. +Name of the new data block for storing the power spectrum (defaults to +`power spectrum of `). """.strip()), Argument(name='bounds', type='point', optional=True, count=2, help=""" @@ -395,42 +527,51 @@ Number of samples per chunk. Use a power of two. help=""" If `True`, each chunk overlaps the previous chunk by half its length. Otherwise, the chunks are end-to-end, and not overlapping. -""".strip()), - Argument(name='output block name', type='string', - help=""" -Name of the new data block for storing the power spectrum (defaults to -`power spectrum of `). """.strip()), ], help=self.__doc__, plugin=plugin) def _run(self, hooke, inqueue, outqueue, params): - data = params['curve'].data[params['block']] - col = data.info['columns'].index(params['column']) - d = data[:,col] + params = self.__setup_params(hooke=hooke, params=params) + data = self._get_column(hooke=hooke, params=params) + bounds = params['bounds'] if bounds != None: - d = d[params['bounds'][0]:params['bounds'][1]] + data = data[bounds[0]:bounds[1]] freq_axis,power = unitary_avg_power_spectrum( - d, freq=params['freq'], + data, freq=params['freq'], chunk_size=params['chunk size'], overlap=params['overlap']) - - name,data_units = split_data_label(params['column']) b = Data((len(freq_axis),2), dtype=data.dtype) - if params['output block name'] == None: - params['output block name'] = 'power spectrum of %s %s' % ( - params['output block name'], data.info['name'], params['column']) - b.info['name'] = params['output block name'] + b.info['name'] = params['output block'] b.info['columns'] = [ - join_data_label('frequency axis', params['freq units']), - join_data_label('power density', - '%s^2/%s' % (data_units, params['freq units'])), + params['output freq column'], + params['output power column'], ] - b[:,0] = freq_axis - b[:,1] = power - params['curve'].data.append(b) + self._curve(hooke, params).data.append(b) + self._set_column(hooke, params, block_name='output block', + column_name='output freq column', + values=freq_axis) + self._set_column(hooke, params, block_name='output block', + column_name='output power column', + values=power) outqueue.put(b) + def __setup_params(self, hooke, params): + if params['output block'] in self._block_names(hooke, params): + raise Failure('output block %s already exists in %s.' + % (params['output block'], + self._curve(hooke, params))) + data = self._get_column(hooke=hooke, params=params) + d_name,d_unit = split_data_label(data.info['name']) + if params['output block'] == None: + params['output block'] = 'power spectrum of %s %s' % ( + data.info['name'], params['column']) + self.params['output freq column'] = join_data_label( + 'frequency axis', params['freq units']) + self.params['output power column'] = join_data_label( + 'power density', '%s^2/%s' % (data_units, params['freq units'])) + return params + class OldCruft (object): diff --git a/hooke/util/calculus.py b/hooke/util/calculus.py index e75a371..fa6ee8a 100644 --- a/hooke/util/calculus.py +++ b/hooke/util/calculus.py @@ -27,14 +27,13 @@ import numpy from ..curve import Data -def derivative(data, x_col=0, f_col=1, weights={-1:-0.5, 1:0.5}): +def derivative(x_data, f_data, weights={-1:-0.5, 1:0.5}): """Calculate the discrete derivative (finite difference) of - data[:,f_col] with respect to data[:,x_col]. + f_data with respect to x_data. Examples -------- - >>> import pprint >>> d = Data((5,2), dtype=numpy.float, ... info={'columns':['x', 'x**2']}) >>> for i in range(5): @@ -46,15 +45,9 @@ def derivative(data, x_col=0, f_col=1, weights={-1:-0.5, 1:0.5}): [ 2., 4.], [ 3., 9.], [ 4., 16.]]) - >>> dd = derivative(d) + >>> dd = derivative(x_data=d[:,0], f_data=d[:,1]) >>> dd - Data([[ 0., 1.], - [ 1., 2.], - [ 2., 4.], - [ 3., 6.], - [ 4., 7.]]) - >>> pprint.pprint(dd.info) - {'columns': ['x', 'deriv x**2 with respect to x']} + Data([ 1., 2., 4., 6., 7.]) Notes ----- @@ -62,7 +55,7 @@ def derivative(data, x_col=0, f_col=1, weights={-1:-0.5, 1:0.5}): *Weights* The returned :class:`Data` block shares its x vector with the - input data. The ith df/dx value in the returned data is + input data. The `i`\th df/dx value in the returned data is caclulated with:: (df/dx)[i] = (SUM_j w[j] f[i+j]) / h @@ -95,17 +88,11 @@ def derivative(data, x_col=0, f_col=1, weights={-1:-0.5, 1:0.5}): f[i] - f[0] = f[0] - f[-i] == -(f[-i] - f[0]) """ - output = Data((data.shape[0],2), dtype=data.dtype) - output.info = copy.copy(data.info) - output.info['columns'] = [ - data.info['columns'][x_col], - 'deriv %s with respect to %s' \ - % (data.info['columns'][f_col], data.info['columns'][x_col]), - ] - h = data[1,x_col] - data[0,x_col] + output = Data(f_data.shape, dtype=f_data.dtype) + h = x_data[1] - x_data[0] chunks = [] for i,w in weights.items(): - chunk = numpy.roll(w*data[:,f_col], -i) + chunk = numpy.roll(w*f_data, -i) if i > 0: # chunk shifted down, replace the high `i`s zero = len(chunk) - 1 - i for j in range(1,i+1): @@ -115,6 +102,4 @@ def derivative(data, x_col=0, f_col=1, weights={-1:-0.5, 1:0.5}): for j in range(1,zero+1): chunk[zero-j] = 2*chunk[zero] - chunk[zero+j] chunks.append(chunk) - output[:,0] = data[:,x_col] - output[:,1] = sum(chunks) - return output + return sum(chunks) diff --git a/test/derivative.py b/test/derivative.py new file mode 100644 index 0000000..4a296ee --- /dev/null +++ b/test/derivative.py @@ -0,0 +1,42 @@ +# Copyright (C) 2010 W. Trevor King +# +# 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 +# . + +""" +>>> from hooke.hooke import Hooke, HookeRunner +>>> h = Hooke() +>>> r = HookeRunner() +>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS + +Success + +>>> h = r.run_lines(h, +... ['derivative --x_column "z piezo (m)" --f_column "deflection (m)"'] +... ) # doctest: +ELLIPSIS, +REPORT_UDIFF +Success + +>>> curve = h.playlists.current().current() +>>> approach = curve.data[0] +>>> approach.info['columns'] +['z piezo (m)', 'deflection (m)', 'derivative of deflection with respect to z piezo (m/m)'] +>>> approach[:5,-1] # doctest: +ELLIPSIS +Data([ 3.626...e-10, 3.173...e-10, 2.946...e-10, + 3.173...e-10, 3.400...e-10]) +>>> approach[-5:,-1] # doctest: +ELLIPSIS +Data([ 1.020...e-10, -6.800...e-11, -3.400...e-10, + -1.133...e-10, 9.067...e-11]) +""" diff --git a/test/difference.py b/test/difference.py index 43ee58b..1800e0b 100644 --- a/test/difference.py +++ b/test/difference.py @@ -33,12 +33,12 @@ Success >>> retract = curve.data[1] >>> retract.info['columns'] ['z piezo (m)', 'deflection (m)', 'difference of retract deflection and approach deflection (m)'] ->>> retract[:5,-1] -Data([ -3.89891186e-09, -4.19359706e-09, -4.42027798e-09, - -4.78296746e-09, -5.10032075e-09]) ->>> retract[-5:,-1] -Data([ 4.08025660e-10, 2.04012830e-10, 4.53361844e-10, - 5.21366121e-10, 1.08806843e-09]) +>>> retract[:5,-1] # doctest: +ELLIPSIS +Data([ -3.898...e-09, -4.193...e-09, -4.420...e-09, + -4.782...e-09, -5.100...e-09]) +>>> retract[-5:,-1] # doctest: +ELLIPSIS +Data([ 4.080...e-10, 2.040...e-10, 4.533...e-10, + 5.213...e-10, 1.088...e-09]) Note the differences are not near zero because the z piezo position runs in opposite directions for the two blocks. -- 2.26.2