<h3 dir="auto">Proposed new feature or change:</h3>
<p dir="auto">All single-argument <code class="notranslate">np.FUNC</code> functions can be implemented as <code class="notranslate">np.ndarray.FUNC</code>.</p>
<p dir="auto"><strong>Example usage:</strong></p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# Currently invalid. :(
x.ceil()"><pre class="notranslate"><span class="pl-c"># Currently invalid. :(</span>
<span class="pl-s1">x</span>.<span class="pl-en">ceil</span>()</pre></div>
<p dir="auto"><strong>Rationale:</strong></p>
<ul dir="auto">
<li><a href="https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray" rel="nofollow"><code class="notranslate">np.ndarray</code></a> already implements some methods:</li>
</ul>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="all
any
argmax
argmin
argpartition
argsort
astype
byteswap
choose
clip
compress
conj
conjugate
copy
cumprod
cumsum
diagonal
dump
dumps
fill
flatten
getfield
item
itemset
max
mean
min
newbyteorder
nonzero
partition
prod
ptp
put
ravel
repeat
reshape
resize
round
searchsorted
setfield
setflags
sort
squeeze
std
sum
swapaxes
take
tobytes
tofile
tolist
tostring
trace
transpose
var
view"><pre class="notranslate"><span class="pl-s1">all</span>
<span class="pl-s1">any</span>
<span class="pl-s1">argmax</span>
<span class="pl-s1">argmin</span>
<span class="pl-s1">argpartition</span>
<span class="pl-s1">argsort</span>
<span class="pl-s1">astype</span>
<span class="pl-s1">byteswap</span>
<span class="pl-s1">choose</span>
<span class="pl-s1">clip</span>
<span class="pl-s1">compress</span>
<span class="pl-s1">conj</span>
<span class="pl-s1">conjugate</span>
<span class="pl-s1">copy</span>
<span class="pl-s1">cumprod</span>
<span class="pl-s1">cumsum</span>
<span class="pl-s1">diagonal</span>
<span class="pl-s1">dump</span>
<span class="pl-s1">dumps</span>
<span class="pl-s1">fill</span>
<span class="pl-s1">flatten</span>
<span class="pl-s1">getfield</span>
<span class="pl-s1">item</span>
<span class="pl-s1">itemset</span>
<span class="pl-s1">max</span>
<span class="pl-s1">mean</span>
<span class="pl-s1">min</span>
<span class="pl-s1">newbyteorder</span>
<span class="pl-s1">nonzero</span>
<span class="pl-s1">partition</span>
<span class="pl-s1">prod</span>
<span class="pl-s1">ptp</span>
<span class="pl-s1">put</span>
<span class="pl-s1">ravel</span>
<span class="pl-s1">repeat</span>
<span class="pl-s1">reshape</span>
<span class="pl-s1">resize</span>
<span class="pl-s1">round</span>
<span class="pl-s1">searchsorted</span>
<span class="pl-s1">setfield</span>
<span class="pl-s1">setflags</span>
<span class="pl-s1">sort</span>
<span class="pl-s1">squeeze</span>
<span class="pl-s1">std</span>
<span class="pl-s1">sum</span>
<span class="pl-s1">swapaxes</span>
<span class="pl-s1">take</span>
<span class="pl-s1">tobytes</span>
<span class="pl-s1">tofile</span>
<span class="pl-s1">tolist</span>
<span class="pl-s1">tostring</span>
<span class="pl-s1">trace</span>
<span class="pl-s1">transpose</span>
<span class="pl-s1">var</span>
<span class="pl-s1">view</span></pre></div>
<ul dir="auto">
<li>
<p dir="auto">It is unclear to a user which ones have been implemented and which ones haven't unless they try one and see if their code works/fails. (Or google the docs for each one they want to use.) For instance, <code class="notranslate">np.ndarray.ceil</code> does not exist, but <code class="notranslate">np.ndarray.round</code> does.</p>
</li>
<li>
<p dir="auto">Fully implementing this API style improves interchangeability with libraries such as PyTorch. With this change, there is less need to implement different methods (e.g. <code class="notranslate">torch.sin</code> or <code class="notranslate">np.sin</code>) for functions that just want to think about numerical computations on tensors of any type (<code class="notranslate">np.ndarray</code> or <code class="notranslate">torch.Tensor</code>), e.g.:</p>
</li>
</ul>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="def sin2(x: np.ndarray | torch.Tensor):
    return (x ** 2).sin()"><pre class="notranslate"><span class="pl-k">def</span> <span class="pl-en">sin2</span>(<span class="pl-s1">x</span>: <span class="pl-s1">np</span>.<span class="pl-s1">ndarray</span> <span class="pl-c1">|</span> <span class="pl-s1">torch</span>.<span class="pl-v">Tensor</span>):
    <span class="pl-k">return</span> (<span class="pl-s1">x</span> <span class="pl-c1">**</span> <span class="pl-c1">2</span>).<span class="pl-en">sin</span>()</pre></div>
