Makie Axis xticks: passing (datetimes, labels) fails with MethodError

Hello!

I’m trying to change the DateTime xtick label intervals in my Makie plot by manually passing the xticks parameter to Axis as a tuple with two vectors: (datetimes, labels). According to the current xticks docs:

The following is a list of common tick objects.

Numeric:

  • A vector of numbers
  • (…)

DateTime:

  • A vector of DateTimes
  • A tuple with two vectors (datetimes, labels)
  • DateTimeTicks, the default tick finder for datetime ticks

However, with this MWE

using CairoMakie
fig = Figure()
x = DateTime(2025, 1, 1):Day(1):DateTime(2025, 1, 10) 
ax = Axis(fig[1, 1], xticks=(x, string.(x)))
lines!(ax, x, rand(10))

I get:

MethodError: Cannot `convert` an object of type DateTime to an object of type Float64
The function `convert` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  convert(::Type{T}, ::IntervalArithmetic.ExactReal) where T<:Real
   @ IntervalArithmetic ~/.julia/packages/IntervalArithmetic/55mUT/src/intervals/exact_literals.jl:130
  convert(::Type{T}, ::Unitful.Quantity) where T<:Real
   @ Unitful ~/.julia/packages/Unitful/pKTDo/src/conversion.jl:167
  convert(::Type{T}, ::Unitful.Level) where T<:Real
   @ Unitful ~/.julia/packages/Unitful/pKTDo/src/logarithm.jl:46
  ...

Any idea of what I am missing here?

Thanks in advance!


Environment Packages
  [13f3f980] CairoMakie v0.15.8
  [a93c6f00] DataFrames v1.8.1
  [7073ff75] IJulia v1.33.0
  [945b72a4] MarketData v0.15.1
Full Stacktrace
MethodError: Cannot `convert` an object of type Date to an object of type Float64
The function `convert` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  convert(::Type{T}, ::IntervalArithmetic.ExactReal) where T<:Real
   @ IntervalArithmetic ~/.julia/packages/IntervalArithmetic/55mUT/src/intervals/exact_literals.jl:130
  convert(::Type{T}, ::Unitful.Quantity) where T<:Real
   @ Unitful ~/.julia/packages/Unitful/pKTDo/src/conversion.jl:167
  convert(::Type{T}, ::Unitful.Level) where T<:Real
   @ Unitful ~/.julia/packages/Unitful/pKTDo/src/logarithm.jl:46
  ...


Stacktrace:
  [1] setindex!
    @ ./genericmemory.jl:259 [inlined]
  [2] unsafe_copyto!(dest::Memory{Float64}, doffs::Int64, src::Memory{Date}, soffs::Int64, n::Int64)
    @ Base ./genericmemory.jl:158
  [3] unsafe_copyto!
    @ ./genericmemory.jl:137 [inlined]
  [4] _copyto_impl!
    @ ./array.jl:308 [inlined]
  [5] copyto!
    @ ./array.jl:294 [inlined]
  [6] copyto!
    @ ./array.jl:319 [inlined]
  [7] copyto_axcheck!
    @ ./abstractarray.jl:1176 [inlined]
  [8] Array
    @ ./array.jl:621 [inlined]
  [9] convert
    @ ./array.jl:614 [inlined]
 [10] get_tickvalues
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:678 [inlined]
 [11] get_tickvalues
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:610 [inlined]
 [12] get_ticks
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:598 [inlined]
 [13] get_ticks(::Nothing, ticks::Vector{Date}, scale::Function, formatter::Makie.Automatic, vmin::Float64, vmax::Float64)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/dim-converts/dim-converts.jl:77
 [14] (::Makie.var"#LineAxis##22#LineAxis##23"{Observable{Any}})(::Tuple{Float32, Tuple{Float32, Float32}, Bool}, ::Nothing, limits::Tuple{Float64, Float64}, ticks::Vector{Date}, tickformat::Makie.Automatic, scale::Function)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:449
 [15] (::Observables.MapCallback)(value::Any)
    @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:436
 [16] #map!#298
    @ ~/.julia/packages/Makie/Vn16E/src/scenes.jl:191 [inlined]
 [17] map!
    @ ~/.julia/packages/Makie/Vn16E/src/scenes.jl:181 [inlined]
 [18] Makie.LineAxis(parent::Scene, attrs::Attributes)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:445
 [19] Makie.LineAxis(parent::Scene; kwargs...)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:8
 [20] LineAxis
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/lineaxis.jl:6 [inlined]
 [21] initialize_block!(ax::Axis; palette::Nothing)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks/axis.jl:346
 [22] initialize_block!(ax::Axis)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks/axis.jl:154
 [23] _block(T::Type{Axis}, fig_or_scene::Figure, args::Vector{Any}, kwdict::Dict{Symbol, Any}, bbox::Nothing; kwdict_complete::Bool)
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks.jl:405
 [24] _block
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks.jl:321 [inlined]
 [25] #_block#1538
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks.jl:266 [inlined]
 [26] _block(::Type{Axis}, ::GridPosition; kwargs::@Kwargs{xticks::Vector{Date}, yticks::UnitRange{Int64}, ylabel::String})
    @ Makie ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks.jl:260
 [27] _block
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks.jl:251 [inlined]
 [28] #_#1536
    @ ~/.julia/packages/Makie/Vn16E/src/makielayout/blocks.jl:242 [inlined]
 [29] top-level scope
    @ In[39]:8
 [30] eval(m::Module, e::Any)
1 Like

Yeah, the problem is that Makie tries to use the float axis tick logic on your tick object, because at this point it doesn’t yet know you’re trying to use a datetime axis. You could plot the lines first (then the Axis has changed to datetime mode) and then set the ticks.

1 Like

When dealing with equal interval dates like this, a workaround is to space the ticks with integers and label them with strings converted from the date objects

using CairoMakie
using Dates

# Date values only used to build labels
dates = DateTime(2025, 1, 1):Day(1):DateTime(2025, 1, 10)
labels = Dates.format.(dates, "yyyy-mm-dd")

x = 1:10
y = rand(10)

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

# ticks are numeric, labels are strings
ax.xticks[] = (collect(x), labels)
ax.xticklabelrotation[] = pi/4   # 45 degrees

lines!(ax, x, y)

fig

1 Like

In case it was not clear, I believe what @jules meant is that you can do

using CairoMakie
using Dates
fig = Figure()
x = DateTime(2025, 1, 1):Day(1):DateTime(2025, 1, 10) 
ax, plt = lines(fig[1, 1], x, rand(10); axis = (; xticks = x, xticklabelrotation = π/4))

which gives

:information_source: Note: This has only started working two weeks ago (2025-11-22, with Makie v0.24.7).

1 Like

Yeah, that is a bit non-intuitive, but it did the trick. I wonder if this behavior should be documented, though. I’m marking this as resolved, thanks @jules, @technocrat, and @briochemc!

2 Likes