Makie.jl icon indicating copy to clipboard operation
Makie.jl copied to clipboard

Heatmap with log scale colorbar (cscale) ?

Open ederag opened this issue 4 years ago • 6 comments

Reposted from discourse, with updated CairoMakie, figures and link to documentation.

Makie is awesome, looks like everything is doable :slightly_smiling_face:, for instance, this heatmap with log scale color axis (exactly what I wanted):

image

Code for the above figure
let
	# cf. https://github.com/JuliaPlots/Makie.jl/issues/822#issuecomment-769684652
	# with scale argument that is required now
	struct LogMinorTicks end
	
    function MakieLayout.get_minor_tickvalues(
			::LogMinorTicks, scale, tickvalues, vmin, vmax
	)
    	vals = Float64[]
		extended_tickvalues = [
			tickvalues[1] - (tickvalues[2] - tickvalues[1]);
			tickvalues;
			tickvalues[end] + (tickvalues[end] - tickvalues[end-1]);
		]
    	for (lo, hi) in zip(
				@view(extended_tickvalues[1:end-1]),
				@view(extended_tickvalues[2:end])
			)
        	interval = hi-lo
        	steps = log10.(LinRange(10^lo, 10^hi, 11))
        	append!(vals, steps[2:end-1])
    	end
		return filter(x -> vmin < x < vmax, vals)
	end
	
	custom_formatter(values) = map(
		v -> "10" * Makie.UnicodeFun.to_superscript(round(Int64, v)),
		values
	)
	
	x = 10.0.^(1:0.1:4)
	y = 1.0:0.1:5.0
	data = x .* ones(Float64, 1, length(y))
	clims = (1e1, 1e4)  # natural display
	fig = Figure()
	ax, hm = heatmap(fig[1, 1], x, y, log10.(data), colorrange=log10.(clims),
		axis=(;xscale=log10,
			   xminorticksvisible=true,
			   xminorticks=IntervalsBetween(9))
	         )
	cb = Colorbar(fig[1, 2], hm;
		tickformat=custom_formatter,
		minorticksvisible=true,
		minorticks=LogMinorTicks()
	)
	fig
end

But it was a bit involved for a newcomer. Here is how I would have expected it to work:

x = 10.0.^(1:0.1:4)
y = 1.0:0.1:5.0
data = x .* ones(Float64, 1, length(y))
fig = Figure()
ax, hm = heatmap(fig[1, 1], x, y, data,
	axis=(;xscale=log10, 
		   xminorticksvisible=true, xminorticks=IntervalsBetween(9),
		   cscale=log10)
         )
Colorbar(fig[1, 2], hm)

Currently (CairoMakie v0.6.2) the cscale argument is just ignored: image

also tried zscale and read the documentation of course, in particular https://makie.juliaplots.org/stable/examples/layoutables/axis/index.html#log_scales_and_other_axis_scales

Would it make sense to add this cscale argument, for symetry with xscale or yscale ? It has exactly the same meaning: actually plot log10(axis value) on an underlying linear axis, and tweak ticks, minorticks and tick labels.

ederag avatar Oct 24 '21 09:10 ederag

Following https://discourse.julialang.org/t/heatmap-with-log-scale-colorbar-cscale/63018/2, scale=log10 does not work as expected:

let  
	x = 10.0.^(1:0.1:4)
	y = 1.0:0.1:5.0
	data = x .* ones(Float64, 1, length(y))
	fig = Figure()
	cmap = cgrad(:viridis, scale=:log10)
	ax, hm = heatmap(fig[1, 1], x, y, data; colormap=cmap,
		axis=(;xscale=log10,
			   xminorticksvisible=true,
			   xminorticks=IntervalsBetween(9))
	         )
	cb = Colorbar(fig[1, 2], hm;
		minorticksvisible=true,
		minorticks=IntervalsBetween(9),
		scale=log10
	)
	fig
end

image

The issue is that the colormap is not the regular one. For instance, the blue area is smaller in the scaled colormap, and larger in the heatmap. With this test data, chosen especially to emphasize this issue, the gradient should look like the colormap.

ederag avatar Oct 24 '21 09:10 ederag

Any progress here?

markub3327 avatar Jun 13 '22 13:06 markub3327

