# Coloring similar entries in a matrix with the same color

**URL:** https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160
**Category:** General Usage
**Tags:** colors
**Created:** [December 24, 2024, 9:55pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160 "2024-12-24T21:55:04Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![Shuvomoy\_Das\_Gupta](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuvomoy_das_gupta/32/10069_2.png) [@Shuvomoy\_Das\_Gupta](https://discourse.julialang.org/u/Shuvomoy_Das_Gupta)
#### Post date: [December 24, 2024, 9:55pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/1 "2024-12-24T21:55:04Z")

</div>

I have a matrix `A` with many entries but many of them are very close to each other. A small example of `A` could be:

```julia
A = [1.0001 2.0003 1.0;
    2.0 3.0001 2.0004;
    1.0001 2.0002 1.0]

```

The actual matrix `A` is quite large, but the number of unique elements in the matrix is much smaller subject to the tolerance. I would like to color the numbers that are similar to each other, e.g., for the small example above, I would like to display the matrix `A` in a manner so that `1.0001, 1.0` are in red (say), `2.0003, 2.0002, 2.0004, 2.0` in blue, `3.0001` in green and so on. Is there a way for Julia to achieve this? I am not sure if there is a package that can achieve this. Any tips/suggestions will be much appreciated.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [December 24, 2024, 10:32pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/2 "2024-12-24T22:32:56Z")

</div>

> [@Shuvomoy\_Das\_Gupta](#):
>
> ```julia
> A = [1.0001 2.0003 1.0;
> 2.0 3.0001 2.0004;
> 1.0001 2.0002 1.0]
> 
> ```

Given `A` as defined, the following returns a matrix of colors for the entries (it might even work for other `ndims` values).

```julia
julia> let c = 0, B = similar(A, Int)
    t = foldl((s,n)->(isapprox(last(n[1]),last(n[2]); atol=0.1) || (c += 1;); vcat(s,first(n[2])=>c)),
      IterTools.partition(vcat(CartesianIndex(1, 1)=>NaN,sort(vec(collect(pairs(A))); by = last)), 2, 1); 
      init = Pair{CartesianIndex{ndims(A)},Int}[]
    )
    setindex!.(Ref(B), last.(t), first.(t))
    B
end
3×3 Matrix{Int64}:
 1 2 1
 2 3 2
 1 2 1

```

The `isapprox` in the middle determined the tolerance and can be tweaked and, of course, this should be a function.

And this works only for `sort`able values, even though, any range which has a similarity metric should suffice to do this clustering-like result (but with a different algo).

---

<div class="post-metadata">

### Author: ![Shuvomoy\_Das\_Gupta](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuvomoy_das_gupta/32/10069_2.png) [@Shuvomoy\_Das\_Gupta](https://discourse.julialang.org/u/Shuvomoy_Das_Gupta)
#### Post date: [December 24, 2024, 11:12pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/3 "2024-12-24T23:12:59Z")

</div>

Thanks @Dan, the code produces the following error:

```julia
UndefVarError: `Itr` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] top-level scope
   @ REPL[2]:2

```

I found a package called `https://github.com/KristofferC/Crayons.jl`, I will see if I can use it achieve what I want. Thanks again.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [December 24, 2024, 11:18pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/4 "2024-12-24T23:18:01Z")

</div>

Oh yeah… forgot, `Itr` is what I use for `IterTools` package.  
I’m fixing the post, but you can just do:

```julia
import IterTools as Itr

```

to fix it.

---

<div class="post-metadata">

### Author: ![abulak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abulak/32/28314_2.png) [@abulak](https://discourse.julialang.org/u/abulak)
#### Post date: [December 25, 2024, 11:38pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/5 "2024-12-25T23:38:17Z")

</div>

I’m not sure I can follow @Dan’s solution, but here’s one I spent some time optimizing. It treats `0` somehow specially, but maybe that’s not a problem for you:

```julia
# this one vectorizes, round does not
function unsafe_round(f::AbstractFloat; scale)
    (x, n) = frexp(f)
    y = unsafe_trunc(Int, scale * x) / scale
    return ldexp(y, n)
end

function _clamp_round!(
    A::AbstractArray;
    atol=Base.rtoldefault(real(eltype(A))),
    sigdigits=floor(Int, -log10(atol))
)
    @inbounds for (i, a) in pairs(A)
        A[i] = ifelse(
            abs(a) < atol,
            zero(a),
            unsafe_round(a, scale=10^sigdigits)
        )
    end
    return A
end

function partition(M::AbstractMatrix)
    l = 0 # number of partitions disregarding 0
    d = Dict(zero(eltype(M)) => l)
    res = zeros(Int, size(M))
    for (v, idx) in zip(M, eachindex(res))
        k = get!(d, v, l + 1)
        l = ifelse(k == l + 1, k, l)
        res[idx] = k
    end
    return l, res
end

```

```julia
julia> let A = deepcopy(A), atol = 1e-3
           A = _clamp_round!(A, atol=atol)
           (nparts, M) = partition(A)
           @info nparts
           M
       end
[ Info: 3
3×3 Matrix{Int64}:
 1 2 1
 2 3 2
 1 2 1

julia> let A = deepcopy(A), atol = 1e-4
           A = _clamp_round!(A, atol=atol)
           (nparts, M) = partition(A)
           @info nparts
           M
       end
[ Info: 4
3×3 Matrix{Int64}:
 1 2 1
 2 3 4
 1 2 1

```

@Shuvomoy_Das_Gupta How large are your matrices?  
If you create an even faster solution, please share it here as well!

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [December 26, 2024, 1:21am UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/6 "2024-12-26T01:21:42Z")

</div>

My earlier suggestion was a bit too obfuscated. Hopefully this one is easier to read:

```julia
function cheapcluster(A, tol)
    v = sort(vec(A))
    return getindex.(Ref(Dict(v .=> cumsum(vcat(1,diff(v) .> tol)))),A)
end

```

and with this defined and the A in the OP:

```julia
julia> cheapcluster(A, 0.1)
3×3 Matrix{Int64}:
 1 2 1
 2 3 2
 1 2 1

julia> cheapcluster(A, 1e-4)
3×3 Matrix{Int64}:
 1 4 1
 2 5 4
 1 3 1

```

---

<div class="post-metadata">

### Author: ![yolhan\_mannes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yolhan_mannes/32/220485_2.png) [@yolhan\_mannes](https://discourse.julialang.org/u/yolhan_mannes)
#### Post date: [December 26, 2024, 6:19am UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/7 "2024-12-26T06:19:30Z")

</div>

Même principe avec sortperm

```julia
function cheapcluster(A, tol)
    v = vec(A)
    id = sortperm(v)
    sorted_v = @view v[id]
    clusters = zeros(Int, length(v))
    cluster_id = 1
    clusters[1] = cluster_id

    for i in 2:length(sorted_v)
        if sorted_v[i] - sorted_v[i-1] > tol
            cluster_id += 1
        end
        clusters[i] = cluster_id
    end

    result = zeros(Int, size(A))
    result[id] .= clusters
    return results 
end

```

---

<div class="post-metadata">

### Author: ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)
#### Post date: [December 26, 2024, 1:14pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/8 "2024-12-26T13:14:20Z")

</div>

Given that the intention is to _colorize_ the matrix, I would tackle this from a colormap perspective. Don’t know how representative the _A_ matrix is of the true case, so my solution may not be adequate for it, but for _this A_

```julia
using GMT

C = makecpt(range=(0.5,3.5,1),) # Colormap with boundaries between the the integers
grdimage(A, cmap=C, show=true) # The image

```

 ![g](https://global.discourse-cdn.com/julialang/original/3X/e/4/e4cf52e5a4b26cd7ab30687a6987e919bc50eb0e.png)

The fact that the outer pixels seem to be cut is no error. It results from grid being _grid registered_. A _pixel registerd_ grid can be obtained with

```julia
G = mat2grid(Float32.(A), reg=1); # Better to use Float32 as the image creation implies that conversion under the hood
grdimage(G, cmap=C, show=true)

```

 ![p](https://global.discourse-cdn.com/julialang/original/3X/6/7/677f8773b901d948a11cd2a82afc5ab53e42bbaa.png)

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [December 26, 2024, 2:36pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/9 "2024-12-26T14:36:46Z")

</div>

> [@Shuvomoy\_Das\_Gupta](#):
>
> the number of unique elements in the matrix is much smaller subject to the tolerance

Short answer: probably use `round.(A, sigdigits=2)` or similar to assign colors. (Adjust `sigdigits` as desired, or use `digits` if you want an absolute tolerance.)

Longer answer: approximate comparison is not transitive, so trying to define “uniqueness subject to a tolerance” inevitably will encounter some odd behaviors. This has been a frequently discussed topic. See, for example:

- [`unique()` with `isapprox()` instead of `isequal()` · Issue #19147 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/19147) and [Allow tolerance for `unique` · Issue #26837 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/26837)
- [Unique() to a certain tolerance? - #3 by improbable22](https://discourse.julialang.org/t/unique-to-a-certain-tolerance/23007/3)
- [How to make a Set of real values based on rtol? - #3 by djsegal](https://discourse.julialang.org/t/how-to-make-a-set-of-real-values-based-on-rtol/11769/3)
- [The test isequal(+0.0,-0.0) returns FALSE - #8 by stevengj](https://discourse.julialang.org/t/the-test-isequal-0-0-0-0-returns-false/38404/8)

---

<div class="post-metadata">

### Author: ![Shuvomoy\_Das\_Gupta](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuvomoy_das_gupta/32/10069_2.png) [@Shuvomoy\_Das\_Gupta](https://discourse.julialang.org/u/Shuvomoy_Das_Gupta)
#### Post date: [December 26, 2024, 2:44pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/10 "2024-12-26T14:44:56Z")

</div>

This was the original intent behind my question, I am looking into using Crayons.jl as well. Thanks very much for the code.

---

<div class="post-metadata">

### Author: ![Shuvomoy\_Das\_Gupta](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuvomoy_das_gupta/32/10069_2.png) [@Shuvomoy\_Das\_Gupta](https://discourse.julialang.org/u/Shuvomoy_Das_Gupta)
#### Post date: [December 26, 2024, 2:45pm UTC](https://discourse.julialang.org/t/coloring-similar-entries-in-a-matrix-with-the-same-color/124160/11 "2024-12-26T14:45:50Z")

</div>

The largest matrix in my case will be around 50×50. Thanks very much for the code @abulak .
