<div dir="ltr"><div class="markdown-here-wrapper" style="font-size:1em;font-family:Helvetica,arial,freesans,clean,sans-serif;color:rgb(34,34,34);background-color:rgb(255,255,255);border:none;line-height:1.2"><p style="margin:1em 0px"></p>
<p style="margin:1em 0px">Today, numpy has a <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline">np.ma.mask_rowcols</code> function, which stretches masks along<br>the full length of an axis. For example, given the matrix::</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;overflow:auto;margin:1em 0px"><code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,255)">>>> a2d = np.zeros((3, 3), dtype=int)
>>> a2d[1, 1] = 1
>>> a2d = np.ma.masked_equal(a2d, 1)
>>> print(a2d)
[[0 0 0]
 [0 -- 0]
 [0 0 0]]
</code></pre><p style="margin:1em 0px">The API allows::</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;overflow:auto;margin:1em 0px"><code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,255)">>>> print(np.ma.mask_rowcols(a2d, axis=0))
[[0 0 0]
 [-- -- --]
 [0 0 0]]

>>> print(np.ma.mask_rowcols(a2d, axis=1))
[[0 -- 0]
 [0 -- 0]
 [0 -- 0]]

>>> print(np.ma.mask_rowcols(a2d, axis=None))
[[0 -- 0]
 [-- -- --]
 [0 -- 0]]
</code></pre><p style="margin:1em 0px">However, this function only works for 2D arrays.<br>It would be useful to generalize this to work on ND arrays as well.</p>
<p style="margin:1em 0px">Unfortunately, the current function is messy to generalize, because <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline">axis=0</code> means “spread the mask along axis 1”, and vice versa. Additionally, the name is not particularly good for an ND function.</p>
<p style="margin:1em 0px">My proposal in <a href="https://github.com/numpy/numpy/pull/14998" style="color:rgb(51,51,238);text-decoration:none">PR 14998</a> is to introduce a new function, <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline">mask_extend_axis</code>, which fixes this shortcoming.<br>Given an 3D array::</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;overflow:auto;margin:1em 0px"><code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,255)">>>> a3d = np.zeros((2, 2, 2), dtype=int)
>>> a3d[0, 0, 0] = 1
>>> a3d = np.ma.masked_equal(a3d, 1)
>>> print(a3d)
[[[-- 0]
  [0 0]]

 [[0 0]
  [0 0]]]
</code></pre><p style="margin:1em 0px">This, in my opinion, has clearer axis semantics:</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;overflow:auto;margin:1em 0px"><code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,255)">>>> print(np.ma.mask_extend_axis(a2d, axis=0))
[[[-- 0]
  [0 0]]

 [[-- 0]
  [0 0]]]

>>> print(np.ma.mask_extend_axis(a2d, axis=1))
[[[-- 0]
  [-- 0]]

 [[0 0]
  [0 0]]]

>>> print(np.ma.mask_extend_axis(a2d, axis=2))
[[[-- --]
  [0 0]]

 [[0 0]
  [0 0]]]
</code></pre><p style="margin:1em 0px">Stretching over multiple axes remains possible:</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;overflow:auto;margin:1em 0px"><code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block;padding:0.5em;color:rgb(51,51,51);background:rgb(248,248,255)">>>> print(np.ma.mask_extend_axis(a2d, axis=(1, 2)))
[[[-- --]
  [-- 0]]

 [[0 0]
  [0 0]]]

# extending sequentially is not the same as extending in parallel
>>> print(np.ma.mask_extend_axis(np.ma.mask_extend_axis(a2d, axis=1), axis=2))
[[[-- --]
  [-- --]]

 [[0 0]
  [0 0]]]
