# Custom divergent colormap with unequal sides

**URL:** <https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484>\
**Category:** Visualization\
**Tags:** question, colors, makie\
**Created:** [June 11, 2024, 3:44pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484 "2024-06-11T15:44:22Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [June 11, 2024, 3:44pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/1 "2024-06-11T15:44:22Z")

</div>

I am working with Makie.jl and I wish to create a custom colormap. Taking any divergent colormap provided by Makie, such as `:balance` for example, I want to make a colormap that has the same colors as `:balance` or `:delta`, but the left side spans the range of values 0-1, and the right side spans the range of values 1-4 (instead of what the symmetric 1-2 would dictate).

Is that possible? If yes, how?

---

<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:** [June 11, 2024, 3:49pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/2 "2024-06-11T15:49:11Z")

</div>

> [@Datseris](#):
>
> but the left side spans the range of values 0-1, and the right side spans the range of values 1-4 (instead of what the symmetric 1-2 would dictate).

Can’t you use a standard diverging colormap for the range [-3,3], and then apply it to values v - 1 \in [-1, 3] (so that the left side is truncated)?

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [June 11, 2024, 3:57pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/3 "2024-06-11T15:57:16Z")

</div>

I am sorry, I did not understand the advice. I’ve set up a MWE which I should have done so already in my first post:

```julia
using CairoMakie

x = 1:10
y = 1:10
c = rand(10,10)

cmap = :delta
fig, ax, hm = heatmap(x, y, c; colormap = cmap)
cb = Colorbar(fig[1,2], hm)
fig

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/6/b/6bd5c3d33e351669157d2e2d2354eeab7f18536a.png)

what I want to achieve is that the “blue” part of the color map covers the range (e.g.,) 0-0.25, and the “green” part covers the range 0.25-1.0, instead of the currently symmetric case of covering each 50-50. I want to be able to utlize all colors in the color map, not cut off e.g., the bottom deep blue color.

---

<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:** [June 11, 2024, 4:01pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/4 "2024-06-11T16:01:33Z")

</div>

> [@Datseris](#):
>
> what I want to achieve is that the “blue” part of the color map covers the range (e.g.,) 0-0.25, and the “green” part covers the range 0.25-1.0, instead of the currently symmetric case of covering each 50-50. I want to be able to utlize all colors in the color map, not cut off e.g., the bottom deep blue color.

Maybe just pass your data through the map:

```julia
distort(x) = x < 0.25 ? -3x : x - 0.25

```

which maps it to [-0.75, 0.75], and then apply a standard diverging colormap centered at zero.

Of course, this distorts the data, which is something you have to be cautious about for visualization. Effectively, your colormap is no longer a “perceptually uniform” representation of your data, which is something that the colormap designers have worked hard to avoid.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [June 11, 2024, 4:03pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/5 "2024-06-11T16:03:30Z")

</div>

Yeah, I guess that’s the only way. I wanted to avoid transforming my own data, because this requires me to re-label all ticks in the colorbar. But it’s not a big deal so I’ll go ahead with this way of remapping data.

> [@stevengj](#):
>
> Effectively, your colormap is no longer a “perceptually uniform” representation of your data, which is something that the colormap designers have worked hard to avoid.

Yes, I understand. Thankfully in my particular use case it is not a big problem!

---

<div class="post-metadata">

**Author:** ![JonasWickman](https://avatars.discourse-cdn.com/v4/letter/j/9de0a6/32.png) [@JonasWickman](https://discourse.julialang.org/u/JonasWickman)\
**Post date:** [June 11, 2024, 5:51pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/6 "2024-06-11T17:51:16Z")

</div>

You can distort the colormap rather than the data. The following code is a bit clumsy, but shows the general idea.

```julia
using GLMakie
using ColorSchemes

function get_color_val(data_val, data_min, data_max, data_crit, color_val_crit)

    is_low = data_val ≤ data_crit
    prop_val = is_low ? (data_val-data_min)/(data_crit - data_min) : (data_val-data_crit)/(data_max-data_crit)
    color_val = is_low ? prop_val*color_val_crit : color_val_crit + prop_val*(1-color_val_crit)

end 

X = rand(20,20)
data_min, data_max = 0.0, 1.0
data_crit = 0.25

cmap = [get(ColorSchemes.RdBu, get_color_val(x, data_min, data_max, data_crit, 0.5)) for x ∈ range(0,1;length = 512)]

fig = Figure()
ax = Axis(fig[1,1])
hm = heatmap!(ax, X; colormap = cmap)
Colorbar(fig[1,2], hm)
fig

```

![crit_025](https://global.discourse-cdn.com/julialang/original/3X/5/7/57d62147ba9d846890d76bac8cf1acc609963580.png)  
Note that the white midpoint of the colorscheme has been reset to 0.25

---

<div class="post-metadata">

**Author:** ![lxvm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lxvm/32/50010_2.png) [@lxvm](https://discourse.julialang.org/u/lxvm)\
**Post date:** [June 11, 2024, 6:34pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/7 "2024-06-11T18:34:52Z")

</div>

Makie.jl also has some tools to make divergent colormaps, for example `Makie.diverging_palette(0, 230; mid=0.4)`  
 ![image](https://global.discourse-cdn.com/julialang/original/3X/2/3/23c55e626d91d0f83b41da0ce665d8274641324d.png)  
Here, `0` and `230` are hues in a color wheel from 0 to 360 and `mid` is just a fraction to bias the point where the colors diverge.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [June 11, 2024, 9:33pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/8 "2024-06-11T21:33:40Z")

</div>

I’ve also seen plots where people use an uneven scale on a symmetrical colormap. So you don’t distort the map but the scale. Here’s an example I cooked up a while ago, adjusted to your numbers:

```julia
data = rand(10, 10) .* 4

f = Figure()
ax = Axis(f[1, 1])

upper = 4
lower = 0
midpoint = 1
ratio = (upper - midpoint) / (midpoint - lower)

sc = ReversibleScale(
    x -> x > midpoint ? (x - midpoint) / ratio + midpoint : x,
    x -> x > midpoint ? (x - midpoint) * ratio + midpoint : x
)

hm = heatmap!(ax, data, colorscale = sc, colorrange = (lower, upper), colormap = :delta)
Colorbar(f[1, 2], hm, ticks = lower:upper)
f

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/6/e/6e34559002f5452efb95bac32ed4ac8e1af832f3.png)

Here’s a variant with the upper part going to 10, to show the effect even more:

```julia
upper = 10
lower = 0
midpoint = 1

data = rand(10, 10) .* (upper - lower) .+ lower

f = Figure()
ax = Axis(f[1, 1])

ratio = (upper - midpoint) / (midpoint - lower)

sc = ReversibleScale(
    x -> x > midpoint ? (x - midpoint) / ratio + midpoint : x,
    x -> x > midpoint ? (x - midpoint) * ratio + midpoint : x
)

hm = heatmap!(ax, data, colorscale = sc, colorrange = (lower, upper), colormap = :RdBu)
Colorbar(f[1, 2], hm, ticks = lower:upper)
f

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/a/9/a939c0453763c59130dcbf20edb3a6e4d2c465d7.png)

Might be confusing to some readers though.

Edit: hmm seems like the rendering of the colors in the heatmap is a bit off though with this

Edit2: I just had forgot to scale the input data with the larger limits, I think

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [June 12, 2024, 12:23pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/9 "2024-06-12T12:23:48Z")

</div>

Thanks @jules , the differently scaled colorscale is exactly what I was looking for!

---

<div class="post-metadata">

**Author:** ![cormullion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cormullion/32/49131_2.png) [@cormullion](https://discourse.julialang.org/u/cormullion)\
**Post date:** [June 13, 2024, 8:05am UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/10 "2024-06-13T08:05:40Z")

</div>

I’m glad you got a good solution! I started to write an answer, but was interrupted, so this is now superfluous to requirements… 🙂 But for the record, my idea was to concatenate colorschemes:

```julia
cs = ColorSchemes.delta
cs1 = ColorScheme(get(cs, range(0, 0.5, length=50))) * 
      ColorScheme(get(cs, range(0.5, 1.0, length=200)))

```

 ![Screenshot 2024-06-13 at 08.49.19](https://global.discourse-cdn.com/julialang/original/3X/5/5/5564bc535036833f2547c6b712b185c95d9291bd.png)

I was going to explore some of the tools in ColorSchemeTools.jl such as weighted colorschemes to see if they helped, but no need to, now! 😂

---

<div class="post-metadata">

**Author:** ![lazarusA](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lazarusa/32/6571_2.png) [@lazarusA](https://discourse.julialang.org/u/lazarusA)\
**Post date:** [July 2, 2024, 2:35pm UTC](https://discourse.julialang.org/t/custom-divergent-colormap-with-unequal-sides/115484/11 "2024-07-02T14:35:44Z")

</div>

This solution always need us to specified the colorrange in advance, is there a way to update this colorrange to the values that might be in our current view? think of zooming in, where we will have new bounds, because we will be looking at a subset of all values.
