<p dir="auto">I think that certain functions should return the same type as the input, if they implement <code class="notranslate">__array_wrap__</code>, but aren't necessarily subclass of <code class="notranslate">ndarray</code>. The pandas <code class="notranslate">Series</code> class is an instance of this. We'd like it if functions like <code class="notranslate">where</code>, <code class="notranslate">diff</code>, and a few others returned a <code class="notranslate">Series</code> object similar to how ufuncs work.</p>
<p dir="auto">Here are the current functions that don't behave in the desired way:</p>
<ul dir="auto">
<li><code class="notranslate">zeros_like</code></li>
<li><code class="notranslate">ones_like</code></li>
<li><code class="notranslate">empty_like</code></li>
<li><code class="notranslate">sort</code></li>
<li><code class="notranslate">diff</code></li>
<li><code class="notranslate">where</code></li>
</ul>
<p dir="auto">Here's a related pandas issue: <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="28721737" data-permission-text="Title is private" data-url="https://github.com/pandas-dev/pandas/issues/6539" data-hovercard-type="issue" data-hovercard-url="/pandas-dev/pandas/issues/6539/hovercard" href="https://github.com/pandas-dev/pandas/issues/6539">pandas-dev/pandas#6539</a></p>
<p dir="auto">There may be others as well.</p>
<p dir="auto">Here's an example:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="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.])"><pre class="notranslate"><code class="notranslate">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.])
</code></pre></div>
<p dir="auto">Is there a reason <code class="notranslate">np.diff,np.ones_like,np.empty_like</code> do not call <code class="notranslate">__array_wrap__</code> on exit; am I doing something wrong, or is this a bug? (using numpy 1.7.1)</p>
<p dir="auto">Create a NDLike class which is a container for a ndarray</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="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 &quot;[%s]\n%s&quot; % (type(self),self.values)
:--

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

In [36]: x
Out[36]: 
[&lt;class '__main__.NDLike'&gt;]
[-0.90565484 -0.84590628 -0.66540701 -0.82185958 -0.74112001  0.16294691
 -0.7707505   0.51350363 -1.89373784 -1.24690582]"><pre class="notranslate"><code class="notranslate">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]: 
[&lt;class '__main__.NDLike'&gt;]
[-0.90565484 -0.84590628 -0.66540701 -0.82185958 -0.74112001  0.16294691
 -0.7707505   0.51350363 -1.89373784 -1.24690582]
</code></pre></div>
<p dir="auto">A ufunc calls <code class="notranslate">__array__</code> on function enter, and <code class="notranslate">__array_wrap__</code> on exit</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="In [37]: np.add(x,1)
Out[37]: 
[&lt;class '__main__.NDLike'&gt;]
[ 0.09434516  0.15409372  0.33459299  0.17814042  0.25887999  1.16294691
  0.2292495   1.51350363 -0.89373784 -0.24690582]"><pre class="notranslate"><code class="notranslate">In [37]: np.add(x,1)
Out[37]: 
[&lt;class '__main__.NDLike'&gt;]
[ 0.09434516  0.15409372  0.33459299  0.17814042  0.25887999  1.16294691
  0.2292495   1.51350363 -0.89373784 -0.24690582]
</code></pre></div>
<p dir="auto">I have found that both <code class="notranslate">np.diff, np.ones_like, np.empty_like</code> DO NOT appear<br>
to call <code class="notranslate">__array_wrap__</code> 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. <code class="notranslate">np.ones_like</code> calls <code class="notranslate">empty</code> a c-level call.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="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])
"><pre class="notranslate"><code class="notranslate">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])

</code></pre></div>
<p dir="auto">Is there a reason these do not call <code class="notranslate">__array_wrap__</code> on exit; am I doing something wrong, or is this a bug?</p>
<p dir="auto">Some context:</p>
<p dir="auto">I have refactored pandas <code class="notranslate">Series</code> object from a <code class="notranslate">ndarray</code> sub-class to a sub-class that is common in the pandas hierarchy (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="13763019" data-permission-text="Title is private" data-url="https://github.com/pandas-dev/pandas/issues/3482" data-hovercard-type="pull_request" data-hovercard-url="/pandas-dev/pandas/pull/3482/hovercard" href="https://github.com/pandas-dev/pandas/pull/3482">pandas-dev/pandas#3482</a>), for ease of code use/modification, etc. This issue only comes up if the <code class="notranslate">ndarrayness</code> is somewhat abused in that a user is expecting that a <code class="notranslate">Series</code> object that goes into an ndarray comes out with another <code class="notranslate">Series</code> object. (which is the case for nearly every method/ufunc by using either wrapped methods or the <code class="notranslate">__array__/__array_wrap__</code> machinery)</p>
<p dir="auto">Thanks for taking a look</p>
<p dir="auto">Jeff</p>