Is there a function for the inverse of `ismissing()`

Hi all,

Is there a function that returns the inverse of ismissing()?

Obviously, I can use ismissing(foo) == false but it seems not elegant.

Thanks!

!ismissing?

4 Likes

!ismissing()
Or, generally, for any function foo that returns a boolean value, !foo is a valid function that returns the inverse value.

it’s not because !ismissing() is a separately defined function though, this is defined in Julia Base:

!(f::Function) = (x...)->!f(x...)
4 Likes

To be clear, if you do !ismissing(foo), this just calls ismissing(foo) and then negates the result with !

If you use !ismissing with no function call (...), then Julia uses the higher-order function @jling mentioned, which yields a function that is the antonym of ismissing. This is useful for passing to other functions, e.g. for calling filter(!ismissing, somearray) to extract the non-missing elements.

13 Likes

So the answer is that there is no inverse function to ismissing(). But there are plenty of ways to generate the behaviour that such function would have.

1 Like

anonymous functions are compiled too, so you lose no performance

1 Like

this usually have a different meaning btw, an inverse function of f(), let’s call it g(), should have the property g(f(x)) == x, which is impossible for ismissing for obvious reason.


there’s nothing mystical about built-in functions, you can see the source code:

ismissing(x) = x === missing

you can define your:

isnotmissing(x) = x!== missing

if you really want…

3 Likes

(Yes, I would call what @drarnau wants the “antonym” of ismissing.)

4 Likes

Fwiw I would like for this to exist. The ! makes broadcasting ugly. .!ismissing.(x). But its purely aesthetic so it can live somewhere else.

@. !ismissing(x) isn’t so bad, especially since you probably will have other function calls too. (I feel like @. is under-used.)

3 Likes

It can’t be used in a function call without more parentheses is one issue.

Actually that might be a nice 2.0 breaking change, make

foo(@mymacro a, b)

have @mymacro only apply to a.

@. $foo(!ismissing(x), b) should work, though I don’t know that it is better than foo(.!ismissing.(x), b). But I agree that macro calls inside function arguments are a bit annoying because of the parens.

I have defined the following function for my Jupyter notebooks:

broadwrap(f) = function (args...) broadcast(f, args...) end

But the name could be smaller or a single symbol. This function returns the broadcasted version of a function and I find it useful for some of the DataFrame manipulation functions, which ask for a function working over a whole column as an argument.

This is what ByRow does. It was written to do this exact operation, right?

In DataFramesMeta there is @rtransform, @rselect, @rsubset, etc. for row-wise operations.

No, it seems like they have some distinctions:

The wrapped function is called exactly once for each element. This differs from map and broadcast, which assume for some types of source vectors (e.g. SparseVector) that the wrapped function is pure, allowing them to call the function only once for multiple equal values. When using such types, for maximal performance with pure functions which are relatively costly, use x → map(f, x) instead of ByRow(f).

But it may be the case that I created broadwrap either by ignorance of ByRow or before it existed; nonetheless, I am not sure if there is an advantage of using a more purpose-specific solution as I may end up using broadwrap for one or another thing that does not relate to DataFrames.jl (this is just its main use).

I would consider that a relatively niche difference. It’s also quite useful for data analysis, for example if you have a PooledArray and a function like f(x) = x + rand().

But you are right that ByRow lives in DataFrames.jl. Maybe it should live somewhere else. But I’m not sure where.

The practical problem with this is that, unlike dot calls, it won’t fuse.

Yes… but the goal of broadwrap is to create a function/closure/callable to be passed as an argument, there is a way to guarantee loop fusion when you are passing a function as argument?