Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
a8a80c1
Implemented system for array annotations in neo core
muellerbjoern Jan 24, 2018
bf06783
Added missing line to be able to add annotations later
muellerbjoern Jan 24, 2018
2069d0b
Added documentation and removed functionality from data object classes
muellerbjoern Jan 24, 2018
e80ecbc
Fixed bugs when array_annotations was None by making this impossible and
muellerbjoern Jan 24, 2018
e63b6fe
Checked again, how array annotations should be dealt with in certain
muellerbjoern Jan 24, 2018
d5aa090
Added merging functionality for array annotations.
muellerbjoern Jan 29, 2018
2122bb0
Testing around in test_spiketrain
muellerbjoern Jan 29, 2018
e5bbc13
Found problem to be with numpy getslice and getitem, will need more
muellerbjoern Jan 29, 2018
86ac57f
Fixed small bug in merging of array_annotations in SpikeTrain
muellerbjoern Jan 29, 2018
a2b7593
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Jan 29, 2018
bb7e046
Reverted change of last commit that was done together with merges. Need
muellerbjoern Jan 29, 2018
f7998cd
Playing around, trying to fix deeply rooted bug
muellerbjoern Jan 30, 2018
1e67071
Removed code that was for an unrelated bug
muellerbjoern Jan 31, 2018
0c8f699
TODO comment added
muellerbjoern Feb 1, 2018
b99cb77
Small step towards fixing array_annotations in AnalogSignal __getitem__
muellerbjoern Feb 1, 2018
7d61342
Implemented rescale in class DataObject
muellerbjoern Feb 6, 2018
1614636
More experiments with rescale, they seem to fail
muellerbjoern Feb 6, 2018
1feade8
Enabled generic implementation for rescale, will need to be checked
muellerbjoern Feb 13, 2018
c6417dd
Renamed BaseSignal duplicate_with_new_array to ..._new_data
muellerbjoern Feb 28, 2018
8469137
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Mar 19, 2018
a3c961b
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Mar 21, 2018
e288450
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Mar 23, 2018
79aeb3d
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Mar 26, 2018
94c21d4
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern May 16, 2018
3185d4d
Fixed a bug where getitem would return the wrong annotations
muellerbjoern May 18, 2018
0643843
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern May 18, 2018
501df9e
Completely generalized rescale, which is now dependent on
muellerbjoern May 18, 2018
6b4a2d1
Finished rescale and getitem hopefully
muellerbjoern May 23, 2018
3ee9c9b
Added file for tests for DataObject class and especially array
muellerbjoern May 23, 2018
6cb2b6e
Adapted AnalogSignal test to the renaming of duplicate_with_new_array to
muellerbjoern May 23, 2018
8b09dbc
Fixed bug that would cause general rescaling to fail because of
muellerbjoern May 23, 2018
7fdc530
Minor fixes and comments
muellerbjoern May 23, 2018
2664135
No real changes
muellerbjoern May 24, 2018
db32ab1
Made sure array_annotations are deepcopied
muellerbjoern May 24, 2018
ead3b70
Added tests for DataObject class, especially concerning array
muellerbjoern May 25, 2018
909a4b0
Fixed bug where array annotations were copied when returning them
muellerbjoern May 25, 2018
6872261
Fixed array annotations not being copied on datobj.copy()
muellerbjoern May 25, 2018
0480b23
Make sure array annotations are wrapped when AnalogSignal is sliced
muellerbjoern May 25, 2018
89c897b
Added array annotations to all tests for AnalogSignal
muellerbjoern May 25, 2018
1e305fa
Added reminder
muellerbjoern May 25, 2018
7c93d67
Finished tests of array annotations for AnalogSignal (testing
muellerbjoern Jun 8, 2018
efc84f4
Improved tests for array equality
muellerbjoern Jun 15, 2018
09c1746
Added tests for array annotations for SpikeTrain
muellerbjoern Jun 15, 2018
67e50d0
Finished tests for array annotations for SpikeTrains
muellerbjoern Jun 20, 2018
8cc67fe
Added tests for array annotations for Epoch
muellerbjoern Jun 20, 2018
f0d56c7
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Jul 25, 2018
52bc08d
Added array annotations to tests of Event and Epoch
muellerbjoern Jul 25, 2018
cde32d7
Added test for array annotations when splicing AnalogSignal
muellerbjoern Jul 25, 2018
6c03260
Added array annotation tests for IrregularlySampledSignal
muellerbjoern Jul 25, 2018
e27cef7
Fixed AnalogSignal.time_slice omitting array_annotations
muellerbjoern Jul 25, 2018
55e0dad
Added tests for array annotations for AnalogSignals with multiple
muellerbjoern Jul 25, 2018
39b7670
Array annotations and AnalogSignal splicing
muellerbjoern Jul 25, 2018
280eb0c
More features for array annotations
muellerbjoern Jul 25, 2018
313fb98
Fixed empty array annotations
muellerbjoern Jul 25, 2018
259c3fa
Embedded Epoch.durations and Event/Epoch.labels in array annotations
muellerbjoern Jul 25, 2018
0903fca
Fake_neo consistency of objects
muellerbjoern Jul 25, 2018
657f17d
Adapted BrainwaresrcIO for array annotations
muellerbjoern Jul 26, 2018
5156793
Improved checks for array annotations
muellerbjoern Jul 26, 2018
7b612fa
Refactoring
muellerbjoern Jul 27, 2018
8e6b13f
Some refactoring and adapting comments
muellerbjoern Jul 27, 2018
d52207e
Moved merging array annotations to own method
muellerbjoern Jul 27, 2018
189444b
Removed finished TODOs
muellerbjoern Jul 27, 2018
e0a7c32
Refactoring
muellerbjoern Jul 27, 2018
2116792
Refactoring
muellerbjoern Jul 27, 2018
f01f54c
Added warnings if array annotations are omitted
muellerbjoern Jul 27, 2018
63dd1fd
Made sure that array annotations are always kept
muellerbjoern Jul 27, 2018
87cb993
Removed outdated TODOs
muellerbjoern Jul 30, 2018
ddb71ef
Implemented labels and durations into array annotations
muellerbjoern Jul 30, 2018
e2f51c4
Ensured correct handling of pq.Quantities in array annotations
muellerbjoern Jul 30, 2018
4b1c24b
Made merge_array_annotations private (_merge...)
muellerbjoern Jul 30, 2018
9f83c72
Added docstrings
muellerbjoern Jul 30, 2018
b61366b
Improved handling of Quantities ar array_annotations further
muellerbjoern Jul 30, 2018
c0e5969
Ensured data consistency by NOT copying array annotations
muellerbjoern Jul 30, 2018
bacf820
Generating array_annotations in generate_datasets
muellerbjoern Jul 31, 2018
0f20c99
Added assert_same_array_annotations to tools
muellerbjoern Jul 31, 2018
ff83d49
Corrected array comparison
muellerbjoern Jul 31, 2018
b4c4094
Not copying arr_anns when time_slicing
muellerbjoern Aug 1, 2018
7b96847
Adapted documentation for array_annotations
muellerbjoern Aug 1, 2018
0e39345
Made short description of array annotations clearer
muellerbjoern Aug 2, 2018
933e907
Forcing check_array_annotations whenever setting an array annotation
muellerbjoern Sep 6, 2018
1f9e3ad
Merge branch 'julia/fix_assert' into arr_anns/fix_assert
muellerbjoern Sep 7, 2018
7a615cc
Always checking array_annotation size
muellerbjoern Sep 12, 2018
c99a334
Added tests to check array_annotation dict after unpickling
muellerbjoern Sep 13, 2018
617351f
Cleaned the code
muellerbjoern Sep 13, 2018
3bf07d2
Removed unnecessary commentso
muellerbjoern Sep 13, 2018
03be750
Added tests for implicit checking of array_annotations
muellerbjoern Sep 13, 2018
fa31174
Fixed missing detection of nested dicts as array_annotations
muellerbjoern Sep 13, 2018
7a428d1
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Sep 13, 2018
23dbd8c
Fixed bug where array_annotations in AnalogSignal were not initialize…
muellerbjoern Sep 13, 2018
32ed55e
Added tests for ArrayDict
muellerbjoern Sep 13, 2018
b4b6144
Fixed bug with array_annotations not being an ArrayDict
muellerbjoern Sep 13, 2018
fd3148e
Reset warning filter
muellerbjoern Sep 14, 2018
c036ea1
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Sep 21, 2018
4ccfbe3
Made unclear sentence clearer
muellerbjoern Sep 21, 2018
dec6676
Improved comments and reset warnings filter
muellerbjoern Sep 21, 2018
f500280
Corrected inline comment about check for dimensionality
muellerbjoern Sep 21, 2018
fc6b6d8
Corrected inline comment about check for dimensionality
muellerbjoern Sep 21, 2018
0c91c27
Merge branch 'arr_anns/no_dict' of https://github.com/bjoern1001001/p…
muellerbjoern Sep 21, 2018
9e06d96
[ArrayAnnotations] Documentation improvements
muellerbjoern Oct 2, 2018
1f33335
[ArrayAnnotations] Removed duplicate statement
muellerbjoern Oct 2, 2018
fbf3bc6
Removed a few unnecessary lines
muellerbjoern Oct 2, 2018
2c7bf56
[ArrayAnnotations] Allow empty array_annotations
muellerbjoern Oct 2, 2018
5ea4d05
[ArrayAnnotations] Updated documentation
muellerbjoern Oct 2, 2018
0711831
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Oct 2, 2018
dd1b6ef
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Oct 4, 2018
88c8b13
Merge branch 'arr_anns/no_dict_no_zero' into arr_anns/test3_test
muellerbjoern Oct 4, 2018
a07ee29
Merge branch 'arr_anns/fix_assert' into arr_anns/all
muellerbjoern Oct 4, 2018
2c6e389
[Array Annnotations] Fixed bug caused by merge
muellerbjoern Oct 4, 2018
90bb3ea
Merge branch 'master' of https://github.com/NeuralEnsemble/python-neo…
muellerbjoern Nov 15, 2018
521976a
Merge branch 'arr_anns/test3' of https://github.com/bjoern1001001/pyt…
JuliaSprenger Nov 16, 2018
c9ae4ba
Improved checking of lists as array annotations
muellerbjoern Nov 21, 2018
76a5584
Merge branch 'arr_anns/test3' of https://github.com/bjoern1001001/pyt…
JuliaSprenger Nov 21, 2018
8389f71
Documentation updates and minor changes
JuliaSprenger Nov 21, 2018
dd59f93
Fix array annotation test for warnings
JuliaSprenger Nov 21, 2018
953972b
Fix failing test due to invalid array annotation
JuliaSprenger Nov 22, 2018
07e1134
Merge branch 'master' of github.com:NeuralEnsemble/python-neo into bj…
JuliaSprenger Nov 22, 2018
a9f8b30
Minor change in test implementation
JuliaSprenger Nov 23, 2018
9217af1
Resolve merge conflict
JuliaSprenger Nov 23, 2018
78e241a
Travis testing
JuliaSprenger Nov 23, 2018
5e95a0f
Clean up code
JuliaSprenger Nov 23, 2018
89d9d2b
Pep8ification
JuliaSprenger Nov 23, 2018
ef6ad71
Manual pep8 correction
JuliaSprenger Nov 23, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions doc/source/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,23 @@ limitations on the data types of annotations: they must be "simple" types or
containers (lists, dicts, tuples, NumPy arrays) of simple types, where the simple types
are ``integer``, ``float``, ``complex``, ``Quantity``, ``string``, ``date``, ``time`` and
``datetime``.

Array Annotations
-----------------

Next to "regular" annotations there is also a way to annotate arrays of values
in order to create annotations with one value per data point. Using this feature,
called Array Annotations, the consistency of those annotations with the actual data
is ensured.
Apart from adding those on object construction, Array Annotations can also be added
using the :meth:`array_annotate` method provided by all Neo data objects, e.g.::

>>> sptr = SpikeTrain(times=[1, 2, 3]*pq.s, t_stop=3*pq.s)
>>> sptr.array_annotate(index=[0, 1, 2], relevant=[True, False, True])
>>> print(sptr.array_annotations)
{'index': array([0, 1, 2]), 'relevant': array([ True, False, True])}

Since Array Annotations may be written to a file or database, there are some
limitations on the data types of arrays: they must be 1-dimensional (i.e. not nested)
and contain the same types as annotations:
``integer``, ``float``, ``complex``, ``Quantity``, ``string``, ``date``, ``time`` and ``datetime``.
35 changes: 25 additions & 10 deletions neo/core/analogsignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import quantities as pq

from neo.core.baseneo import BaseNeo, MergeError, merge_annotations
from neo.core.dataobject import DataObject
from neo.core.channelindex import ChannelIndex
from copy import copy, deepcopy

Expand Down Expand Up @@ -54,14 +55,17 @@ def _get_sampling_rate(sampling_rate, sampling_period):

def _new_AnalogSignalArray(cls, signal, units=None, dtype=None, copy=True, t_start=0 * pq.s,
sampling_rate=None, sampling_period=None, name=None, file_origin=None,
description=None, annotations=None, channel_index=None, segment=None):
description=None, array_annotations=None, annotations=None,
channel_index=None, segment=None):
'''
A function to map AnalogSignal.__new__ to function that
does not do the unit checking. This is needed for pickle to work.
'''
obj = cls(signal=signal, units=units, dtype=dtype, copy=copy, t_start=t_start,
sampling_rate=sampling_rate, sampling_period=sampling_period, name=name,
file_origin=file_origin, description=description, **annotations)
obj = cls(signal=signal, units=units, dtype=dtype, copy=copy,
t_start=t_start, sampling_rate=sampling_rate,
sampling_period=sampling_period, name=name,
file_origin=file_origin, description=description,
array_annotations=array_annotations, **annotations)
obj.channel_index = channel_index
obj.segment = segment
return obj
Expand Down Expand Up @@ -116,6 +120,8 @@ class AnalogSignal(BaseSignal):
*Optional attributes/properties*:
:dtype: (numpy dtype or str) Override the dtype of the signal array.
:copy: (bool) True by default.
:array_annotations: (dict) Dict mapping strings to numpy arrays containing annotations \
for all data points