</code></pre><p style="margin:1em 0px">Questions for the mailing list then:</p>
<ul style="padding-left:2em;margin:1em 0px">
<li style="margin:1em 0px">Can you think of a better name than <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline">mask_extend_axis</code>?</li>
<li style="margin:1em 0px">Does my proposed meaning of <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline">axis</code> make more sense to you than the one used by <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:nowrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline">mask_rowcols</code>?</li>
</ul>
<div title="MDH:QWRkaW5nIGFuIG5kIGdlbmVyYWxpemF0aW9uIG9mIGBucC5tYS5tYXNrX3Jvd3Njb2xzYDxicj48
YnI+VG9kYXksIG51bXB5IGhhcyBhIGBucC5tYS5tYXNrX3Jvd2NvbHNgIGZ1bmN0aW9uLCB3aGlj
aCBzdHJldGNoZXMgbWFza3MgYWxvbmc8YnI+dGhlIGZ1bGwgbGVuZ3RoIG9mIGFuIGF4aXMuIEZv
ciBleGFtcGxlLCBnaXZlbiB0aGUgbWF0cml4Ojo8YnI+PGJyPgkmZ3Q7Jmd0OyZndDsgYTJkID0g
bnAuemVyb3MoKDMsIDMpLCBkdHlwZT1pbnQpPGJyPgkmZ3Q7Jmd0OyZndDsgYTJkWzEsIDFdID0g
MTxicj4JJmd0OyZndDsmZ3Q7IGEyZCA9IG5wLm1hLm1hc2tlZF9lcXVhbChhMmQsIDEpPGJyPgkm
Z3Q7Jmd0OyZndDsgcHJpbnQoYTJkKTxicj4JW1swIDAgMF08YnI+CSBbMCAtLSAwXTxicj4JIFsw
IDAgMF1dPGJyPjxicj5UaGUgQVBJIGFsbG93czo6PGJyPjxicj4JJmd0OyZndDsmZ3Q7IHByaW50
KG5wLm1hLm1hc2tfcm93Y29scyhhMmQsIGF4aXM9MCkpPGJyPglbWzAgMCAwXTxicj4JIFstLSAt
LSAtLV08YnI+CSBbMCAwIDBdXTxicj48YnI+CSZndDsmZ3Q7Jmd0OyBwcmludChucC5tYS5tYXNr
X3Jvd2NvbHMoYTJkLCBheGlzPTEpKTxicj4JW1swIC0tIDBdPGJyPgkgWzAgLS0gMF08YnI+CSBb
MCAtLSAwXV08YnI+PGJyPgkmZ3Q7Jmd0OyZndDsgcHJpbnQobnAubWEubWFza19yb3djb2xzKGEy
ZCwgYXhpcz1Ob25lKSk8YnI+CVtbMCAtLSAwXTxicj4JIFstLSAtLSAtLV08YnI+CSBbMCAtLSAw
XV08YnI+PGJyPkhvd2V2ZXIsIHRoaXMgZnVuY3Rpb24gb25seSB3b3JrcyBmb3IgMkQgYXJyYXlz
Ljxicj5JdCB3b3VsZCBiZSB1c2VmdWwgdG8gZ2VuZXJhbGl6ZSB0aGlzIHRvIHdvcmsgb24gTkQg
YXJyYXlzIGFzIHdlbGwuPGJyPjxicj5VbmZvcnR1bmF0ZWx5LCB0aGUgY3VycmVudCBmdW5jdGlv
biBpcyBtZXNzeSB0byBnZW5lcmFsaXplLCBiZWNhdXNlIGBheGlzPTBgIG1lYW5zICJzcHJlYWQg
dGhlIG1hc2sgYWxvbmcgYXhpcyAxIiwgYW5kIHZpY2UgdmVyc2EuIEFkZGl0aW9uYWxseSwgdGhl
IG5hbWUgaXMgbm90IHBhcnRpY3VsYXJseSBnb29kIGZvciBhbiBORCBmdW5jdGlvbi48YnI+PGJy
Pk15IHByb3Bvc2FsIGluIFtQUiAxNDk5OF0oaHR0cHM6Ly9naXRodWIuY29tL251bXB5L251bXB5
L3B1bGwvMTQ5OTgpIGlzIHRvIGludHJvZHVjZSBhIG5ldyBmdW5jdGlvbiwgYG1hc2tfZXh0ZW5k
X2F4aXNgLCB3aGljaCBmaXhlcyB0aGlzIHNob3J0Y29taW5nLjxicj5HaXZlbiBhbiAzRCBhcnJh
eTo6PGJyPjxicj4JJmd0OyZndDsmZ3Q7IGEzZCA9IG5wLnplcm9zKCgyLCAyLCAyKSwgZHR5cGU9
aW50KTxicj4JJmd0OyZndDsmZ3Q7IGEzZFswLCAwLCAwXSA9IDE8YnI+CSZndDsmZ3Q7Jmd0OyBh
M2QgPSBucC5tYS5tYXNrZWRfZXF1YWwoYTNkLCAxKTxicj4JJmd0OyZndDsmZ3Q7IHByaW50KGEz
ZCk8YnI+CVtbWy0tIDBdPGJyPgkgwqBbMCAwXV08YnI+PGJyPgkgW1swIDBdPGJyPgkgwqBbMCAw
XV1dPGJyPjxicj5UaGlzLCBpbiBteSBvcGluaW9uLCBoYXMgY2xlYXJlciBheGlzIHNlbWFudGlj
czo8YnI+PGJyPgkmZ3Q7Jmd0OyZndDsgcHJpbnQobnAubWEubWFza19leHRlbmRfYXhpcyhhMmQs
IGF4aXM9MCkpPGJyPglbW1stLSAwXTxicj4JIMKgWzAgMF1dPGJyPjxicj4JIFtbLS0gMF08YnI+
CSDCoFswIDBdXV08YnI+PGJyPgkmZ3Q7Jmd0OyZndDsgcHJpbnQobnAubWEubWFza19leHRlbmRf
YXhpcyhhMmQsIGF4aXM9MSkpPGJyPglbW1stLSAwXTxicj4JIMKgWy0tIDBdXTxicj48YnI+CSBb
WzAgMF08YnI+CSDCoFswIDBdXV08YnI+PGJyPgkmZ3Q7Jmd0OyZndDsgcHJpbnQobnAubWEubWFz
a19leHRlbmRfYXhpcyhhMmQsIGF4aXM9MikpPGJyPglbW1stLSAtLV08YnI+CSDCoFswIDBdXTxi
cj48YnI+CSBbWzAgMF08YnI+CSDCoFswIDBdXV08YnI+PGJyPlN0cmV0Y2hpbmcgb3ZlciBtdWx0
aXBsZSBheGVzIHJlbWFpbnMgcG9zc2libGU6PGJyPjxicj4JJmd0OyZndDsmZ3Q7IHByaW50KG5w
Lm1hLm1hc2tfZXh0ZW5kX2F4aXMoYTJkLCBheGlzPSgxLCAyKSkpPGJyPglbW1stLSAtLV08YnI+
CSDCoFstLSAwXV08YnI+PGJyPgkgW1swIDBdPGJyPgkgwqBbMCAwXV1dPGJyPjxicj4JIyBleHRl
bmRpbmcgc2VxdWVudGlhbGx5IGlzIG5vdCB0aGUgc2FtZSBhcyBleHRlbmRpbmcgaW4gcGFyYWxs
ZWw8YnI+CSZndDsmZ3Q7Jmd0OyBwcmludChucC5tYS5tYXNrX2V4dGVuZF9heGlzKG5wLm1hLm1h
c2tfZXh0ZW5kX2F4aXMoYTJkLCBheGlzPTEpLCBheGlzPTIpKTxicj4JW1tbLS0gLS1dPGJyPgkg
wqBbLS0gLS1dXTxicj48YnI+CSBbWzAgMF08YnI+CSDCoFswIDBdXV08YnI+PGJyPlF1ZXN0aW9u
cyBmb3IgdGhlIG1haWxpbmcgbGlzdCB0aGVuOjxicj48YnI+KiBDYW4geW91IHRoaW5rIG9mIGEg
YmV0dGVyIG5hbWUgdGhhbiBgbWFza19leHRlbmRfYXhpc2A/PGJyPiogRG9lcyBteSBwcm9wb3Nl
ZCBtZWFuaW5nIG9mIGBheGlzYCBtYWtlIG1vcmUgc2Vuc2UgdG8geW91IHRoYW4gdGhlIG9uZSB1
c2VkIGJ5IGBtYXNrX3Jvd2NvbHNgPzxicj4=" style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0"></div></div></div>