import%20marimo%0A%0A__generated_with%20%3D%20%220.10.18%22%0Aapp%20%3D%20marimo.App(width%3D%22full%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20%23%20this%20tutorial%20is%20built%20with%20marimo!%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%20Tutorial%0A%0A%20%20%20%20%20%20%20%20MArray%20is%20a%20package%20for%20extending%20your%20favorite%20%5BPython%20Array%20API%20Standard%5D(https%3A%2F%2Fdata-apis.org%2Farray-api%2Flatest%2Findex.html)%20compatible%20library%20with%20mask%20capabilities.%20Motivation%20for%20masked%20arrays%20can%20be%20found%20at%20%5B%22What%20is%20a%20masked%20array%3F%22%5D(https%3A%2F%2Fnumpy.org%2Fdevdocs%2Freference%2Fmaskedarray.generic.html%23what-is-a-masked-array).%0A%0A%20%20%20%20%20%20%20%20MArray%20is%20easy%20to%20install%20with%20%60pip%60%2C%20and%20it%20has%20no%20required%20dependencies.%0A%0A%20%20%20%20%20%20%20%20The%20rest%20of%20the%20tutorial%20will%20assume%20that%20we%20want%20to%20add%20masks%20to%20NumPy%20arrays.%20Note%20that%20this%20is%20different%20from%20using%20NumPy's%20built-in%20masked%20arrays%20from%20the%20%60numpy.ma%60%20namespace%20because%20%60numpy.ma%60%20is%20not%20compatible%20with%20the%20array%20API%20standard.%20Even%20the%20base%20NumPy%20namespace%20is%20not%20Array%20API%20compatible%20in%20versions%20of%20NumPy%20prior%20to%202.0%2C%20so%20we%20will%20install%20a%20recent%20version%20of%20NumPy%20to%20work%20with.%0A%0A%20%20%20%20%20%20%20%20To%20create%20a%20version%20of%20the%20NumPy%20namespace%20with%20mask%20support%2C%20use%20Python's%20%60from...import...as%60%20syntax.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20marray%20import%20numpy%20as%20mxp%0A%20%20%20%20return%20(mxp%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%60mxp%60%20exposes%20all%20the%20features%20of%20NumPy%20that%20are%20specified%20in%20the%20Array%20API%20standard%2C%20but%20adds%20masks%20support%20to%20them.%20For%20example%3A%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20simple_array%20%3D%20mxp.arange(3)%0A%20%20%20%20simple_array%0A%20%20%20%20return%20(simple_array%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Just%20as%20%60xp.arange(3)%60%20would%20have%20created%20a%20regular%20NumPy%20array%20with%20elements%20%5B0%2C%201%2C%202%5D%2C%20%60mxp.arange(3)%60%20creates%20an%20%60MArray%60%20object%20with%20these%20elements.%20These%20are%20accessible%20via%20the%20%60data%60%20attribute.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(simple_array)%3A%0A%20%20%20%20simple_array.data%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22The%20difference%20is%20that%20the%20%60MArray%60%20also%20has%20a%20mask%2C%20available%20via%20the%20%60mask%60%20attribute.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(simple_array)%3A%0A%20%20%20%20simple_array.mask%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Because%20all%20of%20the%20elements%20of%20the%20mask%20are%20%60False%60%2C%20this%20%60MArray%60%20will%20behave%20just%20like%20a%20regular%20NumPy%20array.%20That's%20boring.%20Let's%20create%20an%20array%20with%20a%20nontrivial%20mask.%20To%20do%20that%2C%20we'll%20use%20%60mxp.asarray%60.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20x%20%3D%20mxp.asarray(%5B1%2C%202%2C%203%2C%204%5D%2C%20mask%3D%5BFalse%2C%20True%2C%20False%2C%20True%5D)%0A%20%20%20%20x%0A%20%20%20%20return%20(x%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%60marray%60%20is%20intended%20to%20be%20a%20very%20light%20wrapper%20of%20the%20underlying%20array%20library.%20Just%20as%20it%20has%20only%20one%20public%20function%20(%60get_namespace%60)%2C%20it%20makes%20only%20one%20modification%20to%20the%20signature%20of%20a%20wrapped%20library%20function%2C%20which%20we've%20used%20above%3A%20it%20adds%20a%20%60mask%60%20keyword-only%20argument%20to%20the%20%60asarray%60%20function.%0A%0A%20%20%20%20%20%20%20%20Let's%20see%20how%20the%20mask%20changes%20the%20behavior%20of%20common%20functions.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Statistical%20Functions%0A%20%20%20%20%20%20%20%20For%20reducing%20functions%2C%20masked%20elements%20are%20ignored%3B%20the%20result%20is%20the%20same%20as%20if%20the%20masked%20elements%20were%20not%20in%20the%20array.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20x)%3A%0A%20%20%20%20mxp.max(x)%20%20%23%204%20was%20masked%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20x)%3A%0A%20%20%20%20mxp.sum(x)%20%20%23%202%20and%204%20were%20masked%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22For%20the%20only%20non-reducing%20statistical%20function%2C%20%60cumulative_sum%60%2C%20masked%20elements%20do%20not%20contribute%20to%20the%20cumulative%20sum.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20x)%3A%0A%20%20%20%20mxp.cumulative_sum(x)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Note%20that%20the%20elements%20at%20indices%20where%20the%20original%20array%20were%20masked%20remain%20masked.%20Because%20of%20the%20limitations%20of%20the%20underlying%20array%20library%2C%20there%20will%20always%20be%20values%20corresponding%20with%20masked%20elements%20in%20%60data%60%2C%20*but%20these%20values%20should%20be%20considered%20meaningless*.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Utility%20functions%0A%20%20%20%20%20%20%20%20%60all%60%20and%20%60any%60%20work%20like%20the%20reducing%20statistics%20functions.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20y%20%3D%20mxp.asarray(%5BFalse%2C%20False%2C%20False%2C%20True%5D%2C%20mask%3D%5BFalse%2C%20True%2C%20False%2C%20True%5D)%0A%20%20%20%20mxp.all(y)%0A%20%20%20%20return%20(y%2C)%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20y)%3A%0A%20%20%20%20mxp.any(y)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Is%20that%20last%20result%20surprising%3F%20Although%20there%20is%20one%20%60True%60%20in%20%60x.data%60%2C%20it%20is%20ignored%20when%20computing%20%60any%60%20because%20it%20is%20masked.%0A%0A%20%20%20%20%20%20%20%20You%20may%20have%20noticed%20that%20the%20mask%20of%20the%20result%20has%20always%20been%20%60False%60%20in%20these%20examples%20of%20reducing%20functions.%20This%20is%20always%20the%20case%20unless%20*all*%20elements%20of%20the%20array%20are%20masked.%20In%20this%20case%2C%20it%20is%20required%20by%20the%20reducing%20nature%20of%20the%20function%20to%20return%20a%200D%20array%20for%20a%201D%20input%2C%20but%20there%20is%20not%20an%20universally%20accepted%20result%20for%20these%20functions%20when%20all%20elements%20are%20masked.%20(What%20is%20the%20maximum%20of%20an%20empty%20set%3F)%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20y)%3A%0A%20%20%20%20y_all_masked%20%3D%20mxp.asarray(y.data%2C%20mask%3DTrue)%0A%20%20%20%20mxp.any(y_all_masked).mask%0A%20%20%20%20return%20(y_all_masked%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Sorting%20functions%0A%20%20%20%20%20%20%20%20The%20sorting%20functions%20treat%20masked%20values%20as%20undefined%20and%2C%20by%20convention%2C%20append%20them%20to%20the%20end%20of%20the%20returned%20array.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20z_data%20%3D%20%5B8%2C%203%2C%204%2C%201%2C%209%2C%209%2C%205%2C%205%5D%0A%20%20%20%20z_mask%20%3D%20%5B0%2C%200%2C%201%2C%200%2C%201%2C%201%2C%200%2C%200%5D%0A%20%20%20%20z%20%3D%20mxp.asarray(z_data%2C%20mask%3Dz_mask)%0A%20%20%20%20mxp.sort(z)%0A%20%20%20%20return%20z%2C%20z_data%2C%20z_mask%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Where%20did%20those%20huge%20numbers%20come%20from%3F%20We%20emphasize%20again%3A%20*the%20%60data%60%20corresponding%20with%20masked%20elements%20should%20be%20considered%20meaningless*%3B%20they%20are%20just%20placeholders%20that%20allow%20us%20respect%20the%20mask%20while%20doing%20array%20operations%20efficiently.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20z)%3A%0A%20%20%20%20i%20%3D%20mxp.argsort(z)%0A%20%20%20%20i%0A%20%20%20%20return%20(i%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Is%20it%20surprising%20that%20the%20mask%20of%20the%20array%20returned%20by%20%60argsort%60%20is%20all%20False%3F%20These%20are%20the%20indices%20that%20allow%20us%20to%20transform%20the%20original%20array%20into%20the%20sorted%20result.%20We%20can%20confirm%20that%20without%20a%20mask%2C%20these%20indices%20sort%20the%20array%20and%20keep%20the%20right%20elements%20masked.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(i%2C%20z)%3A%0A%20%20%20%20a%20%3D%20z%5Bi.data%5D%0A%20%20%20%20a%0A%20%20%20%20return%20(a%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22*Gotcha%3A*%20Sorting%20is%20not%20supported%20when%20the%20the%20non-masked%20data%20includes%20the%20maximum%20(minimum%20when%20%60descending%3DTrue%60)%20value%20of%20the%20data's%20%60dtype%60.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20z%2C%20z_mask)%3A%0A%20%20%20%20b%20%3D%20mxp.asarray(z%2C%20mask%3Dz_mask%2C%20dtype%3Dmxp.uint8)%0A%20%20%20%20b%5B0%5D%20%3D%202**8%20-%201%0A%20%20%20%20%23%20mxp.sort(b)%0A%20%20%20%20%23%20NotImplementedError%3A%20The%20maximum%20value%20of%20the%20data's%20dtype%20is%20included%20in%20the%20non-masked%20data%3B%20this%20complicates%20sorting%20when%20masked%20values%20are%20present.%0A%20%20%20%20%23%20Consider%20promoting%20to%20another%20dtype%20to%20use%20%60sort%60.%0A%20%20%20%20return%20(b%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22It%20is%20often%20possible%20to%20sidestep%20this%20limitation%20by%20using%20a%20different%20%60dtype%60%20for%20the%20sorting%2C%20then%20converting%20back%20to%20the%20original%20type.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(b%2C%20mxp)%3A%0A%20%20%20%20c%20%3D%20mxp.astype(b%2C%20mxp.uint16)%0A%20%20%20%20c_sorted%20%3D%20mxp.astype(mxp.sort(c)%2C%20mxp.uint8)%0A%20%20%20%20c_sorted%0A%20%20%20%20return%20c%2C%20c_sorted%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Set%20functions%0A%20%20%20%20%20%20%20%20Masked%20elements%20are%20treated%20as%20distinct%20from%20all%20non-masked%20elements%20but%20equivalent%20to%20all%20other%20masked%20elements.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20z)%3A%0A%20%20%20%20z_unique%20%3D%20mxp.unique_counts(z)%0A%20%20%20%20z_unique.values%0A%20%20%20%20return%20(z_unique%2C)%0A%0A%0A%40app.cell%0Adef%20_(z_unique)%3A%0A%20%20%20%20z_unique.counts%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22*Gotcha*%3A%20set%20functions%20have%20the%20same%20limitation%20as%20the%20sorting%20functions%3A%20the%20non-masked%20data%20may%20not%20include%20the%20maximum%20value%20of%20the%20data's%20%60dtype%60.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Manipulation%20functions%0A%20%20%20%20%20%20%20%20Manipulation%20functions%20perform%20the%20same%20operation%20on%20the%20data%20and%20the%20mask.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(a%2C%20mxp)%3A%0A%20%20%20%20mxp.flip(a)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(a%2C%20mxp)%3A%0A%20%20%20%20mxp.stack(%5Ba%2C%20a%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Creation%20functions%0A%20%20%20%20%20%20%20%20Most%20creation%20functions%20create%20arrays%20with%20an%20all-False%20mask.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20mxp.eye(3)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Exceptions%20include%20the%20%60_like%60%20functions%2C%20which%20preserve%20the%20mask%20of%20the%20array%20argument.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(a%2C%20mxp)%3A%0A%20%20%20%20mxp.zeros_like(a)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%60tril%60%20and%20%60triu%60%20also%20preserve%20the%20mask%20of%20the%20indicated%20triangular%20portion%20of%20the%20argument.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20import%20numpy%20as%20xp%0A%0A%20%20%20%20A_data%20%3D%20xp.ones((3%2C%203))%0A%20%20%20%20A_mask%20%3D%20xp.zeros_like(A_data)%0A%20%20%20%20A_mask%5B0%2C%20-1%5D%20%3D%201%0A%20%20%20%20A_mask%5B-1%2C%200%5D%20%3D%201%0A%20%20%20%20A%20%3D%20mxp.asarray(A_data%2C%20mask%3DA_mask)%0A%20%20%20%20A%0A%20%20%20%20return%20A%2C%20A_data%2C%20A_mask%2C%20xp%0A%0A%0A%40app.cell%0Adef%20_(A%2C%20mxp)%3A%0A%20%20%20%20mxp.tril(A)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Searching%20functions%0A%20%20%20%20%20%20%20%20Similarly%20to%20the%20statistics%20functions%2C%20masked%20elements%20are%20treated%20as%20if%20they%20did%20not%20exist.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(z)%3A%0A%20%20%20%20z_with_zeros%20%3D%20z%0A%20%20%20%20z_with_zeros%5B%5B1%2C%20-1%5D%5D%20%3D%200%20%20%23%20add%20some%20zeros%0A%20%20%20%20z_with_zeros%20%20%23%20let's%20remember%20what%20%60z%60%20looks%20like%0A%20%20%20%20return%20(z_with_zeros%2C)%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20z_with_zeros)%3A%0A%20%20%20%20mxp.argmax(z_with_zeros)%20%20%23%209%20is%20masked%2C%20so%208%20(at%20index%200)%20is%20the%20largest%20element%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20z_with_zeros)%3A%0A%20%20%20%20z_i%20%3D%20mxp.nonzero(z_with_zeros)%20%20%23%20Only%20elements%20at%20these%20indices%20are%20nonzero%20*and*%20not%20masked%0A%20%20%20%20z_i%0A%20%20%20%20return%20(z_i%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22The%20correct%20behavior%20of%20indexing%20with%20a%20masked%20array%20is%20ambiguous%2C%20so%20use%20only%20regular%2C%20unmasked%20arrays%20for%20indexing.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(z_i%2C%20z_with_zeros)%3A%0A%20%20%20%20indices%20%3D%20z_i%5B0%5D.data%0A%20%20%20%20z_with_zeros%5Bindices%5D%0A%20%20%20%20return%20(indices%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Elementwise%20functions%0A%20%20%20%20%20%20%20%20Elementwise%20functions%20(and%20operators)%20simply%20perform%20the%20requested%20operation%20on%20the%20%60data%60.%0A%0A%20%20%20%20%20%20%20%20For%20unary%20functions%2C%20the%20mask%20of%20the%20result%20is%20the%20mask%20of%20the%20argument.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20xp)%3A%0A%20%20%20%20d%20%3D%20xp.linspace(0%2C%202*xp.pi%2C%205)%0A%20%20%20%20d%20%3D%20mxp.asarray(d%2C%20mask%3D(d%20%3E%20xp.pi))%0A%20%20%20%20d%0A%20%20%20%20return%20(d%2C)%0A%0A%0A%40app.cell%0Adef%20_(d)%3A%0A%20%20%20%20-d%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(d%2C%20mxp)%3A%0A%20%20%20%20mxp.round(mxp.sin(d))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22For%20binary%20functions%20and%20operators%2C%20the%20mask%20of%20the%20result%20is%20the%20result%20of%20the%20logical%20*or*%20operation%20on%20the%20masks%20of%20the%20arguments.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20e%20%3D%20mxp.asarray(%5B1%2C%202%2C%203%2C%204%5D%2C%20mask%3D%5B1%2C%200%2C%201%2C%200%5D)%0A%20%20%20%20f%20%3D%20mxp.asarray(%5B5%2C%206%2C%207%2C%208%5D%2C%20mask%3D%5B1%2C%201%2C%200%2C%200%5D)%0A%20%20%20%20e%20%2B%20f%0A%20%20%20%20return%20e%2C%20f%0A%0A%0A%40app.cell%0Adef%20_(e%2C%20f%2C%20mxp)%3A%0A%20%20%20%20mxp.pow(f%2C%20e)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Note%20that%20%60np.ma%60%20automatically%20masks%20non-finite%20elements%20produced%20during%20calculations.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20numpy%0A%0A%20%20%20%20g%20%3D%20numpy.ma.masked_array(0%2C%20mask%3DFalse)%0A%20%20%20%20with%20numpy.errstate(divide%3D'ignore'%2C%20invalid%3D'ignore')%3A%0A%20%20%20%20%20%20%20%20h%20%3D%20%5B1%2C%200%5D%20%2F%20g%0A%20%20%20%20h%0A%20%20%20%20return%20g%2C%20h%2C%20numpy%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%60MArray%60%20*does%20not*%20follow%20this%20convention.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20numpy)%3A%0A%20%20%20%20j%20%3D%20mxp.asarray(0%2C%20mask%3DFalse)%0A%20%20%20%20with%20numpy.errstate(divide%3D'ignore'%2C%20invalid%3D'ignore')%3A%0A%20%20%20%20%20%20%20%20k%20%3D%20%5B1%2C%200%5D%20%2F%20j%0A%20%20%20%20k%0A%20%20%20%20return%20j%2C%20k%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22This%20is%20because%20masked%20elements%20are%20often%20used%20to%20represent%20*missing*%20data%2C%20and%20the%20results%20of%20these%20operations%20are%20not%20missing.%20If%20this%20does%20not%20suit%20your%20needs%2C%20mask%20out%20data%20according%20to%20your%20requirements%20after%20performing%20the%20operation.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20numpy%2C%20xp)%3A%0A%20%20%20%20m%20%3D%20mxp.asarray(0%2C%20mask%3DFalse)%0A%20%20%20%20with%20numpy.errstate(divide%3D'ignore'%2C%20invalid%3D'ignore')%3A%0A%20%20%20%20%20%20%20%20n%20%3D%20%5B1%2C%200%5D%20%2F%20m%0A%20%20%20%20mxp.asarray(n.data%2C%20mask%3Dxp.isnan(n.data))%0A%20%20%20%20return%20m%2C%20n%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Linear%20Algebra%20Functions%0A%20%20%20%20%20%20%20%20As%20usual%2C%20linear%20algebra%20functions%20and%20operators%20treat%20masked%20elements%20as%20though%20they%20don't%20exist.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20o%20%3D%20mxp.asarray(%5B1%2C%202%2C%203%2C%204%5D%2C%20mask%3D%5B1%2C%200%2C%201%2C%200%5D)%0A%20%20%20%20p%20%3D%20mxp.asarray(%5B5%2C%206%2C%207%2C%208%5D%2C%20mask%3D%5B1%2C%201%2C%200%2C%200%5D)%0A%20%20%20%20o%20%40%20p%0A%20%20%20%20return%20o%2C%20p%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22The%20exception%20is%20%60matrix_transpose%60%2C%20which%20transposes%20the%20data%20and%20the%20mask.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mxp)%3A%0A%20%20%20%20q%20%3D%20mxp.asarray(%5B%5B1%2C%202%5D%2C%20%5B3%2C%204%5D%5D%2C%20mask%3D%5B%5B1%2C%201%5D%2C%20%5B0%2C%200%5D%5D)%0A%20%20%20%20q%0A%20%20%20%20return%20(q%2C)%0A%0A%0A%40app.cell%0Adef%20_(mxp%2C%20q)%3A%0A%20%20%20%20mxp.matrix_transpose(q)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Conclusion%0A%20%20%20%20%20%20%20%20While%20this%20tutorial%20is%20not%20exhaustive%2C%20we%20hope%20it%20is%20sufficient%20to%20allow%20you%20to%20predict%20the%20results%20of%20operations%20with%20%60MArray%60s%20and%20use%20them%20to%20suit%20your%20needs.%20If%20you'd%20like%20to%20see%20this%20tutorial%20extended%20in%20a%20particular%20way%2C%20please%20%5Bopen%20an%20issue%5D(https%3A%2F%2Fgithub.com%2Fmdhaber%2Fmarray%2Fissues)!%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
df4c5b7a5b1360cd15bc6203d8fec7d0f86de4458d6a53fcc0869992fa5d2484