Note: Any other additional arguments are assumed to be user-specific
metadata and stored in :attr:`annotations`.
Expand Down Expand Up @@ -161,7 +167,7 @@ class AnalogSignal(BaseSignal):

def __new__(cls, signal, units=None, dtype=None, copy=True, t_start=0 * pq.s,
sampling_rate=None, sampling_period=None, name=None, file_origin=None,
description=None, **annotations):
description=None, array_annotations=None, **annotations):
'''
Constructs new :class:`AnalogSignal` from data.

Expand All @@ -188,7 +194,7 @@ def __new__(cls, signal, units=None, dtype=None, copy=True, t_start=0 * pq.s,

def __init__(self, signal, units=None, dtype=None, copy=True, t_start=0 * pq.s,
sampling_rate=None, sampling_period=None, name=None, file_origin=None,
description=None, **annotations):
description=None, array_annotations=None, **annotations):
'''
Initializes a newly constructed :class:`AnalogSignal` instance.
'''
Expand All @@ -199,8 +205,8 @@ def __init__(self, signal, units=None, dtype=None, copy=True, t_start=0 * pq.s,

# Calls parent __init__, which grabs universally recommended
# attributes and sets up self.annotations
BaseNeo.__init__(self, name=name, file_origin=file_origin, description=description,
**annotations)
DataObject.__init__(self, name=name, file_origin=file_origin, description=description,
array_annotations=array_annotations, **annotations)

def __reduce__(self):
'''
Expand All @@ -210,8 +216,8 @@ def __reduce__(self):
return _new_AnalogSignalArray, (self.__class__, np.array(self), self.units, self.dtype,
True, self.t_start, self.sampling_rate,
self.sampling_period, self.name, self.file_origin,
self.description, self.annotations, self.channel_index,
self.segment)
self.description, self.array_annotations,
self.annotations, self.channel_index, self.segment)

