-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: add ndarray/base/where
#10659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
feat: add ndarray/base/where
#10659
Changes from all commits
c65133f
148d9d1
2a17361
e3729ee
cec872e
1c48565
ccad894
2df0faf
5b71c61
c1ddf9b
b672f66
f470119
fefa9ab
1fba261
b89c478
c93b341
4b2706e
5d33b18
d4450cf
8af00b1
afcc970
372165a
f522872
8fb5a0b
e5a8f91
559c59c
9ef3b15
1205cdc
3735e43
7230913
0c3a27c
95e9b66
fb2a747
56b0665
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,221 @@ | ||
| <!-- | ||
|
|
||
| @license Apache-2.0 | ||
|
|
||
| Copyright (c) 2026 The Stdlib Authors. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
|
|
||
| --> | ||
|
|
||
| # Where | ||
|
|
||
| > Apply a condition to elements in two input ndarrays and assign results to elements in an output ndarray. | ||
|
|
||
| <section class="intro"> | ||
|
|
||
| </section> | ||
|
|
||
| <!-- /.intro --> | ||
|
|
||
| <section class="usage"> | ||
|
|
||
| ## Usage | ||
|
|
||
| ```javascript | ||
| var where = require( '@stdlib/ndarray/base/where' ); | ||
| ``` | ||
|
|
||
| #### where( arrays ) | ||
|
|
||
| Applies a condition to elements in two input ndarrays and assigns results to elements in an output ndarray. | ||
|
|
||
| <!-- eslint-disable max-len --> | ||
|
|
||
| ```javascript | ||
| var Float64Array = require( '@stdlib/array/float64' ); | ||
| var Uint8Array = require( '@stdlib/array/uint8' ); | ||
|
|
||
| // Create data buffers: | ||
| var cbuf = new Uint8Array( [ 1, 0, 0, 1, 0, 1 ] ); | ||
| var xbuf = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); | ||
| var ybuf = new Float64Array( [ -1.0, -2.0, -3.0, -4.0, -5.0, -6.0 ] ); | ||
| var obuf = new Float64Array( 6 ); | ||
|
|
||
| // Define the shape of the input and output arrays: | ||
| var shape = [ 3, 1, 2 ]; | ||
|
|
||
| // Define the array strides: | ||
| var sc = [ 2, 2, 1 ]; | ||
| var sx = [ 2, 2, 1 ]; | ||
| var sy = [ 2, 2, 1 ]; | ||
| var so = [ 2, 2, 1 ]; | ||
|
|
||
| // Define the index offsets: | ||
| var oc = 0; | ||
| var ox = 0; | ||
| var oy = 0; | ||
| var oo = 0; | ||
|
|
||
| // Create the input and output ndarrays: | ||
| var condition = { | ||
| 'dtype': 'uint8', | ||
| 'data': cbuf, | ||
| 'shape': shape, | ||
| 'strides': sc, | ||
| 'offset': oc, | ||
| 'order': 'row-major' | ||
| }; | ||
| var x = { | ||
| 'dtype': 'float64', | ||
| 'data': xbuf, | ||
| 'shape': shape, | ||
| 'strides': sx, | ||
| 'offset': ox, | ||
| 'order': 'row-major' | ||
| }; | ||
| var y = { | ||
| 'dtype': 'float64', | ||
| 'data': ybuf, | ||
| 'shape': shape, | ||
| 'strides': sy, | ||
| 'offset': oy, | ||
| 'order': 'row-major' | ||
| }; | ||
| var out = { | ||
| 'dtype': 'float64', | ||
| 'data': obuf, | ||
| 'shape': shape, | ||
| 'strides': so, | ||
| 'offset': oo, | ||
| 'order': 'row-major' | ||
| }; | ||
|
|
||
| // Apply the condition: | ||
| where( [ condition, x, y, out ] ); | ||
|
|
||
| console.log( out.data ); | ||
| // => <Float64Array>[ 1.0, -2.0, -3.0, 4.0, -5.0, 6.0 ] | ||
| ``` | ||
|
|
||
| The function accepts the following arguments: | ||
|
|
||
| - **arrays**: array-like object containing three input ndarrays and one output ndarray. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if this is okay since in the main description 'a condition' and 'two input ndarrays' is used.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you decide to change, it would apply to |
||
|
|
||
| Each provided ndarray should be an object with the following properties: | ||
|
|
||
| - **dtype**: data type. | ||
| - **data**: data buffer. | ||
| - **shape**: dimensions. | ||
| - **strides**: stride lengths. | ||
| - **offset**: index offset. | ||
| - **order**: specifies whether an ndarray is row-major (C-style) or column major (Fortran-style). | ||
|
|
||
| </section> | ||
|
|
||
| <!-- /.usage --> | ||
|
|
||
| <section class="notes"> | ||
|
|
||
| ## Notes | ||
|
|
||
| - `condition` ndarray must be a `boolean` or `uint8` ndarray. | ||
| - `condition`, `x`, `y`, and `out` ndarrays must have the same shape. | ||
| - For very high-dimensional ndarrays which are non-contiguous, one should consider copying the underlying data to contiguous memory before conditionally assigning elements in order to achieve better performance. | ||
|
|
||
| </section> | ||
|
|
||
| <!-- /.notes --> | ||
|
|
||
| <section class="examples"> | ||
|
|
||
| ## Examples | ||
|
|
||
| <!-- eslint no-undef: "error" --> | ||
|
|
||
| ```javascript | ||
| var discreteUniform = require( '@stdlib/random/base/discrete-uniform' ).factory; | ||
| var bernoulli = require( '@stdlib/random/base/bernoulli' ).factory; | ||
| var filledarray = require( '@stdlib/array/filled' ); | ||
| var filledarrayBy = require( '@stdlib/array/filled-by' ); | ||
| var shape2strides = require( '@stdlib/ndarray/base/shape2strides' ); | ||
| var ndarray2array = require( '@stdlib/ndarray/base/to-array' ); | ||
| var where = require( '@stdlib/ndarray/base/where' ); | ||
|
|
||
| var N = 10; | ||
| var shape = [ 5, 2 ]; | ||
| var condition = { | ||
| 'dtype': 'uint8', | ||
| 'data': filledarrayBy( N, 'uint8', bernoulli( 0.5 ) ), | ||
| 'shape': shape, | ||
| 'strides': [ 2, 1 ], | ||
| 'offset': 0, | ||
| 'order': 'row-major' | ||
| }; | ||
| console.log( ndarray2array( condition.data, condition.shape, condition.strides, condition.offset, condition.order ) ); // eslint-disable-line max-len | ||
|
|
||
| var x = { | ||
| 'dtype': 'generic', | ||
| 'data': filledarrayBy( N, 'generic', discreteUniform( 0, 100 ) ), | ||
| 'shape': shape, | ||
| 'strides': [ 2, 1 ], | ||
| 'offset': 0, | ||
| 'order': 'row-major' | ||
| }; | ||
| console.log( ndarray2array( x.data, x.shape, x.strides, x.offset, x.order ) ); | ||
|
|
||
| var y = { | ||
| 'dtype': 'generic', | ||
| 'data': filledarrayBy( N, 'generic', discreteUniform( -100, 0 ) ), | ||
| 'shape': shape, | ||
| 'strides': [ 2, 1 ], | ||
| 'offset': 0, | ||
| 'order': 'row-major' | ||
| }; | ||
| console.log( ndarray2array( y.data, y.shape, y.strides, y.offset, y.order ) ); | ||
|
|
||
| var out = { | ||
| 'dtype': 'generic', | ||
| 'data': filledarray( 0, N, 'generic' ), | ||
| 'shape': shape.slice(), | ||
| 'strides': shape2strides( shape, 'column-major' ), | ||
| 'offset': 0, | ||
| 'order': 'column-major' | ||
| }; | ||
|
|
||
| where( [ condition, x, y, out ] ); | ||
| console.log( ndarray2array( out.data, out.shape, out.strides, out.offset, out.order ) ); // eslint-disable-line max-len | ||
|
|
||
| ``` | ||
|
|
||
| </section> | ||
|
|
||
| <!-- /.examples --> | ||
|
|
||
| <!-- Section for related `stdlib` packages. Do not manually edit this section, as it is automatically populated. --> | ||
|
|
||
| <section class="related"> | ||
|
|
||
| </section> | ||
|
|
||
| <!-- /.related --> | ||
|
|
||
| <section class="links"> | ||
|
|
||
| <!-- <related-links> --> | ||
|
|
||
| <!-- </related-links> --> | ||
|
|
||
| </section> | ||
|
|
||
| <!-- /.links --> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LoayAhmed304 It's better to use
contiguousarrays in this example. Otherwise, the reader has to mentally compute which elements are accessed.Also, why does the
conditionhave to be the first ndarray? why does have to be namedcondition?You need to be consistent with
stdlibconventions. You can usendarray/base/ternaryas a reference. This likely applies to other files as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@headlessNode See https://data-apis.org/array-api/latest/API_specification/generated/array_api.where.html.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^^^ Condition is the first array per the Array API specification.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kgryte IMO this approach doesn't line up with either
stdlibor the Array API standard. The Array API version ofwheretakes three separate arguments:where(condition, x1, x2)whereasndarray/base/*functions are like soternary( [ x, y, z, w ] ).I'd say keep
ndarray/base/wheresomething likewhere( [ x, y, c, o ] )(names can vary), and have the user-facingndarray/wherefollow the Array API Standardwhere( condition, x1, x2 ). That way, we stay consistent with both.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@headlessNode I guess I am not following why
[ condition, x, y, out ]is somehow inconsistent.conditionisn't an auxiliary ndarray, such as sort order, etc. You could simply renamecondition => x,x => y, andy => z, and you'd get your desired naming scheme. I suppose I am not seeing why the condition array shouldn't be the first ndarray. And the reason for it being named "condition" is that is what it behaviorally does.In general, it is fine if an API semantically has different argument names than the underlying kernel package.
ternarycould have just as well called themx1,x2,x3,x4. So I am not quite understanding the consistency argument here and where the confusion would arise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kgryte
conditionnot being auxiliary didn't cross my mind. And aside from the Array Api Standard the first array beingconditionmakes more sense if we think about it like so: "where condition, choose x else y".There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@headlessNode Regarding the
contiguousarrays, I used such example(s) referencing toassignexample, I can update this and other examples if needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use a simpler example here but it's not a hard requirement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. On it.