Thanks, this thread is a lifesaver and such a feature does feel like a conspicuous absence! Particularly the lack of support for using both skipmissing
and dims=
as @thompsonmj mentions.
In my case I needed to find the minimum value in each column of a 2d array mat
, ignoring zeros. I find that all three of the methods below work:
mat = rand(0:5, 5, 11)
1: mapslices(x -> minimum(skipmissing(x)), replace(mat, 0 => missing), dims = 1)
2: mapslices(x -> minimum(filter(!isnan, x)), replace(mat, 0 => NaN), dims = 1)
3: mapslices(x -> minimum(filter(!iszero, x)), mat, dims = 1)
However, these approaches only work if there are no columns which only contain zeros. Which will lead to the error:
ERROR: MethodError: reducing over an empty collection is not allowed
There is a huge performance penalty when doing this with anonymous functions, e.g.
@elapsed minimum(mat, dims = 1)
~ 3.8735e-5s
@elapsed mapslices(x -> minimum(filter(!iszero, x)), mat, dims = 1)
~ 0.159694082s
Defining the function beforehand helps, e.g.
nzmin(x) = minimum(filter(!iszero, x))
nzmin(x,y) = mapslices(nzmin, mat, dims = y)
@elapsed nzmin(mat,1)
~ 0.000339807s
or using a modified version of the generalised function @bjarthur outlines:
_nzfunc(f, A, ::Colon) = f(filter(!iszero, A))
_nzfunc(f, A, dims) = mapslices(a->_nzfunc(f,a,:), A, dims=dims)
nzfunc(f, A; dims=:) = _nzfunc(f, A, dims)
@elapsed nzfunc(minimum, mat, dims=1)
~ 0.000850458
Which incurs a small penalty in passing a function through, relative to defining a specific function.
I’m new new to Julia, so may have missed something here!