def _array_finalize_spec(self, obj):
'''
Expand Down Expand Up @@ -286,10 +292,12 @@ def __getitem__(self, i):
obj = obj.reshape(-1, 1)
if self.channel_index:
obj.channel_index = self.channel_index.__getitem__(k)
obj.array_annotate(**deepcopy(self.array_annotations_at_index(k)))
elif isinstance(i, slice):
obj = super(AnalogSignal, self).__getitem__(i)
if i.start:
obj.t_start = self.t_start + i.start * self.sampling_period
obj.array_annotations = deepcopy(self.array_annotations)
elif isinstance(i, np.ndarray):
# Indexing of an AnalogSignal is only consistent if the resulting number of
# samples is the same for each trace. The time axis for these samples is not
Expand Down Expand Up @@ -485,6 +493,13 @@ def time_slice(self, t_start, t_stop):
# we're going to send the list of indicies so that we get *copy* of the
# sliced data
obj = super(AnalogSignal, self).__getitem__(np.arange(i, j, 1))

# If there is any data remaining, there will be data for every channel
# In this case, array_annotations need to stay available
# super.__getitem__ cannot do this, so it needs to be done here
if len(obj) > 0:
obj.array_annotations = self.array_annotations

obj.t_start = self.t_start + i * self.sampling_period

return obj
Expand Down
118 changes: 51 additions & 67 deletions neo/core/basesignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,38 @@
http://docs.scipy.org/doc/numpy/user/basics.subclassing.html

In brief:
* Constructor :meth:`__new__` for :class:`BaseSignal` doesn't exist.
* Constructor :meth:`__new__` for :class:`BaseSignal` doesn't exist.
Only child objects :class:`AnalogSignal` and :class:`IrregularlySampledSignal`
can be created.
'''

