# Error with \`antijoin\` when encountering \`-0.0\` value?

**URL:** <https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329>\
**Category:** New to Julia\
**Tags:** question, dataframes\
**Created:** [April 11, 2023, 5:56am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329 "2023-04-11T05:56:46Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![phantom](https://avatars.discourse-cdn.com/v4/letter/p/e0b2c6/32.png) [@phantom](https://discourse.julialang.org/u/phantom)\
**Post date:** [April 11, 2023, 5:56am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/1 "2023-04-11T05:56:46Z")

</div>

Hi! suppose I have a `DataFrame` where some of the entries have been rounded to `-0.0`.

```julia
df1 = DataFrame(x = [1, 0, -0.0])
3×1 DataFrame
 Row │ x       
     │ Float64 
─────┼─────────
   1 │ 1.0
   2 │ 0.0
   3 │ -0.0

julia> df2 = DataFrame(x = [1, 0, 0])
3×1 DataFrame
 Row │ x     
     │ Int64 
─────┼───────
   1 │ 1
   2 │ 0
   3 │ 0

```

when I try

```julia
julia> antijoin(df1,df2, on=(:x))

```

rather than en empty `DataFrame` the following error is raised

```julia
ERROR: ArgumentError: currently for numeric values NaN and `-0.0` in their real or imaginary components are not allowed. Use CategoricalArrays.jl to wrap these values in a CategoricalVector to perform the requested join.
Stacktrace:
 [1] DataFrames.DataFrameJoiner(dfl::DataFrame, dfr::DataFrame, on::Symbol, matchmissing::Symbol, kind::Symbol)
   @ DataFrames ~/.julia/packages/DataFrames/LteEl/src/join/composer.jl:94
 [2] _join(df1::DataFrame, df2::DataFrame; on::Symbol, kind::Symbol, makeunique::Bool, indicator::Nothing, validate::Tuple{Bool, Bool}, left_rename::typeof(identity), right_rename::typeof(identity), matchmissing::Symbol, order::Symbol)
   @ DataFrames ~/.julia/packages/DataFrames/LteEl/src/join/composer.jl:497
 [3] #antijoin#674
   @ ~/.julia/packages/DataFrames/LteEl/src/join/composer.jl:1488 [inlined]
 [4] top-level scope
   @ REPL[260]:1

```

However when I try using `CategoricalArrays` as per `Error` raised I get the following result

```julia
julia> df4 = DataFrame(x = categorical([1,0,-0.0]))
3×1 DataFrame
 Row │ x    
     │ Cat… 
─────┼──────
   1 │ 1.0
   2 │ 0.0
   3 │ -0.0

julia> antijoin(df4,df2, on=(:x))
1×1 DataFrame
 Row │ x    
     │ Cat… 
─────┼──────
   1 │ -0.0

```

As opposed to an empty `DataFrame`. I’m a little confused by this because

```julia
df4.x[3]==df2.x[2]
true

```

and

```julia
0.0 == -0.0
true  

```

Just wondering if anyone could provide some insight as to where my understanding went wrong? Thanks!

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [April 11, 2023, 9:45am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/2 "2023-04-11T09:45:53Z")

</div>

Maybe there’s a small documentation “bug”: the `antijoin` documentation only says the following about the comparison function:

> - `matchmissing` : …; `isequal` is used for comparisons of  
> rows for equality

Presumably this last sentence concerns all comparisons (including rows without missing values) and should be in the general part of the docstring, rather than the `matchmissing` bullet point. Maybe @bkamins can confirm.

Anyway, this explains the observed behavior:

```julia
julia> isequal(-0.0, 0)
false

```

I don’t know if there’s a way to make DataFrame consider `0` and `-0.0` as equal.

However, making joins on floating point values is weird and rarely a good idea. In your particular case, maybe the right solution is to round the values to integers? See the difference in the two following examples:

```julia
julia> round.(x)
3-element Vector{Float64}:
  1.0
  0.0
 -0.0

julia> round.(Int, x)
3-element Vector{Int64}:
 1
 0
 0

```

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [April 11, 2023, 9:48am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/3 "2023-04-11T09:48:43Z")

</div>

The reason is that `==` is not a test of equality used in joins.  
The test of equality is `isequal`:

```julia
julia> isequal(0.0, -0.0)
false

```

This difference is exactly the reason why joining on `-0.0` is by default disallowed as the result could confuse the user.

Note that the same is for e.g. `Set` or `Dict`:

```julia
julia> Set([0.0, -0.0])
Set{Float64} with 2 elements:
  0.0
  -0.0

julia> Dict(0.0=>1, -0.0=>2)
Dict{Float64, Int64} with 2 entries:
  0.0 => 1
  -0.0 => 2

```

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [April 11, 2023, 9:51am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/4 "2023-04-11T09:51:58Z")

</div>

> [@sijo](#):
>
> I don’t know if there’s a way to make DataFrame consider `0` and `-0.0` as equal.

This is unrelated with DataFrames.jl. Julia Base does not consider `0.0` and `-0.0` as equal. What DataFrames.jl does is just a consequence.

In general doing joins on floats should be discouraged. It is more reliable to use integers as @sijo suggested.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [April 11, 2023, 9:59am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/5 "2023-04-11T09:59:36Z")

</div>

> [@bkamins](#):
>
> This is unrelated with DataFrames.jl. Julia Base does not consider `0.0` and `-0.0` as equal.

`Base.isequal` doesn’t but `Base.==` does… I think it’s not obvious for users which function is used by DataFrames.jl for this kind of comparison (and whether the default function can be changed by the user) so maybe the corresponding doc sentence should be moved outside the `matchmissing` bullet point?

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [April 11, 2023, 6:11pm UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/6 "2023-04-11T18:11:46Z")

</div>

> [@sijo](#):
>
> `Base.==` does

It does, but this operator does not produce equivalence classes over values so it cannot be used in such context.

There are two reasons:

1. `==` does not guarantee to return `true` or `false`.
2. `NaN != NaN` (which breaks relexivity required for equivalence operator)

(I am writing this to higlight that it is not a choice if we use `isequal` or `==`, and we chose `isequal`; the point is that `==` cannot be used)

Still - I understand your point that the fact that `isequal` is used can be more exposed in the documentation. Are you willing to propose a PR (if not I will do it).

---

<div class="post-metadata">

**Author:** ![phantom](https://avatars.discourse-cdn.com/v4/letter/p/e0b2c6/32.png) [@phantom](https://discourse.julialang.org/u/phantom)\
**Post date:** [April 12, 2023, 6:43am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/7 "2023-04-12T06:43:54Z")

</div>

Thank you both so much for the clarification! It is really helpful. Some of the data I have in columns is rounded out to 2 or 3 decimals so I don’t think I can use integers. Would there be some other work around to use joins (other than scaling the values)?

If I replace all the `-0.0` values with 0, are there other reasons that joins should be discouraged when using floats?

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [April 12, 2023, 7:40am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/8 "2023-04-12T07:40:41Z")

</div>

The other case is `NaN` values.  
In general maybe you will prefer to work with [GitHub - JuliaMath/FixedPointDecimals.jl: Julia fixed-point decimals built from integers](https://github.com/JuliaMath/FixedPointDecimals.jl) as they are designed for your use case.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [April 12, 2023, 10:04am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/9 "2023-04-12T10:04:41Z")

</div>

> [@bkamins](#):
>
> Are you willing to propose a PR

Sure 🙂 done [here](https://github.com/JuliaData/DataFrames.jl/pull/3313).

---

<div class="post-metadata">

**Author:** ![phantom](https://avatars.discourse-cdn.com/v4/letter/p/e0b2c6/32.png) [@phantom](https://discourse.julialang.org/u/phantom)\
**Post date:** [April 12, 2023, 11:05am UTC](https://discourse.julialang.org/t/error-with-antijoin-when-encountering-0-0-value/97329/10 "2023-04-12T11:05:13Z")

</div>

Thank you both so much!
