Skip to main content

Time series

Lilaq has built-in support for plotting time series with datetime values. In addition to arrays of float values, most plotting functions also accept an array of datetimes as coordinate inputs.

#lq.diagram(
title: [Size of the Lilaq source code],
ylabel: [Size in kilobyte],
xlabel: [Time],
lq.plot(
(
datetime(year: 2025, month: 3, day: 14),
datetime(year: 2025, month: 4, day: 2),
datetime(year: 2025, month: 5, day: 14),
datetime(year: 2025, month: 7, day: 8),
datetime(year: 2025, month: 8, day: 27),
),
(254, 265, 295, 301, 330)
)
)

In order to adjust the viewing limits, diagram.xlim and diagram.ylim also accept datetimes.

When a diagram contains a plot that uses datetimes along one axis, the scale along for this axis is changed to a datetime scale that activates the adaptive tick locator designed for finding appropriate datetime locations.

Formatting options

By default, Lilaq tries to find a good distance between axis ticks which can be in years, months, days, hours, minutes, or seconds. The dates and times are then displayed in a space-saving manner which can be configured in various ways.

Supplying a fixed format string

One way to configure the formatter is through a fixed datetime format string or a function datetime => content.

#lq.diagram(
xaxis: (
format-ticks: lq.tick-format.datetime.with(
format: "[year]/[month]",
),
tick-args: (density: 80%)
),
lq.plot(
(
datetime(year: 2025, month: 1, day: 2),
datetime(year: 2025, month: 4, day: 2),
datetime(year: 2025, month: 8, day: 14),
),
(2, 3, 1)
)
)

Such formats are often difficult to accommodate on an xx-axis due to the length of tick labels. If using the smart formatter (see next section) is no option, you can instead rotate the labels.

#show: lq.show_(
lq.tick-label.with(kind: "x"),
rotate.with(-90deg, reflow: true)
)

#lq.diagram(
xaxis: (
format-ticks: lq.tick-format.datetime.with(
format: "[year]/[month]",
),
),
..
)

Modifying the component formats

By default, tick-format.datetime.format is set to tick-format.datetime-smart-format. The smart formatter removes redundancies and produces short tick labels. It does so by

  • only displaying the leading component of the changing date (e.g., the month if the ticks are placed on the first of every (other) month),
  • replacing the first month/day/hour etc. of a year/month/day etc. by the year/month/day (through the tick-format.datetime-smart-first element),
  • and adding any missing information as an offset to the end of the axis (through the tick-format.datetime-smart-offset element).

Period formats

For example, we can use a set rule to change how months are displayed from short names to long ones.

#show: lq.tick-format.set-datetime-smart-format(
month: "[month repr:long]"
)

The datetime-smart-format element has a field for each period: year, month, day, hour, minute, and second. All accept a datetime format string or a function with the signature datetime => content.

#show: lq.tick-format.set-datetime-smart-format(
day: "[weekday repr:short]"
)

The first of a period

Note that in the last snippet "January" has been replaced by "2025". You can disable this behaviour by setting tick-format.datetime-smart-format.smart-first to false.

#show: lq.tick-format.set-datetime-smart-format(
smart-first: false
)

What is displayed for the first of a period can be configured through the tick-format.datetime-smart-first element. Let us turn the smart-first back on and change what is displayed for the first month, i.e., January, to the last two digits of the year:

#show: lq.tick-format.set-datetime-smart-first(
month: "[year repr:last_two]",
)

In some cases, it can be nice to show the month name below the first day of the month. This can also be achieved by making use of tick-format.datetime-smart-first.

#show: lq.tick-format.set-datetime-smart-first(day: "[day]\n[month repr:short]")

#lq.diagram(
width: 10cm,
lq.plot(
(
datetime(year: 2011, month: 1, day: 1),
datetime(year: 2011, month: 3, day: 4)
),
(1, 2)
)
)

The same can be done for month: "[month repr:short]\n[year]" etc.

Axis offset

In order to complete the datetime information on an axis, the offset shows the missing components of a date or time. For example, if the axis only shows days, the offset tells the year and month.

#lq.diagram(
xscale: "datetime",
height: 20pt,
yaxis: none,
xlim: (
datetime(year: 2025, month: 3, day: 14),
datetime(year: 2025, month: 3, day: 19),
)
)

The offset is configured through the element tick-format.datetime-smart-offset. Let us change the case where the offset needs to display the date up to the month.

#show: lq.tick-format.set-datetime-smart-offset(
month: "[year] [month repr:long]",
)

Of course, it is always possible to override the offset entirely by setting axis.offset to some content value.

Internationalization/Localization

Unfortunately, the built-in display method of the datetime type does not support localization and can only display the month names in English. In order to overcome this problem, you can use a function like the following to format a month:

#let french-months = (
"Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
"Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
)
#show: lq.tick-format.set-datetime-smart-format(
month: dt => french-months.at(dt.month() - 1),
)