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

Found invalid x-limits in heatmap with xscale=log10 #4320

Open
SimonEnsemble opened this issue Sep 5, 2024 · 6 comments
Open

Found invalid x-limits in heatmap with xscale=log10 #4320

SimonEnsemble opened this issue Sep 5, 2024 · 6 comments
Labels
bug conversions Mainly `convert_arguments` heatmap & image transformation related to `Transformation` objects

Comments

@SimonEnsemble
Copy link

  • Makie v0.20.10
  • [no, Makie won't even compile if I do this. ERROR: LoadError: MethodError: no method matching to_rotation(::Nothing)] can you reproduce the bug with a fresh environment ? (]activate --temp; add Makie)
  • [Mac M1, but Windows gives same error] What platform + GPU are you on?

there is no problem with taking log10 of C nor eps but internally when deciding on xticks, it gives an error.

minimal example:

C = [3.1, 17.6, 100.0]
eps = [0.01, 2.6, 5.4, 8.8]
M = [54.6823  44.1771  41.0193  51.0635
39.2212  32.7295  34.6756  48.9483
42.1372  34.1846  34.422   48.9483]

heatmap(C, eps, M, colormap=:berlin25, axis=(; xscale=log10))

the error:

Found invalid x-limits (-4.15f0, 141.2f0) for scale log10 which is defined on the interval 0.0 .. Inf (open)
Stack trace

Here is what happened, the most recent locations are first:

    error(s::String) @ [error.jl:35](https://github.com/JuliaLang/julia/tree/0b4590a5507d3f3046e5bafc007cacbbfc9b310b/base/error.jl#L35)
    autolimits(ax::Makie.Axis, dim::Int64) @ axis.jl:941
    xautolimits @ axis.jl:953
    reset_limits!(ax::Makie.Axis; xauto::Bool, yauto::Bool, zauto::Bool) @ axis.jl:572
    reset_limits! @ axis.jl:559
    tightlimits! @ helpers.jl:100
    plot!(ax::Makie.Axis, plot::MakieCore.Heatmap{Tuple{Vector{Float32}, Vector{Float32}, Matrix{Float32}}}) @ figureplotting.jl:319
    plot!(fa::Makie.FigureAxis, plot::MakieCore.Heatmap{Tuple{Vector{Float32}, Vector{Float32}, Matrix{Float32}}}) @ figureplotting.jl:313
    _create_plot(::Function, ::Dict{Symbol, Any}, ::Vector{Float64}, ::Vararg{Any}) @ figureplotting.jl:250
    heatmap(::Vector{Float64}, ::Vararg{Any}; kw::@Kwargs{colormap::Symbol, axis::@NamedTuple{xscale::typeof(log10)}}) @ recipes.jl:175
@asinghvi17
Copy link
Member

asinghvi17 commented Sep 5, 2024

Ah, this is because heatmap is a CellGrid()-like plot.

Cell grids are defined by length(xs) == size(zs, 1) + 1, length(ys) == size(zs, 2) + 1, in which the x and y axes encode cell boundaries.

ys[3]|----------|----------|----------|----------|
     |          |          |          |          |
     | zs[1, 2] | zs[2, 2] | zs[3, 2] | zs[4, 2] |
ys[2]|----------|----------|----------|----------|
     |          |          |          |          |
     | zs[1, 1] | zs[2, 1] | zs[3, 1] | zs[4, 1] |
     |          |          |          |          |
ys[1]|----------|----------|----------|----------|
     xs[1]      xs[2]      xs[3]      xs[4]      xs[5]

However, in your case, you've only provided vertex information, where length(xs) == size(zs, 1) , length(ys) == size(zs, 2). This means that Makie has to reinterpret the vertex formulation as a cell grid, where the vertices are at the centers of the cells. You can sort of think of this as a Voronoi tessellation of the set of vertices that are encoded by the grid.

In doing so, Makie goes to -4 in the x axis as the leftmost boundary of this cell grid.

You have two options to solve this:

  1. Provide cell-grid data to heatmap, by defining the leftmost point on your grid as the first point in C (and do the same for eps). This provides you the most control but is a pain in the neck.
  2. Use a vertex-like grid plot, such as surface(C, eps, M; colormap = :berlin25, interpolate = false, shading = NoShading, axis = (; type = Axis, xscale = log10)), which looks like this:

The code that does this is here:

Makie.jl/src/conversions.jl

Lines 303 to 330 in 8617742

################################################################################
# GridBased #
################################################################################
function edges(v::AbstractVector{T}) where T
T_out = float_type(T)
l = length(v)
if l == 1
return T_out[v[1] - 0.5, v[1] + 0.5]
else
# Equivalent to
# mids = 0.5 .* (v[1:end-1] .+ v[2:end])
# borders = [2v[1] - mids[1]; mids; 2v[end] - mids[end]]
borders = T_out[0.5 * (v[max(1, i)] + v[min(end, i+1)]) for i in 0:length(v)]
borders[1] = 2borders[1] - borders[2]
borders[end] = 2borders[end] - borders[end-1]
return borders
end
end
function adjust_axes(::CellGrid, x::RealVector, y::RealVector, z::AbstractMatrix)
x̂, ŷ = map((x, y), size(z)) do v, sz
return length(v) == sz ? edges(v) : v
end
return x̂, ŷ, z
end
adjust_axes(::VertexGrid, x, y, z) = x, y, z

For additional intuition: consider heatmap(rand(4, 3)).

Note that the x axis starts at 0.5, instead of the 1 you might expect, and similar offsets for all of the axes. Hope this helps!
download-6

@ederag
Copy link

ederag commented Sep 8, 2024

Or use a scaling function that extends to the negative side, such as Makie.pseudolog10
or Makie.Symlog10(n) where n is the threshold between linear and logarithmic scaling.
They appear in the xscale documentation.
It gives less control than the approach described above, of course.

C = [3.1, 17.6, 100.0]
eps = [0.01, 2.6, 5.4, 8.8]
M = [54.6823  44.1771  41.0193  51.0635
39.2212  32.7295  34.6756  48.9483
42.1372  34.1846  34.422   48.9483]

heatmap(C, eps, M, colormap=:berlin25, axis=(; xscale=Makie.pseudolog10))

Screenshot_20240908_115807

@ffreyer ffreyer added conversions Mainly `convert_arguments` transformation related to `Transformation` objects heatmap & image labels Sep 14, 2024
@haakon-e
Copy link
Contributor

haakon-e commented Oct 16, 2024

A colleague of mine ran into this issue today in trying to plot data on a time, z grid where z is a stretched grid, e.g. z = [15, 45.06, 75.12]. As it stands, I wish the error message could be more helpful to resolve the issue.

Is there appetite to do some checking for this case when generating an error message? Or at least be more verbose about what's going on, like:

You tried to plot a heatmap with data defined on cell centers. This results in (x/y) bounds -1 to 100, but the (x/y) scale is only valid (some values). Try (something else).

(print rest of error message)

Alternatively, could there be added support for a way to define the extremal cell boundaries manually?

Same issue as: #2615 (comment)

@asinghvi17
Copy link
Member

define the extremal cell boundaries manually?

You can already do that. If you have a matrix of size (n, m), you can pass an x-vector of length n+1 and a y-vector of length m+1 to indicate that they define cell edges and not cell centers.

You can construct them as follows:

julia> Makie.edges(C)
4-element Vector{Float64}:
  -4.150000000000001
  10.350000000000001
  58.8
 141.2

julia> Makie.edges(eps)
5-element Vector{Float64}:
 -1.285
  1.305
  4.0
  7.1000000000000005
 10.5

julia> Cedges = [1e-3, 10.35, 58.8, 141.2]
4-element Vector{Float64}:
   0.001
  10.35
  58.8
 141.2

julia> epsedges = [1e-3, 1.305, 4.0, 7.1, 10.5]
5-element Vector{Float64}:
  0.001
  1.305
  4.0
  7.1
 10.5

maybe use clamp.(Makie.edges(array), xmin, xmax) or so.

Image

@asinghvi17
Copy link
Member

checking for this case when generating an error message

This could only happen in the backend, but maybe a more intricate transform interface would help here. I'm out this weekend but will whip up a prototype next weekend since I want that for GeoMakie as well - then the transform would be the one throwing the error, and would have access to all necessary context.

@haakon-e
Copy link
Contributor

Thanks for the feedback! I like the explicit call to edges + clamp, so might do that for the time being.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug conversions Mainly `convert_arguments` heatmap & image transformation related to `Transformation` objects
Projects
None yet
Development

No branches or pull requests

5 participants