How to adjust the number and format of DateTime ticks using Plots.jl?

I am trying to plot a time series with Plots.jl. The x axis is in DateTime format, and I want to format the dates and limit the number of ticks.

The following reproducible example serves to show what I am trying to accomplish.

# Load Packages -----------------------------------------
using Dates
using Plots

#= Now we create data for our example. In my case, 
I read a file that has only the `time_range` and the 
`y` values =#
start_time = DateTime("2023-01-01T00:00:00");
end_time = DateTime("2023-01-02T18:00:00");

time_range = collect(start_time:Hour(1):end_time);
y = rand(length(time_range));

plot(time_range,y)

# Up to this point, all works as expected. But then, to modify the `xticks`, I do the following. 
time_ticks = Dates.format.(collect(start_time:Hour(4):end_time),"Ip");
xticks!(time_ticks)

I want to format the x axis so it only shows the hours in AM/PM format. Also, I don’t need every hour in the date range, only a few hours in between are fine. This is OK because the starting day, month, and year for the plot will go in the title.

The command xticks! doesn’t work. I’m given the error message below. I don’t understand the error message, though. Thank you for your help.

ERROR: MethodError: no method matching xticks!(::Vector{String})

Closest candidates are:
  xticks!(::Union{Symbol, Tuple{AbstractVector{T}, AbstractVector{S}}, AbstractVector{T}} where {T<:Real, S<:AbstractString}; kw...)
   @ Plots C:\Users\xxxxx\.julia\packages\Plots\rz1WP\src\shorthands.jl:479 
  xticks!(::AbstractVector{T}, ::AbstractVector{S}; kw...) where {T<:Real, S<:AbstractString}
   @ Plots C:\Users\xxxxx\.julia\packages\Plots\rz1WP\src\shorthands.jl:485 
  xticks!(::Union{Plots.Plot, Plots.Subplot}, ::Union{Symbol, Tuple{AbstractVector{T}, AbstractVector{S}}, AbstractVector{T}} where {T<:Real, S<:AbstractString}; kw...)
   @ Plots C:\Users\xxxxx\.julia\packages\Plots\rz1WP\src\shorthands.jl:487 
  ...
1 Like

You need to pass the ticks first as numbers, then as strings, ie something like xticks!((1,2,3), ("ticklabel1", "ticklabel2", "ticklabel3"))

2 Likes

You can try this:

# (continued)
plot(time_range, y)
t = start_time:Hour(4):end_time
time_ticks = Dates.format.(t,"Ip")
xticks!(Dates.datetime2epochms.(t .- Year(1)), time_ticks)

The code above handles some apparent limitation of xticks!() fuction with dates, as well as the bug: the origin of the internal representation of Date is not the “epoch day”, but it is off by one year

However, if we don’t use the xticks!() function, but plot(; xticks) key argument, it becomes simpler:

plot(time_range, y, xticks=(t, time_ticks))
3 Likes