Thank Jonathan,

Good to confirm this isn't something inappropriate I'm doing. I give up 
transparency here in my application, so I'll just work around it. I leave it up 
to wiser numpy heads as to whether it's worth altering these numpy.ma functions 
to enable subclassing.

Best,
Ryan

On Feb 13, 2016, at 11:48 AM, Jonathan Helmus <jjhel...@gmail.com> wrote:

> 
> 
> On 2/12/16 6:06 PM, Gutenkunst, Ryan N - (rgutenk) wrote:
>> Hello all,
>> 
>> In 2009 I developed an application that uses a subclass of masked arrays as 
>> a central data object. My subclass Spectrum possesses additional attributes 
>> along with many custom methods. It was very convenient to be able to use 
>> standard numpy functions for doing arithmetic on these objects. However, my 
>> code broke with numpy 1.10. I've finally had a chance to track down the 
>> problem, and I am hoping someone can suggest a workaround.
>> 
>> See below for an example, which is as minimal as I could concoct. In this 
>> case, I have a Spectrum object that I'd like to take the logarithm of using 
>> numpy.ma.log, while preserving the value of the "folded" attribute. Up to 
>> numpy 1.9, this worked as expected, but in numpy 1.10 and 1.11 the attribute 
>> is not preserved.
>> 
>> The change in behavior appears to be driven by a commit made on Jun 16th, 
>> 2015 by Marten van Kerkwijk. In particular, the commit changed 
>> _MaskedUnaryOperation.__call__ so that the result array's update_from method 
>> is no longer called with the input array as the argument, but rather the 
>> result of the numpy UnaryOperation (old line 889, new line 885). Because 
>> that UnaryOperation doesn't carry my new attribute, it's not present for 
>> update_from to access. I notice that similar changes were made to 
>> MaskedBinaryOperation, although I haven't tested those. It's not clear to me 
>> from the commit message why this particular change was made, so I don't know 
>> whether this new behavior is intentional.
>> 
>> I know that subclassing arrays isn't widely encouraged, but it has been very 
>> convenient in my code. Is it still possible to subclass masked_array in such 
>> a way that functions like numpy.ma.log preserve additional attributes? If 
>> so, can someone point me in the right direction?
>> 
>> Thanks!
>> Ryan
>> 
>> *** Begin example
>> 
>> import numpy
>> print 'Working with numpy {0}'.format(numpy.__version__)
>> 
>> class Spectrum(numpy.ma.masked_array):
>>     def __new__(cls, data, mask=numpy.ma.nomask, data_folded=None):
>>         subarr = numpy.ma.masked_array(data, mask=mask, keep_mask=True,
>>                                        shrink=True)
>>         subarr = subarr.view(cls)
>>         subarr.folded = data_folded
>> 
>>         return subarr
>> 
>>     def __array_finalize__(self, obj):
>>         if obj is None:
>>             return
>>         numpy.ma.masked_array.__array_finalize__(self, obj)
>>         self.folded = getattr(obj, 'folded', 'unspecified')
>> 
>>     def _update_from(self, obj):
>>         print('Input to update_from: {0}'.format(repr(obj)))
>>         numpy.ma.masked_array._update_from(self, obj)
>>         self.folded = getattr(obj, 'folded', 'unspecified')
>> 
>>     def __repr__(self):
>>         return 'Spectrum(%s, folded=%s)'\
>>                 % (str(self), str(self.folded))
>> 
>> fs1 = Spectrum([2,3,4.], data_folded=True)
>> fs2 = numpy.ma.log(fs1)
>> print('fs2.folded status: {0}'.format(fs2.folded))
>> print('Expectation is True, achieved with numpy 1.9')
>> 
>> *** End example
>> 
>> --
>> Ryan Gutenkunst
>> Assistant Professor
>> Molecular and Cellular Biology
>> University of Arizona
>> phone: (520) 626-0569, office LSS 325
>> http://gutengroup.mcb.arizona.edu
>> Latest paper: "Computationally efficient composite likelihood statistics for 
>> demographic inference"
>> Molecular Biology and Evolution; http://dx.doi.org/10.1093/molbev/msv255
> Ryan,
> 
> I'm not sure if you will be able to get this to work as in NumPy 1.9, but the 
> __array_wrap__ method is intended to be the mechanism for subclasses to set 
> their return type, adjust metadata, etc [1].  Unfortunately, the numpy.ma.log 
> function does not seem to make a call to  __array_wrap__ (at least in NumPy 
> 1.10.2) although numpy.log does:
> 
> from __future__ import print_function
> import numpy
> print('Working with numpy {0}'.format(numpy.__version__))
> 
> 
> class Spectrum(numpy.ma.masked_array):
>    def __new__(cls, data, mask=numpy.ma.nomask, data_folded=None):
>        subarr = numpy.ma.masked_array(data, mask=mask, keep_mask=True,
>                                       shrink=True)
>        subarr = subarr.view(cls)
>        subarr.folded = data_folded
> 
>        return subarr
> 
>    def __array_finalize__(self, obj):
>        if obj is None:
>            return
>        numpy.ma.masked_array.__array_finalize__(self, obj)
>        self.folded = getattr(obj, 'folded', 'unspecified')
> 
>    def __array_wrap__(self, out_arr, context=None):
>        print('__array_wrap__ called')
>        return numpy.ndarray.__array_wrap__(self, out_arr, context)
> 
>    def __repr__(self):
>        return 'Spectrum(%s, folded=%s)'\
>                % (str(self), str(self.folded))
> 
> fs1 = Spectrum([2,3,4.], data_folded=True)
> 
> print('numpy.ma.log:')
> fs2 = numpy.ma.log(fs1)
> print('fs2 type:', type(fs2))
> print('fs2.folded status: {0}'.format(fs2.folded))
> 
> print('numpy.log:')
> fs3 = numpy.log(fs1)
> print('fs3 type:', type(fs3))
> print('fs3.folded status: {0}'.format(fs3.folded))
> 
> ----
> $ python example.py
> Working with numpy 1.10.2
> numpy.ma.log:
> fs2 type: <class '__main__.Spectrum'>
> fs2.folded status: unspecified
> numpy.log:
> __array_wrap__ called
> fs3 type: <class '__main__.Spectrum'>
> fs3.folded status: True
> 
> 
> The change mentioned in the original message was made in pull request 3907 
> [2] in case anyone wants to have a look.
> 
> Cheers,
> 
>    - Jonathan Helmus
> 
> [1] 
> http://docs.scipy.org/doc/numpy-1.10.1/user/basics.subclassing.html#array-wrap-for-ufuncs
> [2] https://github.com/numpy/numpy/pull/3907
> _______________________________________________
> NumPy-Discussion mailing list
> NumPy-Discussion@scipy.org
> https://mail.scipy.org/mailman/listinfo/numpy-discussion

--
Ryan Gutenkunst
Assistant Professor
Molecular and Cellular Biology
University of Arizona
phone: (520) 626-0569, office LSS 325
http://gutengroup.mcb.arizona.edu
Latest paper: "Computationally efficient composite likelihood statistics for 
demographic inference"
Molecular Biology and Evolution; http://dx.doi.org/10.1093/molbev/msv255

_______________________________________________
NumPy-Discussion mailing list
NumPy-Discussion@scipy.org
https://mail.scipy.org/mailman/listinfo/numpy-discussion

Reply via email to