<p dir="auto">Instead of:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="def sin2(x: np.ndarray | torch.Tensor):
    if isinstance(x, np.ndarray):
        return np.sin(x ** 2)
    else:
        return (x ** 2).sin()"><pre class="notranslate"><span class="pl-k">def</span> <span class="pl-en">sin2</span>(<span class="pl-s1">x</span>: <span class="pl-s1">np</span>.<span class="pl-s1">ndarray</span> <span class="pl-c1">|</span> <span class="pl-s1">torch</span>.<span class="pl-v">Tensor</span>):
    <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">x</span>, <span class="pl-s1">np</span>.<span class="pl-s1">ndarray</span>):
        <span class="pl-k">return</span> <span class="pl-s1">np</span>.<span class="pl-en">sin</span>(<span class="pl-s1">x</span> <span class="pl-c1">**</span> <span class="pl-c1">2</span>)
    <span class="pl-k">else</span>:
        <span class="pl-k">return</span> (<span class="pl-s1">x</span> <span class="pl-c1">**</span> <span class="pl-c1">2</span>).<span class="pl-en">sin</span>()</pre></div>
<ul dir="auto">
<li>Fluent API styles are very natural for chained computations.</li>
</ul>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="((x ** 2).mean(axis=-1).sqrt() &gt; x.mean(axis=-1).sqrt()).nonzero()"><pre class="notranslate">((<span class="pl-s1">x</span> <span class="pl-c1">**</span> <span class="pl-c1">2</span>).<span class="pl-en">mean</span>(<span class="pl-s1">axis</span><span class="pl-c1">=</span><span class="pl-c1">-</span><span class="pl-c1">1</span>).<span class="pl-en">sqrt</span>() <span class="pl-c1">&gt;</span> <span class="pl-s1">x</span>.<span class="pl-en">mean</span>(<span class="pl-s1">axis</span><span class="pl-c1">=</span><span class="pl-c1">-</span><span class="pl-c1">1</span>).<span class="pl-en">sqrt</span>()).<span class="pl-en">nonzero</span>()</pre></div>
<p dir="auto">versus:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="np.nonzero(np.sqrt(np.mean(x ** 2, axis=-1)) &gt; np.sqrt(np.mean(x, axis=1)))"><pre class="notranslate"><span class="pl-s1">np</span>.<span class="pl-en">nonzero</span>(<span class="pl-s1">np</span>.<span class="pl-en">sqrt</span>(<span class="pl-s1">np</span>.<span class="pl-en">mean</span>(<span class="pl-s1">x</span> <span class="pl-c1">**</span> <span class="pl-c1">2</span>, <span class="pl-s1">axis</span><span class="pl-c1">=</span><span class="pl-c1">-</span><span class="pl-c1">1</span>)) <span class="pl-c1">&gt;</span> <span class="pl-s1">np</span>.<span class="pl-en">sqrt</span>(<span class="pl-s1">np</span>.<span class="pl-en">mean</span>(<span class="pl-s1">x</span>, <span class="pl-s1">axis</span><span class="pl-c1">=</span><span class="pl-c1">1</span>)))</pre></div>
<ul dir="auto">
<li>More consistent API for numpy-based libraries (e.g. <code class="notranslate">pandas</code>) that frequently use chained methods in other parts of their API.</li>
</ul>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="df.groupby(&quot;column&quot;).agg(...)
df[&quot;column&quot;].ceil()  # looks much more natural with pandas than np.ceil(df[&quot;column&quot;])"><pre class="notranslate"><span class="pl-s1">df</span>.<span class="pl-en">groupby</span>(<span class="pl-s">"column"</span>).<span class="pl-en">agg</span>(...)
<span class="pl-s1">df</span>[<span class="pl-s">"column"</span>].<span class="pl-en">ceil</span>()  <span class="pl-c"># looks much more natural with pandas than np.ceil(df["column"])</span></pre></div>
<hr>
<p dir="auto">A (not necessarily exhaustive) list of possible single-argument FUNCs:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="negative
positive
absolute
fabs
rint
sign
conj
conjugate
exp
exp2
log
log2
log10
expm1
log1p
sqrt
square
cbrt
reciprocal
sin
cos
tan
arcsin
arccos
arctan
sinh
cosh
tanh
arcsinh
arccosh
arctanh
degrees
radians
deg2rad
rad2deg
invert
logical_not
isfinite
isinf
isnan
isnat
fabs
signbit
spacing
modf
frexp
floor
ceil
trunc"><pre class="notranslate"><span class="pl-s1">negative</span>
<span class="pl-s1">positive</span>
<span class="pl-s1">absolute</span>
<span class="pl-s1">fabs</span>
<span class="pl-s1">rint</span>
<span class="pl-s1">sign</span>
<span class="pl-s1">conj</span>
<span class="pl-s1">conjugate</span>
<span class="pl-s1">exp</span>
<span class="pl-s1">exp2</span>
<span class="pl-s1">log</span>
<span class="pl-s1">log2</span>
<span class="pl-s1">log10</span>
<span class="pl-s1">expm1</span>
<span class="pl-s1">log1p</span>
<span class="pl-s1">sqrt</span>
<span class="pl-s1">square</span>
<span class="pl-s1">cbrt</span>
<span class="pl-s1">reciprocal</span>
<span class="pl-s1">sin</span>
<span class="pl-s1">cos</span>
<span class="pl-s1">tan</span>
<span class="pl-s1">arcsin</span>
<span class="pl-s1">arccos</span>
<span class="pl-s1">arctan</span>
<span class="pl-s1">sinh</span>
<span class="pl-s1">cosh</span>
<span class="pl-s1">tanh</span>
<span class="pl-s1">arcsinh</span>
<span class="pl-s1">arccosh</span>
<span class="pl-s1">arctanh</span>
<span class="pl-s1">degrees</span>
<span class="pl-s1">radians</span>
<span class="pl-s1">deg2rad</span>
<span class="pl-s1">rad2deg</span>
<span class="pl-s1">invert</span>
<span class="pl-s1">logical_not</span>
<span class="pl-s1">isfinite</span>
<span class="pl-s1">isinf</span>
<span class="pl-s1">isnan</span>
<span class="pl-s1">isnat</span>
<span class="pl-s1">fabs</span>
<span class="pl-s1">signbit</span>
<span class="pl-s1">spacing</span>
<span class="pl-s1">modf</span>
<span class="pl-s1">frexp</span>
<span class="pl-s1">floor</span>
<span class="pl-s1">ceil</span>
<span class="pl-s1">trunc</span></pre></div>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; src = np.ma.masked_where([0, 1, 0], [1, 1337, 1])
&gt;&gt;&gt; dst = np.ma.masked_where([1, 0, 0, 0, 0], np.zeros(5))
&gt;&gt;&gt; dst_view = dst.transpose()
# check the views look ok
&gt;&gt;&gt; dst
masked_array(data = [-- 0 0 0 0],
             mask = [ True False False False False],
       fill_value = 999999)