# needed for Python 3 compatibility
from __future__ import absolute_import, division, print_function

import copy
import logging

import numpy as np
import quantities as pq

from neo.core.baseneo import BaseNeo, MergeError, merge_annotations
from neo.core.dataobject import DataObject, ArrayDict
from neo.core.channelindex import ChannelIndex

logger = logging.getLogger("Neo")


class BaseSignal(BaseNeo, pq.Quantity):
class BaseSignal(DataObject):
'''
This is the base class from which all signal objects inherit:
:class:`AnalogSignal` and :class:`IrregularlySampledSignal`.

This class contains all common methods of both child classes.
It uses the following child class attributes:

:_necessary_attrs: a list of the attributes that the class must have.
:_necessary_attrs: a list of the attributes that the class must have.

:_recommended_attrs: a list of the attributes that the class may
:_recommended_attrs: a list of the attributes that the class may
optionally have.
'''

Expand All @@ -58,16 +60,21 @@ def __array_finalize__(self, obj):

User-specified values are only relevant for construction from
constructor, and these are set in __new__ in the child object.
Then they are just copied over here. Default values for the
Then they are just copied over here. Default values for the
specific attributes for subclasses (:class:`AnalogSignal`
and :class:`IrregularlySampledSignal`) are set in
and :class:`IrregularlySampledSignal`) are set in
:meth:`_array_finalize_spec`
'''
super(BaseSignal, self).__array_finalize__(obj)
self._array_finalize_spec(obj)

# The additional arguments
self.annotations = getattr(obj, 'annotations', {})
# Add empty array annotations, because they cannot always be copied,
# but do not overwrite existing ones from slicing etc.
# This ensures the attribute exists
if not hasattr(self, 'array_annotations'):
self.array_annotations = ArrayDict(self._get_arr_ann_length())

# Globally recommended attributes
self.name = getattr(obj, 'name', None)
Expand All @@ -83,18 +90,24 @@ def _rescale(self, signal, units=None):
'''
Check that units are present, and rescale the signal if necessary.
This is called whenever a new signal is
created from the constructor. See :meth:`__new__' in
created from the constructor. See :meth:`__new__' in
:class:`AnalogSignal` and :class:`IrregularlySampledSignal`
'''
if units is None:
if not hasattr(signal, "units"):
raise ValueError("Units must be specified")
elif isinstance(signal, pq.Quantity):
# could improve this test, what if units is a string?
if units != signal.units:
# This test always returns True, i.e. rescaling is always executed if one of the units
# is a pq.CompoundUnit. This is fine because rescaling is correct anyway.
if pq.quantity.validate_dimensionality(units) != signal.dimensionality:
signal = signal.rescale(units)
return signal

def rescale(self, units):
obj = super(BaseSignal, self).rescale(units)
obj.channel_index = self.channel_index
return obj

def __getslice__(self, i, j):
'''
Get a slice from :attr:`i` to :attr:`j`.attr[0]
Expand All @@ -118,6 +131,9 @@ def _apply_operator(self, other, op, *args):
f = getattr(super(BaseSignal, self), op)
new_signal = f(other, *args)
new_signal._copy_data_complement(self)
# _copy_data_complement can't always copy array annotations,
# so this needs to be done locally
new_signal.array_annotations = copy.deepcopy(self.array_annotations)
return new_signal