You're applying the scale twice AFAICT. Creatimg the colorbar without scale should lead to the "correct" colormap showing up.

asinghvi17 avatar Jun 13 '22 14:06 asinghvi17

@asinghvi17 creating the colorbar without scale yields a colorbar without "log scale" ticks, and the colors are not as in the goal image (the first one):

image

code
let  
	x = 10.0.^(1:0.1:4)
	y = 1.0:0.1:5.0
	data = x .* ones(Float64, 1, length(y))
	fig = Figure()
	cmap = cgrad(:viridis, scale=:log10)
	ax, hm = heatmap(fig[1, 1], x, y, data; colormap=cmap,
		axis=(;xscale=log10,
			   xminorticksvisible=true,
			   xminorticks=IntervalsBetween(9))
	         )
	cb = Colorbar(fig[1, 2], hm;
		minorticksvisible=true,
		minorticks=IntervalsBetween(9),
		#scale=log10  # without this, the colorbar ticks are not log spaced
	)
	fig
end

ederag avatar Jun 13 '22 15:06 ederag

Oops, I guess I misinterpreted where the bug was. Which Makie versions are you on? There was a bug which was fixed recently (Makie 0.17.4 afaik)...

asinghvi17 avatar Jun 13 '22 15:06 asinghvi17

The latest: CairoMakie v0.8.5 and Makie 0.17.5 in the manifest.

ederag avatar Jun 13 '22 16:06 ederag

A workaround is to modify the scale and limits of the colorbar axis after creating it. This seems to re-label the axis as desired without also transforming the actual color bar.

x = 10.0.^(1:0.1:4)
y = 1.0:0.1:5.0
data = x .* ones(Float64, 1, length(y))
fig = Figure()
ax, hm = heatmap(fig[1, 1], x, y, log10.(data))
ax.xscale = log10
cb = Colorbar(fig[1, 2], hm)
cb.axis.attributes[:scale][] = log10
cb.axis.attributes[:limits][] = exp10.(cb.axis.attributes[:limits][])

plot_1

SimonWoods avatar Oct 09 '22 21:10 SimonWoods

Unfortunately the workaround doesn't work when values start below 10:

x = 10.0.^(-1:0.1:3)
y = -1.0:0.1:3.0
data = x .* ones(Float64, 1, length(y))
fig = Figure()
ax, hm = heatmap(fig[1, 1], x, y, log10.(data))
ax.xscale = log10
cb = Colorbar(fig[1, 2], hm)
cb.axis.attributes[:scale][] = log10
cb.axis.attributes[:limits][] = exp10.(cb.axis.attributes[:limits][])
fig

reports error:

DomainError with -1.0:
log10 will only return a complex result if called with a complex argument. Try log10(Complex(x)).

Stacktrace:
  [1] throw_complex_domainerror(f::Symbol, x::Float64)
    @ Base.Math ./math.jl:33
  [2] _log(x::Float64, base::Val{10}, func::Symbol)
    @ Base.Math ./special/log.jl:301
  [3] log10
    @ ./special/log.jl:268 [inlined]
  [4] _broadcast_getindex_evalf
    @ ./broadcast.jl:670 [inlined]
  [5] _broadcast_getindex
    @ ./broadcast.jl:643 [inlined]
  [6] getindex
    @ ./broadcast.jl:597 [inlined]
  [7] macro expansion
    @ ./broadcast.jl:961 [inlined]
  [8] macro expansion
    @ ./simdloop.jl:77 [inlined]
  [9] copyto!
    @ ./broadcast.jl:960 [inlined]
 [10] copyto!
    @ ./broadcast.jl:913 [inlined]
 [11] copy

vfonov avatar Jan 04 '23 18:01 vfonov

I encountered the same issue. Is there a fix?

davidschlegel avatar Feb 22 '23 17:02 davidschlegel

https://github.com/MakieOrg/Makie.jl/pull/2493 would fix this.

t-bltg avatar Feb 22 '23 17:02 t-bltg

Great, I"ll wait then until merged.

davidschlegel avatar Feb 22 '23 17:02 davidschlegel

Can be closed (fixed by https://github.com/MakieOrg/Makie.jl/pull/2900).

t-bltg avatar Jul 20 '23 19:07 t-bltg