gerbil/axis
Types
An axis describe how data can be plotted in a chart. There’s many different kind of axes, which one you pick will depend on the visualisations you want to build:
int,float: to display numerical data.timestamp,date: to display time series.categorical: for data that is not described by some numeric variable.
By default, an axis will automatically pick a number of ticks to show, how to print labels, and the minimum and maximum values to display. If needed, you can also change all of these options:
min,infer_min,max,infer_max: to change how the axis bounds are picked.show_labels,hide_labels: to change if and how labels are be displayed.ticks,infer_ticks: to decide which values should have a tick on an axis.
pub opaque type Axis(value)
Values
pub fn categorical() -> Axis(value)
Creates a new axis used to display categorical values.
By default this axis has no labels: categorical axes can be used to plot any Gleam value so you have to specify how those values can be turned into strings using the
show_labelsfunction.
This axis is a good fit when you want to show values that are not described by some numeric variable, like the names of volleyball teams, month of the year, days of the week, or the names of programming languages.
let programming_language =
axis.categorical()
|> axis.show_labels(programming_language_to_string)
let github_stars =
axis.int()
|> axis.min(0)
chart.new(x: programming_language, y: github_stars)
|> chart.add(chart.vertical_bars([], [
chart.bar(Gleam, 0, 21_900, []),
chart.bar(Elixir, 0, 26_600, []),
chart.bar(Erlang, 0, 12_300, []),
]))
pub fn date() -> Axis(calendar.Date)
Create an axis that is used to display Date values.
Much like timestamp, this axis is a good fit when you want
to show data changing over time when precise timestamp measurements are not
an option.
pub fn float() -> Axis(Float)
Creates an axis that is used to display Float values.
This axis is a good fit when you want to show numeric floating point data like the global average temperature over years, an height, or the average rating of a movie.
let average_rating =
axis.float()
|> axis.max(5.0)
let movie =
axis.categorical()
|> axis.show_labels(fn(title) { title })
chart.new(x: movie, y: average_rating)
|> chart.add(
chart.vertical_bars([], [
chart.bar("Gerbils", 0.0, 4.5, []),
chart.bar("Gerbils 2", 0.0, 3.0, []),
chart.bar("Gerbils 3-D", 0.0, 3.2, []),
chart.bar("Gerbils Revenge", 0.0, 1.2, []),
]),
)
pub fn infer_max(axis: Axis(value)) -> Axis(value)
Let the axis figure out which value is the maximum, based on what is plotted in the chart this is used in.
This is useful if you don’t know the range of values you’re dealing with in advance.
pub fn infer_min(axis: Axis(value)) -> Axis(value)
Let the axis figure out which value is the minimum, based on what is plotted in the chart this is used in.
This is useful if you don’t know the range of values you’re dealing with in advance.
pub fn infer_ticks(
axis: Axis(value),
preferred_count preferred_count: Int,
) -> Axis(value)
The axis will figure out how to display “pleasant” ticks automatically based on the range of its values.
This takes an indication of how many ticks you’d like to see, but there’s no guarantee that amount of ticks is going to be displayed, the axis might decide to show slightly more, or slightly less ticks!
pub fn int() -> Axis(Int)
Creates an axis that is used to display Int values.
This axis is a good fit when you want to show numeric integer data like the number of stars of GitHub repos, the points scored by a sports team, or the population of a city.
let year = axis.int()
let population = axis.int()
chart.new(x: year, y: population)
|> chart.add(
chart.line([], [
chart.point(2001, 90_951, []),
chart.point(2010, 97_056, []),
chart.point(2015, 96_758, []),
chart.point(2020, 96_520, []),
chart.point(2024, 95_730, []),
]),
)
pub fn max(axis: Axis(value), max: value) -> Axis(value)
Sets a fixed maximum for an axis, points above this maximum will not be displayed.
Categorical axes do not define an order on their values, so this function has no effect on those.
pub fn min(axis: Axis(value), min: value) -> Axis(value)
Sets a fixed minimum for an axis, points below this minimum will not be displayed.
Categorical axes do not define an order on their values, so this function has no effect on those.
pub fn show_labels(
axis: Axis(value),
label: fn(value) -> String,
) -> Axis(value)
Show the axis’ labels using the given function. The function takes as input the value for which we want to display a label.
pub fn ticks(
axis: Axis(value),
with next: fn(value) -> value,
) -> Axis(value)
Defines how ticks are generated for this axis. This accepts a function that, given the value of a tick, returns the value the next tick should have. Ticks will be generated starting from the axis’ minimum value, and will stop at the axis’ maximum value.
Say I have an axis displaying Float temperatures and I want to show a tick
every 5 degrees:
axis.float()
|> axis.ticks(fn(previous) { previous +. 5.0 })
This way the axis will have a tick every 5 degrees, starting from the minimum temperature. Most of the times you will also want to set the minimum value:
axis.float()
|> axis.min(0.0)
|> axis.ticks(fn(previous) { previous +. 5.0 })
This axis will have a tick at 0.0, 5.0, 10.0, … until the maximum value is reached.
pub fn timestamp() -> Axis(timestamp.Timestamp)
Create an axis that is used to display Timestamp values.
By default this axis has no labels: correctly displaying a
Timestampis highly dependent on locale, and timezone; things this library cannot guess for you! If you want to show labels you can use theshow_labelsfunction implementing whatever logic makes the most sense for your domain.
This axis is a good fit when you want to show data series changing over time like the chance of rainfall over the hours of a day, the sales of a product over a quarter, or the temperature of a room over a day.
// Remember we have to decide how to display labels: here I'm producing a
// tick every hour, starting from 8am. I decided to just show the hour of
// the day: that's a good enough granularity for the data I'm dealing with.
let time =
axis.timestamp()
|> axis.show_labels(fn(timestamp) {
let italy_offset = duration.hours(2)
let #(_date, time) = timestamp.to_calendar(timestamp, italy_offset)
int.to_string(time.hours)
|> string.pad_start(to: 2, with: "0")
})
|> axis.min(time_08_00)
|> axis.ticks(timestamp.add(_, duration.hours(1)))
let living_room_temperature =
axis.float()
|> axis.min(0.0)
chart.new(x: time, y: living_room_temperature)
|> chart.add(
chart.line([], [
chart.point(time_08_05, 17.0, []),
chart.point(time_12_00, 20.1, []),
chart.point(time_17_30, 22.4, []),
chart.point(time_19_10, 20.5, []),
chart.point(time_22_00, 18.9, []),
]),
)