def _get_required_attributes(self, signal, units):
Expand All @@ -133,49 +149,32 @@ def _get_required_attributes(self, signal, units):
required_attributes['units'] = units
return required_attributes

def rescale(self, units):
'''
Return a copy of the signal converted to the specified
units
'''
to_dims = pq.quantity.validate_dimensionality(units)
if self.dimensionality == to_dims:
to_u = self.units
signal = np.array(self)
else:
to_u = pq.Quantity(1.0, to_dims)
from_u = pq.Quantity(1.0, self.dimensionality)
try:
cf = pq.quantity.get_conversion_factor(from_u, to_u)
except AssertionError:
raise ValueError('Unable to convert between units of "%s" \
and "%s"' % (from_u._dimensionality,
to_u._dimensionality))
signal = cf * self.magnitude
required_attributes = self._get_required_attributes(signal, to_u)
new = self.__class__(**required_attributes)
new._copy_data_complement(self)
new.channel_index = self.channel_index
new.segment = self.segment
new.annotations.update(self.annotations)
return new

def duplicate_with_new_array(self, signal):
def duplicate_with_new_data(self, signal, units=None):
'''
Create a new signal with the same metadata but different data.
Required attributes of the signal are used.
Note: Array annotations can not be copied here because length of data can change
'''
if units is None:
units = self.units
# else:
# units = pq.quantity.validate_dimensionality(units)

