# DataFrames groupby() by a column of mutable custom type

**URL:** https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632
**Category:** General Usage
**Tags:** dataframes, mutable-structure
**Created:** [June 9, 2021, 3:06pm UTC](https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632 "2021-06-09T15:06:23Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![AndreLC](https://avatars.discourse-cdn.com/v4/letter/a/7ab992/32.png) [@AndreLC](https://discourse.julialang.org/u/AndreLC)
#### Post date: [June 9, 2021, 3:06pm UTC](https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632/1 "2021-06-09T15:06:24Z")

</div>

Here is a small example:

```julia
mutable struct Mystring
    str::String
end

using DataFrames

strs = ["a","b","a","b"];
mystrs = Mystring.(strs);
df = DataFrame(col_str=strs, col_mystr=mystrs);
println(df)

println(groupby(df, :col_str))
println(groupby(df, :col_mystr))

```

I would expect both `groupby()`'s to return the same groups (g1 gets rows 1 and 3, and g2 gets rows 2 and 4). Instead, the `groupby(df, :col_mystr)` returns 4 groups, each having a single row.

I tried overloading the simple comparison operators, but the result did not change:

```julia
Base.:(==)(str1::Mystring, str2::Mystring) = str1.str == str2.str
Base.:(>)(str1::Mystring, str2::Mystring) = str1.str > str2.str
Base.:(<)(str1::Mystring, str2::Mystring) = str1.str < str2.str

```

Important is also that this behavior is specific to mutable struct; if `Mystring` is declared as an immutable `struct`, `groupby()` works.

What am I missing?

Thanks 🙂

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [June 9, 2021, 4:08pm UTC](https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632/2 "2021-06-09T16:08:56Z")

</div>

Very interesting! Great to see someone experimenting with grouped dataframes and custom types.

I’m glad that this works for an immutable type. Though I would have expected `hash` to be the thing you need to define rather than `==`.

I don’t know what’s going on with mutable types. This is very interesting. I am pinging @bkamins on this.

Hopefully when we understand the behavior we can add this to the docs.

---

<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: [June 9, 2021, 4:26pm UTC](https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632/3 "2021-06-09T16:26:27Z")

</div>

The standard thing happens, the equality is checked with `isequal` not `==` (otherwise `missing` would not be handled correctly for instance as they would not produce `Bool` but `missing`). You need to define `isequal` and in consequence also `hash` for your type.

See:

```julia
  isequal(x, y)

  Similar to ==, except for the treatment of floating point numbers and of missing values. isequal treats all floating-point NaN values as equal to each other, treats -0.0 as unequal to
  0.0, and missing as equal to missing. Always returns a Bool value.

  isequal is the comparison function used by hash tables (Dict). isequal(x,y) must imply that hash(x) == hash(y).

```

---

<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: [June 9, 2021, 4:29pm UTC](https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632/4 "2021-06-09T16:29:09Z")

</div>

This is implicitly implied by this line in `groupby` documentation:

> `GroupedDataFrame` also supports the dictionary interface.

but indeed we could be explicit here.

---

<div class="post-metadata">

### Author: ![AndreLC](https://avatars.discourse-cdn.com/v4/letter/a/7ab992/32.png) [@AndreLC](https://discourse.julialang.org/u/AndreLC)
#### Post date: [June 9, 2021, 4:44pm UTC](https://discourse.julialang.org/t/dataframes-groupby-by-a-column-of-mutable-custom-type/62632/5 "2021-06-09T16:44:07Z")

</div>

I was not aware `isequal` was not the same as `==`, thanks.

For future reference, this is the solution:

```julia
Base.isequal(str1::Mystring, str2::Mystring) = str1.str == str2.str
Base.hash(str::Mystring, h::UInt64) = hash(str.str, h)

```

Thanks again!
