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', 'driver', 'note', 'command stack',
296 'blocks', 'block names', '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_driver(self, curve):
331 def _get_note(self, curve):
332 return curve.info.get('note', None)
334 def _get_command_stack(self, curve):
335 return curve.command_stack
337 def _get_blocks(self, curve):
338 return len(curve.data)
340 def _get_block_names(self, curve):
341 return [block.info['name'] for block in curve.data]
343 def _get_block_sizes(self, curve):
344 return [block.shape for block in curve.data]
347 class BlockInfoCommand (BlockCommand):
348 """Get selected information about a :class:`hooke.curve.Curve` data block.
350 def __init__(self, plugin):
351 super(BlockInfoCommand, self).__init__(
352 name='block info', arguments=[
354 name='key', count=-1, optional=False,
355 help='Dot-separted (.) key selection regexp.'),
359 File name for the output (appended).
362 help=self.__doc__, plugin=plugin)
364 def _run(self, hooke, inqueue, outqueue, params):
365 block = self._block(hooke, params)
366 values = {'index': self._block_index(hooke, params)}
367 for key in params['key']:
368 keys = [(0, key.split('.'), block.info)]
370 index,key_stack,info = keys.pop(0)
371 regexp = re.compile(key_stack[index])
373 for k,v in info.items():
376 new_stack = copy.copy(key_stack)
378 if index+1 == len(key_stack):
380 for k in new_stack[:-1]:
384 vals[new_stack[-1]] = v
386 keys.append((index+1, new_stack, v))
389 'no match found for %s (%s) in %s'
390 % (key_stack[index], key, sorted(info.keys())))
391 if params['output'] != None:
392 curve = self._curve(hooke, params)
393 with open(params['output'], 'a') as f:
394 yaml.dump({curve.name:{
396 block.info['name']: values
401 class DeltaCommand (BlockCommand):
402 """Get distance information between two points.
404 With two points A and B, the returned distances are A-B.
406 def __init__(self, plugin):
407 super(DeltaCommand, self).__init__(
410 Argument(name='point', type='point', optional=False, count=2,
412 Indicies of points bounding the selected data.
414 Argument(name='SI', type='bool', default=False,
416 Return distances in SI notation.
419 help=self.__doc__, plugin=plugin)
421 def _run(self, hooke, inqueue, outqueue, params):
422 data = self._block(hooke, params)
423 As = data[params['point'][0],:]
424 Bs = data[params['point'][1],:]
425 ds = [A-B for A,B in zip(As, Bs)]
426 if params['SI'] == False:
427 out = [(name, d) for name,d in zip(data.info['columns'], ds)]
430 for name,d in zip(data.info['columns'], ds):
431 n,units = split_data_label(name)
433 (n, ppSI(value=d, unit=units, decimals=2)))
437 class ExportCommand (BlockCommand):
438 """Export a :class:`hooke.curve.Curve` data block as TAB-delimeted
441 A "#" prefixed header will optionally appear at the beginning of
442 the file naming the columns.
444 def __init__(self, plugin):
445 super(ExportCommand, self).__init__(
448 Argument(name='output', type='file', default='curve.dat',
450 File name for the output data. Defaults to 'curve.dat'
452 Argument(name='header', type='bool', default=True,
454 True if you want the column-naming header line.
457 help=self.__doc__, plugin=plugin)
459 def _run(self, hooke, inqueue, outqueue, params):
460 data = self._block(hooke, params)
462 with open(params['output'], 'w') as f:
463 if params['header'] == True:
464 f.write('# %s \n' % ('\t'.join(data.info['columns'])))
465 numpy.savetxt(f, data, delimiter='\t')
468 class DifferenceCommand (ColumnAddingCommand):
469 """Calculate the difference between two columns of data.
471 The difference is added to block A as a new column.
473 Note that the command will fail if the columns have different
474 lengths, so be careful when differencing columns from different
477 def __init__(self, plugin):
478 super(DifferenceCommand, self).__init__(
482 'Name of block A in A-B. Defaults to the first block'),
484 'Name of block B in A-B. Defaults to matching `block A`.'),
489 Column of data from block A to difference. Defaults to the first column.
493 Column of data from block B to difference. Defaults to matching `column A`.
497 ('output column', None,
499 Name of the new column for storing the difference (without units, defaults to
500 `difference of <block A> <column A> and <block B> <column B>`).
503 help=self.__doc__, plugin=plugin)
505 def _run(self, hooke, inqueue, outqueue, params):
506 self._add_to_command_stack(params)
507 params = self._setup_params(hooke=hooke, params=params)
508 data_A = self._get_column(hooke=hooke, params=params,
509 block_name='block A',
510 column_name='column A')
511 data_B = self._get_column(hooke=hooke, params=params,
512 block_name='block B',
513 column_name='column B')
514 out = data_A - data_B
515 self._set_column(hooke=hooke, params=params,
516 block_name='block A',
517 column_name='output column',
520 def _setup_params(self, hooke, params):
521 curve = self._curve(hooke, params)
522 if params['block A'] == None:
523 params['block A'] = curve.data[0].info['name']
524 if params['block B'] == None:
525 params['block B'] = params['block A']
526 block_A = self._block(hooke, params=params, name='block A')
527 block_B = self._block(hooke, params=params, name='block B')
528 if params['column A'] == None:
529 params['column A'] = block.info['columns'][0]
530 if params['column B'] == None:
531 params['column B'] = params['column A']
532 a_name,a_unit = split_data_label(params['column A'])
533 b_name,b_unit = split_data_label(params['column B'])
535 raise Failure('Unit missmatch: %s != %s' % (a_unit, b_unit))
536 if params['output column'] == None:
537 params['output column'] = join_data_label(
538 'difference of %s %s and %s %s' % (
539 block_A.info['name'], a_name,
540 block_B.info['name'], b_name),
543 params['output column'] = join_data_label(
544 params['output column'], a_unit)
548 class DerivativeCommand (ColumnAddingCommand):
549 """Calculate the derivative (actually, the discrete differentiation)
552 See :func:`hooke.util.calculus.derivative` for implementation
555 def __init__(self, plugin):
556 super(DerivativeCommand, self).__init__(
560 'Column of data block to differentiate with respect to.'),
562 'Column of data block to differentiate.'),
565 ('output column', None,
567 Name of the new column for storing the derivative (without units, defaults to
568 `derivative of <f column> with respect to <x column>`).
572 Argument(name='weights', type='dict', default={-1:-0.5, 1:0.5},
574 Weighting scheme dictionary for finite differencing. Defaults to
575 central differencing.
578 help=self.__doc__, plugin=plugin)
580 def _run(self, hooke, inqueue, outqueue, params):
581 self._add_to_command_stack(params)
582 params = self._setup_params(hooke=hooke, params=params)
583 x_data = self._get_column(hooke=hooke, params=params,
584 column_name='x column')
585 f_data = self._get_column(hooke=hooke, params=params,
586 column_name='f column')
588 x_data=x_data, f_data=f_data, weights=params['weights'])
589 self._set_column(hooke=hooke, params=params,
590 column_name='output column',
593 def _setup_params(self, hooke, params):
594 curve = self._curve(hooke, params)
595 x_name,x_unit = split_data_label(params['x column'])
596 f_name,f_unit = split_data_label(params['f column'])
597 d_unit = '%s/%s' % (f_unit, x_unit)
598 if params['output column'] == None:
599 params['output column'] = join_data_label(
600 'derivative of %s with respect to %s' % (
604 params['output column'] = join_data_label(
605 params['output column'], d_unit)
609 class PowerSpectrumCommand (ColumnAddingCommand):
610 """Calculate the power spectrum of a data column.
612 def __init__(self, plugin):
613 super(PowerSpectrumCommand, self).__init__(
614 name='power spectrum',
616 Argument(name='output block', type='string',
618 Name of the new data block for storing the power spectrum (defaults to
619 `power spectrum of <source block name> <source column name>`).
621 Argument(name='bounds', type='point', optional=True, count=2,
623 Indicies of points bounding the selected data.
625 Argument(name='freq', type='float', default=1.0,
629 Argument(name='freq units', type='string', default='Hz',
631 Units for the sampling frequency.
633 Argument(name='chunk size', type='int', default=2048,
635 Number of samples per chunk. Use a power of two.
637 Argument(name='overlap', type='bool', default=False,
639 If `True`, each chunk overlaps the previous chunk by half its length.
640 Otherwise, the chunks are end-to-end, and not overlapping.
643 help=self.__doc__, plugin=plugin)
645 def _run(self, hooke, inqueue, outqueue, params):
646 self._add_to_command_stack(params)
647 params = self._setup_params(hooke=hooke, params=params)
648 data = self._get_column(hooke=hooke, params=params)
649 bounds = params['bounds']
651 data = data[bounds[0]:bounds[1]]
652 freq_axis,power = unitary_avg_power_spectrum(
653 data, freq=params['freq'],
654 chunk_size=params['chunk size'],
655 overlap=params['overlap'])
656 b = Data((len(freq_axis),2), dtype=data.dtype)
657 b.info['name'] = params['output block']
658 b.info['columns'] = [
659 params['output freq column'],
660 params['output power column'],
662 self._curve(hooke, params).data.append(b)
663 self._set_column(hooke, params, block_name='output block',
664 column_name='output freq column',
666 self._set_column(hooke, params, block_name='output block',
667 column_name='output power column',
671 def _setup_params(self, hooke, params):
672 if params['output block'] in self._block_names(hooke, params):
673 raise Failure('output block %s already exists in %s.'
674 % (params['output block'],
675 self._curve(hooke, params)))
676 data = self._get_column(hooke=hooke, params=params)
677 d_name,d_unit = split_data_label(data.info['name'])
678 if params['output block'] == None:
679 params['output block'] = 'power spectrum of %s %s' % (
680 data.info['name'], params['column'])
681 self.params['output freq column'] = join_data_label(
682 'frequency axis', params['freq units'])
683 self.params['output power column'] = join_data_label(
684 'power density', '%s^2/%s' % (data_units, params['freq units']))
688 class ClearStackCommand (CurveCommand):
689 """Empty a curve's command stack.
691 def __init__(self, plugin):
692 super(ClearStackCommand, self).__init__(
693 name='clear curve command stack',
694 help=self.__doc__, plugin=plugin)
695 i,arg = [(i,arg) for i,arg in enumerate(self.arguments)
696 if arg.name == 'curve'][0]
698 arg.callback = unloaded_current_curve_callback
699 self.arguments[i] = arg
701 def _run(self, hooke, inqueue, outqueue, params):
702 curve = self._curve(hooke, params)
703 curve.command_stack = CommandStack()
706 class OldCruft (object):
708 def do_forcebase(self,args):
712 Measures the difference in force (in pN) between a point and a baseline
713 took as the average between two points.
715 The baseline is fixed once for a given curve and different force measurements,
716 unless the user wants it to be recalculated
718 Syntax: forcebase [rebase]
719 rebase: Forces forcebase to ask again the baseline
720 max: Instead of asking for a point to measure, asks for two points and use
721 the maximum peak in between
723 rebase=False #if true=we select rebase
724 maxpoint=False #if true=we measure the maximum peak
726 plot=self._get_displayed_plot()
727 whatset=1 #fixme: for all sets
728 if 'rebase' in args or (self.basecurrent != self.current.path):
734 print 'Select baseline'
735 self.basepoints=self._measure_N_points(N=2, whatset=whatset)
736 self.basecurrent=self.current.path
739 print 'Select two points'
740 points=self._measure_N_points(N=2, whatset=whatset)
741 boundpoints=[points[0].index, points[1].index]
744 y=min(plot.vectors[whatset][1][boundpoints[0]:boundpoints[1]])
746 print 'Chosen interval not valid. Try picking it again. Did you pick the same point as begin and end of interval?'
748 print 'Select point to measure'
749 points=self._measure_N_points(N=1, whatset=whatset)
750 #whatplot=points[0].dest
751 y=points[0].graph_coords[1]
753 #fixme: code duplication
754 boundaries=[self.basepoints[0].index, self.basepoints[1].index]
756 to_average=plot.vectors[whatset][1][boundaries[0]:boundaries[1]] #y points to average
758 avg=np.mean(to_average)
760 print str(forcebase*(10**12))+' pN'
761 to_dump='forcebase '+self.current.path+' '+str(forcebase*(10**12))+' pN'
762 self.outlet.push(to_dump)
765 def do_slope(self,args):
769 Measures the slope of a delimited chunk on the return trace.
770 The chunk can be delimited either by two manual clicks, or have
771 a fixed width, given as an argument.
773 Syntax: slope [width]
774 The facultative [width] parameter specifies how many
775 points will be considered for the fit. If [width] is
776 specified, only one click will be required.
777 (c) Marco Brucale, Massimo Sandal 2008
780 # Reads the facultative width argument
786 # Decides between the two forms of user input, as per (args)
788 # Gets the Xs of two clicked points as indexes on the current curve vector
789 print 'Click twice to delimit chunk'
790 points=self._measure_N_points(N=2,whatset=1)
792 print 'Click once on the leftmost point of the chunk (i.e.usually the peak)'
793 points=self._measure_N_points(N=1,whatset=1)
795 slope=self._slope(points,fitspan)
797 # Outputs the relevant slope parameter
800 to_dump='slope '+self.current.path+' '+str(slope)
801 self.outlet.push(to_dump)
803 def _slope(self,points,fitspan):
804 # Calls the function linefit_between
805 parameters=[0,0,[],[]]
807 clickedpoints=[points[0].index,points[1].index]
810 clickedpoints=[points[0].index-fitspan,points[0].index]
813 parameters=self.linefit_between(clickedpoints[0],clickedpoints[1])
815 print 'Cannot fit. Did you click twice the same point?'
818 # Outputs the relevant slope parameter
820 print str(parameters[0])
821 to_dump='slope '+self.curve.path+' '+str(parameters[0])
822 self.outlet.push(to_dump)
824 # Makes a vector with the fitted parameters and sends it to the GUI
825 xtoplot=parameters[2]
829 ytoplot.append((x*parameters[0])+parameters[1])
831 clickvector_x, clickvector_y=[], []
833 clickvector_x.append(item.graph_coords[0])
834 clickvector_y.append(item.graph_coords[1])
836 lineplot=self._get_displayed_plot(0) #get topmost displayed plot
838 lineplot.add_set(xtoplot,ytoplot)
839 lineplot.add_set(clickvector_x, clickvector_y)
842 if lineplot.styles==[]:
843 lineplot.styles=[None,None,None,'scatter']
845 lineplot.styles+=[None,'scatter']
846 if lineplot.colors==[]:
847 lineplot.colors=[None,None,'black',None]
849 lineplot.colors+=['black',None]
852 self._send_plot([lineplot])
857 def linefit_between(self,index1,index2,whatset=1):
859 Creates two vectors (xtofit,ytofit) slicing out from the
860 current return trace a portion delimited by the two indexes
862 Then does a least squares linear fit on that slice.
863 Finally returns [0]=the slope, [1]=the intercept of the
864 fitted 1st grade polynomial, and [2,3]=the actual (x,y) vectors
866 (c) Marco Brucale, Massimo Sandal 2008
868 # Translates the indexes into two vectors containing the x,y data to fit
869 xtofit=self.plots[0].vectors[whatset][0][index1:index2]
870 ytofit=self.plots[0].vectors[whatset][1][index1:index2]
872 # Does the actual linear fitting (simple least squares with numpy.polyfit)
874 linefit=np.polyfit(xtofit,ytofit,1)
876 return (linefit[0],linefit[1],xtofit,ytofit)