Issue 1:
I think that certain functions should return the same type as the input, if they implement __array_wrap__, but aren't necessarily subclass of ndarray. The pandas Series class is an instance of this. We'd like it if functions like where, diff, and a few others returned a Series object similar to how ufuncs work.

Here are the current functions that don't behave in the desired way:

zeros_like

ones_like

empty_like

sort

diff

where

Here's a related pandas issue: pandas-dev/pandas#6539

There may be others as well.

Here's an example:

In [13]: import pandas as pd

In [14]: import numpy as np

In [15]: s = pd.Series(np.random.randn(10))

In [16]: s
Out[16]:
0 0.5885
1 -0.0710
2 0.0832
3 0.7473
4 0.9818
5 1.0533
6 -2.3336
7 0.5666
8 0.4181
9 0.2505
dtype: float64

In [17]: np.abs(s)
Out[17]:
0 0.5885
1 0.0710
2 0.0832
3 0.7473
4 0.9818
5 1.0533
6 2.3336
7 0.5666
8 0.4181
9 0.2505
dtype: float64

In [18]: np.em
np.emath np.empty np.empty_like

In [18]: np.empty_like(s)
Out[18]: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

Issue 2:
Is there a reason np.diff,np.ones_like,np.empty_like do not call __array_wrap__ on exit; am I doing something wrong, or is this a bug? (using numpy 1.7.1)

Create a NDLike class which is a container for a ndarray

In [34]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class NDLike(object):
: def __init__(self, values):
: self.values = values
: def __array__(self, result=None):
: return self.values
: def __array_wrap__(self, result):
: return type(self)(result)
: def __repr__(self):
: return "[%s]\n%s" % (type(self),self.values)
:--

In [35]: x = NDLike(randn(10))

In [36]: x
Out[36]:
[<class '__main__.NDLike'>]
[-0.90565484 -0.84590628 -0.66540701 -0.82185958 -0.74112001 0.16294691
-0.7707505 0.51350363 -1.89373784 -1.24690582]

A ufunc calls __array__ on function enter, and __array_wrap__ on exit

In [37]: np.add(x,1)
Out[37]:
[<class '__main__.NDLike'>]
[ 0.09434516 0.15409372 0.33459299 0.17814042 0.25887999 1.16294691
0.2292495 1.51350363 -0.89373784 -0.24690582]

I have found that both np.diff, np.ones_like, np.empty_like DO NOT appear

to call __array_wrap__ on function exit. This only difference that I can see is that these are basically pieces of python code wrapping other code, rather than direct c-level calls, e.g. np.ones_like calls empty a c-level call.

In [38]: np.diff(x)
Out[38]:
array([ 0.05974856, 0.18049927, -0.15645257, 0.08073957, 0.90406691,
-0.93369741, 1.28425413, -2.40724147, 0.64683202])

In [39]: np.ones_like(x)
Out[39]: array([ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])

In [40]: np.empty_like(x)
Out[40]:
array([ 6.93756448e-310, 2.35850497e-316, 2.58198627e-316,
2.60377180e-316, 2.59158775e-316, 1.34657789e-316,
1.27678658e-316, 0.00000000e+000, 2.46663143e-316,
1.63041663e-322])

Is there a reason these do not call __array_wrap__ on exit; am I doing something wrong, or is this a bug?

Some context:

I have refactored pandas Series object from a ndarray sub-class to a sub-class that is common in the pandas hierarchy (pandas-dev/pandas#3482), for ease of code use/modification, etc. This issue only comes up if the ndarrayness is somewhat abused in that a user is expecting that a Series object that goes into an ndarray comes out with another Series object. (which is the case for nearly every method/ufunc by using either wrapped methods or the __array__/__array_wrap__ machinery)

Thanks for taking a look

Jeff