&gt;&gt;&gt; dst_view
masked_array(data = [-- 0 0 0 0],
             mask = [ True False False False False],
       fill_value = 999999)

# now write a slice of the view
&gt;&gt;&gt; dst_view[2:5] = src
&gt;&gt;&gt; dst_view
masked_array(data = [-- 0 1 -- 1],
             mask = [ True False False  True False],
       fill_value = 999999)

# but looking back at the original, the data was updated but the mask was not!
&gt;&gt;&gt; dst
masked_array(data = [-- 0 1 1337 1],
             mask = [ True False False False False],
       fill_value = 999999)"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">src</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-s1">ma</span>.<span class="pl-en">masked_where</span>([<span class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span class="pl-c1">0</span>], [<span class="pl-c1">1</span>, <span class="pl-c1">1337</span>, <span class="pl-c1">1</span>])
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-s1">ma</span>.<span class="pl-en">masked_where</span>([<span class="pl-c1">1</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>, <span class="pl-c1">0</span>], <span class="pl-s1">np</span>.<span class="pl-en">zeros</span>(<span class="pl-c1">5</span>))
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst_view</span> <span class="pl-c1">=</span> <span class="pl-s1">dst</span>.<span class="pl-en">transpose</span>()
<span class="pl-c"># check the views look ok</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst</span>
<span class="pl-en">masked_array</span>(<span class="pl-s1">data</span> <span class="pl-c1">=</span> [<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">0</span> <span class="pl-c1">0</span> <span class="pl-c1">0</span> <span class="pl-c1">0</span>],
             <span class="pl-s1">mask</span> <span class="pl-c1">=</span> [ <span class="pl-c1">True</span> <span class="pl-v">False</span> <span class="pl-c1">False</span> <span class="pl-v">False</span> <span class="pl-c1">False</span>],
       <span class="pl-s1">fill_value</span> <span class="pl-c1">=</span> <span class="pl-c1">999999</span>)
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst_view</span>
<span class="pl-en">masked_array</span>(<span class="pl-s1">data</span> <span class="pl-c1">=</span> [<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">0</span> <span class="pl-c1">0</span> <span class="pl-c1">0</span> <span class="pl-c1">0</span>],
             <span class="pl-s1">mask</span> <span class="pl-c1">=</span> [ <span class="pl-c1">True</span> <span class="pl-v">False</span> <span class="pl-c1">False</span> <span class="pl-v">False</span> <span class="pl-c1">False</span>],
       <span class="pl-s1">fill_value</span> <span class="pl-c1">=</span> <span class="pl-c1">999999</span>)

<span class="pl-c"># now write a slice of the view</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst_view</span>[<span class="pl-c1">2</span>:<span class="pl-c1">5</span>] <span class="pl-c1">=</span> <span class="pl-s1">src</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst_view</span>
<span class="pl-en">masked_array</span>(<span class="pl-s1">data</span> <span class="pl-c1">=</span> [<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">0</span> <span class="pl-c1">1</span> <span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">1</span>],
             <span class="pl-s1">mask</span> <span class="pl-c1">=</span> [ <span class="pl-c1">True</span> <span class="pl-v">False</span> <span class="pl-c1">False</span>  <span class="pl-v">True</span> <span class="pl-c1">False</span>],
       <span class="pl-s1">fill_value</span> <span class="pl-c1">=</span> <span class="pl-c1">999999</span>)

<span class="pl-c"># but looking back at the original, the data was updated but the mask was not!</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">dst</span>
<span class="pl-en">masked_array</span>(<span class="pl-s1">data</span> <span class="pl-c1">=</span> [<span class="pl-c1">-</span><span class="pl-c1">-</span> <span class="pl-c1">0</span> <span class="pl-c1">1</span> <span class="pl-c1">1337</span> <span class="pl-c1">1</span>],
             <span class="pl-s1">mask</span> <span class="pl-c1">=</span> [ <span class="pl-c1">True</span> <span class="pl-v">False</span> <span class="pl-c1">False</span> <span class="pl-v">False</span> <span class="pl-c1">False</span>],
       <span class="pl-s1">fill_value</span> <span class="pl-c1">=</span> <span class="pl-c1">999999</span>)</pre></div>