1 # Copyright (C) 2008-2010 Alberto Gomez-Casado
3 # Massimo Sandal <devicerandom@gmail.com>
4 # W. Trevor King <wking@drexel.edu>
6 # This file is part of Hooke.
8 # Hooke is free software: you can redistribute it and/or modify it
9 # under the terms of the GNU Lesser General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # Hooke is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
16 # Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with Hooke. If not, see
20 # <http://www.gnu.org/licenses/>.
22 """The ``curve`` module provides :class:`CurvePlugin` and several
23 associated :class:`hooke.command.Command`\s for handling
24 :mod:`hooke.curve` classes.
33 from ..command import Command, Argument, Failure
34 from ..command_stack import CommandStack
35 from ..curve import Data
36 from ..engine import CommandMessage
37 from ..util.calculus import derivative
38 from ..util.fft import unitary_avg_power_spectrum
39 from ..util.si import ppSI, join_data_label, split_data_label
41 from .playlist import current_playlist_callback
44 # Define common or complicated arguments
46 def current_curve_callback(hooke, command, argument, value, load=True):
49 playlist = current_playlist_callback(hooke, command, argument, value)
50 curve = playlist.current(load=load)
52 raise Failure('No curves in %s' % playlist)
55 def unloaded_current_curve_callback(hooke, command, argument, value):
56 return current_curve_callback(
57 hooke=hooke, command=command, argument=argument, value=value,
60 CurveArgument = Argument(
61 name='curve', type='curve', callback=current_curve_callback,
63 :class:`hooke.curve.Curve` to act on. Defaults to the current curve
64 of the current playlist.
67 def _name_argument(name, default, help):
70 return Argument(name=name, type='string', default=default, help=help)
72 def block_argument(*args, **kwargs):
75 return _name_argument(*args, **kwargs)
77 def column_argument(*args, **kwargs):
80 return _name_argument(*args, **kwargs)
83 # Define useful command subclasses
85 class CurveCommand (Command):
86 """A :class:`~hooke.command.Command` operating on a
87 :class:`~hooke.curve.Curve`.
89 def __init__(self, **kwargs):
90 if 'arguments' in kwargs:
91 kwargs['arguments'].insert(0, CurveArgument)
93 kwargs['arguments'] = [CurveArgument]
94 super(CurveCommand, self).__init__(**kwargs)
96 def _curve(self, hooke, params):
97 """Get the selected curve.
101 `hooke` is intended to attach the selected curve to the local
102 playlist; the returned curve should not be effected by the
103 state of `hooke`. This is important for reliable
104 :class:`~hooke.command_stack.CommandStack`\s.
106 # HACK? rely on params['curve'] being bound to the local hooke
107 # playlist (i.e. not a copy, as you would get by passing a
108 # curve through the queue). Ugh. Stupid queues. As an
109 # alternative, we could pass lookup information through the
111 return params['curve']
113 def _add_to_command_stack(self, params):
114 """Store the command name and current `params` values in the
115 curve's `.command_stack`.
117 If this would duplicate the command currently on top of the
118 stack, no action is taken. Call early on, or watch out for
119 repeated param processing.
121 Recommended practice is to *not* lock in argument values that
122 are loaded from the plugin's :attr:`.config`.
126 Perhaps we should subclass :meth:`_run` and use :func:`super`,
127 or embed this in :meth:`run` to avoid subclasses calling this
128 method explicitly, with all the tedium and brittality that
129 implies. On the other hand, the current implemtnation allows
130 CurveCommands that don't effect the curve itself
131 (e.g. :class:`GetCommand`) to avoid adding themselves to the
134 if params['stack'] == True:
135 curve = self._curve(hooke=None, params=params)
136 if (len(curve.command_stack) > 0
137 and curve.command_stack[-1].command == self.name
138 and curve.command_stack[-1].arguments == params):
139 pass # no need to place duplicate calls on the stack.
141 curve.command_stack.append(CommandMessage(
142 self.name, dict(params)))
145 class BlockCommand (CurveCommand):
146 """A :class:`CurveCommand` operating on a :class:`~hooke.curve.Data` block.
148 def __init__(self, blocks=None, **kwargs):
150 blocks = [('block', None, 'Name of the data block to act on.')]
152 for name,default,help in blocks:
153 block_args.append(block_argument(name, default, help))
154 self._block_arguments = block_args
155 if 'arguments' not in kwargs:
156 kwargs['arguments'] = []
157 kwargs['arguments'] = block_args + kwargs['arguments']
158 super(BlockCommand, self).__init__(**kwargs)
160 def _block_names(self, hooke, params):
161 curve = self._curve(hooke, params)
162 return [b.info['name'] for b in curve.data]
164 def _block_index(self, hooke, params, name=None):
166 name = self._block_arguments[0].name
167 block_name = params[name]
168 if block_name == None:
169 curve = self._curve(hooke=hooke, params=params)
170 if len(curve.data) == 0:
171 raise Failure('no blocks in %s' % curve)
172 block_name = curve.data[0].info['name']
173 names = self._block_names(hooke=hooke, params=params)
175 return names.index(block_name)
176 except ValueError, e:
177 curve = self._curve(hooke, params)
178 raise Failure('no block named %s in %s (%s): %s'
179 % (block_name, curve, names, e))
181 def _block(self, hooke, params, name=None):
182 # HACK? rely on params['block'] being bound to the local hooke
183 # playlist (i.e. not a copy, as you would get by passing a
184 # block through the queue). Ugh. Stupid queues. As an
185 # alternative, we could pass lookup information through the
187 curve = self._curve(hooke, params)
188 index = self._block_index(hooke, params, name)
189 return curve.data[index]
192 class ColumnAccessCommand (BlockCommand):
193 """A :class:`BlockCommand` accessing a :class:`~hooke.curve.Data`
196 def __init__(self, columns=None, **kwargs):
198 columns = [('column', None, 'Name of the data column to act on.')]
200 for name,default,help in columns:
201 column_args.append(column_argument(name, default, help))
202 self._column_arguments = column_args
203 if 'arguments' not in kwargs:
204 kwargs['arguments'] = []
205 kwargs['arguments'] = column_args + kwargs['arguments']
206 super(ColumnAccessCommand, self).__init__(**kwargs)
208 def _get_column(self, hooke, params, block_name=None, column_name=None):
209 if column_name == None:
210 column_name = self._column_arguments[0].name
211 column_name = params[column_name]
212 block = self._block(hooke, params, block_name)
213 columns = block.info['columns']
215 column_index = columns.index(column_name)
216 except ValueError, e:
217 raise Failure('%s not in %s (%s): %s'
218 % (column_name, block.info['name'], columns, e))
219 return block[:,column_index]
222 class ColumnAddingCommand (ColumnAccessCommand):
223 """A :class:`ColumnAccessCommand` that also adds columns.
225 def __init__(self, new_columns=None, **kwargs):
226 if new_columns == None:
229 for name,default,help in new_columns:
230 column_args.append(column_argument(name, default, help))
231 self._new_column_arguments = column_args
232 if 'arguments' not in kwargs:
233 kwargs['arguments'] = []
234 kwargs['arguments'] = column_args + kwargs['arguments']
235 super(ColumnAddingCommand, self).__init__(**kwargs)
237 def _get_column(self, hooke, params, block_name=None, column_name=None):
238 if column_name == None and len(self._column_arguments) == 0:
239 column_name = self._new_column_arguments[0].name
240 return super(ColumnAddingCommand, self)._get_column(
241 hooke=hooke, params=params, block_name=block_name,
242 column_name=column_name)
244 def _set_column(self, hooke, params, block_name=None, column_name=None,
246 if column_name == None:
247 column_name = self._column_arguments[0].name
248 column_name = params[column_name]
249 block = self._block(hooke=hooke, params=params, name=block_name)
250 if column_name not in block.info['columns']:
251 new = Data((block.shape[0], block.shape[1]+1), dtype=block.dtype)
252 new.info = copy.deepcopy(block.info)
254 new.info['columns'].append(column_name)
256 block_index = self._block_index(hooke, params, name=block_name)
257 self._curve(hooke, params).data[block_index] = block
258 column_index = block.info['columns'].index(column_name)
259 block[:,column_index] = values
264 class CurvePlugin (Builtin):
266 super(CurvePlugin, self).__init__(name='curve')
268 GetCommand(self), InfoCommand(self), BlockInfoCommand(self),
269 DeltaCommand(self), ExportCommand(self), DifferenceCommand(self),
270 DerivativeCommand(self), PowerSpectrumCommand(self),
271 ClearStackCommand(self)]
276 class GetCommand (CurveCommand):
277 """Return a :class:`hooke.curve.Curve`.
279 def __init__(self, plugin):
280 super(GetCommand, self).__init__(
281 name='get curve', help=self.__doc__, plugin=plugin)
283 def _run(self, hooke, inqueue, outqueue, params):
284 outqueue.put(self._curve(hooke, params))
287 class InfoCommand (CurveCommand):
288 """Get selected information about a :class:`hooke.curve.Curve`.
290 def __init__(self, plugin):
292 Argument(name='all', type='bool', default=False, count=1,
293 help='Get all curve information.'),
295 self.fields = ['name', 'path', 'experiment', 'driver', 'filetype',
296 'note', 'command stack', 'blocks', 'block sizes']
297 for field in self.fields:
298 args.append(Argument(
299 name=field, type='bool', default=False, count=1,
300 help='Get curve %s' % field))
301 super(InfoCommand, self).__init__(
302 name='curve info', arguments=args,
303 help=self.__doc__, plugin=plugin)
305 def _run(self, hooke, inqueue, outqueue, params):
306 curve = self._curve(hooke, params)
308 for key in self.fields:
309 fields[key] = params[key]
310 if reduce(lambda x,y: x or y, fields.values()) == False:
311 params['all'] = True # No specific fields set, default to 'all'
312 if params['all'] == True:
313 for key in self.fields:
316 for key in self.fields:
317 if fields[key] == True:
318 get = getattr(self, '_get_%s' % key.replace(' ', '_'))
319 lines.append('%s: %s' % (key, get(curve)))
320 outqueue.put('\n'.join(lines))
322 def _get_name(self, curve):
325 def _get_path(self, curve):
328 def _get_experiment(self, curve):
329 return curve.info.get('experiment', None)
331 def _get_driver(self, curve):
334 def _get_filetype(self, curve):
335 return curve.info.get('filetype', None)
337 def _get_note(self, curve):
338 return curve.info.get('note', None)
340 def _get_command_stack(self, curve):
341 return curve.command_stack
343 def _get_blocks(self, curve):
344 return len(curve.data)
346 def _get_block_sizes(self, curve):
347 return [block.shape for block in curve.data]
350 class BlockInfoCommand (BlockCommand):
351 """Get selected information about a :class:`hooke.curve.Curve` data block.
353 def __init__(self, plugin):
354 super(BlockInfoCommand, self).__init__(
355 name='block info', arguments=[
357 name='key', count=-1, optional=False,
358 help='Dot-separted (.) key selection regexp.'),
362 File name for the output (appended).
365 help=self.__doc__, plugin=plugin)
367 def _run(self, hooke, inqueue, outqueue, params):
368 block = self._block(hooke, params)
369 values = {'index': self._block_index(hooke, params)}
370 for key in params['key']:
371 keys = [(0, key.split('.'), block.info)]
373 index,key_stack,info = keys.pop(0)
374 regexp = re.compile(key_stack[index])
376 for k,v in info.items():
379 new_stack = copy.copy(key_stack)
381 if index+1 == len(key_stack):
383 for k in new_stack[:-1]:
387 vals[new_stack[-1]] = v
389 keys.append((index+1, new_stack, v))
391 raise ValueError('no match found for %s in %s'
392 % (key_stack[index], key))
393 if params['output'] != None:
394 curve = self._curve(hooke, params)
395 with open(params['output'], 'a') as f:
396 yaml.dump({curve.name:{
398 block.info['name']: values
403 class DeltaCommand (BlockCommand):
404 """Get distance information between two points.
406 With two points A and B, the returned distances are A-B.
408 def __init__(self, plugin):
409 super(DeltaCommand, self).__init__(
412 Argument(name='point', type='point', optional=False, count=2,
414 Indicies of points bounding the selected data.
416 Argument(name='SI', type='bool', default=False,
418 Return distances in SI notation.
421 help=self.__doc__, plugin=plugin)
423 def _run(self, hooke, inqueue, outqueue, params):
424 data = self._block(hooke, params)
425 As = data[params['point'][0],:]
426 Bs = data[params['point'][1],:]
427 ds = [A-B for A,B in zip(As, Bs)]
428 if params['SI'] == False:
429 out = [(name, d) for name,d in zip(data.info['columns'], ds)]
432 for name,d in zip(data.info['columns'], ds):
433 n,units = split_data_label(name)
435 (n, ppSI(value=d, unit=units, decimals=2)))
439 class ExportCommand (BlockCommand):
440 """Export a :class:`hooke.curve.Curve` data block as TAB-delimeted
443 A "#" prefixed header will optionally appear at the beginning of
444 the file naming the columns.
446 def __init__(self, plugin):
447 super(ExportCommand, self).__init__(
450 Argument(name='output', type='file', default='curve.dat',
452 File name for the output data. Defaults to 'curve.dat'
454 Argument(name='header', type='bool', default=True,
456 True if you want the column-naming header line.
459 help=self.__doc__, plugin=plugin)
461 def _run(self, hooke, inqueue, outqueue, params):
462 data = self._block(hooke, params)
464 with open(params['output'], 'w') as f:
465 if params['header'] == True:
466 f.write('# %s \n' % ('\t'.join(data.info['columns'])))
467 numpy.savetxt(f, data, delimiter='\t')
470 class DifferenceCommand (ColumnAddingCommand):
471 """Calculate the difference between two columns of data.
473 The difference is added to block A as a new column.
475 Note that the command will fail if the columns have different
476 lengths, so be careful when differencing columns from different
479 def __init__(self, plugin):
480 super(DifferenceCommand, self).__init__(
484 'Name of block A in A-B. Defaults to the first block'),
486 'Name of block B in A-B. Defaults to matching `block A`.'),
491 Column of data from block A to difference. Defaults to the first column.
495 Column of data from block B to difference. Defaults to matching `column A`.
499 ('output column', None,
501 Name of the new column for storing the difference (without units, defaults to
502 `difference of <block A> <column A> and <block B> <column B>`).
505 help=self.__doc__, plugin=plugin)
507 def _run(self, hooke, inqueue, outqueue, params):
508 self._add_to_command_stack(params)
509 params = self._setup_params(hooke=hooke, params=params)
510 data_A = self._get_column(hooke=hooke, params=params,
511 block_name='block A',
512 column_name='column A')
513 data_B = self._get_column(hooke=hooke, params=params,
514 block_name='block B',
515 column_name='column B')
516 out = data_A - data_B
517 self._set_column(hooke=hooke, params=params,
518 block_name='block A',
519 column_name='output column',
522 def _setup_params(self, hooke, params):
523 curve = self._curve(hooke, params)
524 if params['block A'] == None:
525 params['block A'] = curve.data[0].info['name']
526 if params['block B'] == None:
527 params['block B'] = params['block A']
528 block_A = self._block(hooke, params=params, name='block A')
529 block_B = self._block(hooke, params=params, name='block B')
530 if params['column A'] == None:
531 params['column A'] = block.info['columns'][0]
532 if params['column B'] == None:
533 params['column B'] = params['column A']
534 a_name,a_unit = split_data_label(params['column A'])
535 b_name,b_unit = split_data_label(params['column B'])
537 raise Failure('Unit missmatch: %s != %s' % (a_unit, b_unit))
538 if params['output column'] == None:
539 params['output column'] = join_data_label(
540 'difference of %s %s and %s %s' % (
541 block_A.info['name'], a_name,
542 block_B.info['name'], b_name),
545 params['output column'] = join_data_label(
546 params['output column'], a_unit)
550 class DerivativeCommand (ColumnAddingCommand):
551 """Calculate the derivative (actually, the discrete differentiation)
554 See :func:`hooke.util.calculus.derivative` for implementation
557 def __init__(self, plugin):
558 super(DerivativeCommand, self).__init__(
562 'Column of data block to differentiate with respect to.'),
564 'Column of data block to differentiate.'),
567 ('output column', None,
569 Name of the new column for storing the derivative (without units, defaults to
570 `derivative of <f column> with respect to <x column>`).
574 Argument(name='weights', type='dict', default={-1:-0.5, 1:0.5},
576 Weighting scheme dictionary for finite differencing. Defaults to
577 central differencing.
580 help=self.__doc__, plugin=plugin)
582 def _run(self, hooke, inqueue, outqueue, params):
583 self._add_to_command_stack(params)
584 params = self._setup_params(hooke=hooke, params=params)
585 x_data = self._get_column(hooke=hooke, params=params,
586 column_name='x column')
587 f_data = self._get_column(hooke=hooke, params=params,
588 column_name='f column')
590 x_data=x_data, f_data=f_data, weights=params['weights'])
591 self._set_column(hooke=hooke, params=params,
592 column_name='output column',
595 def _setup_params(self, hooke, params):
596 curve = self._curve(hooke, params)
597 x_name,x_unit = split_data_label(params['x column'])
598 f_name,f_unit = split_data_label(params['f column'])
599 d_unit = '%s/%s' % (f_unit, x_unit)
600 if params['output column'] == None:
601 params['output column'] = join_data_label(
602 'derivative of %s with respect to %s' % (
606 params['output column'] = join_data_label(
607 params['output column'], d_unit)
611 class PowerSpectrumCommand (ColumnAddingCommand):
612 """Calculate the power spectrum of a data column.
614 def __init__(self, plugin):
615 super(PowerSpectrumCommand, self).__init__(
616 name='power spectrum',
618 Argument(name='output block', type='string',
620 Name of the new data block for storing the power spectrum (defaults to
621 `power spectrum of <source block name> <source column name>`).
623 Argument(name='bounds', type='point', optional=True, count=2,
625 Indicies of points bounding the selected data.
627 Argument(name='freq', type='float', default=1.0,
631 Argument(name='freq units', type='string', default='Hz',
633 Units for the sampling frequency.
635 Argument(name='chunk size', type='int', default=2048,
637 Number of samples per chunk. Use a power of two.
639 Argument(name='overlap', type='bool', default=False,
641 If `True`, each chunk overlaps the previous chunk by half its length.
642 Otherwise, the chunks are end-to-end, and not overlapping.
645 help=self.__doc__, plugin=plugin)
647 def _run(self, hooke, inqueue, outqueue, params):
648 self._add_to_command_stack(params)
649 params = self._setup_params(hooke=hooke, params=params)
650 data = self._get_column(hooke=hooke, params=params)
651 bounds = params['bounds']
653 data = data[bounds[0]:bounds[1]]
654 freq_axis,power = unitary_avg_power_spectrum(
655 data, freq=params['freq'],
656 chunk_size=params['chunk size'],
657 overlap=params['overlap'])
658 b = Data((len(freq_axis),2), dtype=data.dtype)
659 b.info['name'] = params['output block']
660 b.info['columns'] = [
661 params['output freq column'],
662 params['output power column'],
664 self._curve(hooke, params).data.append(b)
665 self._set_column(hooke, params, block_name='output block',
666 column_name='output freq column',
668 self._set_column(hooke, params, block_name='output block',
669 column_name='output power column',
673 def _setup_params(self, hooke, params):
674 if params['output block'] in self._block_names(hooke, params):
675 raise Failure('output block %s already exists in %s.'
676 % (params['output block'],
677 self._curve(hooke, params)))
678 data = self._get_column(hooke=hooke, params=params)
679 d_name,d_unit = split_data_label(data.info['name'])
680 if params['output block'] == None:
681 params['output block'] = 'power spectrum of %s %s' % (
682 data.info['name'], params['column'])
683 self.params['output freq column'] = join_data_label(
684 'frequency axis', params['freq units'])
685 self.params['output power column'] = join_data_label(
686 'power density', '%s^2/%s' % (data_units, params['freq units']))
690 class ClearStackCommand (CurveCommand):
691 """Empty a curve's command stack.
693 def __init__(self, plugin):
694 super(ClearStackCommand, self).__init__(
695 name='clear curve command stack',
696 help=self.__doc__, plugin=plugin)
697 i,arg = [(i,arg) for i,arg in enumerate(self.arguments)
698 if arg.name == 'curve'][0]
700 arg.callback = unloaded_current_curve_callback
701 self.arguments[i] = arg
703 def _run(self, hooke, inqueue, outqueue, params):
704 curve = self._curve(hooke, params)
705 curve.command_stack = CommandStack()
708 class OldCruft (object):
710 def do_forcebase(self,args):
714 Measures the difference in force (in pN) between a point and a baseline
715 took as the average between two points.
717 The baseline is fixed once for a given curve and different force measurements,
718 unless the user wants it to be recalculated
720 Syntax: forcebase [rebase]
721 rebase: Forces forcebase to ask again the baseline
722 max: Instead of asking for a point to measure, asks for two points and use
723 the maximum peak in between
725 rebase=False #if true=we select rebase
726 maxpoint=False #if true=we measure the maximum peak
728 plot=self._get_displayed_plot()
729 whatset=1 #fixme: for all sets
730 if 'rebase' in args or (self.basecurrent != self.current.path):
736 print 'Select baseline'
737 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
738 self.basecurrent=self.current.path
741 print 'Select two points'
742 points=self._measure_N_points(N=2, whatset=whatset)
743 boundpoints=[points[0].index, points[1].index]
746 y=min(plot.vectors[whatset][1][boundpoints[0]:boundpoints[1]])
748 print 'Chosen interval not valid. Try picking it again. Did you pick the same point as begin and end of interval?'
750 print 'Select point to measure'
751 points=self._measure_N_points(N=1, whatset=whatset)
752 #whatplot=points[0].dest
753 y=points[0].graph_coords[1]
755 #fixme: code duplication
756 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
758 to_average=plot.vectors[whatset][1][boundaries[0]:boundaries[1]] #y points to average
760 avg=np.mean(to_average)
762 print str(forcebase*(10**12))+' pN'
763 to_dump='forcebase '+self.current.path+' '+str(forcebase*(10**12))+' pN'
764 self.outlet.push(to_dump)
767 def do_slope(self,args):
771 Measures the slope of a delimited chunk on the return trace.
772 The chunk can be delimited either by two manual clicks, or have
773 a fixed width, given as an argument.
775 Syntax: slope [width]
776 The facultative [width] parameter specifies how many
777 points will be considered for the fit. If [width] is
778 specified, only one click will be required.
779 (c) Marco Brucale, Massimo Sandal 2008
782 # Reads the facultative width argument
788 # Decides between the two forms of user input, as per (args)
790 # Gets the Xs of two clicked points as indexes on the current curve vector
791 print 'Click twice to delimit chunk'
792 points=self._measure_N_points(N=2,whatset=1)
794 print 'Click once on the leftmost point of the chunk (i.e.usually the peak)'
795 points=self._measure_N_points(N=1,whatset=1)
797 slope=self._slope(points,fitspan)
799 # Outputs the relevant slope parameter
802 to_dump='slope '+self.current.path+' '+str(slope)
803 self.outlet.push(to_dump)
805 def _slope(self,points,fitspan):
806 # Calls the function linefit_between
807 parameters=[0,0,[],[]]
809 clickedpoints=[points[0].index,points[1].index]
812 clickedpoints=[points[0].index-fitspan,points[0].index]
815 parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
817 print 'Cannot fit. Did you click twice the same point?'
820 # Outputs the relevant slope parameter
822 print str(parameters[0])
823 to_dump='slope '+self.curve.path+' '+str(parameters[0])
824 self.outlet.push(to_dump)
826 # Makes a vector with the fitted parameters and sends it to the GUI
827 xtoplot=parameters[2]
831 ytoplot.append((x*parameters[0])+parameters[1])
833 clickvector_x, clickvector_y=[], []
835 clickvector_x.append(item.graph_coords[0])
836 clickvector_y.append(item.graph_coords[1])
838 lineplot=self._get_displayed_plot(0) #get topmost displayed plot
840 lineplot.add_set(xtoplot,ytoplot)
841 lineplot.add_set(clickvector_x, clickvector_y)
844 if lineplot.styles==[]:
845 lineplot.styles=[None,None,None,'scatter']
847 lineplot.styles+=[None,'scatter']
848 if lineplot.colors==[]:
849 lineplot.colors=[None,None,'black',None]
851 lineplot.colors+=['black',None]
854 self._send_plot([lineplot])
859 def linefit_between(self,index1,index2,whatset=1):
861 Creates two vectors (xtofit,ytofit) slicing out from the
862 current return trace a portion delimited by the two indexes
864 Then does a least squares linear fit on that slice.
865 Finally returns [0]=the slope, [1]=the intercept of the
866 fitted 1st grade polynomial, and [2,3]=the actual (x,y) vectors
868 (c) Marco Brucale, Massimo Sandal 2008
870 # Translates the indexes into two vectors containing the x,y data to fit
871 xtofit=self.plots[0].vectors[whatset][0][index1:index2]
872 ytofit=self.plots[0].vectors[whatset][1][index1:index2]
874 # Does the actual linear fitting (simple least squares with numpy.polyfit)
876 linefit=np.polyfit(xtofit,ytofit,1)
878 return (linefit[0],linefit[1],xtofit,ytofit)