# signal is the new signal
required_attributes = self._get_required_attributes(signal, self.units)
required_attributes = self._get_required_attributes(signal, units)
new = self.__class__(**required_attributes)
new._copy_data_complement(self)
new.annotations.update(self.annotations)
# Note: Array annotations are not copied here, because it is not ensured
# that the same number of signals is used and they would possibly make no sense
# when combined with another signal
return new

def _copy_data_complement(self, other):
'''
Copy the metadata from another signal.
Required and recommended attributes of the signal are used.
Note: Array annotations can not be copied here because length of data can change
'''
all_attr = {self._recommended_attrs, self._necessary_attrs}
for sub_at in all_attr:
Expand All @@ -184,6 +183,9 @@ def _copy_data_complement(self, other):
setattr(self, attr[0], getattr(other, attr[0], None))
setattr(self, 'annotations', getattr(other, 'annotations', None))

# Note: Array annotations cannot be copied because length of data can be changed # here
# which would cause inconsistencies

def __rsub__(self, other, *args):
'''
Backwards subtraction (other-self)
Expand Down Expand Up @@ -223,23 +225,6 @@ def __div__(self, other, *args):
__radd__ = __add__
__rmul__ = __sub__

def as_array(self, units=None):
"""
Return the signal as a plain NumPy array.

If `units` is specified, first rescale to those units.
"""
if units:
return self.rescale(units).magnitude
else:
return self.magnitude

def as_quantity(self):
"""
Return the signal as a quantities array.
"""
return self.view(pq.Quantity)

def merge(self, other):
'''
Merge another signal into this one.
Expand Down Expand Up @@ -279,26 +264,25 @@ def merge(self, other):
kwargs[name] = attr_self
else:
kwargs[name] = "merge(%s, %s)" % (attr_self, attr_other)
merged_annotations = merge_annotations(self.annotations,
other.annotations)
merged_annotations = merge_annotations(self.annotations, other.annotations)
kwargs.update(merged_annotations)
signal = self.__class__(stack, units=self.units, dtype=self.dtype,
copy=False, t_start=self.t_start,
sampling_rate=self.sampling_rate,
**kwargs)

kwargs['array_annotations'] = self._merge_array_annotations(other)

signal = self.__class__(stack, units=self.units, dtype=self.dtype, copy=False,
t_start=self.t_start, sampling_rate=self.sampling_rate, **kwargs)
signal.segment = self.segment

if hasattr(self, "lazy_shape"):
signal.lazy_shape = merged_lazy_shape

# merge channel_index (move to ChannelIndex.merge()?)
if self.channel_index and other.channel_index:
signal.channel_index = ChannelIndex(
index=np.arange(signal.shape[1]),
channel_ids=np.hstack([self.channel_index.channel_ids,
other.channel_index.channel_ids]),
channel_names=np.hstack([self.channel_index.channel_names,
other.channel_index.channel_names]))
signal.channel_index = ChannelIndex(index=np.arange(signal.shape[1]),
channel_ids=np.hstack(
[self.channel_index.channel_ids, other.channel_index.channel_ids]),
channel_names=np.hstack(
[self.channel_index.channel_names, other.channel_index.channel_names]))
else:
signal.channel_index = ChannelIndex(index=np.arange(signal.shape[1]))

Expand Down
Loading