Histogram of dates in Plots.jl

I’ve come across this problem many times, but I can’t find any mentions of it anywhere on here or the Plots repo. Consider:

julia> using Plots

julia> histogram(rand(Date(2000, 1, 1):Day(1):Date(2001, 1, 1), 100))

image

Here, Plots uses Dates.value(date) on the x-axis, which means one has to be pretty good at mental arithmetic to interpret what the graph actually shows. I’ve tried using xformatter = x -> string(Date(0) + Day(x)) but to no avail (yes, I’ve also forgotten which function gives a Date from the Dates.value of a date, something I asked Jacob a long time ago on Slack…)

Are there issues related to this that I’ve missed? And does anybody have an idea for a workaround?

Relatedly, I just realized that histogram entirely ignores xformatter and yformatter kwargs:

julia> histogram(rand(1_000:2_000, 100), yformatter = x -> x/1_000)

image

but

julia> plot(rand(1_000:2_000, 100), yformatter = x -> x/1_000)

image

This seems to work (no bells and whistles):

using Dates, Plots; gr()

d1, d2 = Date(2000, 1, 1),  Date(2001, 1, 1)
d = rand(d1:Day(1):d2, 100)
de = Dates.date2epochdays.(d)
xb = range(Dates.date2epochdays(d1), stop = Dates.date2epochdays(d2), length=5)
xd = Dates.epochdays2date.(round.(Int,xb))

histogram(de, bins = xb, xticks=false, label="Dates histogram")
plot!(xticks=(xb,xd), ylims=(0,40))

Plots_histogram_of_dates

1 Like

Thanks, although it is of course severly limiting if one has to specify bins manually…

If one knows the exact algorithm used by histogram(), one could set the manual value in workaround above equal to the automatic value.
This could be obtained from a command like:

nbins = length(fit(Histogram,de).edges[1]) - 1

Actually I’ve found an easier workaround: while histogram ignores formatters, they can be added to an existing histogram if it is plotted again. So:

plot(histogram(rand(Date(2000, 1, 1):Day(1):Date(2001, 1, 1), 100)),
    xformatter = x -> Dates.epochdays2date(x+365))

image

(Thanks for reminding me of the epochdays2date function!)

2 Likes

@nilshg, could you please verify the plot posted as the dates displayed run back to 1998 while the input data are from 2000 onward.

Indeed - it looks as though Dates.value is offset by one year from the epochdays2date

julia> Dates.value(Date(2000, 1, 1))
730120

julia> Dates.epochdays2date(730120)
1999-01-01

so above one needs Dates.epochdays2date(x+365) to get the right answer

1 Like

I’m pretty sure I ran into this offset by 1 year issue quite a while ago. I ended up defining a conversion function that adjusted by the 1 year at the time.

That code has long since gone by the wayside.