<div dir="ltr">Hi,<div><br></div><div>I'm a long-time user of Numpy; I had a question and I didn't know where else to ask. (It's not a bug—otherwise I would have posted it at <a href="https://github.com/numpy/numpy/issues">https://github.com/numpy/numpy/issues</a>).</div><div><br></div><div>Has anyone noticed that indexing an array with integer arrays (i.e. numpy.take) is a function composition? For example, suppose you have any two non-negative functions of integers:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">def</span> <span class="gmail-nf" style="margin:0px;padding:0px;color:blue">f</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-n" style="margin:0px;padding:0px">x</span><span class="gmail-p" style="margin:0px;padding:0px">):</span></span></pre></div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)">    <span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">return</span> <span class="gmail-n" style="margin:0px;padding:0px">x</span><span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">**</span><span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">2</span> <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">-</span> <span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">5</span><span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">*</span><span class="gmail-n" style="margin:0px;padding:0px">x</span> <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">+</span> <span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">10</span></span></pre></div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">def</span> <span class="gmail-nf" style="margin:0px;padding:0px;color:blue">g</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-n" style="margin:0px;padding:0px">y</span><span class="gmail-p" style="margin:0px;padding:0px">):</span></span></pre></div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)">    <span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">return</span> <span class="gmail-nb" style="margin:0px;padding:0px;color:green">max</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">0</span><span class="gmail-p" style="margin:0px;padding:0px">,</span> <span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">2</span><span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">*</span><span class="gmail-n" style="margin:0px;padding:0px">y</span> <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">-</span> <span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">10</span><span class="gmail-p" style="margin:0px;padding:0px">)</span> <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">+</span> <span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">3</span></span></pre></div></blockquote><div><div><br></div><div>and you sample them as arrays, as well as their composition <font face="monospace"><span class="gmail-n" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">g</span><span class="gmail-p" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">(</span><span class="gmail-n" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">f</span><span class="gmail-p" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">(•</span><span class="gmail-p" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">))</span></font>:<br></div><div><br></div></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-n" style="margin:0px;padding:0px">F</span>   <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">=</span> <span class="gmail-n" style="margin:0px;padding:0px">numpy</span><span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">.</span><span class="gmail-n" style="margin:0px;padding:0px">array</span><span class="gmail-p" style="margin:0px;padding:0px">([</span><span class="gmail-n" style="margin:0px;padding:0px">f</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-n" style="margin:0px;padding:0px">i</span><span class="gmail-p" style="margin:0px;padding:0px">)</span> <span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">for</span> <span class="gmail-n" style="margin:0px;padding:0px">i</span> <span class="gmail-ow" style="margin:0px;padding:0px;color:rgb(170,34,255);font-weight:bold">in</span> <span class="gmail-n" style="margin:0px;padding:0px">range</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">10</span><span class="gmail-p" style="margin:0px;padding:0px">)])</span>     <span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic"># F is f at 10 elements</span></span></pre></div></div><div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-n" style="margin:0px;padding:0px">G</span>   <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">=</span> <span class="gmail-n" style="margin:0px;padding:0px">numpy</span><span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">.</span><span class="gmail-n" style="margin:0px;padding:0px">array</span><span class="gmail-p" style="margin:0px;padding:0px">([</span><span class="gmail-n" style="margin:0px;padding:0px">g</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-n" style="margin:0px;padding:0px">i</span><span class="gmail-p" style="margin:0px;padding:0px">)</span> <span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">for</span> <span class="gmail-n" style="margin:0px;padding:0px">i</span> <span class="gmail-ow" style="margin:0px;padding:0px;color:rgb(170,34,255);font-weight:bold">in</span> <span class="gmail-n" style="margin:0px;padding:0px">range</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">100</span><span class="gmail-p" style="margin:0px;padding:0px">)])</span>    <span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic"># G is g at enough elements to include max(f)</span></span></pre></div></div><div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-n" style="margin:0px;padding:0px">GoF</span> <span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">=</span> <span class="gmail-n" style="margin:0px;padding:0px">numpy</span><span class="gmail-o" style="margin:0px;padding:0px;color:rgb(102,102,102)">.</span><span class="gmail-n" style="margin:0px;padding:0px">array</span><span class="gmail-p" style="margin:0px;padding:0px">([</span><span class="gmail-n" style="margin:0px;padding:0px">g</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-n" style="margin:0px;padding:0px">f</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-n" style="margin:0px;padding:0px">i</span><span class="gmail-p" style="margin:0px;padding:0px">))</span> <span class="gmail-k" style="margin:0px;padding:0px;color:rgb(0,128,0);font-weight:bold">for</span> <span class="gmail-n" style="margin:0px;padding:0px">i</span> <span class="gmail-ow" style="margin:0px;padding:0px;color:rgb(170,34,255);font-weight:bold">in</span> <span class="gmail-n" style="margin:0px;padding:0px">range</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-mi" style="margin:0px;padding:0px;color:rgb(102,102,102)">10</span><span class="gmail-p" style="margin:0px;padding:0px">)])</span>  <span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic"># GoF is g∘f at 10 elements</span></span></pre></div></div></blockquote><div><div><br></div><div>Indexing G by F (<font face="monospace"><span class="gmail-n" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">G</span><span class="gmail-p" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">[</span><span class="gmail-n" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">F</span><span class="gmail-p" style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap;margin:0px;padding:0px">]</span></font>) returns the same result as the sampled composition (<span style="color:rgb(51,51,51);font-size:14px;white-space:pre-wrap"><font face="monospace">GoF</font></span>):</div></div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-nb" style="margin:0px;padding:0px;color:green">print</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-s2" style="margin:0px;padding:0px;color:rgb(186,33,33)">"G</span><span class="gmail-se" style="margin:0px;padding:0px;color:rgb(187,102,34);font-weight:bold">\u2218</span><span class="gmail-s2" style="margin:0px;padding:0px;color:rgb(186,33,33)">F ="</span><span class="gmail-p" style="margin:0px;padding:0px">,</span> <span class="gmail-n" style="margin:0px;padding:0px">G</span><span class="gmail-p" style="margin:0px;padding:0px">[</span><span class="gmail-n" style="margin:0px;padding:0px">F</span><span class="gmail-p" style="margin:0px;padding:0px">])</span>   <span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic"># integer indexing</span></span></pre></div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span style="background-color:rgb(255,255,255)"><span class="gmail-nb" style="margin:0px;padding:0px;color:green">print</span><span class="gmail-p" style="margin:0px;padding:0px">(</span><span class="gmail-s2" style="margin:0px;padding:0px;color:rgb(186,33,33)">"g</span><span class="gmail-se" style="margin:0px;padding:0px;color:rgb(187,102,34);font-weight:bold">\u2218</span><span class="gmail-s2" style="margin:0px;padding:0px;color:rgb(186,33,33)">f ="</span><span class="gmail-p" style="margin:0px;padding:0px">,</span> <span class="gmail-n" style="margin:0px;padding:0px">GoF</span><span class="gmail-p" style="margin:0px;padding:0px">)</span>    <span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic"># array of the composed functions</span></span></pre></div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic;background-color:rgb(255,255,255)"><pre style="margin-top:0px;margin-bottom:0px;padding:0px;color:rgb(0,0,0);border-radius:0px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:0px;vertical-align:baseline;font-style:normal">G∘F = [13  5  3  3  5 13 25 41 61 85]</pre></span></pre></div><div><pre style="margin-top:0px;margin-bottom:0px;padding:0px;font-size:14px;color:rgb(51,51,51);border-radius:4px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:none"><span class="gmail-c1" style="margin:0px;padding:0px;color:rgb(64,128,128);font-style:italic;background-color:rgb(255,255,255)"><pre style="margin-top:0px;margin-bottom:0px;padding:0px;color:rgb(0,0,0);border-radius:0px;line-height:inherit;word-break:break-all;white-space:pre-wrap;border:0px;vertical-align:baseline;font-style:normal">g∘f = [13  5  3  3  5 13 25 41 61 85]</pre></span></pre></div></blockquote><div><br></div><div>This isn't a proof, but I think it's easy to see that it would be true for any non-negative functions (negative index handling spoils this property). </div><div><br></div><div>It might sound like a purely academic point, but I've noticed that I've been able to optimize and simplify some code by taking advantage of the associative property of function composition, repeatedly applying numpy.take on arrays of integers before applying the fully composed index to my data. As an example of an optimization, if I have to do the same thing to N data arrays, it helps to prepare a single integer index and apply it to the N data arrays instead of modifying all N data arrays in multiple steps. As an example of a simplification, if I need to modify arrays in recursion, it's easier to reason about the recursion if only the terminal case applies an index to data, with the non-terminal steps applying indexes to indexes.</div><div><br></div><div>This is such a basic property that I bet it has a name, and there's probably some literature on it, like what you could find if you were interested in monads in Haskell. But I haven't been able to find the right search strings—what would you call this property? Is there a literature on it and its uses?</div><div><br></div><div>Thanks!</div><div>-- Jim</div><div><br></div><div><br></div><div><br></div></div>