Module Hugin_kit.Scale
Scales.
A scale denotes a normalisation, a function from a domain of data values to the reals that maps the domain onto [0;1], with an inverse where one exists. Ticks chooses the ticks of a scale.
There are three kinds of scales (kind): quantitative scales over floats (linear, log, symlog, pow, custom), temporal scales over instants (time) and categorical scales over category names (band). The first two are continuous.
A scale value is a specification. Each of its properties is either set, by an argument of its constructor or by the functions of specifications, or unset, and the scale means the normalisation its properties give once each unset one takes its default. Specifications combine with merge and imply, and fit sets the domain from observed data.
Normalisation
Continuous scales
A continuous scale has a domain [a;b] with a <= b and a strictly monotone transform T, which its constructor fixes. It normalises x to
N x = (T x - T a) / (T b - T a)
which is exactly 0. at a, 1. at b, and beyond [0;1] for x outside the domain: scales extrapolate. The formula is evaluated without overflow, so this holds on [-.max_float;max_float] too. A domain is constant if its ends' transforms are the same float, T a = T b: always if a = b, and for distinct ends too close for the transform to separate, such as [1e308;1.0000000000000002e308] on a log scale. A constant domain normalises every value that is not missing to 0.5. A reversed scale normalises x to 1 - N x, and a clamped scale clamps the result into [0;1], after reversing. The transforms are:
linear:T x = x;login baseb:T x = log x / log b, defined forx > 0;symlogwith constantc:T x = sign x * log1p (|x| / c);powwith exponente:T x = sign x * |x / m| ** e, wheremis the greater magnitude of the domain's ends, or1.if both are0.;custom: the caller'sforwardfunction.
A temporal scale normalises instants by the same formula with T the identity, the differences t - a and b - a taken exactly in nanoseconds and each rounded once to a float. Its domain is constant iff a = b.
Categorical scales
A band scale over n categories with padding p divides [0;1] into n steps of width 1 / (n + p), in domain order, with p / (2 (n + p)) left over at each end, and normalises the category c_i at index i of its domain, from 0, to the centre of its step
N c_i = (i + ((1 + p) / 2)) / (n + p)
or to 1 - N c_i if the scale is reversed. A band is its step narrowed by p: its width is (1 - p) / (n + p) (bandwidth).
Missing values
A value is missing for a scale, and normalises to nan, if it is nan or infinite, not positive on a log scale, a value at which a custom transform is not finite, or a string that names no category of a band scale.
Types
type 'd tThe type for scales over domain values of type 'd.
type _ kind = The type for kinds of scales. The kind of a scale is fixed by the type of its domain values.
type categories = The type for the categories of band scales. Wherever a scale takes or returns a category, a labelled category is named by its label and an indexed one by its integer as string_of_int writes it. Arrays are copied where they enter or leave a scale.
type _ domain = The type for domains, one case per kind, so let Floats (a, b) = domain s is exhaustive.
Constructors
A constructor fixes the kind of its scale and, for a quantitative scale, its transform. Each of its optional arguments is a property: given, it is set; omitted, it is unset and counts as the default stated here. The properties are:
name, the name of the scale. Unset, the scale specifies the default scale of the role that reads it.domain, the domain, final as given:fitneither widens nor rounds a set domain. A continuous domain(a, b)must have finite ends witha <= b. Unset, the domain is the default its constructor states untilfitsets it.nice, whetherfitrounds a fitted domain outward (Nice domains). Unset counts astrue.zero, whetherfitwidens a fitted domain to include0.(Nice domains). Unset counts asfalse.clamp, whether normalised values are clamped into [0;1]. Unset counts asfalse.reverse, whether normalised values run from1to0. Unset counts asfalse.stepped, whether the roles reading a quantitative scale take a range that is constant between consecutive levels: the normalised ends of the domain and the normalised ticks between them that the figure drawing the scale chooses. A value takes the range at the middle of the interval between consecutive levels that holds it, each interval holding its lower level and the last also its upper one, and a value beyond the domain the range of the step at its nearer end. It leavesnormalizeunchanged. Unset counts asfalse.ticks, the guide values that the ticks, legend entries and colour bars of the scale show, in place of thoseTicks.choosepicks, and that stepped ranges step at: those of its values that lie in the domain and are not missing for the scale, each once, without minor ticks (Ticks.of_values). On a band scale they limit the labels of an axis and the entries of a legend to the categories named. The array is copied in and out. Unset, the figure drawing the scale chooses them.notation, the notation tick labels of a quantitative scale are written in (Labels). Unset, they follow the rules for quantities and logarithms.scheme, the colours that colour roles paint with. A continuous scale colours a value withScheme.colorof its normalised value, and a band scale overncategories colours categoryiwith colouriofScheme.colors n, whether or not it is reversed: reversing a band scale moves its categories and keeps their colours. Unset, a role takes its theme's scheme on a continuous scale and its theme's palette on a band scale.areas, the symbol areas in square points that the size role draws at the normalised values0and1. Both must be finite and not negative. Unset, the role takes its theme's areas.symbols, the symbols that the symbol role draws the categories of a band scale with: categoryiwithsymbols.(i mod k)forksymbols. The array must not be empty, and is copied in and out. Unset, the role takes its theme's set for the way the mark paints its symbols.dashes, the dash patterns that the dash role draws the categories of a band scale with: categoryiwithdashes.(i mod k)forkpatterns. The array must not be empty, and is copied in and out. Unset, the role takesDash.all.unknown, the colour that colour roles paint missing values with. Unset, missing values draw no paint.
Constructors raise Invalid_argument if a property breaks the constraint stated for it.
val linear :
?name:string ->
?domain:(float * float) ->
?nice:bool ->
?zero:bool ->
?clamp:bool ->
?reverse:bool ->
?stepped:bool ->
?ticks:float array ->
?notation:Number.notation ->
?scheme:Scheme.t ->
?areas:(float * float) ->
?unknown:Hugin_gg.Color.t ->
unit ->
float tlinear () is a linear scale. Its default domain is [0;1].
val log :
?base:float ->
?name:string ->
?domain:(float * float) ->
?nice:bool ->
?clamp:bool ->
?reverse:bool ->
?stepped:bool ->
?ticks:float array ->
?notation:Number.notation ->
?scheme:Scheme.t ->
?areas:(float * float) ->
?unknown:Hugin_gg.Color.t ->
unit ->
float tlog ~base () is a logarithmic scale in base base, which defaults to 10. and must be finite and greater than 1.. Its domain must be positive, and its default domain is [1;base]. It takes no zero, since 0. is missing on a log scale.
The rules for ticks, tick labels and minor ticks (Ticks) that name an integer base mean a base that is an integer from 2 to 16; a greater integer base, whose powers hold more multiples than an axis can show, follows the rules of other bases.
val symlog :
?constant:float ->
?name:string ->
?domain:(float * float) ->
?nice:bool ->
?zero:bool ->
?clamp:bool ->
?reverse:bool ->
?stepped:bool ->
?ticks:float array ->
?notation:Number.notation ->
?scheme:Scheme.t ->
?areas:(float * float) ->
?unknown:Hugin_gg.Color.t ->
unit ->
float tsymlog ~constant () is a symmetric logarithmic scale with constant constant, which defaults to 1. and must be finite and positive. Its default domain is [0;1].
val pow :
exponent:float ->
?name:string ->
?domain:(float * float) ->
?nice:bool ->
?zero:bool ->
?clamp:bool ->
?reverse:bool ->
?stepped:bool ->
?ticks:float array ->
?notation:Number.notation ->
?scheme:Scheme.t ->
?areas:(float * float) ->
?unknown:Hugin_gg.Color.t ->
unit ->
float tpow ~exponent () is a power scale with exponent exponent, which must be finite and positive. Its default domain is [0;1].
val custom :
transform:string ->
forward:(float -> float) ->
inverse:(float -> float) ->
?name:string ->
?domain:(float * float) ->
?nice:bool ->
?zero:bool ->
?clamp:bool ->
?reverse:bool ->
?stepped:bool ->
?ticks:float array ->
?notation:Number.notation ->
?scheme:Scheme.t ->
?areas:(float * float) ->
?unknown:Hugin_gg.Color.t ->
unit ->
float tcustom ~transform ~forward ~inverse () is a quantitative scale with transform forward, whose inverse is inverse, and which transform names in printed forms. Its default domain is [0;1], its nice domains are those of a linear scale over the same domain, and its tick candidates the decimal steps of one (Ticks). forward must be strictly monotone where it is finite, inverse must be its inverse there, and neither may read mutable state; results are unspecified otherwise. A set domain must have ends that are not missing, where forward is finite. An unfitted custom scale whose default ends are missing, such as a logit, normalises every value to nan.
val time :
?name:string ->
?domain:(Time.t * Time.t) ->
?nice:bool ->
?clamp:bool ->
?reverse:bool ->
?tz_offset_s:Time.tz_offset_s ->
?ticks:Time.t array ->
?scheme:Scheme.t ->
?unknown:Hugin_gg.Color.t ->
unit ->
Time.t ttime () is a temporal scale whose ticks and nice domains follow the calendar at the offset tz_offset_s (see Time.tz_offset_s); unset counts as 0. Its default domain is the first day of 1970 UTC, from Time.epoch to one day later.
val band :
?name:string ->
?domain:categories ->
?padding:float ->
?reverse:bool ->
?wrap:int ->
?ticks:string array ->
?scheme:Scheme.t ->
?symbols:Symbol.t array ->
?dashes:Dash.t array ->
?unknown:Hugin_gg.Color.t ->
unit ->
string tband () is a categorical scale (Categorical scales) with:
padding, the fraction of each step that is not band, in [0;1];1.makes bands points. Unset counts as0..wrap, at least1, the number of panels per row when a facet channel reads the scale. Unset, panels do not wrap.
Its default domain is Labels [||], without categories. A domain's labels must be distinct and its integers strictly increasing.
Properties
equal_kind k k' is Some Type.Equal iff k and k' are the same kind.
bandwidth s is the width of the bands of s in normalised units, (1 - p) / (n + p) for n categories and padding p, and 0. if s has no category.
length s is the length of the domain of s in the units its normalisation divides: |T b - T a| for a continuous domain [a;b] and the transform T (Continuous scales), in seconds on a temporal scale, and n + p on a band scale of n categories and padding p, the steps that [0;1] holds (Categorical scales), or 0. without categories. It is infinity if the difference overflows and nan if an end of the domain is missing.
symbols s is the symbols of s, if set, as a fresh array.
dashes s is the dash patterns of s, if set, as a fresh array.
val notation : float t -> Number.notation optionnotation s is the notation of the tick labels of s, if set.
val unknown : 'd t -> Hugin_gg.Color.t optionunknown s is the colour of the missing values of s, if set.
type transform = The type for the transforms of quantitative scales (Continuous scales). A custom transform is known by its name.
val tz_offset_s : Time.t t -> Time.tz_offset_stz_offset_s s is the offset of the calendar of s, 0 if unset.
Normalising and inverting
normalize s x is the normalised value of x on s (Normalisation), nan iff x or an end of the domain of s is missing for s; only the default domain of a custom scale can have a missing end. normalize s may be applied once and reused: the partial application computes what depends on s alone.
normalize_index s k is normalize s c for the category c at the index k of the domain of s, and nan if k is not an index of the domain. Like normalize s, normalize_index s may be applied once and reused.
invert s u is the value that s normalises to u:
- on a continuous scale,
Some xfor thexwithnormalize s x = uup to rounding,ufirst clamped into [0;1] ifsclamps; on a constant domain [a;b],Some afor every finiteu. It isNoneifuis not finite, ifxis missing forsor, for an instant, ifxis not representable. Instants are rounded to the nearest nanosecond. - on a band scale,
Some cfor the categorycwhose step containsu, the earlier in domain order on the boundary of two steps, andNoneif no step containsu: in the outer padding, outside [0;1], or without categories.
Time intervals
Temporal ticks and nice domains fall on the boundaries (Time.interval) of one of these intervals, finest first:
1,2and5times10^knanoseconds forkfrom0to8;1,5,15and30seconds, and as many minutes;1,3,6and12hours;1and2days, and as many weeks;1,3and6months;1,2,5,10,20,50and100years, then every greater1,2or5times a power of ten years.
To compare durations, a month counts 30 days and a year 365 days; the boundaries themselves are those of the calendar.
Specifications and fitting
Nice domains
zero and nice change fitted domains only. zero widens a domain to include 0. if 0. is not missing for the scale: never on a log scale. nice then rounds the ends of a domain that is not constant outward:
- on linear, pow and custom scales, to multiples of the step
m × 10^knearest by ratio tot, a tenth of the domain's length, repeated until that step stops changing, at most ten times. Withtwrittenf × 10^k,1 <= f < 10,mis10iff >= √50,5iff >= √10,2iff >= √2and1otherwise. For the domain [a;b],tis(b -. a) /. 10., or twice(b /. 2. -. a /. 2.) /. 10.ifb -. aoverflows. A multiple is the float nearesti × m × 10^kfor its integer indexi, so3 × 0.1is0.3; - on log scales, to integer powers of the base;
- on symlog scales of constant
c, an end farther thancfrom zero to a power of ten or its negation, or tocor-cif that power is withincof zero, and an end withincof zero to0.,cor-c; - on temporal scales, to boundaries of the time interval whose duration is nearest by ratio to a tenth of the domain's length, the coarser of two equally near, repeated until that interval stops changing, at most ten times.
On every kind, an end whose rounded value is not finite, is missing for the scale or, for an instant, is not representable stays as it is.
type property = The type for the properties of scales. Each case stands for the constructor argument pp_property names, and Transform for the constructor itself with its base, constant, exponent or functions.
sets p s is true iff s sets p. Every constructor sets Transform.
merge s s' is Ok m if no property is set to different values in s and s', where m sets each property that s or s' sets to its value there, and Error p otherwise, for the first such property p in the order of property. Values compare as equal compares them, so two custom transforms differ unless their functions are physically equal, and labelled and indexed categories always differ. merge is commutative and associative on its successes, and merge s s is Ok s.
imply i s is s with each property that s leaves unset taken from i, except the name, which imply keeps from s or keeps absent. Since every constructor sets the transform, the transform is that of s. A domain that i sets is taken only if it satisfies the constraints the constructor of s states on domains.
missing s x is true where an element of x is missing for s, and has the shape and placement of x. Elements are converted to floats first, so an integer beyond 253 is rounded, and only the transform of s is read. It is computed where x lies, except on a custom scale, whose transform is an OCaml function: x is then read to the host, and the mask computed there is placed beside x.
Raises Invalid_argument if x has a complex or boolean dtype.
fit observed s is s with nice and zero unset and its domain set to:
- the domain of
s, if set; - otherwise, for
Some (Floats (lo, hi)), [lo;hi], the hull of the observed values, widened byzeroand rounded bynice(Nice domains); - for
Some (Instants (lo, hi)), the hull [lo;hi] of the observed instants, rounded bynice; - for
Some (Categories c),c; - for
None, when nothing was observed, the default domain ofs.
Once the domain is set nice and zero have no effect, so the result normalises as s does with that domain, and fitting it again changes nothing: fit o (fit o' s) is fit o' s.
Raises Invalid_argument if the domain of s is unset and observed breaks the constraints the constructor of s states on domains.
with_domain d s is s with its domain set to d.
Raises Invalid_argument if d breaks the constraints the constructor of s states on domains.
pp_property ppf p formats p in lowercase, as the argument that sets it is named, such as domain or tz_offset_s.
Comparing and formatting
equal s s' is true iff s and s' set the same properties to equal values: floats by Float.equal, instants by Time.equal, colours by Hugin_gg.Color.equal, schemes by Scheme.equal, arrays element by element, symbols by Symbol.equal, dash patterns by Dash.equal, and the functions of custom transforms physically. A property unset differs from the same property set to its default.