Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heatmap with log scale colorbar (cscale) ? #1405

Closed
ederag opened this issue Oct 24, 2021 · 12 comments
Closed

Heatmap with log scale colorbar (cscale) ? #1405

ederag opened this issue Oct 24, 2021 · 12 comments

Comments

@ederag
Copy link

ederag commented Oct 24, 2021

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

Makie is awesome, looks like everything is doable 🙂,
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
Copy link
Author

ederag commented Oct 24, 2021

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.

@markub3327
Copy link

Any progress here?

@asinghvi17
Copy link
Member

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

@ederag
Copy link
Author

ederag commented Jun 13, 2022

@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

@asinghvi17
Copy link
Member

asinghvi17 commented Jun 13, 2022

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)...

@ederag
Copy link
Author

ederag commented Jun 13, 2022

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

@SimonWoods
Copy link

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

@vfonov
Copy link

vfonov commented Jan 4, 2023

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

@davidschlegel
Copy link

I encountered the same issue. Is there a fix?

@t-bltg
Copy link
Collaborator

t-bltg commented Feb 22, 2023

#2493 would fix this.

@davidschlegel
Copy link

Great, I"ll wait then until merged.

@t-bltg
Copy link
Collaborator

t-bltg commented Jul 20, 2023

Can be closed (fixed by #2900).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants