diff --git a/python/dlisio/plumbing/channel.py b/python/dlisio/plumbing/channel.py index 44b4ff7c0..03c1f8bfd 100644 --- a/python/dlisio/plumbing/channel.py +++ b/python/dlisio/plumbing/channel.py @@ -165,6 +165,18 @@ def curves(self): """ Returns a numpy ndarray with the curves-values. + Notes + ----- + + This method should only be used if there is only *one* channel of + interest in a particular frame. + + Due to the memory-layout of dlis-files, reading a single channel from + disk and reading the entire frame is almost equally fast. That means + reading channels from the same frame one-by-one with this method is + _way_ slower than reading the entire frame with :func:`Frame.curves()` + and then indexing on the channels-of-interest. + Examples -------- @@ -203,7 +215,7 @@ def curves(self): ------- curves : np.ndarray """ - return self.frame.curves()[self.name] + return np.copy(self.frame.curves()[self.name]) def describe_attr(self, buf, width, indent, exclude): describe_description(buf, self.long_name, width, indent, exclude) diff --git a/python/docs/examples.rst b/python/docs/examples.rst index c1f880199..df4de66b7 100644 --- a/python/docs/examples.rst +++ b/python/docs/examples.rst @@ -179,6 +179,11 @@ which returns a structured numpy array that support common slicing operations: >>> curve[0:5] array([852606., 852606., 852606., 852606., 852606.], dtype=float32) +Note that its almost always considerably faster to read curves-data with +:py:func:`dlisio.plumbing.Frame.curves()`. Please refer to +:py:func:`dlisio.plumbing.Channel.curves()` for further elaboration on why this +is. + Access all curves in a frame with :py:func:`dlisio.plumbing.Frame.curves()`. The returned structured numpy array can be indexed by Channel mnemonics and/or sliced by samples: diff --git a/python/tests/test_curves.py b/python/tests/test_curves.py index 6d35e389a..75a09c1a4 100644 --- a/python/tests/test_curves.py +++ b/python/tests/test_curves.py @@ -73,6 +73,16 @@ def makeframe(): frame.link() return frame +def test_curves_are_copy(f): + # All channel.curves() really does is to slice the full frame array + # returned by frame.curves(). Make sure the returned slice is a copy not a + # view. Returning a view makes it impossible to free up any memory from + # the original array, hence holding on to way more memory than needed. + + channel = f.object('CHANNEL', 'CHANN1') + curves = channel.curves() + assert curves.flags['OWNDATA'] + def test_curves_values(f): frame = f.object('FRAME', 'FRAME1', 10, 0) curves = frame.curves()