8000 ENH ufunc called on memmap return a ndarray by lesteve · Pull Request #7406 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH ufunc called on memmap return a ndarray #7406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions doc/release/1.12.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ were doing. This caused a complication in the downstream 'pandas' library
that encountered an issue with 'numpy' compatibility. Now, all array-like
methods in this module are called with keyword arguments instead.

Operations on np.memmap objects return numpy arrays in most cases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously operations on a memmap (e.g. adding 1 to it) object would
misleadingly return a memmap instance even if the result was actually
not memmapped. Also reduction of a memmap (e.g. ``.sum(axis=None``)
return a numpy scalar instead of a 0d memmap.

Deprecations
============

Expand Down
21 changes: 21 additions & 0 deletions numpy/core/memmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,24 @@ def flush(self):
"""
if self.base is not None and hasattr(self.base, 'flush'):
self.base.flush()

def __array_wrap__(self, arr, context=None):
arr = super(memmap, self).__array_wrap__(arr, context)

# Return a memmap if a memmap was given as the output of the
# ufunc. Leave the arr class unchanged if self is not a memmap
# to keep original memmap subclasses behavior
if self is arr or type(self) is not memmap:
return arr
# Return scalar instead of 0d memmap, e.g. for np.sum with
# axis=None
if arr.shape == ():
return arr[()]
# Return ndarray otherwise
return arr.view(np.ndarray)

def __getitem__(self, index):
res = super(memmap, self).__getitem__(index)
if type(res) is memmap and res._mmap is None:
return res.view(type=ndarray)
return res
49 changes: 48 additions & 1 deletion numpy/core/tests/test_memmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import shutil
from tempfile import NamedTemporaryFile, TemporaryFile, mktemp, mkdtemp

from numpy import memmap
from numpy import (
memmap, sum, average, product, ndarray, isscalar, add, subtract, multiply)

from numpy import arange, allclose, asarray
from numpy.testing import (
TestCase, run_module_suite, assert_, assert_equal, assert_array_equal,
Expand Down Expand Up @@ -126,5 +128,50 @@ def test_view(self):
new_array = asarray(fp)
assert_(new_array.base is fp)

def test_ufunc_return_ndarray(self):
fp = memmap(self.tmpfp, dtype=self.dtype, shape=self.shape)
fp[:] = self.data

for unary_op in [sum, average, product]:
result = unary_op(fp)
assert_(isscalar(result))
assert_(result.__class__ is self.data[0, 0].__class__)

assert_(unary_op(fp, axis=0).__class__ is ndarray)
assert_(unary_op(fp, axis=1).__class__ is ndarray)

for binary_op in [add, subtract, multiply]:
assert_(binary_op(fp, self.data).__class__ is ndarray)
assert_(binary_op(self.data, fp).__class__ is ndarray)
assert_(binary_op(fp, fp).__class__ is ndarray)

fp += 1
assert(fp.__class__ is memmap)
add(fp, 1, out=fp)
assert(fp.__class__ is memmap)

6F4D def test_getitem(self):
fp = memmap(self.tmpfp, dtype=self.dtype, shape=self.shape)
fp[:] = self.data

assert_(fp[1:, :-1].__class__ is memmap)
# Fancy indexing returns a copy that is not memmapped
assert_(fp[[0, 1]].__class__ is ndarray)

def test_memmap_subclass(self):
class MemmapSubClass(memmap):
pass

fp = MemmapSubClass(self.tmpfp, dtype=self.dtype, shape=self.shape)
fp[:] = self.data

# We keep previous behavior for subclasses of memmap, i.e. the
# ufunc and __getitem__ output is never turned into a ndarray
assert_(sum(fp, axis=0).__class__ is MemmapSubClass)
assert_(sum(fp).__class__ is MemmapSubClass)
assert_(fp[1:, :-1].__class__ is MemmapSubClass)
assert(fp[[0, 1]].__class__ is MemmapSubClass)


if __name__ == "__main__":
run_module_suite()
0