Skip to content

Units

AlgebraOfGraphics supports input data with units, currently Unitful.jl and DynamicQuantities.jl have extensions implemented.

Let's first create a unitful version of the penguins dataset:

julia
using AlgebraOfGraphics
using CairoMakie
using Unitful
using DataFrames

df = DataFrame(AlgebraOfGraphics.penguins())
df.bill_length = df.bill_length_mm .* u"mm"
df.bill_depth = uconvert.(u"cm", df.bill_depth_mm .* u"mm")
df.flipper_length = df.flipper_length_mm .* u"mm"
df.body_mass = df.body_mass_g .* u"g"
select!(df, Not([:bill_length_mm, :bill_depth_mm, :flipper_length_mm, :body_mass_g]))

first(df, 5)

\begin{tabular}{r|ccccccc} & species & island & sex & bill_length & bill_depth & flipper_length & body_mass\ \hline & String & String & String & Quantity… & Quantity… & Quantity… & Quantity…\ \hline 1 & Adelie & Torgersen & male & 39.1 mm & 1.87 cm & 181 mm & 3750 g \ 2 & Adelie & Torgersen & female & 39.5 mm & 1.74 cm & 186 mm & 3800 g \ 3 & Adelie & Torgersen & female & 40.3 mm & 1.8 cm & 195 mm & 3250 g \ 4 & Adelie & Torgersen & female & 36.7 mm & 1.93 cm & 193 mm & 3450 g \ 5 & Adelie & Torgersen & male & 39.3 mm & 2.06 cm & 190 mm & 3650 g \ \end

When we plot columns with units, the units are automatically appended to the respective labels:

julia
spec = data(df) *
    mapping(:bill_length, :bill_depth, color = :body_mass) *
    visual(Scatter)
draw(spec)

Labels are separate from units, so we can relabel without affecting the unit suffixes:

julia
spec = data(df) *
    mapping(:bill_length => "Bill length", :bill_depth => "Bill depth", color = :body_mass => "Body mass") *
    visual(Scatter)
draw(spec)

We can choose a different display unit per scale via the unit scale property:

julia
draw(
    spec,
    scales(
        X = (; unit = u"cm"),
        Y = (; unit = u"mm"),
        Color = (; unit = u"kg")
    )
)

If we plot different units on the same scale, all the units have to be dimensionally compatible and will be auto-converted to the same unit.

julia
layer1 = data(df) * mapping(:bill_length, color = direct("Bill length")) * visual(Density)
layer2 = data(df) * mapping(:bill_depth, color = direct("Bill depth")) * visual(Density)

draw(layer1 + layer2)

AlgebraOfGraphics will complain if we try to plot dimensionally incompatible units on the same scale:

julia
layer1 = data(df) * mapping(:bill_length, color = direct("Bill length")) * visual(Density)
layer2 = data(df) * mapping(:body_mass, color = direct("Body mass")) * visual(Density)

draw(layer1 + layer2)
┌ Warning: Assignment to `layer1` in soft scope is ambiguous because a global variable by the same name exists: `layer1` will be treated as a new local. Disambiguate by using `local layer1` to suppress this warning or `global layer1` to assign to the existing global variable.
└ @ units.md:72
┌ Warning: Assignment to `layer2` in soft scope is ambiguous because a global variable by the same name exists: `layer2` will be treated as a new local. Disambiguate by using `local layer2` to suppress this warning or `global layer2` to assign to the existing global variable.
└ @ units.md:73
DimensionError: 2700.0 g and 32.1 mm are not dimensionally compatible.

In the next example, we make a facet plot with wide data, and even though there are two y axes, there's just one underlying Y scale being fit. Therefore, both columns get unit-converted:

julia
spec_wide = data(df) *
    mapping(:sex, [:bill_length => "Bill length", :bill_depth => "Bill depth"], layout = dims(1)) *
    visual(Violin)

draw(spec_wide)

Again, we can force a different display unit via the scale options.

julia
draw(spec_wide, scales(Y = (; unit = u"cm"), Layout = (; legend = false)))