Public API for plotje -- composable plotting in Clojure.
Public API for plotje -- composable plotting in Clojure.
(->pose x)(->pose x caller)Lift the input to a pose. The first atomic step of the pipeline. Polymorphic on input:
pose-kind (validated, *config*
captured, Kindly auto-render metadata attached); idempotent on
input that already carries the metadata, so repeated lifts are
cheap;:data set and no mapping, run through
prepare-pose so the Kindly metadata is attached.Throws on nil or non-collection scalars. Use (pj/pose) for an
explicit empty leaf instead of passing nil.
The optional caller argument names the public-facing function
shown in error messages, so users see "pj/lay-point requires
data..." rather than an internal helper name. Defaults to
"pj/->pose".
(->pose data) -- raw dataset becomes a leaf pose(->pose pose) -- already a pose; idempotent lift(->pose {:x :a :y :b}) -- a mapping becomes a leaf carrying it,
with no data, to be completed by pj/with-data or by the pose it
is placed inLift the input to a pose. The first atomic step of the pipeline.
Polymorphic on input:
- a pose-shaped map flows through `pose-kind` (validated, `*config*`
captured, Kindly auto-render metadata attached); idempotent on
input that already carries the metadata, so repeated lifts are
cheap;
- raw data (a dataset, vector of row maps, or column map) becomes
a leaf pose with `:data` set and no mapping, run through
`prepare-pose` so the Kindly metadata is attached.
Throws on nil or non-collection scalars. Use `(pj/pose)` for an
explicit empty leaf instead of passing nil.
The optional `caller` argument names the public-facing function
shown in error messages, so users see "pj/lay-point requires
data..." rather than an internal helper name. Defaults to
"pj/->pose".
- `(->pose data)` -- raw dataset becomes a leaf pose
- `(->pose pose)` -- already a pose; idempotent lift
- `(->pose {:x :a :y :b})` -- a mapping becomes a leaf carrying it,
with no data, to be completed by `pj/with-data` or by the pose it
is placed in(aesthetic-roles)What a distinction given to each aesthetic is put to work as, as a map from aesthetic to role.
:positional -- the mark is placed by the value.:appearance -- the marks share a place and are told apart by how
they look, and a legend says which is which.:grouping -- the marks share a place and are not told apart.:panel -- each value gets a panel of its own, told apart by a
strip label. pj/facet writes these.A role is not a category: a category is a value a categorical column holds, which is what a role is given.
A function rather than a value, as pj/shape-symbols is: the
registry grows from one release to the next.
What a distinction given to each aesthetic is put to work as, as a map from aesthetic to role. - `:positional` -- the mark is placed by the value. - `:appearance` -- the marks share a place and are told apart by how they look, and a legend says which is which. - `:grouping` -- the marks share a place and are not told apart. - `:panel` -- each value gets a panel of its own, told apart by a strip label. `pj/facet` writes these. A role is not a category: a category is a value a categorical column holds, which is what a role is given. A function rather than a value, as `pj/shape-symbols` is: the registry grows from one release to the next.
What each aesthetic's scale accepts, one entry per aesthetic that has a scale, in display order.
Each entry carries :aesthetic, the :types that aesthetic can be
read through, and the spec :keys it reads beside :type and
:domain, which belong to every scale. Both are ordered vectors,
and aesthetics with the same capabilities are adjacent so a table
can group them.
The panel aesthetics, :col and :row, come last with no types and
no keys: the one setting they take is a :domain, which orders
their panels.
The same tables pj/scale and a mapping's :scale validate
against, so a reference table built from this cannot drift from what
they enforce. Use it as pj/config-key-docs is used.
What each aesthetic's scale accepts, one entry per aesthetic that has a scale, in display order. Each entry carries `:aesthetic`, the `:types` that aesthetic can be read through, and the spec `:keys` it reads beside `:type` and `:domain`, which belong to every scale. Both are ordered vectors, and aesthetics with the same capabilities are adjacent so a table can group them. The panel aesthetics, `:col` and `:row`, come last with no types and no keys: the one setting they take is a `:domain`, which orders their panels. The same tables `pj/scale` and a mapping's `:scale` validate against, so a reference table built from this cannot drift from what they enforce. Use it as `pj/config-key-docs` is used.
(arrange plots)(arrange plots-or-data opts-or-cells)(arrange data-or-pose cells opts)Arrange multiple poses in a grid. Returns a composite pose
that renders through the compositor via membrane -- so :svg,
:bufimg, and any other membrane target work uniformly.
Each cell is a pose, leaf or composite, or -- drawn from data given
first, below -- a column or a mapping. A composite cell holds its
own grid. nil is a cell left empty: it keeps its
place in the layout and draws nothing, whatever data and layers the
composite carries. Pre-rendered hiccup is not accepted;
build your own [:div ...] if you need to combine already-rendered
values outside the library.
The options map holds the options below and any plot option
pj/options takes:
(arrange cells {:x-label "X"}) is
(-> (arrange cells) (pj/options {:x-label "X"})), and the option
reaches every cell that does not set it. A key that is neither is
reported, as pj/options reports one.
:cols -- explicit column count (default: min(4, n-plots)).
:title -- centered title band above the grid.
:subtitle -- a line under the title; :caption -- a line under
the grid, at the right.
:width -- total composite width.
:height -- total composite height.
:share-scales -- the axes the cells share. A set of axes,
#{:x :y}, shares each among the cells drawing the same column on
it; :x is the horizontal axis and :y the vertical one, also
for a cell drawn with :coord :flip. A map says which columns go
together on an axis:
{:y [[:hwy :cty]]} shares :y between the cells drawing :hwy
and :cty, {:y :all} between every cell, and {:y :same-column}
is what the set says. A shared axis covers what the cells sharing
it draw; a categorical one lists every category they hold. A count
or density axis is shared among cells that count the same column,
and a group or :all shares it between columns. By default, cells
that split the data of the pose they are arranged into share each
axis that draws one column, and other cells share nothing.
pj/options takes the same option for the panels of a facet.
:align-panels -- give every cell the same drawing area, by
reserving the widest y-label pad and legend column a cell needs on
all of them (and, for a row, the tallest x-label pad). Two cells
whose y axes label at different widths otherwise get different
panel widths, so a shared x axis covers a different extent in
each. Pair it with :share-scales where the cells are meant to be
read against one another.
(arrange [fr-a fr-b] {:cols 1 :share-scales #{:x} :align-panels true})
-- a column of cells whose x axes line up.
(arrange [fr-a fr-b]) -- 1x2 row.
(arrange [fr-a fr-b fr-c] {:cols 2 :width 900}) -- 2x2 grid (wraps).
(arrange [[fr-a fr-b] [fr-c fr-d]]) -- explicit 2x2 grid.
Given data first, the cells are drawn from it, so a cell need say no more than which columns its panel draws and the layers are added once at the root:
(-> data (arrange [{:x :a :y :b} {:x :c :y :d}]) (lay-point))(-> data (arrange cells {:cols 2}) (lay-point))(-> data (pose {:x :a :y :b}) (arrange [{:where {:g "u"}} {:where {:g "v"}}]) (lay-point))
-- a cell holding :where draws the rows that match, alone or
beside a mapping. Cells that split the data by one column's
values, in the data's order, draw what pj/facet draws.The arity is decided by the second argument: a sequential one is the cell list, so the first is data or a pose; a map is the options, so the first is the cells.
A cell is what one argument after the data is to pj/pose: a
column, :a, is the cell {:x :a}; a mapping is a mapping; a pose
is a pose. So a cell and (pj/pose nil cell) draw the same. nil
is the exception: here a cell left empty, where after the data in
pj/pose it names no column. A vector is never a cell. It is a list of cells -- a row,
in a list of rows, and otherwise an arrangement of its own. A list
is a list of rows when its first element is a vector, and then every
element is a row:
(arrange data [:a :b :c]) -- a panel per column.(arrange data [{:x :a :y :b} {:x :c :y :d}]) -- a panel per
pair of columns.(arrange data [[:a :b] [:c :d]]) -- a 2x2 grid of panels, a
column each.(arrange data [:a [:b :c]]) -- a panel of :a beside an
arrangement of :b and :c.(arrange pose [:a :b]) -- the pose's data, mapping, layers, row
filter and plot options, with a panel per column; on a composite
pose, the cells appended, laid out as one call with the longer
list lays them out.(arrange data [(pj/pose nil :a :b) (pj/pose nil :c :d)]) -- a
pose with no data is drawn from the data given first, so a list of
pairs is (mapv #(apply pj/pose nil %) pairs).(arrange data [(pj/lay-point nil :a :b) {:x :c :y :d}]) -- a cell
with a layer of its own, beside a cell left to the layers of the
arrangement.Arrange multiple poses in a grid. Returns a composite pose
that renders through the compositor via membrane -- so `:svg`,
`:bufimg`, and any other membrane target work uniformly.
Each cell is a pose, leaf or composite, or -- drawn from data given
first, below -- a column or a mapping. A composite cell holds its
own grid. `nil` is a cell left empty: it keeps its
place in the layout and draws nothing, whatever data and layers the
composite carries. Pre-rendered hiccup is not accepted;
build your own `[:div ...]` if you need to combine already-rendered
values outside the library.
The options map holds the options below and any plot option
`pj/options` takes:
`(arrange cells {:x-label "X"})` is
`(-> (arrange cells) (pj/options {:x-label "X"}))`, and the option
reaches every cell that does not set it. A key that is neither is
reported, as `pj/options` reports one.
- `:cols` -- explicit column count (default: min(4, n-plots)).
- `:title` -- centered title band above the grid.
- `:subtitle` -- a line under the title; `:caption` -- a line under
the grid, at the right.
- `:width` -- total composite width.
- `:height` -- total composite height.
- `:share-scales` -- the axes the cells share. A set of axes,
`#{:x :y}`, shares each among the cells drawing the same column on
it; `:x` is the horizontal axis and `:y` the vertical one, also
for a cell drawn with `:coord :flip`. A map says which columns go
together on an axis:
`{:y [[:hwy :cty]]}` shares `:y` between the cells drawing `:hwy`
and `:cty`, `{:y :all}` between every cell, and `{:y :same-column}`
is what the set says. A shared axis covers what the cells sharing
it draw; a categorical one lists every category they hold. A count
or density axis is shared among cells that count the same column,
and a group or `:all` shares it between columns. By default, cells
that split the data of the pose they are arranged into share each
axis that draws one column, and other cells share nothing.
`pj/options` takes the same option for the panels of a facet.
- `:align-panels` -- give every cell the same drawing area, by
reserving the widest y-label pad and legend column a cell needs on
all of them (and, for a row, the tallest x-label pad). Two cells
whose y axes label at different widths otherwise get different
panel widths, so a shared x axis covers a different extent in
each. Pair it with `:share-scales` where the cells are meant to be
read against one another.
- `(arrange [fr-a fr-b] {:cols 1 :share-scales #{:x} :align-panels true})`
-- a column of cells whose x axes line up.
- `(arrange [fr-a fr-b])` -- 1x2 row.
- `(arrange [fr-a fr-b fr-c] {:cols 2 :width 900})` -- 2x2 grid (wraps).
- `(arrange [[fr-a fr-b] [fr-c fr-d]])` -- explicit 2x2 grid.
**Given data first**, the cells are drawn from it, so a cell need
say no more than which columns its panel draws and the layers are
added once at the root:
- `(-> data (arrange [{:x :a :y :b} {:x :c :y :d}]) (lay-point))`
- `(-> data (arrange cells {:cols 2}) (lay-point))`
- `(-> data (pose {:x :a :y :b}) (arrange [{:where {:g "u"}} {:where {:g "v"}}]) (lay-point))`
-- a cell holding `:where` draws the rows that match, alone or
beside a mapping. Cells that split the data by one column's
values, in the data's order, draw what `pj/facet` draws.
The arity is decided by the second argument: a sequential one is
the cell list, so the first is data or a pose; a map is the options,
so the first is the cells.
A cell is what one argument after the data is to `pj/pose`: a
column, `:a`, is the cell `{:x :a}`; a mapping is a mapping; a pose
is a pose. So a cell and `(pj/pose nil cell)` draw the same. `nil`
is the exception: here a cell left empty, where after the data in
`pj/pose` it names no column. A vector is never a cell. It is a list of cells -- a row,
in a list of rows, and otherwise an arrangement of its own. A list
is a list of rows when its first element is a vector, and then every
element is a row:
- `(arrange data [:a :b :c])` -- a panel per column.
- `(arrange data [{:x :a :y :b} {:x :c :y :d}])` -- a panel per
pair of columns.
- `(arrange data [[:a :b] [:c :d]])` -- a 2x2 grid of panels, a
column each.
- `(arrange data [:a [:b :c]])` -- a panel of `:a` beside an
arrangement of `:b` and `:c`.
- `(arrange pose [:a :b])` -- the pose's data, mapping, layers, row
filter and plot options, with a panel per column; on a composite
pose, the cells appended, laid out as one call with the longer
list lays them out.
- `(arrange data [(pj/pose nil :a :b) (pj/pose nil :c :d)])` -- a
pose with no data is drawn from the data given first, so a list of
pairs is `(mapv #(apply pj/pose nil %) pairs)`.
- `(arrange data [(pj/lay-point nil :a :b) {:x :c :y :d}])` -- a cell
with a layer of its own, beside a cell left to the layers of the
arrangement.(composite-draft? x)Return true if x is a composite draft (a tree of sub-drafts with
shared chrome-spec, returned by pj/draft on a composite pose).
Return true if x is a composite draft (a tree of sub-drafts with shared chrome-spec, returned by `pj/draft` on a composite pose).
(composite-plan? x)Return true if x is a composite plan (a tree of sub-plots with shared chrome).
Return true if x is a composite plan (a tree of sub-plots with shared chrome).
(compound-key-aesthetics)The aesthetics that read several columns as one distinction, united, as a set. Every other aesthetic that takes a column reports a vector as several columns where one goes.
{:group [:part :dimension]} is one grouping keyed on the pair, and
{:col [:part :dimension]} draws a panel per combination the data
holds -- while {:color [:part :dimension]} reports an error: a
vector of columns where one goes is read as a series, which is
written on :x or :y. The three spellings of a vector read alike:
bare, {:series [...]} and {:column [...]}.
The same set the check reads, so a table built from this cannot
drift from what is enforced. Use it as pj/aesthetic-scales is
used.
A function rather than a value, as pj/aesthetic-roles is: the
registry grows from one release to the next.
The aesthetics that read several columns as one distinction, united,
as a set. Every other aesthetic that takes a column reports a vector
as several columns where one goes.
`{:group [:part :dimension]}` is one grouping keyed on the pair, and
`{:col [:part :dimension]}` draws a panel per combination the data
holds -- while `{:color [:part :dimension]}` reports an error: a
vector of columns where one goes is read as a series, which is
written on `:x` or `:y`. The three spellings of a vector read alike:
bare, `{:series [...]}` and `{:column [...]}`.
The same set the check reads, so a table built from this cannot
drift from what is enforced. Use it as `pj/aesthetic-scales` is
used.
A function rather than a value, as `pj/aesthetic-roles` is: the
registry grows from one release to the next.(config)Return the effective resolved configuration as a map.
Merges: library defaults < plotje.edn < set-config! < *config*.
Useful for inspecting which values are in effect.
(config) -- show current resolved config.Return the effective resolved configuration as a map. Merges: library defaults < `plotje.edn` < `set-config!` < `*config*`. Useful for inspecting which values are in effect. - `(config)` -- show current resolved config.
Documentation metadata for configuration keys. Maps each config key to [category description]. Use with (pj/config) to build reference tables.
Documentation metadata for configuration keys. Maps each config key to [category description]. Use with (pj/config) to build reference tables.
(coord pose coord-type)Set coordinate transform on a pose. The coord applies to the pose it is called on and to everything below it, as a scale does: called on the pose you are building it covers every panel, and called on one cell before the cells are arranged, that cell alone. On a composite pose it attaches to the root, so every descendant leaf inherits it at plan time.
Supported coord-types:
:cartesian -- standard x-right, y-up mapping (the default).:flip -- swap x and y axes (horizontal bars / boxplots).:fixed -- equal aspect ratio (1 data unit = 1 data unit).:polar -- radial mapping: x to angle, y to radius.A pose that names a column on :y alone, under layers that draw from
one column, is drawn on its side, and takes :flip or no coordinate
system: :cartesian, :polar and :fixed report an error.
Set coordinate transform on a pose. The coord applies to the pose it is called on and to everything below it, as a scale does: called on the pose you are building it covers every panel, and called on one cell before the cells are arranged, that cell alone. On a composite pose it attaches to the root, so every descendant leaf inherits it at plan time. Supported coord-types: - `:cartesian` -- standard x-right, y-up mapping (the default). - `:flip` -- swap x and y axes (horizontal bars / boxplots). - `:fixed` -- equal aspect ratio (1 data unit = 1 data unit). - `:polar` -- radial mapping: x to angle, y to radius. A pose that names a column on `:y` alone, under layers that draw from one column, is drawn on its side, and takes `:flip` or no coordinate system: `:cartesian`, `:polar` and `:fixed` report an error.
(coord-doc k)Return the prose description for a coordinate type keyword.
Returns "(no description)" if no [:key :doc] defmethod is registered.
(coord-doc :polar) returns "Radial mapping: x->angle, y->radius".Return the prose description for a coordinate type keyword. Returns `"(no description)"` if no `[:key :doc]` defmethod is registered. - `(coord-doc :polar)` returns `"Radial mapping: x->angle, y->radius"`.
(cross xs ys)Deprecated: it will be removed in a future release. Builds a vector
of [x y] pairs from two column-name sequences, and warns, or
reports an error under :strict as the retired names do. A pair is
no cell of pj/arrange, where a vector is a list of cells. For a
grid of panels with the xs across and the ys down, write
(pj/matrix data xs ys); for a panel per pair,
write each pair as a mapping, {:x :a :y :c}.
(pj/cross [:a :b] [:c :d]) returns [[:a :c] [:a :d] [:b :c] [:b :d]].Deprecated: it will be removed in a future release. Builds a vector
of `[x y]` pairs from two column-name sequences, and warns, or
reports an error under `:strict` as the retired names do. A pair is
no cell of `pj/arrange`, where a vector is a list of cells. For a
grid of panels with the `xs` across and the `ys` down, write
`(pj/matrix data xs ys)`; for a panel per pair,
write each pair as a mapping, `{:x :a :y :c}`.
- `(pj/cross [:a :b] [:c :d])` returns `[[:a :c] [:a :d] [:b :c] [:b :d]]`.(draft pose)(draft pose opts)Resolve raw input into a draft. Literal composition of the atomic
steps: (-> x ->pose pose->draft). The 2-arity folds opts into
the pose with pj/options first, mirroring pj/plan and pj/plot:
(-> x ->pose (options opts) draft).
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as pj/infer-mapping gives one, so (draft data)
works without an explicit pj/pose call.
For a leaf pose, returns a LeafDraft record (:layers is a
vector of flat maps, one per applicable layer with merged scope;
:opts carries the pose-level options that flow into the plan
stage). For a composite pose, and for a leaf with a facet over
columns, which is drawn as one cell per column, returns a
CompositeDraft carrying
per-leaf drafts (each contextualized -- shared-scale domains
injected, suppress-* flags applied), the resolved chrome geometry,
and the layout (path -> rect).
(draft pose)(draft pose {:width 800 :title "Plot"})Resolve raw input into a draft. Literal composition of the atomic
steps: `(-> x ->pose pose->draft)`. The 2-arity folds opts into
the pose with `pj/options` first, mirroring `pj/plan` and `pj/plot`:
`(-> x ->pose (options opts) draft)`.
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as `pj/infer-mapping` gives one, so `(draft data)`
works without an explicit `pj/pose` call.
For a leaf pose, returns a `LeafDraft` record (`:layers` is a
vector of flat maps, one per applicable layer with merged scope;
`:opts` carries the pose-level options that flow into the plan
stage). For a composite pose, and for a leaf with a facet over
columns, which is drawn as one cell per column, returns a
`CompositeDraft` carrying
per-leaf drafts (each contextualized -- shared-scale domains
injected, suppress-* flags applied), the resolved chrome geometry,
and the layout (path -> rect).
- `(draft pose)`
- `(draft pose {:width 800 :title "Plot"})`(draft->membrane draft)(draft->membrane draft opts)Compose draft -> plan -> membrane. The 2-arity takes an opts map
for plan->membrane (e.g. {:tooltip true}).
Render-stage options set on the original pose via pj/options
(:theme, :color-values, ...) ride on the draft's :opts and form
the base; any opts passed here override them per key. This keeps
the explicit pipeline consistent with pj/plot, which feeds the
pose's opts into the membrane stage.
(draft->membrane (draft pose))(draft->membrane (draft pose) {:tooltip true})Compose draft -> plan -> membrane. The 2-arity takes an opts map
for `plan->membrane` (e.g. `{:tooltip true}`).
Render-stage options set on the original pose via `pj/options`
(`:theme`, `:color-values`, ...) ride on the draft's `:opts` and form
the base; any opts passed here override them per key. This keeps
the explicit pipeline consistent with `pj/plot`, which feeds the
pose's opts into the membrane stage.
- `(draft->membrane (draft pose))`
- `(draft->membrane (draft pose) {:tooltip true})`(draft->plan draft)Single-step transition: convert a draft into a plan. Dispatches on
draft shape -- a LeafDraft carries :layers and pose-level :opts
that flow into plan/draft->plan; a CompositeDraft goes through
compositor/composite-draft->plan (which uses the chrome-spec already
baked in at draft emission).
Plan-stage opts (:width, :height, :title, ...) ride on the
draft itself -- on the LeafDraft's :opts for leaves, on the
CompositeDraft's chrome-spec for composites. Set them on the pose
via pj/options before drafting.
(draft->plan (draft pose))Single-step transition: convert a draft into a plan. Dispatches on draft shape -- a `LeafDraft` carries `:layers` and pose-level `:opts` that flow into `plan/draft->plan`; a `CompositeDraft` goes through `compositor/composite-draft->plan` (which uses the chrome-spec already baked in at draft emission). Plan-stage opts (`:width`, `:height`, `:title`, ...) ride on the draft itself -- on the `LeafDraft`'s `:opts` for leaves, on the `CompositeDraft`'s chrome-spec for composites. Set them on the pose via `pj/options` before drafting. - `(draft->plan (draft pose))`
(draft->plot draft format opts)Compose draft -> plan -> plot for the given format.
Render-stage options set on the original pose via pj/options
(:theme, :color-values, ...) ride on the draft's :opts and form
the base; the passed opts override them per key.
(draft->plot (draft pose) :svg {})(draft->plot (draft pose) :bufimg {})Compose draft -> plan -> plot for the given format.
Render-stage options set on the original pose via `pj/options`
(`:theme`, `:color-values`, ...) ride on the draft's `:opts` and form
the base; the passed opts override them per key.
- `(draft->plot (draft pose) :svg {})`
- `(draft->plot (draft pose) :bufimg {})`(draft? x)Return true if x is a draft -- the intermediate representation
produced by pj/pose->draft (and so by pj/draft). A draft is
either a LeafDraft record (leaf pose) or a CompositeDraft
record (composite pose). Used by cross-stage misuse guards on
pj/plan and pj/plot.
Return true if x is a draft -- the intermediate representation produced by `pj/pose->draft` (and so by `pj/draft`). A draft is either a `LeafDraft` record (leaf pose) or a `CompositeDraft` record (composite pose). Used by cross-stage misuse guards on `pj/plan` and `pj/plot`.
(explain-membrane membrane)Explain why a membrane does not conform to the Malli schema.
Returns nil if valid, or a Malli explanation map if invalid.
(explain-membrane (membrane pose))Explain why a membrane does not conform to the Malli schema. Returns `nil` if valid, or a Malli explanation map if invalid. - `(explain-membrane (membrane pose))`
(explain-plan plan)Explain why a plan does not conform to the Malli schema.
Returns nil if valid, or a Malli explanation map if invalid.
(explain-plan (plan pose))Explain why a plan does not conform to the Malli schema. Returns `nil` if valid, or a Malli explanation map if invalid. - `(explain-plan (plan pose))`
(explain-pose pose)Explain why a pose does not conform to the Malli schema.
Returns nil if valid, or a Malli explanation map if invalid.
(explain-pose (assoc (lay-point data :x :y) :layers {}))Explain why a pose does not conform to the Malli schema.
Returns `nil` if valid, or a Malli explanation map if invalid.
- `(explain-pose (assoc (lay-point data :x :y) :layers {}))`(facet pose col)(facet pose col options)Facet a pose by a column: one panel per value the column holds,
placed across. (pj/facet my-pose {:row :species}) places them down.
Faceting is a mapping. (pj/facet my-pose :species) writes
{:col :species} into the pose's mapping, so it follows the same
scope rules as :color or :size -- written on a pose it reaches
every layer and every sub-pose below, and a sub-pose that writes its
own :col overrides it. This is what lets a composite be faceted,
and lets one cell of a composite be faceted differently from
another.
:col and :row are aesthetics, so the mapping may be written out
in full, and several columns under either unite into one compound
key, as they do under :group -- pj/compound-key-aesthetics lists
the three:
(pj/facet my-pose :species) -- one panel per species.(pj/facet my-pose [:part :dimension]) -- one panel per observed
combination, which is a compound key. {:col :part :row :dimension}
fills the rectangle instead.{:col {:column :species}} -- the same as the first, written out.The panels are drawn in the order the data holds the column's
values. A :domain specifies another order, as it does for a
categorical axis and a legend: (pj/scale my-pose :col {:domain [...]}), or beside the column, (pj/facet my-pose {:column :species :scale {:domain [...]}}).
A map without :column says where panels come from, and may name
columns rather than categories. A map of columns under :x and :y
draws a panel per column, and with both, pairs the columns one by
one:
(pj/facet my-pose {:y [:hwy :cty]}) -- a panel per column.(pj/facet my-pose {:x [:a :b] :y [:c :d]}) -- two panels, :a
against :c and :b against :d.Under :col and :row the map draws a grid: each entry is a column
of categories or a map of columns, and the grid has a panel for every
pair of an entry's value across and an entry's value down, whether
the data holds the pair or not:
(pj/facet my-pose {:col :year :row :drv}) -- categories by
categories.(pj/facet my-pose {:col :species :row {:y [:a :b]}}) -- categories
across, columns down.(pj/facet my-pose {:col {:x cols} :row {:y cols}}) -- a scatter
plot matrix: every column of cols against every column of cols,
the panel of a column against itself drawing that one column (a
histogram, or a bar chart of counts for a categorical column).
{:col {:x xs} :row {:y ys}} crosses two lists, and nil in a
vector of columns leaves that place for a marginal distribution.Categories are written into the pose's mapping and columns under its
:facet, and the two are crossed when the pose is drawn. So a grid
may also be written as two calls, in either order -- (pj/facet my-pose {:y [:a :b]}) and then (pj/facet :species) -- and a facet
over categories written on an arrangement crosses a cell's facet over
columns. A third distinction, where both directions are taken,
reports an error.
The columns are checked against the data when pj/facet is called.
A layer naming one of the facet's columns is drawn only in the
panels of that column. A second map of columns crosses the first
where it names the other direction -- {:x xs} and then {:row {:y ys}} is {:col {:x xs} :row {:y ys}}; any other second map of
columns, or one on a composite pose, reports an error.
nil in place of the column cancels the facet a pose above writes:
(pj/facet cell nil) on one cell of a faceted arrangement draws that
cell as one panel.
A map of plot options may come last, and is what pj/options takes:
(pj/facet my-pose :species {:share-scales #{:x}}) is
(-> my-pose (pj/facet :species) (pj/options {:share-scales #{:x}})).
Facet a pose by a column: one panel per value the column holds,
placed across. `(pj/facet my-pose {:row :species})` places them down.
Faceting is a mapping. `(pj/facet my-pose :species)` writes
`{:col :species}` into the pose's mapping, so it follows the same
scope rules as `:color` or `:size` -- written on a pose it reaches
every layer and every sub-pose below, and a sub-pose that writes its
own `:col` overrides it. This is what lets a composite be faceted,
and lets one cell of a composite be faceted differently from
another.
`:col` and `:row` are aesthetics, so the mapping may be written out
in full, and several columns under either unite into one compound
key, as they do under `:group` -- `pj/compound-key-aesthetics` lists
the three:
- `(pj/facet my-pose :species)` -- one panel per species.
- `(pj/facet my-pose [:part :dimension])` -- one panel per observed
combination, which is a compound key. `{:col :part :row :dimension}`
fills the rectangle instead.
- `{:col {:column :species}}` -- the same as the first, written out.
The panels are drawn in the order the data holds the column's
values. A `:domain` specifies another order, as it does for a
categorical axis and a legend: `(pj/scale my-pose :col {:domain
[...]})`, or beside the column, `(pj/facet my-pose {:column :species
:scale {:domain [...]}})`.
A map without `:column` says where panels come from, and may name
columns rather than categories. A map of columns under `:x` and `:y`
draws a panel per column, and with both, pairs the columns one by
one:
- `(pj/facet my-pose {:y [:hwy :cty]})` -- a panel per column.
- `(pj/facet my-pose {:x [:a :b] :y [:c :d]})` -- two panels, `:a`
against `:c` and `:b` against `:d`.
Under `:col` and `:row` the map draws a grid: each entry is a column
of categories or a map of columns, and the grid has a panel for every
pair of an entry's value across and an entry's value down, whether
the data holds the pair or not:
- `(pj/facet my-pose {:col :year :row :drv})` -- categories by
categories.
- `(pj/facet my-pose {:col :species :row {:y [:a :b]}})` -- categories
across, columns down.
- `(pj/facet my-pose {:col {:x cols} :row {:y cols}})` -- a scatter
plot matrix: every column of `cols` against every column of `cols`,
the panel of a column against itself drawing that one column (a
histogram, or a bar chart of counts for a categorical column).
`{:col {:x xs} :row {:y ys}}` crosses two lists, and `nil` in a
vector of columns leaves that place for a marginal distribution.
Categories are written into the pose's mapping and columns under its
`:facet`, and the two are crossed when the pose is drawn. So a grid
may also be written as two calls, in either order -- `(pj/facet
my-pose {:y [:a :b]})` and then `(pj/facet :species)` -- and a facet
over categories written on an arrangement crosses a cell's facet over
columns. A third distinction, where both directions are taken,
reports an error.
The columns are checked against the data when `pj/facet` is called.
A layer naming one of the facet's columns is drawn only in the
panels of that column. A second map of columns crosses the first
where it names the other direction -- `{:x xs}` and then `{:row {:y
ys}}` is `{:col {:x xs} :row {:y ys}}`; any other second map of
columns, or one on a composite pose, reports an error.
`nil` in place of the column cancels the facet a pose above writes:
`(pj/facet cell nil)` on one cell of a faceted arrangement draws that
cell as one panel.
A map of plot options may come last, and is what `pj/options` takes:
`(pj/facet my-pose :species {:share-scales #{:x}})` is
`(-> my-pose (pj/facet :species) (pj/options {:share-scales #{:x}}))`.(facet-grid pose col-col row-col)Retired. (pj/facet-grid my-pose :a :b) warns, or reports an error
under :strict as the other retired names do, and returns
(pj/facet my-pose {:col :a :row :b}), the pose the warning names.
With nil on one side the warning names, and the call returns, the
facet in the other direction alone: (pj/facet my-pose :a) or
(pj/facet my-pose {:row :b}).
Retired. `(pj/facet-grid my-pose :a :b)` warns, or reports an error
under `:strict` as the other retired names do, and returns
`(pj/facet my-pose {:col :a :row :b})`, the pose the warning names.
With `nil` on one side the warning names, and the call returns, the
facet in the other direction alone: `(pj/facet my-pose :a)` or
`(pj/facet my-pose {:row :b})`.(frames plan-or-pose)Where a plot's panels sit on the canvas, and how to get between data space and drawing space.
Takes a plan or a pose; a pose is planned first. Returns a map:
:canvas -- [x y width height] of the whole image, in drawing units:panels -- one entry per panel, each carrying :row, :col,
:coord, :x-domain, :y-domain, :x-scale, :y-scale,
:invertible? and :framesA panel's :frames names two rectangles, both [x y width height]
in canvas coordinates: :panel-box (the panel with its axis margin)
and :drawing-area (the background inside that margin, where data
marks are clipped). The canvas is reported once, at the top: it
belongs to the plot rather than to any panel.
The result contains no functions, so it can be printed, compared and
read back from pr-str. To map between the spaces, pass a panel entry
to pj/to-drawing or pj/to-data.
For a composite, every cell's panels report canvas coordinates, so their rectangles can be compared without further arithmetic.
This is the same computation the renderer draws with. Use it to place your own annotations beside a plot, to compose a Plotje membrane with hand-built Membrane views, or to read a pointer position back as data.
(frames my-pose)(-> my-plan frames :panels first :frames :drawing-area)Where a plot's panels sit on the canvas, and how to get between data space and drawing space. Takes a plan or a pose; a pose is planned first. Returns a map: - `:canvas` -- `[x y width height]` of the whole image, in drawing units - `:panels` -- one entry per panel, each carrying `:row`, `:col`, `:coord`, `:x-domain`, `:y-domain`, `:x-scale`, `:y-scale`, `:invertible?` and `:frames` A panel's `:frames` names two rectangles, both `[x y width height]` in canvas coordinates: `:panel-box` (the panel with its axis margin) and `:drawing-area` (the background inside that margin, where data marks are clipped). The canvas is reported once, at the top: it belongs to the plot rather than to any panel. The result contains no functions, so it can be printed, compared and read back from `pr-str`. To map between the spaces, pass a panel entry to `pj/to-drawing` or `pj/to-data`. For a composite, every cell's panels report canvas coordinates, so their rectangles can be compared without further arithmetic. This is the same computation the renderer draws with. Use it to place your own annotations beside a plot, to compose a Plotje membrane with hand-built Membrane views, or to read a pointer position back as data. - `(frames my-pose)` - `(-> my-plan frames :panels first :frames :drawing-area)`
(infer-mapping fr)Infer and attach a default mapping to a pose that carries data but
no mapping yet -- the fresh leaf pj/->pose produces from raw
input. Position and color are taken from the first 1-3 columns
(1 column to :x, 2 columns to :x and :y, 3 columns add
:color).
The default is written where neither the pose's mapping nor any
layer names an :x or a :y column, and where the first layer
names no column, whatever later layers name. Where only a :y is
named and a layer's type draws only from two columns, the first
column nothing names becomes :x. A :y is never filled in. A
composite pose, and data of four or more columns, are returned
unchanged, and so is a pose already filled in, so the step is
idempotent and safe to include anywhere in a pipeline. The draft runs
the same step on every leaf, cells of a composite included. This is
the step that lets raw data render a default: the shortcuts
pj/draft, pj/plan, pj/membrane, pj/plot and pj/save apply
it to what they are given. pj/pose and pj/->pose write no
mapping.
(-> data pj/->pose pj/infer-mapping) -- lift, then default map(pj/infer-mapping built-pose) -- no-op on an already-mapped poseInfer and attach a default mapping to a pose that carries data but no mapping yet -- the fresh leaf `pj/->pose` produces from raw input. Position and color are taken from the first 1-3 columns (1 column to `:x`, 2 columns to `:x` and `:y`, 3 columns add `:color`). The default is written where neither the pose's mapping nor any layer names an `:x` or a `:y` column, and where the first layer names no column, whatever later layers name. Where only a `:y` is named and a layer's type draws only from two columns, the first column nothing names becomes `:x`. A `:y` is never filled in. A composite pose, and data of four or more columns, are returned unchanged, and so is a pose already filled in, so the step is idempotent and safe to include anywhere in a pipeline. The draft runs the same step on every leaf, cells of a composite included. This is the step that lets raw data render a default: the shortcuts `pj/draft`, `pj/plan`, `pj/membrane`, `pj/plot` and `pj/save` apply it to what they are given. `pj/pose` and `pj/->pose` write no mapping. - `(-> data pj/->pose pj/infer-mapping)` -- lift, then default map - `(pj/infer-mapping built-pose)` -- no-op on an already-mapped pose
(lay pose-or-data layer-type-key)(lay pose-or-data layer-type-key mapping)Add a root-scope layer. The layer attaches to :layers and flows to
every descendant leaf at plan time (composite) or renders on the
single panel (leaf).
The layer type is named either by its keyword or by the entry
pj/layer-type-lookup answers with; both behave the same. An entry
is a map naming its :mark; any other value reports an error.
nil with a mapping is a template, as in pj/lay-*:
(pj/lay nil :point {:x :a :y :b}) is a pose with that layer and
no data.
Add a root-scope layer. The layer attaches to `:layers` and flows to
every descendant leaf at plan time (composite) or renders on the
single panel (leaf).
The layer type is named either by its keyword or by the entry
`pj/layer-type-lookup` answers with; both behave the same. An entry
is a map naming its `:mark`; any other value reports an error.
`nil` with a mapping is a template, as in `pj/lay-*`:
`(pj/lay nil :point {:x :a :y :b})` is a pose with that layer and
no data.(lay-area pose-or-data)(lay-area pose-or-data x-or-mapping)(lay-area pose-or-data x y-or-mapping)(lay-area pose-or-data x y mapping)Add :area layer type -- filled region between y and the baseline.
Requires x and y (both numerical). Accepts :color (fill), :alpha,
and an opt-in outline on the top curve: :stroke (outline color),
:stroke-width, :stroke-dash.
The mapping accepts: :alpha :color :color-type :data :group
:in :mark :offset-x :offset-y :position
:stat :stroke :stroke-dash :stroke-width
:tooltip :x :x-type :y :y-type.
Add `:area` layer type -- filled region between y and the baseline.
Requires x and y (both numerical). Accepts `:color` (fill), `:alpha`,
and an opt-in outline on the top curve: `:stroke` (outline color),
`:stroke-width`, `:stroke-dash`.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:mark` `:offset-x` `:offset-y` `:position`
`:stat` `:stroke` `:stroke-dash` `:stroke-width`
`:tooltip` `:x` `:x-type` `:y` `:y-type`.(lay-band-h _pose-or-data)(lay-band-h pose-or-data x-or-mapping)(lay-band-h pose-or-data x y-or-mapping)(lay-band-h pose-or-data x y mapping)Add :band-h layer -- horizontal shaded band between y = y-min and y = y-max.
The bounds are written in the mapping rather than read from a column; :y-min and :y-max are
required and :y-min must be <= :y-max.
Accepts :y-min (required), :y-max (required), :color (a
written color) and :alpha. Bounds may be numeric or temporal
(LocalDate, LocalDateTime, Instant, java.util.Date); temporal values
are converted internally to match the y-axis scale.
{:in :drawing-area} reads the bounds as drawing units from the top
left of the panel background instead of as data values.
The band is drawn in the order its layer was written, so one written
before a scatter sits under its points.
Written with no columns, the band is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the band only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the band is still drawn on that panel.
(lay-band-h pose {:y-min 2 :y-max 4}) -- root-level, flows to every panel.(lay-band-h pose :height :weight {:y-min 2 :y-max 4}) -- on a composite
pose, in the cell drawing :height and :weight.(lay-band-h pose {:y-min 2 :y-max 4 :color "blue" :alpha 0.3})
-- with color and opacity overrides.The mapping accepts: :alpha :color :data :in :mark :offset-x
:offset-y :stat :tooltip :x :y :y-max
:y-min.
Add `:band-h` layer -- horizontal shaded band between y = y-min and y = y-max.
The bounds are written in the mapping rather than read from a column; `:y-min` and `:y-max` are
required and `:y-min` must be <= `:y-max`.
Accepts `:y-min` (required), `:y-max` (required), `:color` (a
written color) and `:alpha`. Bounds may be numeric or temporal
(LocalDate, LocalDateTime, Instant, java.util.Date); temporal values
are converted internally to match the y-axis scale.
`{:in :drawing-area}` reads the bounds as drawing units from the top
left of the panel background instead of as data values.
The band is drawn in the order its layer was written, so one written
before a scatter sits under its points.
Written with no columns, the band is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the band only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the band is still drawn on that panel.
- `(lay-band-h pose {:y-min 2 :y-max 4})` -- root-level, flows to every panel.
- `(lay-band-h pose :height :weight {:y-min 2 :y-max 4})` -- on a composite
pose, in the cell drawing `:height` and `:weight`.
- `(lay-band-h pose {:y-min 2 :y-max 4 :color "blue" :alpha 0.3})`
-- with color and opacity overrides.
The mapping accepts: `:alpha` `:color` `:data` `:in` `:mark` `:offset-x`
`:offset-y` `:stat` `:tooltip` `:x` `:y` `:y-max`
`:y-min`.(lay-band-v _pose-or-data)(lay-band-v pose-or-data x-or-mapping)(lay-band-v pose-or-data x y-or-mapping)(lay-band-v pose-or-data x y mapping)Add :band-v layer -- vertical shaded band between x = x-min and x = x-max.
The bounds are written in the mapping rather than read from a column; :x-min and :x-max are
required and :x-min must be <= :x-max.
Accepts :x-min (required), :x-max (required), :color (a
written color) and :alpha. Bounds may be numeric or temporal
(LocalDate, LocalDateTime, Instant, java.util.Date); temporal values
are converted internally to match the x-axis scale.
{:in :drawing-area} reads the bounds as drawing units from the top
left of the panel background instead of as data values.
The band is drawn in the order its layer was written, so one written
before a scatter sits under its points.
Written with no columns, the band is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the band only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the band is still drawn on that panel.
(lay-band-v pose {:x-min 4 :x-max 6}) -- root-level, flows to every panel.(lay-band-v pose :height :weight {:x-min 4 :x-max 6}) -- on a composite
pose, in the cell drawing :height and :weight.(lay-band-v pose {:x-min 4 :x-max 6 :color "blue" :alpha 0.3})
-- with color and opacity overrides.The mapping accepts: :alpha :color :data :in :mark :offset-x
:offset-y :stat :tooltip :x :x-max :x-min
:y.
Add `:band-v` layer -- vertical shaded band between x = x-min and x = x-max.
The bounds are written in the mapping rather than read from a column; `:x-min` and `:x-max` are
required and `:x-min` must be <= `:x-max`.
Accepts `:x-min` (required), `:x-max` (required), `:color` (a
written color) and `:alpha`. Bounds may be numeric or temporal
(LocalDate, LocalDateTime, Instant, java.util.Date); temporal values
are converted internally to match the x-axis scale.
`{:in :drawing-area}` reads the bounds as drawing units from the top
left of the panel background instead of as data values.
The band is drawn in the order its layer was written, so one written
before a scatter sits under its points.
Written with no columns, the band is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the band only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the band is still drawn on that panel.
- `(lay-band-v pose {:x-min 4 :x-max 6})` -- root-level, flows to every panel.
- `(lay-band-v pose :height :weight {:x-min 4 :x-max 6})` -- on a composite
pose, in the cell drawing `:height` and `:weight`.
- `(lay-band-v pose {:x-min 4 :x-max 6 :color "blue" :alpha 0.3})`
-- with color and opacity overrides.
The mapping accepts: `:alpha` `:color` `:data` `:in` `:mark` `:offset-x`
`:offset-y` `:stat` `:tooltip` `:x` `:x-max` `:x-min`
`:y`.(lay-bar pose-or-data)(lay-bar pose-or-data x-or-mapping)(lay-bar pose-or-data x y-or-mapping)(lay-bar pose-or-data x y mapping)Add a :bar layer type.
:y, as in
(pj/lay-bar pose {:y :class}), the bars are drawn across. Two
categorical columns report an error.The stat is inferred from whether a y column is present, and overridable:
pass {:stat :count} to count even with a y column, or {:stat :identity}
to require an explicit height. :color (with :position :dodge/:stack)
gives grouped or stacked bars.
The categorical axis can be x (vertical bars) or y (horizontal bars):
(pj/lay-bar pose :value :category) with a categorical y draws horizontal value
bars, no pj/coord needed. (Stacked/filled horizontal bars are not yet
supported directly -- put the category on x and add (pj/coord :flip).)
To read a numerical column as categories, write the type of the axis
it is named on: {:x-type :categorical} for a column on :x,
{:y-type :categorical} for a column on :y.
When both axes are numeric or temporal ((pj/lay-bar pose :x :y) with no
categorical axis), each bar sits at its x position with a width taken from
0.9 of the smallest gap between adjacent x values -- a time-series or
numeric-position bar chart. Grouped numeric bars currently overlap rather
than dodge.
:bar-width sets how wide a bar is drawn, in the unit its axis offers. On
a categorical axis it is the fraction of the category band the bar fills,
0.8 by default, as :box-width is for a box: {:bar-width 0.4} draws a
bar half the usual thickness, which is how a second bar layer is read as an
overlay rather than a stack. On a numeric or temporal axis, where there is
no band, it is a width in data units.
The mapping accepts: :alpha :bar-width :color :color-type :data
:group :in :mark :offset-x :offset-y
:position :stat :tooltip :x :x-type :y
:y-type.
Add a `:bar` layer type.
- One column: counts the rows of each category. Named on `:y`, as in
`(pj/lay-bar pose {:y :class})`, the bars are drawn across. Two
categorical columns report an error.
- Two columns (x and y): uses the y value directly as the bar height.
The stat is inferred from whether a y column is present, and overridable:
pass `{:stat :count}` to count even with a y column, or `{:stat :identity}`
to require an explicit height. `:color` (with `:position` `:dodge`/`:stack`)
gives grouped or stacked bars.
The categorical axis can be x (vertical bars) or y (horizontal bars):
`(pj/lay-bar pose :value :category)` with a categorical y draws horizontal value
bars, no `pj/coord` needed. (Stacked/filled horizontal bars are not yet
supported directly -- put the category on x and add `(pj/coord :flip)`.)
To read a numerical column as categories, write the type of the axis
it is named on: `{:x-type :categorical}` for a column on `:x`,
`{:y-type :categorical}` for a column on `:y`.
When both axes are numeric or temporal (`(pj/lay-bar pose :x :y)` with no
categorical axis), each bar sits at its x position with a width taken from
`0.9` of the smallest gap between adjacent x values -- a time-series or
numeric-position bar chart. Grouped numeric bars currently overlap rather
than dodge.
`:bar-width` sets how wide a bar is drawn, in the unit its axis offers. On
a categorical axis it is the fraction of the category band the bar fills,
`0.8` by default, as `:box-width` is for a box: `{:bar-width 0.4}` draws a
bar half the usual thickness, which is how a second bar layer is read as an
overlay rather than a stack. On a numeric or temporal axis, where there is
no band, it is a width in data units.
The mapping accepts: `:alpha` `:bar-width` `:color` `:color-type` `:data`
`:group` `:in` `:mark` `:offset-x` `:offset-y`
`:position` `:stat` `:tooltip` `:x` `:x-type` `:y`
`:y-type`.(lay-boxplot pose-or-data)(lay-boxplot pose-or-data x-or-mapping)(lay-boxplot pose-or-data x y-or-mapping)(lay-boxplot pose-or-data x y mapping)Add :boxplot layer type -- box-and-whisker plot.
Requires categorical x and numerical y. Shows median, quartiles,
whiskers, and outliers. Accepts :color for grouped boxplots.
The mapping accepts: :alpha :box-width :color :color-type :data
:group :in :mark :offset-x :offset-y
:position :size :stat :tooltip :x :x-type
:y :y-type.
Add `:boxplot` layer type -- box-and-whisker plot.
Requires categorical x and numerical y. Shows median, quartiles,
whiskers, and outliers. Accepts `:color` for grouped boxplots.
The mapping accepts: `:alpha` `:box-width` `:color` `:color-type` `:data`
`:group` `:in` `:mark` `:offset-x` `:offset-y`
`:position` `:size` `:stat` `:tooltip` `:x` `:x-type`
`:y` `:y-type`.(lay-contour pose-or-data)(lay-contour pose-or-data x-or-mapping)(lay-contour pose-or-data x y-or-mapping)(lay-contour pose-or-data x y mapping)Add :contour layer type -- iso-density contour lines from 2D KDE.
Requires x and y (both numerical). Accepts {:levels 10} for
the number of contour levels.
The mapping accepts: :alpha :color :color-type :data :group
:in :levels :mark :offset-x :offset-y
:position :size :stat :tooltip :x :x-type
:y :y-type.
Add `:contour` layer type -- iso-density contour lines from 2D KDE.
Requires x and y (both numerical). Accepts {`:levels` 10} for
the number of contour levels.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:levels` `:mark` `:offset-x` `:offset-y`
`:position` `:size` `:stat` `:tooltip` `:x` `:x-type`
`:y` `:y-type`.(lay-density pose-or-data)(lay-density pose-or-data x-or-mapping)(lay-density pose-or-data x y-or-mapping)(lay-density pose-or-data x y mapping)Add :density layer type -- kernel density estimate curve.
Draws from one numerical column: (lay-density pose :hwy) draws the
curve upright, and (lay-density pose {:y :hwy}) draws it on its
side. Accepts :color (fill),
:bandwidth, and an opt-in outline on the curve: :stroke (outline
color), :stroke-width, :stroke-dash (:dashed/:dotted/:solid
or a raw [dash gap] vector).
The mapping accepts: :alpha :bandwidth :color :color-type :data
:group :in :mark :offset-x :offset-y
:position :stat :stroke :stroke-dash
:stroke-width :tooltip :trim :x :x-type
:y :y-type.
Add `:density` layer type -- kernel density estimate curve.
Draws from one numerical column: `(lay-density pose :hwy)` draws the
curve upright, and `(lay-density pose {:y :hwy})` draws it on its
side. Accepts `:color` (fill),
`:bandwidth`, and an opt-in outline on the curve: `:stroke` (outline
color), `:stroke-width`, `:stroke-dash` (`:dashed`/`:dotted`/`:solid`
or a raw `[dash gap]` vector).
The mapping accepts: `:alpha` `:bandwidth` `:color` `:color-type` `:data`
`:group` `:in` `:mark` `:offset-x` `:offset-y`
`:position` `:stat` `:stroke` `:stroke-dash`
`:stroke-width` `:tooltip` `:trim` `:x` `:x-type`
`:y` `:y-type`.(lay-density-2d pose-or-data)(lay-density-2d pose-or-data x-or-mapping)(lay-density-2d pose-or-data x y-or-mapping)(lay-density-2d pose-or-data x y mapping)Add :density-2d layer type -- 2D kernel density heatmap.
Requires x and y (both numerical). Produces a smoothed density
surface as colored tiles with a continuous gradient legend.
The mapping accepts: :alpha :color :color-type :data
:density-2d-grid :group :in :mark :offset-x
:offset-y :position :stat :tooltip :x
:x-type :y :y-type.
Add `:density-2d` layer type -- 2D kernel density heatmap.
Requires x and y (both numerical). Produces a smoothed density
surface as colored tiles with a continuous gradient legend.
The mapping accepts: `:alpha` `:color` `:color-type` `:data`
`:density-2d-grid` `:group` `:in` `:mark` `:offset-x`
`:offset-y` `:position` `:stat` `:tooltip` `:x`
`:x-type` `:y` `:y-type`.(lay-errorbar pose-or-data)(lay-errorbar pose-or-data x-or-mapping)(lay-errorbar pose-or-data x y-or-mapping)(lay-errorbar pose-or-data x y mapping)Add :errorbar layer type -- vertical error bars from pre-computed bounds.
Requires x, y, and {:y-min :col :y-max :col} for lower/upper bounds.
The mapping accepts: :alpha :cap-width :color :color-type :data
:dx :dy :group :in :mark :offset-x
:offset-y :position :size :stat :tooltip
:x :x-type :y :y-max :y-min :y-type.
Add `:errorbar` layer type -- vertical error bars from pre-computed bounds.
Requires x, y, and {`:y-min` `:col` `:y-max` `:col`} for lower/upper bounds.
The mapping accepts: `:alpha` `:cap-width` `:color` `:color-type` `:data`
`:dx` `:dy` `:group` `:in` `:mark` `:offset-x`
`:offset-y` `:position` `:size` `:stat` `:tooltip`
`:x` `:x-type` `:y` `:y-max` `:y-min` `:y-type`.(lay-histogram pose-or-data)(lay-histogram pose-or-data x-or-mapping)(lay-histogram pose-or-data x y-or-mapping)(lay-histogram pose-or-data x y mapping)Add :histogram layer type -- bin numerical values into bars.
Draws from one column: (lay-histogram pose :hwy) draws upright
bars, and (lay-histogram pose {:y :hwy}) draws the same histogram on
its side. Accepts :bins (count), :binwidth, :color,
:normalize (:density for density-normalized heights).
The mapping accepts: :alpha :bins :binwidth :color :color-type
:data :group :in :mark :normalize
:offset-x :offset-y :position :stat
:tooltip :x :x-type :y :y-type.
Add `:histogram` layer type -- bin numerical values into bars.
Draws from one column: `(lay-histogram pose :hwy)` draws upright
bars, and `(lay-histogram pose {:y :hwy})` draws the same histogram on
its side. Accepts `:bins` (count), `:binwidth`, `:color`,
`:normalize` (`:density` for density-normalized heights).
The mapping accepts: `:alpha` `:bins` `:binwidth` `:color` `:color-type`
`:data` `:group` `:in` `:mark` `:normalize`
`:offset-x` `:offset-y` `:position` `:stat`
`:tooltip` `:x` `:x-type` `:y` `:y-type`.(lay-interval-h pose-or-data)(lay-interval-h pose-or-data x-or-mapping)(lay-interval-h pose-or-data x y-or-mapping)(lay-interval-h pose-or-data x y mapping)Add :interval-h layer type -- horizontal bar from x to x-end at categorical y.
Each row becomes one rectangle; the y column is treated categorically
so each distinct value occupies its own lane.
Required: x (numeric or temporal start), y (categorical lane),
:x-end column ref in opts (numeric or temporal end).
Accepts :color, :alpha, :interval-thickness (band fill fraction,
0.0-1.0, default 0.7).
(lay-interval-h data :start :task {:x-end :end :color :status})
The mapping accepts: :alpha :color :color-type :data :group
:in :interval-thickness :mark :offset-x
:offset-y :stat :tooltip :x :x-end
:x-type :y :y-type.
Add `:interval-h` layer type -- horizontal bar from x to x-end at categorical y.
Each row becomes one rectangle; the y column is treated categorically
so each distinct value occupies its own lane.
Required: x (numeric or temporal start), y (categorical lane),
:x-end column ref in opts (numeric or temporal end).
Accepts `:color`, `:alpha`, `:interval-thickness` (band fill fraction,
0.0-1.0, default 0.7).
(lay-interval-h data :start :task {:x-end :end :color :status})
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:interval-thickness` `:mark` `:offset-x`
`:offset-y` `:stat` `:tooltip` `:x` `:x-end`
`:x-type` `:y` `:y-type`.(lay-label pose-or-data)(lay-label pose-or-data x-or-mapping)(lay-label pose-or-data x y-or-mapping)(lay-label pose-or-data x y mapping)Add :label layer type -- text labels on a background box at data
coordinates, for readability over dense data.
:label is the :text layer type with {:box true} preset, so it
draws through the same mark and takes the same options, including
:align-x/:align-y (defaults :left/:center); the box follows the
anchored text. Pass :box to shape it -- {:box {:corner-radius 0}}
for square corners, {:box false} for bare text.
The mapping accepts: :align-x :align-y :alpha :box :color
:color-type :data :dx :dy :font-size
:font-style :font-weight :group :in :mark
:offset-x :offset-y :position :stat :text
:tooltip :x :x-type :y :y-type.
Add `:label` layer type -- text labels on a background box at data
coordinates, for readability over dense data.
`:label` is the `:text` layer type with `{:box true}` preset, so it
draws through the same mark and takes the same options, including
`:align-x`/`:align-y` (defaults `:left`/`:center`); the box follows the
anchored text. Pass `:box` to shape it -- `{:box {:corner-radius 0}}`
for square corners, `{:box false}` for bare text.
The mapping accepts: `:align-x` `:align-y` `:alpha` `:box` `:color`
`:color-type` `:data` `:dx` `:dy` `:font-size`
`:font-style` `:font-weight` `:group` `:in` `:mark`
`:offset-x` `:offset-y` `:position` `:stat` `:text`
`:tooltip` `:x` `:x-type` `:y` `:y-type`.(lay-line pose-or-data)(lay-line pose-or-data x-or-mapping)(lay-line pose-or-data x y-or-mapping)(lay-line pose-or-data x y mapping)Add :line layer type -- connected line through data points.
Requires x (numerical) and y (numerical).
Accepts :color, :alpha, :size (stroke width), :stroke-dash
(:dashed/:dotted/:solid or a raw [dash gap] vector), :dx,
:dy.
The mapping accepts: :alpha :color :color-type :data :dx :dy
:group :in :mark :offset-x :offset-y
:position :size :stat :stroke-dash :tooltip
:x :x-type :y :y-type.
Add `:line` layer type -- connected line through data points.
Requires x (numerical) and y (numerical).
Accepts `:color`, `:alpha`, `:size` (stroke width), `:stroke-dash`
(`:dashed`/`:dotted`/`:solid` or a raw `[dash gap]` vector), `:dx`,
`:dy`.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:dx` `:dy`
`:group` `:in` `:mark` `:offset-x` `:offset-y`
`:position` `:size` `:stat` `:stroke-dash` `:tooltip`
`:x` `:x-type` `:y` `:y-type`.(lay-lollipop pose-or-data)(lay-lollipop pose-or-data x-or-mapping)(lay-lollipop pose-or-data x y-or-mapping)(lay-lollipop pose-or-data x y mapping)Add :lollipop layer type -- dot on a stem from the baseline.
Requires categorical x and numerical y. Like a value bar but with
a circle+line instead of a filled rectangle.
The mapping accepts: :alpha :color :color-type :data :group
:in :mark :offset-x :offset-y :position
:size :stat :tooltip :x :x-type :y
:y-type.
Add `:lollipop` layer type -- dot on a stem from the baseline.
Requires categorical x and numerical y. Like a value bar but with
a circle+line instead of a filled rectangle.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:mark` `:offset-x` `:offset-y` `:position`
`:size` `:stat` `:tooltip` `:x` `:x-type` `:y`
`:y-type`.(lay-point pose-or-data)(lay-point pose-or-data x-or-mapping)(lay-point pose-or-data x y-or-mapping)(lay-point pose-or-data x y mapping)Add a :point (scatter) layer to a pose.
With no columns, the layer reads the panel's columns; on a composite pose it is added at the root and drawn in every cell. With columns, the layer keeps them on its own mapping and is drawn where the pose draws those columns: on its panel, or under a facet over columns, in the panels of those columns. On a composite pose, a layer naming columns joins the last cell that draws those columns, or is appended as a new cell.
(lay-point pose) -- a layer reading the panel's columns.(lay-point pose {:color :species}) -- the same, with a mapping.(lay-point data :x :y) -- the data becomes a pose, and the layer
names its columns.(lay-point data :x :y {:color :c}) -- the same, with a mapping.The mapping accepts: :alpha :color :color-type :data :dx :dy
:group :in :jitter :mark :offset-x
:offset-y :position :shape :size :stat
:tooltip :x :x-type :y :y-type.
Add a `:point` (scatter) layer to a pose.
With no columns, the layer reads the panel's columns; on a composite
pose it is added at the root and drawn in every cell. With columns,
the layer keeps them on its own mapping and is drawn where the pose
draws those columns: on its panel, or under a facet over columns, in
the panels of those columns. On a composite pose, a layer naming columns joins the last
cell that draws those columns, or is appended as a new cell.
- `(lay-point pose)` -- a layer reading the panel's columns.
- `(lay-point pose {:color :species})` -- the same, with a mapping.
- `(lay-point data :x :y)` -- the data becomes a pose, and the layer
names its columns.
- `(lay-point data :x :y {:color :c})` -- the same, with a mapping.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:dx` `:dy`
`:group` `:in` `:jitter` `:mark` `:offset-x`
`:offset-y` `:position` `:shape` `:size` `:stat`
`:tooltip` `:x` `:x-type` `:y` `:y-type`.(lay-ridgeline pose-or-data)(lay-ridgeline pose-or-data x-or-mapping)(lay-ridgeline pose-or-data x y-or-mapping)(lay-ridgeline pose-or-data x y mapping)Add :ridgeline layer type -- stacked density curves by category.
Requires categorical x and numerical y. Categories stack vertically
with density curves rendered horizontally.
The mapping accepts: :alpha :bandwidth :color :color-type :data
:group :in :mark :offset-x :offset-y
:position :stat :tooltip :trim :x :x-type
:y :y-type.
Add `:ridgeline` layer type -- stacked density curves by category.
Requires categorical x and numerical y. Categories stack vertically
with density curves rendered horizontally.
The mapping accepts: `:alpha` `:bandwidth` `:color` `:color-type` `:data`
`:group` `:in` `:mark` `:offset-x` `:offset-y`
`:position` `:stat` `:tooltip` `:trim` `:x` `:x-type`
`:y` `:y-type`.(lay-rug pose-or-data)(lay-rug pose-or-data x-or-mapping)(lay-rug pose-or-data x y-or-mapping)(lay-rug pose-or-data x y mapping)Add :rug layer type -- short tick marks along the axis showing individual values.
Draws from one column: (lay-rug pose :hwy) marks the values along
:x, and (lay-rug pose {:y :hwy}) along :y. A rug naming no
column marks the panel's :x; a written :side (:x, :y or
:both) decides the axis. Often layered with density or scatter.
The mapping accepts: :alpha :color :color-type :data :group
:in :length :mark :offset-x :offset-y
:position :side :stat :tooltip :x :x-type
:y :y-type.
Add `:rug` layer type -- short tick marks along the axis showing individual values.
Draws from one column: `(lay-rug pose :hwy)` marks the values along
`:x`, and `(lay-rug pose {:y :hwy})` along `:y`. A rug naming no
column marks the panel's `:x`; a written `:side` (`:x`, `:y` or
`:both`) decides the axis. Often layered with density or scatter.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:length` `:mark` `:offset-x` `:offset-y`
`:position` `:side` `:stat` `:tooltip` `:x` `:x-type`
`:y` `:y-type`.(lay-rule-h _pose-or-data)(lay-rule-h pose-or-data x-or-mapping)(lay-rule-h pose-or-data x y-or-mapping)(lay-rule-h pose-or-data x y mapping)Add :rule-h layer -- horizontal reference line at y = y-intercept.
The value is written in the mapping rather than read from a column; :y-intercept is required.
Accepts :y-intercept (numeric or temporal -- LocalDate, LocalDateTime,
Instant, java.util.Date), :color (a written color), :alpha (the
line's opacity), :size (its width) and :stroke-dash
(:dashed/:dotted/:solid or a raw [dash gap] vector).
Temporal values are converted internally to match the y-axis scale
so a date-axis intercept needs no manual conversion.
{:in :drawing-area} reads the intercept as drawing units from the
top left of the panel background instead of as a data value.
The rule is drawn in the order its layer was written, so one written
before a scatter sits under its points and one written after sits
over them.
Written with no columns, the rule is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the rule only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the rule is still drawn on that panel.
(lay-rule-h pose {:y-intercept 3}) -- root-level, flows to every panel.(lay-rule-h pose :height :weight {:y-intercept 3}) -- on a composite
pose, in the cell drawing :height and :weight.(lay-rule-h pose {:y-intercept 3 :color "red"}) -- with override color.(lay-rule-h pose {:y-intercept (java.time.LocalDate/parse "2024-01-01")})
-- temporal intercept on a date axis.The mapping accepts: :alpha :color :data :in :mark :offset-x
:offset-y :size :stat :stroke-dash :tooltip
:x :y :y-intercept.
Add `:rule-h` layer -- horizontal reference line at y = y-intercept.
The value is written in the mapping rather than read from a column; `:y-intercept` is required.
Accepts `:y-intercept` (numeric or temporal -- LocalDate, LocalDateTime,
Instant, java.util.Date), `:color` (a written color), `:alpha` (the
line's opacity), `:size` (its width) and `:stroke-dash`
(`:dashed`/`:dotted`/`:solid` or a raw `[dash gap]` vector).
Temporal values are converted internally to match the y-axis scale
so a date-axis intercept needs no manual conversion.
`{:in :drawing-area}` reads the intercept as drawing units from the
top left of the panel background instead of as a data value.
The rule is drawn in the order its layer was written, so one written
before a scatter sits under its points and one written after sits
over them.
Written with no columns, the rule is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the rule only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the rule is still drawn on that panel.
- `(lay-rule-h pose {:y-intercept 3})` -- root-level, flows to every panel.
- `(lay-rule-h pose :height :weight {:y-intercept 3})` -- on a composite
pose, in the cell drawing `:height` and `:weight`.
- `(lay-rule-h pose {:y-intercept 3 :color "red"})` -- with override color.
- `(lay-rule-h pose {:y-intercept (java.time.LocalDate/parse "2024-01-01")})`
-- temporal intercept on a date axis.
The mapping accepts: `:alpha` `:color` `:data` `:in` `:mark` `:offset-x`
`:offset-y` `:size` `:stat` `:stroke-dash` `:tooltip`
`:x` `:y` `:y-intercept`.(lay-rule-v _pose-or-data)(lay-rule-v pose-or-data x-or-mapping)(lay-rule-v pose-or-data x y-or-mapping)(lay-rule-v pose-or-data x y mapping)Add :rule-v layer -- vertical reference line at x = x-intercept.
The value is written in the mapping rather than read from a column; :x-intercept is required.
Accepts :x-intercept (numeric or temporal -- LocalDate, LocalDateTime,
Instant, java.util.Date), :color (a written color), :alpha (the
line's opacity), :size (its width) and :stroke-dash
(:dashed/:dotted/:solid or a raw [dash gap] vector).
Temporal values are converted internally to match the x-axis scale
so a date-axis intercept needs no manual conversion.
{:in :drawing-area} reads the intercept as drawing units from the
top left of the panel background instead of as a data value.
The rule is drawn in the order its layer was written, so one written
before a scatter sits under its points and one written after sits
over them.
Written with no columns, the rule is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the rule only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the rule is still drawn on that panel.
(lay-rule-v pose {:x-intercept 5}) -- root-level, flows to every panel.(lay-rule-v pose :height :weight {:x-intercept 5}) -- on a composite
pose, in the cell drawing :height and :weight.(lay-rule-v pose {:x-intercept 5 :color "red"}) -- with override color.(lay-rule-v pose {:x-intercept #inst "2008-09-15"}) -- temporal
intercept on a date axis.The mapping accepts: :alpha :color :data :in :mark :offset-x
:offset-y :size :stat :stroke-dash :tooltip
:x :x-intercept :y.
Add `:rule-v` layer -- vertical reference line at x = x-intercept.
The value is written in the mapping rather than read from a column; `:x-intercept` is required.
Accepts `:x-intercept` (numeric or temporal -- LocalDate, LocalDateTime,
Instant, java.util.Date), `:color` (a written color), `:alpha` (the
line's opacity), `:size` (its width) and `:stroke-dash`
(`:dashed`/`:dotted`/`:solid` or a raw `[dash gap]` vector).
Temporal values are converted internally to match the x-axis scale
so a date-axis intercept needs no manual conversion.
`{:in :drawing-area}` reads the intercept as drawing units from the
top left of the panel background instead of as a data value.
The rule is drawn in the order its layer was written, so one written
before a scatter sits under its points and one written after sits
over them.
Written with no columns, the rule is drawn on the pose's panel,
and on a composite pose in every cell. On a composite pose the
4-arity draws the rule only in the cell that draws those two
columns, and on a cell of its own where no cell does. On a
pose with one panel, a column the panel does not draw is not read,
and a warning names it; the rule is still drawn on that panel.
- `(lay-rule-v pose {:x-intercept 5})` -- root-level, flows to every panel.
- `(lay-rule-v pose :height :weight {:x-intercept 5})` -- on a composite
pose, in the cell drawing `:height` and `:weight`.
- `(lay-rule-v pose {:x-intercept 5 :color "red"})` -- with override color.
- `(lay-rule-v pose {:x-intercept #inst "2008-09-15"})` -- temporal
intercept on a date axis.
The mapping accepts: `:alpha` `:color` `:data` `:in` `:mark` `:offset-x`
`:offset-y` `:size` `:stat` `:stroke-dash` `:tooltip`
`:x` `:x-intercept` `:y`.(lay-segment pose-or-data)(lay-segment pose-or-data x-or-mapping)(lay-segment pose-or-data x y-or-mapping)(lay-segment pose-or-data x y mapping)Add :segment layer type -- a straight line from (x, y) to
(x-end, y-end), one per row.
:x-end and :y-end each take a column or a written value, and an
end left out is the start's own value. So {:y-end 0} draws a stem
from each point down to the zero line -- a stem plot, a lollipop
without its dot -- and {:x-end :x1 :y-end :y1} draws each row's
segment between two points. :arrow puts an arrow head on :end,
:start or :both ends. A bare name is read as a column, as it is
on :x, so a category is written as a value: {:x-end {:value "Q4"}}.
(lay-segment data :index :distance {:y-end 0}) -- stems.(lay-segment data :x0 :y0 {:x-end :x1 :y-end :y1 :arrow :end})(lay-segment data :quarter :sales {:x-end {:value "Q4"}}) --
each row's segment runs to the Q4 category.The mapping accepts: :alpha :arrow :color :color-type :data
:dx :dy :group :in :mark :offset-x
:offset-y :size :stat :stroke-dash :tooltip
:x :x-end :x-type :y :y-end :y-type.
Add `:segment` layer type -- a straight line from (x, y) to
(x-end, y-end), one per row.
`:x-end` and `:y-end` each take a column or a written value, and an
end left out is the start's own value. So `{:y-end 0}` draws a stem
from each point down to the zero line -- a stem plot, a lollipop
without its dot -- and `{:x-end :x1 :y-end :y1}` draws each row's
segment between two points. `:arrow` puts an arrow head on `:end`,
`:start` or `:both` ends. A bare name is read as a column, as it is
on `:x`, so a category is written as a value: `{:x-end {:value
"Q4"}}`.
- `(lay-segment data :index :distance {:y-end 0})` -- stems.
- `(lay-segment data :x0 :y0 {:x-end :x1 :y-end :y1 :arrow :end})`
- `(lay-segment data :quarter :sales {:x-end {:value "Q4"}})` --
each row's segment runs to the Q4 category.
The mapping accepts: `:alpha` `:arrow` `:color` `:color-type` `:data`
`:dx` `:dy` `:group` `:in` `:mark` `:offset-x`
`:offset-y` `:size` `:stat` `:stroke-dash` `:tooltip`
`:x` `:x-end` `:x-type` `:y` `:y-end` `:y-type`.(lay-smooth pose-or-data)(lay-smooth pose-or-data x-or-mapping)(lay-smooth pose-or-data x y-or-mapping)(lay-smooth pose-or-data x y mapping)Add :smooth layer type -- a smoothed trend line.
Defaults to LOESS (local regression). Pass {:stat :linear-model} for
ordinary least squares instead. Requires x and y (both numerical).
Accepts {:confidence-band true} for a confidence ribbon, and
:stroke-dash.
The mapping accepts: :alpha :bandwidth :bootstrap-resamples :color
:color-type :confidence-band :data :dx :dy
:group :in :level :mark :offset-x
:offset-y :position :size :stat
:stroke-dash :tooltip :x :x-type :y
:y-type.
Add `:smooth` layer type -- a smoothed trend line.
Defaults to LOESS (local regression). Pass {`:stat` `:linear-model`} for
ordinary least squares instead. Requires x and y (both numerical).
Accepts {`:confidence-band` true} for a confidence ribbon, and
`:stroke-dash`.
The mapping accepts: `:alpha` `:bandwidth` `:bootstrap-resamples` `:color`
`:color-type` `:confidence-band` `:data` `:dx` `:dy`
`:group` `:in` `:level` `:mark` `:offset-x`
`:offset-y` `:position` `:size` `:stat`
`:stroke-dash` `:tooltip` `:x` `:x-type` `:y`
`:y-type`.(lay-step pose-or-data)(lay-step pose-or-data x-or-mapping)(lay-step pose-or-data x y-or-mapping)(lay-step pose-or-data x y mapping)Add :step layer type -- staircase line (horizontal then vertical).
Requires x and y (both numerical). Accepts :stroke-dash.
The mapping accepts: :alpha :color :color-type :data :group
:in :mark :offset-x :offset-y :position
:size :stat :stroke-dash :tooltip :x
:x-type :y :y-type.
Add `:step` layer type -- staircase line (horizontal then vertical).
Requires x and y (both numerical). Accepts `:stroke-dash`.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:mark` `:offset-x` `:offset-y` `:position`
`:size` `:stat` `:stroke-dash` `:tooltip` `:x`
`:x-type` `:y` `:y-type`.(lay-summary pose-or-data)(lay-summary pose-or-data x-or-mapping)(lay-summary pose-or-data x y-or-mapping)(lay-summary pose-or-data x y mapping)Add :summary layer type -- mean +/- standard error per category.
Requires categorical x and numerical y. Shows a point at the mean
with error bars for +/- 1 SE. Accepts :color for grouped summaries.
The mapping accepts: :alpha :color :color-type :data :group
:in :mark :offset-x :offset-y :position
:size :stat :tooltip :x :x-type :y
:y-type.
Add `:summary` layer type -- mean +/- standard error per category.
Requires categorical x and numerical y. Shows a point at the mean
with error bars for +/- 1 SE. Accepts `:color` for grouped summaries.
The mapping accepts: `:alpha` `:color` `:color-type` `:data` `:group`
`:in` `:mark` `:offset-x` `:offset-y` `:position`
`:size` `:stat` `:tooltip` `:x` `:x-type` `:y`
`:y-type`.(lay-text pose-or-data)(lay-text pose-or-data x-or-mapping)(lay-text pose-or-data x y-or-mapping)(lay-text pose-or-data x y mapping)Add :text layer type -- text labels at data coordinates.
Requires x, y, and {:text :column} for label content.
:align-x (:left/:center/:right, default :left) and :align-y
(:top/:center/:bottom, default :center) set which part of the
text lands on the data point -- e.g. :align-x :right tucks the label
inside a bar's end, extending leftward.
:box puts the text on a background box: true for the default box,
or a map of box properties ({:corner-radius 8}). pj/lay-label is
this layer with the box on.
The mapping accepts: :align-x :align-y :alpha :box :color
:color-type :data :dx :dy :font-size
:font-style :font-weight :group :in :mark
:offset-x :offset-y :position :stat :text
:tooltip :x :x-type :y :y-type.
Add `:text` layer type -- text labels at data coordinates.
Requires x, y, and {`:text` `:column`} for label content.
`:align-x` (`:left`/`:center`/`:right`, default `:left`) and `:align-y`
(`:top`/`:center`/`:bottom`, default `:center`) set which part of the
text lands on the data point -- e.g. `:align-x :right` tucks the label
inside a bar's end, extending leftward.
`:box` puts the text on a background box: `true` for the default box,
or a map of box properties (`{:corner-radius 8}`). `pj/lay-label` is
this layer with the box on.
The mapping accepts: `:align-x` `:align-y` `:alpha` `:box` `:color`
`:color-type` `:data` `:dx` `:dy` `:font-size`
`:font-style` `:font-weight` `:group` `:in` `:mark`
`:offset-x` `:offset-y` `:position` `:stat` `:text`
`:tooltip` `:x` `:x-type` `:y` `:y-type`.(lay-tile pose-or-data)(lay-tile pose-or-data x-or-mapping)(lay-tile pose-or-data x y-or-mapping)(lay-tile pose-or-data x y mapping)Add :tile layer type -- colored grid cells (heatmap).
With :fill or :color: one cell per row, colored from a column.
With neither: auto-binned 2D histogram (stat :bin2d).
A categorical :color column colors each cell from the palette,
one color per category, as it colors any mark: :color-values and
:color-label apply, and the fill settings, which shape a gradient,
do not.
A numeric :color column is read as the fill. Its cells and their
legend read the fill settings: a :fill scale spec, then a :color
one, then the :fill-* plot options, then the :color-* ones --
:fill-label before :color-label for the legend title.
Given both :fill and :color, the cells are painted from :fill
and :color is not drawn, with a warning.
The mapping accepts: :alpha :color :color-type :data
:density-2d-grid :fill :group :in :mark
:offset-x :offset-y :position :stat
:tooltip :x :x-type :y :y-type.
Add `:tile` layer type -- colored grid cells (heatmap).
With `:fill` or `:color`: one cell per row, colored from a column.
With neither: auto-binned 2D histogram (stat `:bin2d`).
A categorical `:color` column colors each cell from the palette,
one color per category, as it colors any mark: `:color-values` and
`:color-label` apply, and the fill settings, which shape a gradient,
do not.
A numeric `:color` column is read as the fill. Its cells and their
legend read the fill settings: a `:fill` scale spec, then a `:color`
one, then the `:fill-*` plot options, then the `:color-*` ones --
`:fill-label` before `:color-label` for the legend title.
Given both `:fill` and `:color`, the cells are painted from `:fill`
and `:color` is not drawn, with a warning.
The mapping accepts: `:alpha` `:color` `:color-type` `:data`
`:density-2d-grid` `:fill` `:group` `:in` `:mark`
`:offset-x` `:offset-y` `:position` `:stat`
`:tooltip` `:x` `:x-type` `:y` `:y-type`.(lay-violin pose-or-data)(lay-violin pose-or-data x-or-mapping)(lay-violin pose-or-data x y-or-mapping)(lay-violin pose-or-data x y mapping)Add :violin layer type -- mirrored density estimate by category.
Requires categorical x and numerical y. Accepts :color, :bandwidth.
The mapping accepts: :alpha :bandwidth :color :color-type :data
:group :in :mark :offset-x :offset-y
:position :size :stat :tooltip :trim :x
:x-type :y :y-type.
Add `:violin` layer type -- mirrored density estimate by category.
Requires categorical x and numerical y. Accepts `:color`, `:bandwidth`.
The mapping accepts: `:alpha` `:bandwidth` `:color` `:color-type` `:data`
`:group` `:in` `:mark` `:offset-x` `:offset-y`
`:position` `:size` `:stat` `:tooltip` `:trim` `:x`
`:x-type` `:y` `:y-type`.Documentation for the keys a pj/lay-* mapping accepts: aesthetics
and layer settings such as :stat, :bins and :position.
Maps each key to a description string.
Documentation for the keys a `pj/lay-*` mapping accepts: aesthetics and layer settings such as `:stat`, `:bins` and `:position`. Maps each key to a description string.
Deprecated: the earlier name of pj/layer-mapping-docs, the same
map. It will be removed in the next release.
Deprecated: the earlier name of `pj/layer-mapping-docs`, the same map. It will be removed in the next release.
(layer-type-lookup k)Look up a registered layer type by keyword. Returns the layer-type map
(with :mark, :stat, :position, :doc), or nil if not found.
(layer-type-lookup :histogram) returns {:mark :bar, :stat :bin, ...}.Look up a registered layer type by keyword. Returns the layer-type map
(with `:mark`, `:stat`, `:position`, `:doc`), or `nil` if not found.
- `(layer-type-lookup :histogram)` returns `{:mark :bar, :stat :bin, ...}`.(layer-type? x)Return true if x is a layer type (mark + stat + position bundle from the registry).
Return true if x is a layer type (mark + stat + position bundle from the registry).
(leaf-draft? x)Return true if x is a leaf draft (a LeafDraft record carrying
:layers -- a vector of layer maps -- and :opts -- the
pose-level options that flow into the plan stage).
Return true if x is a leaf draft (a `LeafDraft` record carrying `:layers` -- a vector of layer maps -- and `:opts` -- the pose-level options that flow into the plan stage).
(leaf-plan? x)Return true if x is a leaf plan (single-pose resolved geometry).
Return true if x is a leaf plan (single-pose resolved geometry).
(marginal pose side)(marginal pose side layer-type)(marginal pose side layer-type opts)Add a marginal panel beside a leaf pose: a distribution of one of the pose's columns, drawn in a thin panel that shares the main panel's axis for that column and carries its own value scale.
(marginal pose :top) -- a density of the :x column, above.(marginal pose :right) -- a density of the :y column, to the
right, drawn on its side.(marginal pose :top :histogram) -- a histogram instead.(marginal pose :right :density {:size 0.3}) -- a thicker panel.:size is the marginal's share of the height for :top and of the
width for :right, 0.25 by default, and the only key the last map
takes; any other reports an error. This map holds the marginal
panel's own setting, not a mapping: :size here is not the
aesthetic. The marginal's ticks and axis
title along the shared axis are dropped, since the main panel's axis
describes both, and the two drawing areas are aligned so a value
sits at the same place in each.
A :right marginal is drawn under pj/coord :flip, so its value
axis runs across the panel and its baseline stands at the left edge,
against the main panel.
A second layer added to the pose is drawn on the pose's panel; a marginal is drawn beside that panel, on a value axis of its own.
Add a marginal panel beside a leaf pose: a distribution of one of
the pose's columns, drawn in a thin panel that shares the main
panel's axis for that column and carries its own value scale.
- `(marginal pose :top)` -- a density of the `:x` column, above.
- `(marginal pose :right)` -- a density of the `:y` column, to the
right, drawn on its side.
- `(marginal pose :top :histogram)` -- a histogram instead.
- `(marginal pose :right :density {:size 0.3})` -- a thicker panel.
`:size` is the marginal's share of the height for `:top` and of the
width for `:right`, `0.25` by default, and the only key the last map
takes; any other reports an error. This map holds the marginal
panel's own setting, not a mapping: `:size` here is not the
aesthetic. The marginal's ticks and axis
title along the shared axis are dropped, since the main panel's axis
describes both, and the two drawing areas are aligned so a value
sits at the same place in each.
A `:right` marginal is drawn under `pj/coord :flip`, so its value
axis runs across the panel and its baseline stands at the left edge,
against the main panel.
A second layer added to the pose is drawn on the pose's panel; a
marginal is drawn beside that panel, on a value axis of its own.(mark-doc k)Return the prose description for a mark keyword.
Returns "(no description)" if no [:key :doc] defmethod is registered.
(mark-doc :point) returns "Filled circle".Return the prose description for a mark keyword. Returns `"(no description)"` if no `[:key :doc]` defmethod is registered. - `(mark-doc :point)` returns `"Filled circle"`.
(matrix data-or-pose cols)(matrix data-or-pose cols options)(matrix data-or-pose xs ys)(matrix data-or-pose xs ys options)A grid of panels crossing columns with columns: every column of xs
across against every column of ys down. It is the facet
{:col {:x xs} :row {:y ys}}, and returns the pose pj/facet
returns for it:
(pj/matrix data xs ys options) is
(pj/facet data {:col {:x xs} :row {:y ys}} options).
(pj/matrix data cols) -- a scatter plot matrix, the columns
crossed with themselves. The panel of a column against itself
draws that one column: a histogram, or the counts of a categorical
column.(pj/matrix data xs ys) -- xs across, ys down.(pj/matrix data [:a nil] [nil :b]) -- nil leaves a place with
no column on that axis, so the cell draws the distribution of the
other: a scatter plot of :a and :b with a marginal
distribution of each. The corner, nil on both axes, is left
empty. {:col-weights [3 1] :row-weights [1 3]} draws the
marginals smaller.The first argument is data or a pose; a mapping such as
{:color :species} is written with pj/pose, before or after.
A map of plot options may come last, as pj/facet takes it. Layers
added afterwards are drawn as in any facet over columns: a layer
reading one column on the diagonal, a layer reading two off it.
A grid of panels crossing columns with columns: every column of `xs`
across against every column of `ys` down. It is the facet
`{:col {:x xs} :row {:y ys}}`, and returns the pose `pj/facet`
returns for it:
`(pj/matrix data xs ys options)` is
`(pj/facet data {:col {:x xs} :row {:y ys}} options)`.
- `(pj/matrix data cols)` -- a scatter plot matrix, the columns
crossed with themselves. The panel of a column against itself
draws that one column: a histogram, or the counts of a categorical
column.
- `(pj/matrix data xs ys)` -- `xs` across, `ys` down.
- `(pj/matrix data [:a nil] [nil :b])` -- `nil` leaves a place with
no column on that axis, so the cell draws the distribution of the
other: a scatter plot of `:a` and `:b` with a marginal
distribution of each. The corner, `nil` on both axes, is left
empty. `{:col-weights [3 1] :row-weights [1 3]}` draws the
marginals smaller.
The first argument is data or a pose; a mapping such as
`{:color :species}` is written with `pj/pose`, before or after.
A map of plot options may come last, as `pj/facet` takes it. Layers
added afterwards are drawn as in any facet over columns: a layer
reading one column on the diagonal, a layer reading two off it.(membrane pose)(membrane pose opts)Resolve a pose into a PlotjeMembrane. Literal composition of the
atomic steps: (let [pose (->pose x), opts (:opts pose {})] (-> pose pose->draft draft->plan (plan->membrane opts))).
The let lifts the pose once so the chain can pluck pose-level
opts and pass them to plan->membrane. The 2-arity folds opts
into the pose with pj/options first.
Returns a PlotjeMembrane -- a Membrane UI component (implements
IOrigin, IBounds, IChildren) carrying the rendered drawables
plus plan-derived width and height; the title, when set, rides as
:plotje/title. Render-time options (:tooltip, :theme,
:color-values, :color-range, :color-midpoint) ride along on the
pose's :opts and reach plan->membrane through this call.
Useful for exploring rendering targets beyond the SVG and Java2D
backends Plotje wires in today: any Membrane backend can consume
the result of pj/membrane via the standard Membrane protocols.
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as pj/infer-mapping gives one, so
(membrane data) works without an explicit pj/pose call.
(membrane pose)(membrane pose {:tooltip true})Resolve a pose into a `PlotjeMembrane`. Literal composition of the
atomic steps: `(let [pose (->pose x), opts (:opts pose {})]
(-> pose
pose->draft
draft->plan
(plan->membrane opts)))`.
The let lifts the pose once so the chain can pluck pose-level
opts and pass them to `plan->membrane`. The 2-arity folds opts
into the pose with `pj/options` first.
Returns a `PlotjeMembrane` -- a Membrane UI component (implements
`IOrigin`, `IBounds`, `IChildren`) carrying the rendered drawables
plus plan-derived width and height; the title, when set, rides as
`:plotje/title`. Render-time options (`:tooltip`, `:theme`,
`:color-values`, `:color-range`, `:color-midpoint`) ride along on the
pose's `:opts` and reach `plan->membrane` through this call.
Useful for exploring rendering targets beyond the SVG and Java2D
backends Plotje wires in today: any Membrane backend can consume
the result of `pj/membrane` via the standard Membrane protocols.
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as `pj/infer-mapping` gives one, so
`(membrane data)` works without an explicit `pj/pose` call.
- `(membrane pose)`
- `(membrane pose {:tooltip true})`(membrane->plot membrane-tree format opts)Convert a PlotjeMembrane into a figure for the given format.
Dispatches on format keyword; :svg is always available.
Reads width and height from the membrane via
(membrane.ui/width m) / (membrane.ui/height m) (so any
Membrane backend can introspect the canvas size), and the title
from (:plotje/title m).
(membrane->plot (plan->membrane (plan pose)) :svg {})Convert a `PlotjeMembrane` into a figure for the given format.
Dispatches on format keyword; `:svg` is always available.
Reads width and height from the membrane via
`(membrane.ui/width m)` / `(membrane.ui/height m)` (so any
Membrane backend can introspect the canvas size), and the title
from `(:plotje/title m)`.
- `(membrane->plot (plan->membrane (plan pose)) :svg {})`(membrane-mark-doc k)Return the prose description for how a mark renders to membrane drawables.
Returns "(no description)" if no [:key :doc] defmethod is registered.
(membrane-mark-doc :point) returns "Translated colored rounded-rectangles".Return the prose description for how a mark renders to membrane drawables. Returns `"(no description)"` if no `[:key :doc]` defmethod is registered. - `(membrane-mark-doc :point)` returns `"Translated colored rounded-rectangles"`.
(membrane? x)Return true if x is a PlotjeMembrane -- the value returned by
pj/plan->membrane and pj/membrane. A PlotjeMembrane is a
Membrane UI component (implements IOrigin, IBounds,
IChildren) carrying the rendered drawables and plan-derived
width/height; the plot title rides as :plotje/title.
Return true if x is a `PlotjeMembrane` -- the value returned by `pj/plan->membrane` and `pj/membrane`. A `PlotjeMembrane` is a Membrane UI component (implements `IOrigin`, `IBounds`, `IChildren`) carrying the rendered drawables and plan-derived width/height; the plot title rides as `:plotje/title`.
(options pose opts)Set plot-level options (title, labels, width, height, etc.).
A later value replaces an earlier one, except :theme and :config,
whose settings combine key by key -- as a composite's options combine
with a cell's.
:width and :height are coerced to long (rounded) so the plan carries
integer dimensions through to render. On a composite pose
the options attach to the root so every descendant leaf inherits
them at plan time.
Set plot-level options (title, labels, width, height, etc.). A later value replaces an earlier one, except `:theme` and `:config`, whose settings combine key by key -- as a composite's options combine with a cell's. `:width` and `:height` are coerced to long (rounded) so the plan carries integer dimensions through to render. On a composite pose the options attach to the root so every descendant leaf inherits them at plan time.
(overlay pose-or-data)(overlay pose-or-data on?)Retired. The layers of a pose share one panel, so (pj/overlay pose)
returns the pose unchanged and warns, or reports an error under
:strict as the other retired names do.
(pj/overlay pose false) reports an error: separate panels come from
a split over the columns, as in (pj/facet pose {:y [:a :b]}), or
from separate poses given to pj/arrange.
Retired. The layers of a pose share one panel, so `(pj/overlay pose)`
returns the pose unchanged and warns, or reports an error under
`:strict` as the other retired names do.
`(pj/overlay pose false)` reports an error: separate panels come from
a split over the columns, as in `(pj/facet pose {:y [:a :b]})`, or
from separate poses given to `pj/arrange`.Documentation for the panel aesthetics -- the mapping keys that send
each value of a distinction to a panel of its own. Written on a
pose, not on a layer. pj/facet writes them, given a column or a
map whose :col or :row names categories. Maps each key to a
description string.
Documentation for the panel aesthetics -- the mapping keys that send each value of a distinction to a panel of its own. Written on a pose, not on a layer. `pj/facet` writes them, given a column or a map whose `:col` or `:row` names categories. Maps each key to a description string.
(plan pose)(plan pose opts)Convert a pose into a plan. Literal composition of the atomic
steps: (-> x ->pose pose->draft draft->plan). The 2-arity folds
opts into the pose with pj/options first:
(-> x ->pose (options opts) plan).
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as pj/infer-mapping gives one, so (plan data)
works without an explicit pj/pose call.
For a leaf pose, returns a Plan record, with one panel per category
where the pose is faceted by a column. For a composite pose, and for
a leaf with a facet over columns, which is drawn as one cell per
column, returns a CompositePlan record
with :sub-plots tying each leaf path to its rect and sub-plan,
plus :chrome carrying the resolved layout geometry (title-band,
grid-rect, strip labels, shared-legend spec).
(plan pose)(plan pose {:title "My Plot"})Convert a pose into a plan. Literal composition of the atomic
steps: `(-> x ->pose pose->draft draft->plan)`. The 2-arity folds
opts into the pose with `pj/options` first:
`(-> x ->pose (options opts) plan)`.
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as `pj/infer-mapping` gives one, so `(plan data)`
works without an explicit `pj/pose` call.
For a leaf pose, returns a `Plan` record, with one panel per category
where the pose is faceted by a column. For a composite pose, and for
a leaf with a facet over columns, which is drawn as one cell per
column, returns a `CompositePlan` record
with `:sub-plots` tying each leaf path to its rect and sub-plan,
plus `:chrome` carrying the resolved layout geometry (title-band,
grid-rect, strip labels, shared-legend spec).
- `(plan pose)`
- `(plan pose {:title "My Plot"})`(plan->membrane plan-data)(plan->membrane plan-data opts)Convert a plan into a PlotjeMembrane -- a Membrane UI component
carrying the rendered drawables, plan-derived width and height,
and the plot title.
The 1-arity uses no rendering options. The 2-arity takes an
opts map with optional :tooltip, :theme, :color-values, etc.
The result implements membrane.ui IOrigin, IBounds, and
IChildren, so width and height are accessible via
(membrane.ui/width m) and (membrane.ui/height m). The title,
when set, rides as :plotje/title. Future per-membrane
attributes use the same :plotje/* namespaced-keyword convention.
The shape is captured by the PlotjeMembraneSchema in
scicloj.plotje.impl.membrane.
(plan->membrane (plan fr))(plan->membrane (plan fr) {:tooltip true})Convert a plan into a `PlotjeMembrane` -- a Membrane UI component
carrying the rendered drawables, plan-derived width and height,
and the plot title.
The 1-arity uses no rendering options. The 2-arity takes an
opts map with optional `:tooltip`, `:theme`, `:color-values`, etc.
The result implements `membrane.ui` `IOrigin`, `IBounds`, and
`IChildren`, so width and height are accessible via
`(membrane.ui/width m)` and `(membrane.ui/height m)`. The title,
when set, rides as `:plotje/title`. Future per-membrane
attributes use the same `:plotje/*` namespaced-keyword convention.
The shape is captured by the `PlotjeMembraneSchema` in
`scicloj.plotje.impl.membrane`.
- `(plan->membrane (plan fr))`
- `(plan->membrane (plan fr) {:tooltip true})`(plan->plot plan format opts)Convert a plan into a figure for the given format.
Dispatches on format keyword. Each renderer is a separate namespace
that registers a defmethod; :svg is always available.
(plan->plot (plan fr) :svg {})(plan->plot (plan fr) :plotly {})Convert a plan into a figure for the given format.
Dispatches on format keyword. Each renderer is a separate namespace
that registers a defmethod; `:svg` is always available.
- `(plan->plot (plan fr) :svg {})`
- `(plan->plot (plan fr) :plotly {})`(plan-layer? x)Return true if x is a plan-layer (resolved geometry for one mark).
Return true if x is a plan-layer (resolved geometry for one mark).
(plan? x)Return true if x is a plan (leaf or composite) -- the resolved
geometry returned by pj/plan.
Return true if x is a plan (leaf or composite) -- the resolved geometry returned by `pj/plan`.
(plot pose)(plot pose opts)Render a pose to a figure. The format keyword in the pose's
:opts ({:format :svg} -- default; {:format :bufimg} for
raster PNG via Java2D; or any other registered backend) selects
which membrane->plot defmethod runs.
On a composite pose, leaves are rendered individually and tiled
via the layout in the resolved chrome, in the same chosen format.
The pose flows through the canonical
pose -> draft -> plan -> membrane -> plot pipeline for both
leaf and composite shapes. pj/plot is a literal composition of
the public atomic steps:
(let [pose (->pose x) opts (:opts pose {}) fmt (or (:format opts) :svg) plan (-> pose pose->draft draft->plan)] (-> plan (plan->membrane opts) (membrane->plot fmt opts)))
Plan-derived dimensions ride as record fields on the membrane
(accessed via membrane.ui/width/membrane.ui/height); the
title rides as :plotje/title. membrane->plot reads them from
there.
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as pj/infer-mapping gives one, so (plot data)
renders the inferred default instead of a blank figure.
(plot pose)(plot pose {:width 800 :title "My Plot"})(plot pose {:format :bufimg}) -- returns a BufferedImage.Render a pose to a figure. The format keyword in the pose's
`:opts` (`{:format :svg}` -- default; `{:format :bufimg}` for
raster PNG via Java2D; or any other registered backend) selects
which `membrane->plot` defmethod runs.
On a composite pose, leaves are rendered individually and tiled
via the layout in the resolved chrome, in the same chosen format.
The pose flows through the canonical
`pose -> draft -> plan -> membrane -> plot` pipeline for both
leaf and composite shapes. pj/plot is a literal composition of
the public atomic steps:
`(let [pose (->pose x)
opts (:opts pose {})
fmt (or (:format opts) :svg)
plan (-> pose pose->draft draft->plan)]
(-> plan
(plan->membrane opts)
(membrane->plot fmt opts)))`
Plan-derived dimensions ride as record fields on the membrane
(accessed via `membrane.ui/width`/`membrane.ui/height`); the
title rides as `:plotje/title`. `membrane->plot` reads them from
there.
Raw data (a dataset or a bare collection of values) is given a
default mapping first, as `pj/infer-mapping` gives one, so `(plot data)`
renders the inferred default instead of a blank figure.
- `(plot pose)`
- `(plot pose {:width 800 :title "My Plot"})`
- `(plot pose {:format :bufimg})` -- returns a BufferedImage.Documentation for plot-level option keys. These are accepted by pj/options, pj/plan, and pj/plot but are inherently per-plot (text content or nested config override). Maps each key to [category description].
Documentation for plot-level option keys. These are accepted by pj/options, pj/plan, and pj/plot but are inherently per-plot (text content or nested config override). Maps each key to [category description].
(pose)(pose pose-or-data)(pose pose-or-data x-or-mapping)(pose pose-or-data x y-or-mapping)(pose pose-or-data x y mapping)Construct or extend a pose.
On raw data (first argument is not itself a pose):
(pj/pose) -- empty leaf.(pj/pose data) -- leaf with data and no mapping. On 1-3 column
datasets the draft infers one (:x, then :y, then :color), so
the pose renders without an explicit mapping call.(pj/pose data {:color :species}) -- leaf with aesthetic mapping.(pj/pose data :x-col) -- leaf with {:x :x-col}.(pj/pose data :x-col {:color :c}) -- :x and a mapping.(pj/pose data :x-col :y-col) -- leaf with :x and :y.(pj/pose data :x-col :y-col {:color :c}) -- :x, :y and a mapping.(pj/pose data p), where p is a pose -- p drawn from data,
as a cell of pj/arrange is; (pj/pose nil p) is p. A pose
carrying data of its own keeps it. A mapping after p merges into
its mapping; columns after p, or a pose before it, report an
error.(pj/pose data [:a :b :c]) -- several columns where one column
goes are a series, drawn on one panel with a colour for each
column. Pairs of columns, [[:a :b] [:c :d]], report an error:
pj/arrange takes each pair as a mapping, {:x :a :y :b}.Threaded over an existing pose (first argument is a pose):
(pj/pose fr) -- pass-through; lifts a literal map for notebook
auto-render if it is not already tagged.(pj/pose fr :x-col :y-col) -- merge :x and :y into the
pose's mapping (the root's, on a composite), replacing the ones it
had. Layers that name no columns of their own read the new ones.
pj/pose does not add panels; for panels side by side, use
pj/arrange.(pj/pose fr :x-col :y-col {:color :c}) -- same, with the
aesthetic merged too.(pj/pose fr {:color :c}) -- aesthetic-only: merge into the
mapping.(pj/pose fr {}) is fr, and writing the same mapping twice gives
the same pose.(pj/pose fr {:data X :color :c}) -- extend mapping AND replace the
top-level data with X.(pj/pose fr {:where {:drv "f"}}) -- a row filter, kept on the
pose: it draws only the rows whose columns hold those values. A
second :where merges into the first, a column at a time. Written
beside a mapping on raw data too, and as (pj/pose nil {:where ...}) it is the cell of pj/arrange written {:where ...}.On a hand-built pose-shaped map (1-arity, input has :layers,
:poses, or a map-valued :mapping): the map is validated and tagged with Kindly auto-render
metadata, but its keys are not reordered and its :data is not
coerced -- the typed shape is preserved verbatim. A composite is
supported at any depth: a sub-pose that itself has :poses nests,
which is the shape pj/arrange builds and the shape it accepts
among its own inputs.
Writing a mapping out in full. Any mapping value may be written as a map naming its source, and optionally which side of the scale to read it through:
{:column :species} -- the column, even where a value of that
name could be drawn.{:value "blue"} -- the color, even where the data carries a
column called blue.{:scale false} -- draw the value as it stands rather than pass
it through the aesthetic's scale. On a column this is the only
route to an identity scale: {:color {:column :hex :scale false}}
draws the colors the column holds.{:scale true} -- read it as data. {:color {:value "Model A" :scale true}} draws one palette color and earns a legend entry.The conventions decide when :scale is absent: a column passes
through the scale, a written value is drawn on the appearance
aesthetics and is a data value on :x and :y. See
pj/layer-mapping-docs for what each aesthetic accepts, and
pj/scale for choosing a scale's type.
Construct or extend a pose.
**On raw data (first argument is not itself a pose):**
- `(pj/pose)` -- empty leaf.
- `(pj/pose data)` -- leaf with data and no mapping. On 1-3 column
datasets the draft infers one (`:x`, then `:y`, then `:color`), so
the pose renders without an explicit mapping call.
- `(pj/pose data {:color :species})` -- leaf with aesthetic mapping.
- `(pj/pose data :x-col)` -- leaf with `{:x :x-col}`.
- `(pj/pose data :x-col {:color :c})` -- `:x` and a mapping.
- `(pj/pose data :x-col :y-col)` -- leaf with `:x` and `:y`.
- `(pj/pose data :x-col :y-col {:color :c})` -- `:x`, `:y` and a mapping.
- `(pj/pose data p)`, where `p` is a pose -- `p` drawn from `data`,
as a cell of `pj/arrange` is; `(pj/pose nil p)` is `p`. A pose
carrying data of its own keeps it. A mapping after `p` merges into
its mapping; columns after `p`, or a pose before it, report an
error.
- `(pj/pose data [:a :b :c])` -- several columns where one column
goes are a series, drawn on one panel with a colour for each
column. Pairs of columns, `[[:a :b] [:c :d]]`, report an error:
`pj/arrange` takes each pair as a mapping, `{:x :a :y :b}`.
**Threaded over an existing pose (first argument is a pose):**
- `(pj/pose fr)` -- pass-through; lifts a literal map for notebook
auto-render if it is not already tagged.
- `(pj/pose fr :x-col :y-col)` -- merge `:x` and `:y` into the
pose's mapping (the root's, on a composite), replacing the ones it
had. Layers that name no columns of their own read the new ones.
`pj/pose` does not add panels; for panels side by side, use
`pj/arrange`.
- `(pj/pose fr :x-col :y-col {:color :c})` -- same, with the
aesthetic merged too.
- `(pj/pose fr {:color :c})` -- aesthetic-only: merge into the
mapping.
- `(pj/pose fr {})` is `fr`, and writing the same mapping twice gives
the same pose.
- `(pj/pose fr {:data X :color :c})` -- extend mapping AND replace the
top-level data with X.
- `(pj/pose fr {:where {:drv "f"}})` -- a row filter, kept on the
pose: it draws only the rows whose columns hold those values. A
second `:where` merges into the first, a column at a time. Written
beside a mapping on raw data too, and as `(pj/pose nil {:where
...})` it is the cell of `pj/arrange` written `{:where ...}`.
**On a hand-built pose-shaped map (1-arity, input has `:layers`,
`:poses`, or a map-valued `:mapping`):** the map is validated and tagged with Kindly auto-render
metadata, but its keys are not reordered and its `:data` is not
coerced -- the typed shape is preserved verbatim. A composite is
supported at any depth: a sub-pose that itself has `:poses` nests,
which is the shape `pj/arrange` builds and the shape it accepts
among its own inputs.
**Writing a mapping out in full.** Any mapping value may be written
as a map naming its source, and optionally which side of the scale
to read it through:
- `{:column :species}` -- the column, even where a value of that
name could be drawn.
- `{:value "blue"}` -- the color, even where the data carries a
column called blue.
- `{:scale false}` -- draw the value as it stands rather than pass
it through the aesthetic's scale. On a column this is the only
route to an identity scale: `{:color {:column :hex :scale false}}`
draws the colors the column holds.
- `{:scale true}` -- read it as data. `{:color {:value "Model A"
:scale true}}` draws one palette color and earns a legend entry.
The conventions decide when `:scale` is absent: a column passes
through the scale, a written value is drawn on the appearance
aesthetics and is a data value on `:x` and `:y`. See
`pj/layer-mapping-docs` for what each aesthetic accepts, and
`pj/scale` for choosing a scale's type.(pose->draft pose)Single-step transition: convert a pose into a draft. Dispatches on
pose shape -- a leaf pose becomes a LeafDraft (a record carrying
:layers -- a vector of one map per applicable layer with merged
scope -- and :opts -- the pose-level options that flow into the
plan stage); a composite pose, and a leaf with a facet over columns,
which is drawn as one cell per column, becomes a CompositeDraft carrying
per-leaf drafts (each contextualized with shared-scale domains and
chrome-driven opt adjustments), the resolved chrome geometry, and
the layout (path -> rect).
(pose->draft (pj/lay-point data :x :y))Single-step transition: convert a pose into a draft. Dispatches on pose shape -- a leaf pose becomes a `LeafDraft` (a record carrying `:layers` -- a vector of one map per applicable layer with merged scope -- and `:opts` -- the pose-level options that flow into the plan stage); a composite pose, and a leaf with a facet over columns, which is drawn as one cell per column, becomes a `CompositeDraft` carrying per-leaf drafts (each contextualized with shared-scale domains and chrome-driven opt adjustments), the resolved chrome geometry, and the layout (path -> rect). - `(pose->draft (pj/lay-point data :x :y))`
(pose? x)Return true if x is a pose-shaped plain map: one carrying
:layers, :poses, or a map-valued :mapping.
Return true if x is a pose-shaped plain map: one carrying `:layers`, `:poses`, or a map-valued `:mapping`.
(position-doc k)Return the prose description for a position keyword.
Returns "(no description)" if no [:key :doc] defmethod is registered.
(position-doc :dodge) returns "Shift groups side-by-side within a band".Return the prose description for a position keyword. Returns `"(no description)"` if no `[:key :doc]` defmethod is registered. - `(position-doc :dodge)` returns `"Shift groups side-by-side within a band"`.
(registered-layer-types)Return all registered layer types as a map of keyword -> layer-type map. Useful for generating documentation tables.
Return all registered layer types as a map of keyword -> layer-type map. Useful for generating documentation tables.
(save pose path)(save pose path opts)Save a plot to a file. Format resolution, in precedence order:
:format in the 3-arity opts map wins (must be :svg or
:png).:format on the pose's :opts (:svg or :png; legacy
:bufimg is translated to :png)..svg -> :svg,
.png -> :png).:svg.When the resolved format and the path extension disagree, prints a warning -- the file still gets the bytes the resolved format produces, but the extension is misleading.
The save vocabulary names the file format. The plot vocabulary
(pj/plot's :format) names the JVM return type -- :svg for
hiccup, :bufimg for a Java2D BufferedImage. A pose-level
:format flows into both contexts; save reinterprets :bufimg
as :png because the file on disk is a PNG.
Arguments:
pose -- a pose, or raw data (a dataset or a bare collection of
values), which is given a default mapping first, as
pj/infer-mapping gives one.path -- file path (string or java.io.File).opts -- same options as plot, but :format accepts only :svg
or :png.Tooltip and brush interactivity are not included in saved files.
Returns the written file as a java.io.File carrying :kind/image
metadata, so evaluating a pj/save call in a notebook also shows
the saved chart. The file prints as its path and compares equal to
a plain java.io.File on the same path, so (str (pj/save ...))
still gives the path string.
(save my-pose "plot.svg") -- SVG.(save my-pose "plot.png") -- inferred PNG.(save my-pose "plot.svg" {:format :png}) -- opts override (warns).Save a plot to a file. Format resolution, in precedence order:
1. `:format` in the 3-arity `opts` map wins (must be `:svg` or
`:png`).
2. `:format` on the pose's `:opts` (`:svg` or `:png`; legacy
`:bufimg` is translated to `:png`).
3. Otherwise inferred from the path extension (`.svg` -> `:svg`,
`.png` -> `:png`).
4. Default `:svg`.
When the resolved format and the path extension disagree, prints
a warning -- the file still gets the bytes the resolved format
produces, but the extension is misleading.
The save vocabulary names the file format. The plot vocabulary
(`pj/plot`'s `:format`) names the JVM return type -- `:svg` for
hiccup, `:bufimg` for a Java2D BufferedImage. A pose-level
`:format` flows into both contexts; save reinterprets `:bufimg`
as `:png` because the file on disk is a PNG.
Arguments:
- `pose` -- a pose, or raw data (a dataset or a bare collection of
values), which is given a default mapping first, as
`pj/infer-mapping` gives one.
- `path` -- file path (string or `java.io.File`).
- `opts` -- same options as plot, but `:format` accepts only `:svg`
or `:png`.
Tooltip and brush interactivity are not included in saved files.
Returns the written file as a `java.io.File` carrying `:kind/image`
metadata, so evaluating a `pj/save` call in a notebook also shows
the saved chart. The file prints as its path and compares equal to
a plain `java.io.File` on the same path, so `(str (pj/save ...))`
still gives the path string.
- `(save my-pose "plot.svg")` -- SVG.
- `(save my-pose "plot.png")` -- inferred PNG.
- `(save my-pose "plot.svg" {:format :png})` -- opts override (warns).(scale pose aesthetic scale-type)Set scale on a pose. The scale applies to the pose it is called on
and to everything below it: called on the pose you are building, it
covers the whole plot; called on one cell before the cells are
arranged, that cell alone, so two cells can carry different scales.
Under faceting every panel comes from one pose, so the panels share
a type; {:share-scales #{}} varies their domains.
A mapping written out in full can name a scale too, for that one
mapping: {:size {:column :weight :scale :log}}. Where a scale is
set at more than one scope the settings accumulate and the inner
scope wins, key by key. Written on the same pose, this call is the
inner one; written on a layer, the layer's mapping is.
Accepts a type keyword or a scale spec map. :type and :domain
belong to every scale. The rest are per aesthetic, and a key an
aesthetic does not read reports an error:
:label, the title of whatever explains its
scale to a reader: the axis for :x and :y, the legend for the
rest. It wins over the <aesthetic>-label plot option -- :x-label,
:color-label and their siblings -- which names the same thing one
scope further out.:x and :y take :include, a value the axis has to reach, or a
collection of them. {:include 0} puts zero on the axis so that
lengths drawn along it are proportional to the values, and the
value lands exactly at the edge of the panel. :include extends
the extent the data gives where :domain replaces it, so the two
are not written together, and it is a set of values where a
:domain is an ordered pair.:x and :y take :breaks (explicit tick locations; []
draws the axis with no ticks, tick labels or grid lines, as
ggplot2's breaks = NULL does, while nil leaves the default
ticks),
:tick-labels (custom tick text paired with :breaks),
:n-ticks (about this many ticks) and :tick-spacing (about
this much room in drawing units per tick). A numeric axis reads
whichever of the last two is named; a categorical one is ticked at
its categories, which :n-ticks thins.:size and :alpha take :range -- what the aesthetic spans, in
the quantity the mark draws it as, so [2 8] on :size is a
radius in drawing units.:size further takes :by, how a value spreads across that range
(:sqrt by default, or :linear or :area), and :from-zero,
which anchors both the domain and the range at zero so that twice
the value is twice the ink.:shape takes :values, the marker symbols to draw with.:color and :fill take :range, the gradient a numeric column
is read through; :color also takes :values, the colours a
categorical column is drawn in. Both take :midpoint, the value
the middle of the gradient is drawn at.A value outside :domain is drawn at the nearer end rather than
dropped, so a narrower domain says what the reader should compare
without leaving rows off the panel. On an axis a :domain is two
finite numbers, or two dates where the column is temporal; against a
categorical column it is the list of categories, in the order they
are to be drawn.
Aesthetics and accepted scale types:
:x, :y) accept :linear, :log,
:categorical.:size, :alpha, :fill,
:color) accept :linear and :log only -- :categorical does
not apply.:shape accepts :categorical
only -- :linear and :log do not apply to a discrete encoding.:group reports an error: it draws nothing of its own, so there is no
scale to set. It splits a layer into one drawn group per value, and
the order of those groups is the order of the data.
To take an aesthetic off its scale for one mapping rather than
choose a type for the whole plot, write that mapping out in full:
{:color {:column :hex :scale false}} draws the column's values as
they stand, and {:size {:value 7 :scale true}} sends a written
value through the scale. See pj/layer-mapping-docs for :color and
:size.
A :domain has two readings, and the domain itself decides which:
two numbers are a range, and anything else is a list of categories.
On a categorical :color or :shape column it gives the
order the categories are placed in, which the legend and the palette
both follow. A category the list leaves out is still drawn, ordered
after the ones listed, with a warning. On :shape, :values
supplies the symbols to draw those categories with in that same
order; pj/shape-symbols lists the ones available.
On :col and :row a :domain gives the order the panels of a
facet are drawn in, by the same rule: the listed categories first,
and a category the list leaves out after them, with a warning. It is
the one scale setting a panel aesthetic takes, and it orders the
panels of one column, not of a compound key.
On a numeric :color or :fill column it gives the two ends of the
gradient, as it gives the ends of the drawn range on :size and
:alpha. That is how every panel of a facet can be given one scale
to share.
:tick-labels requires :breaks and must match it in count. Use it
to draw numeric breaks with text of your own -- for example, days of
the week on a tile heatmap.
:n-ticks asks for about that many ticks. On a categorical axis it
thins a crowded one, which otherwise labels every category; on a
numeric axis it replaces the count that :tick-spacing would give.
:tick-spacing asks for about that much room, in drawing units,
per tick, and the count follows from how many fit. It is a target
in the way :n-ticks is: the ticks are still rounded to values a
reader can read off, so the room each one ends up with can come out
under the number asked for. It steers the choice of numeric ticks
and does nothing on a categorical axis, which says so. The :x-tick-spacing / :y-tick-spacing plot options name the
same setting one scope further out.
(scale pose :x :log) -- log scale on x-axis.(scale pose :x {:type :categorical :domain [...]}) -- explicit
category order.(scale pose :x {:n-ticks 8}) -- about eight ticks, which on a
crowded categorical axis thins it to eight of its categories.(scale pose :y {:type :linear :breaks [0 5 10]}) -- pin tick locations.(scale pose :x {:type :linear :breaks [1 2 3 4 5 6 7] :tick-labels ["Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"]})
-- numeric positions with custom tick text.(scale pose :y {:type :log :domain [1 1000]}) -- log scale with
explicit range.(scale pose :size :log) -- log-spaced point sizes.(scale pose :size {:range [3 14]}) -- wider points than the
default 2 to 8.(scale pose :size {:by :area :from-zero true}) -- a point of
twice the value covers twice the ink.(scale pose :fill :log) -- log-spaced tile fill.(scale pose :shape {:type :categorical :domain [...]}) -- shape
legend order.(scale pose :shape {:values [:cross :plus]}) -- pick the symbols.(scale pose :col {:domain [4 5 6 8]}) -- the order of a facet's
panels.Set scale on a pose. The scale applies to the pose it is called on
and to everything below it: called on the pose you are building, it
covers the whole plot; called on one cell before the cells are
arranged, that cell alone, so two cells can carry different scales.
Under faceting every panel comes from one pose, so the panels share
a type; `{:share-scales #{}}` varies their domains.
A mapping written out in full can name a scale too, for that one
mapping: `{:size {:column :weight :scale :log}}`. Where a scale is
set at more than one scope the settings accumulate and the inner
scope wins, key by key. Written on the same pose, this call is the
inner one; written on a layer, the layer's mapping is.
Accepts a type keyword or a scale spec map. `:type` and `:domain`
belong to every scale. The rest are per aesthetic, and a key an
aesthetic does not read reports an error:
- Every aesthetic takes `:label`, the title of whatever explains its
scale to a reader: the axis for `:x` and `:y`, the legend for the
rest. It wins over the `<aesthetic>-label` plot option -- `:x-label`,
`:color-label` and their siblings -- which names the same thing one
scope further out.
- `:x` and `:y` take `:include`, a value the axis has to reach, or a
collection of them. `{:include 0}` puts zero on the axis so that
lengths drawn along it are proportional to the values, and the
value lands exactly at the edge of the panel. `:include` extends
the extent the data gives where `:domain` replaces it, so the two
are not written together, and it is a set of values where a
`:domain` is an ordered pair.
- `:x` and `:y` take `:breaks` (explicit tick locations; `[]`
draws the axis with no ticks, tick labels or grid lines, as
ggplot2's `breaks = NULL` does, while `nil` leaves the default
ticks),
`:tick-labels` (custom tick text paired with `:breaks`),
`:n-ticks` (about this many ticks) and `:tick-spacing` (about
this much room in drawing units per tick). A numeric axis reads
whichever of the last two is named; a categorical one is ticked at
its categories, which `:n-ticks` thins.
- `:size` and `:alpha` take `:range` -- what the aesthetic spans, in
the quantity the mark draws it as, so `[2 8]` on `:size` is a
radius in drawing units.
- `:size` further takes `:by`, how a value spreads across that range
(`:sqrt` by default, or `:linear` or `:area`), and `:from-zero`,
which anchors both the domain and the range at zero so that twice
the value is twice the ink.
- `:shape` takes `:values`, the marker symbols to draw with.
- `:color` and `:fill` take `:range`, the gradient a numeric column
is read through; `:color` also takes `:values`, the colours a
categorical column is drawn in. Both take `:midpoint`, the value
the middle of the gradient is drawn at.
A value outside `:domain` is drawn at the nearer end rather than
dropped, so a narrower domain says what the reader should compare
without leaving rows off the panel. On an axis a `:domain` is two
finite numbers, or two dates where the column is temporal; against a
categorical column it is the list of categories, in the order they
are to be drawn.
Aesthetics and accepted scale types:
- The axis aesthetics (`:x`, `:y`) accept `:linear`, `:log`,
`:categorical`.
- The continuous appearance aesthetics (`:size`, `:alpha`, `:fill`,
`:color`) accept `:linear` and `:log` only -- `:categorical` does
not apply.
- The discrete appearance aesthetic `:shape` accepts `:categorical`
only -- `:linear` and `:log` do not apply to a discrete encoding.
`:group` reports an error: it draws nothing of its own, so there is no
scale to set. It splits a layer into one drawn group per value, and
the order of those groups is the order of the data.
To take an aesthetic off its scale for one mapping rather than
choose a type for the whole plot, write that mapping out in full:
`{:color {:column :hex :scale false}}` draws the column's values as
they stand, and `{:size {:value 7 :scale true}}` sends a written
value through the scale. See `pj/layer-mapping-docs` for `:color` and
`:size`.
A `:domain` has two readings, and the domain itself decides which:
two numbers are a range, and anything else is a list of categories.
On a categorical `:color` or `:shape` column it gives the
order the categories are placed in, which the legend and the palette
both follow. A category the list leaves out is still drawn, ordered
after the ones listed, with a warning. On `:shape`, `:values`
supplies the symbols to draw those categories with in that same
order; `pj/shape-symbols` lists the ones available.
On `:col` and `:row` a `:domain` gives the order the panels of a
facet are drawn in, by the same rule: the listed categories first,
and a category the list leaves out after them, with a warning. It is
the one scale setting a panel aesthetic takes, and it orders the
panels of one column, not of a compound key.
On a numeric `:color` or `:fill` column it gives the two ends of the
gradient, as it gives the ends of the drawn range on `:size` and
`:alpha`. That is how every panel of a facet can be given one scale
to share.
`:tick-labels` requires `:breaks` and must match it in count. Use it
to draw numeric breaks with text of your own -- for example, days of
the week on a tile heatmap.
`:n-ticks` asks for about that many ticks. On a categorical axis it
thins a crowded one, which otherwise labels every category; on a
numeric axis it replaces the count that `:tick-spacing` would give.
`:tick-spacing` asks for about that much room, in drawing units,
per tick, and the count follows from how many fit. It is a target
in the way `:n-ticks` is: the ticks are still rounded to values a
reader can read off, so the room each one ends up with can come out
under the number asked for. It steers the choice of numeric ticks
and does nothing on a categorical axis, which says so. The `:x-tick-spacing` / `:y-tick-spacing` plot options name the
same setting one scope further out.
- `(scale pose :x :log)` -- log scale on x-axis.
- `(scale pose :x {:type :categorical :domain [...]})` -- explicit
category order.
- `(scale pose :x {:n-ticks 8})` -- about eight ticks, which on a
crowded categorical axis thins it to eight of its categories.
- `(scale pose :y {:type :linear :breaks [0 5 10]})` -- pin tick locations.
- `(scale pose :x {:type :linear :breaks [1 2 3 4 5 6 7]
:tick-labels ["Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"]})`
-- numeric positions with custom tick text.
- `(scale pose :y {:type :log :domain [1 1000]})` -- log scale with
explicit range.
- `(scale pose :size :log)` -- log-spaced point sizes.
- `(scale pose :size {:range [3 14]})` -- wider points than the
default 2 to 8.
- `(scale pose :size {:by :area :from-zero true})` -- a point of
twice the value covers twice the ink.
- `(scale pose :fill :log)` -- log-spaced tile fill.
- `(scale pose :shape {:type :categorical :domain [...]})` -- shape
legend order.
- `(scale pose :shape {:values [:cross :plus]})` -- pick the symbols.
- `(scale pose :col {:domain [4 5 6 8]})` -- the order of a facet's
panels.(scale-doc k)Return the prose description for a scale keyword.
Returns "(no description)" if no [:key :doc] defmethod is registered.
(scale-doc :linear) returns "Continuous linear mapping".Return the prose description for a scale keyword. Returns `"(no description)"` if no `[:key :doc]` defmethod is registered. - `(scale-doc :linear)` returns `"Continuous linear mapping"`.
The series a mapping value asks for, as {:cols [...] :as label}, or
nil where it asks for none. See pose/series-mapping.
The series a mapping value asks for, as `{:cols [...] :as label}`, or
nil where it asks for none. See `pose/series-mapping`.(series-mapping? v)True of a mapping value that asks for a series.
True of a mapping value that asks for a series.
(set-config! m)Set global config overrides. Persists across calls until reset.
(set-config! {:color-values :dark2 :theme {:bg "#FFFFFF"}}) --
override the categorical colours and the background.(set-config! nil) -- reset to defaults.Set global config overrides. Persists across calls until reset.
- `(set-config! {:color-values :dark2 :theme {:bg "#FFFFFF"}})` --
override the categorical colours and the background.
- `(set-config! nil)` -- reset to defaults.(shape-palette)The symbols assigned to categories automatically, in order. A plot with more categories than these repeats a symbol, so two categories cannot be told apart, and that warns at plan time.
Published apart from pj/shape-symbols because the two answer
different questions: this is what a plot draws when you say nothing,
and pj/shape-symbols is what you may write. Adding to this one
changes which symbol every existing plot gives each category; adding
to the other takes nothing away.
A function rather than a value, as pj/config is.
The symbols assigned to categories automatically, in order. A plot with more categories than these repeats a symbol, so two categories cannot be told apart, and that warns at plan time. Published apart from `pj/shape-symbols` because the two answer different questions: this is what a plot draws when you say nothing, and `pj/shape-symbols` is what you may write. Adding to this one changes which symbol every existing plot gives each category; adding to the other takes nothing away. A function rather than a value, as `pj/config` is.
(shape-symbols)Every marker symbol a :shape mapping can draw.
(pj/shape-palette) is the first part, in the order categories are
assigned. The rest are drawn only when named -- :circle-open is a
ring rather than a disc, which keeps overlapping points countable.
Pass a selection of these as :values to
(pj/scale pose :shape {:values [...]}) to choose them yourself, or
name one for a whole layer with {:shape :circle-open}.
A function rather than a value, as pj/config is: the list grows
from one release to the next, and a value read at load time could
not follow it. Adding a symbol of your own is not supported -- the
shapes are drawn by a fixed table in the renderer.
Every marker symbol a `:shape` mapping can draw.
`(pj/shape-palette)` is the first part, in the order categories are
assigned. The rest are drawn only when named -- `:circle-open` is a
ring rather than a disc, which keeps overlapping points countable.
Pass a selection of these as `:values` to
`(pj/scale pose :shape {:values [...]})` to choose them yourself, or
name one for a whole layer with `{:shape :circle-open}`.
A function rather than a value, as `pj/config` is: the list grows
from one release to the next, and a value read at load time could
not follow it. Adding a symbol of your own is not supported -- the
shapes are drawn by a fixed table in the renderer.(stat-doc k)Return the prose description for a stat keyword.
Returns "(no description)" if no [:key :doc] defmethod is registered.
(stat-doc :bin) returns "Bin numerical values into ranges".Return the prose description for a stat keyword. Returns `"(no description)"` if no `[:key :doc]` defmethod is registered. - `(stat-doc :bin)` returns `"Bin numerical values into ranges"`.
(svg-summary svg-or-pose)(svg-summary svg-or-pose theme)Extract structural summary from SVG hiccup for testing.
Returns a map of what the SVG draws -- :width, :height, :panels,
:points, :lines, :dashed-lines, :dash-patterns, :polygons,
:tiles, :visible-tiles, :texts, :bold-texts, :italic-texts,
:label-boxes and :clips, and the sets :sizes, :colors,
:alphas and :shapes of the values the marks are drawn with --
useful for asserting plot structure.
Accepts SVG hiccup, or anything pj/plot accepts, which is rendered
to SVG first: a pose, or raw data, drawn as pj/plot draws it.
Anything else is reported by pj/plot.
(svg-summary (plot fr)) -- summary of rendered SVG.(svg-summary my-pose) -- auto-renders pose (leaf or composite).Extract structural summary from SVG hiccup for testing. Returns a map of what the SVG draws -- `:width`, `:height`, `:panels`, `:points`, `:lines`, `:dashed-lines`, `:dash-patterns`, `:polygons`, `:tiles`, `:visible-tiles`, `:texts`, `:bold-texts`, `:italic-texts`, `:label-boxes` and `:clips`, and the sets `:sizes`, `:colors`, `:alphas` and `:shapes` of the values the marks are drawn with -- useful for asserting plot structure. Accepts SVG hiccup, or anything `pj/plot` accepts, which is rendered to SVG first: a pose, or raw data, drawn as `pj/plot` draws it. Anything else is reported by `pj/plot`. - `(svg-summary (plot fr))` -- summary of rendered SVG. - `(svg-summary my-pose)` -- auto-renders pose (leaf or composite).
(to-data panel data)(to-data panel cx cy)What data values the canvas coordinates name, for one panel of
pj/frames. The direction an interaction reads: which value is
under the pointer, which range a selection covers.
Throws under a coordinate system with no inverse. :polar maps x and
y together to an angle and a radius, so a canvas position there does
not name one pair of data values. A panel entry reports which case it
is in :invertible?.
(to-data panel 412.0 88.5) returns [x y] in data values(to-data panel {:x [412.0] :y [88.5]}) returns a dataset with the
same two column names, now in data valuesA continuous axis answers with numbers, so its column is :float64,
and a coordinate pj/to-drawing produced comes back as the value it
was given. A categorical axis answers with the category whose band
holds the coordinate, so its column holds those -- which is what an
interaction wants, and is not the inverse of pj/to-drawing there:
a place between two categories, such as 1.5, comes back as one of
them.
A pose, a plan or the whole frames map in the panel's place reports an error naming which of them it got and the call that reaches a panel entry from there.
What data values the canvas coordinates name, for one panel of
`pj/frames`. The direction an interaction reads: which value is
under the pointer, which range a selection covers.
Throws under a coordinate system with no inverse. `:polar` maps x and
y together to an angle and a radius, so a canvas position there does
not name one pair of data values. A panel entry reports which case it
is in `:invertible?`.
- `(to-data panel 412.0 88.5)` returns `[x y]` in data values
- `(to-data panel {:x [412.0] :y [88.5]})` returns a dataset with the
same two column names, now in data values
A continuous axis answers with numbers, so its column is `:float64`,
and a coordinate `pj/to-drawing` produced comes back as the value it
was given. A categorical axis answers with the category whose band
holds the coordinate, so its column holds those -- which is what an
interaction wants, and is not the inverse of `pj/to-drawing` there:
a place between two categories, such as `1.5`, comes back as one of
them.
A pose, a plan or the whole frames map in the panel's place reports
an error naming which of them it got and the call that reaches a
panel entry from there.(to-drawing panel data)(to-drawing panel x y)Where data values land on the canvas, for one panel of pj/frames.
Takes a panel entry -- an element of (:panels (frames plot)) -- and
either one x and y, or a dataset of them with :x and :y columns.
The dataset arity maps whole columns and builds the panel's scales
once, so it is the one to reach for when placing many of them.
(to-drawing panel 3.2 21.0) returns [x y] in canvas coordinates(to-drawing panel {:x [3.2 4.0] :y [21.0 18.5]}) returns a dataset
with the same two column names, now in canvas coordinatesA dataset rather than a collection of pairs because the two
coordinates of a point share one index space, which a dataset states
and two loose sequences only promise. Anything tc/dataset coerces
works.
The result is in canvas coordinates, measured from the top left of the
whole image. A {:in :drawing-area} layer measures from the drawing
area's own corner instead, so drawing these coordinates back means
subtracting that corner first.
A pose, a plan or the whole frames map in the panel's place reports an error naming which of them it got and the call that reaches a panel entry from there.
A categorical axis is a band scale, and it answers two kinds of
value. A category sits at the middle of its band. A number is a
place among the categories, counted from one: 1 is the first
category, 1.5 sits halfway to the second, and on n categories
the axis runs from 0.5 to n + 0.5. Anything else throws, naming
the value and the categories it could have been -- a category the
axis does not carry, or a number past the ends of the axis.
Under :coord :flip the arguments stay in data order, even though a
panel entry's :x-domain and :y-domain describe the drawn axes and
the flip has already swapped those.
Where data values land on the canvas, for one panel of `pj/frames`.
Takes a panel entry -- an element of `(:panels (frames plot))` -- and
either one x and y, or a dataset of them with `:x` and `:y` columns.
The dataset arity maps whole columns and builds the panel's scales
once, so it is the one to reach for when placing many of them.
- `(to-drawing panel 3.2 21.0)` returns `[x y]` in canvas coordinates
- `(to-drawing panel {:x [3.2 4.0] :y [21.0 18.5]})` returns a dataset
with the same two column names, now in canvas coordinates
A dataset rather than a collection of pairs because the two
coordinates of a point share one index space, which a dataset states
and two loose sequences only promise. Anything `tc/dataset` coerces
works.
The result is in canvas coordinates, measured from the top left of the
whole image. A `{:in :drawing-area}` layer measures from the drawing
area's own corner instead, so drawing these coordinates back means
subtracting that corner first.
A pose, a plan or the whole frames map in the panel's place reports
an error naming which of them it got and the call that reaches a
panel entry from there.
A categorical axis is a band scale, and it answers two kinds of
value. A category sits at the middle of its band. A number is a
place among the categories, counted from one: `1` is the first
category, `1.5` sits halfway to the second, and on `n` categories
the axis runs from `0.5` to `n + 0.5`. Anything else throws, naming
the value and the categories it could have been -- a category the
axis does not carry, or a number past the ends of the axis.
Under `:coord :flip` the arguments stay in data order, even though a
panel entry's `:x-domain` and `:y-domain` describe the drawn axes and
the flip has already swapped those.(valid-membrane? membrane)Check if a membrane conforms to the Malli schema.
(valid-membrane? (membrane pose)) -- true if valid.Check if a membrane conforms to the Malli schema. - `(valid-membrane? (membrane pose))` -- true if valid.
(valid-plan? plan)Check if a plan conforms to the Malli schema.
(valid-plan? (plan pose)) -- true if valid.Check if a plan conforms to the Malli schema. - `(valid-plan? (plan pose))` -- true if valid.
(valid-pose? pose)Check if a pose conforms to the Malli schema.
(valid-pose? (lay-point data :x :y)) -- true if valid.The schema is structural and deliberately permissive: it says what
shape a pose has, not whether the columns it names are in the data.
A pose built by pj/pose, pj/lay-*, pj/options, pj/facet,
pj/arrange, pj/coord or pj/scale conforms by construction; the
check is for a pose that has been reached into and changed, which is
the one place the constructors cannot speak for.
Check if a pose conforms to the Malli schema. - `(valid-pose? (lay-point data :x :y))` -- true if valid. The schema is structural and deliberately permissive: it says what shape a pose has, not whether the columns it names are in the data. A pose built by `pj/pose`, `pj/lay-*`, `pj/options`, `pj/facet`, `pj/arrange`, `pj/coord` or `pj/scale` conforms by construction; the check is for a pose that has been reached into and changed, which is the one place the constructors cannot speak for.
(with-config config-map & body)Execute body with thread-local config overrides.
Overrides take precedence over set-config! and defaults,
but plot options still win.
(with-config {:theme {:bg "#FFF"}} (plot ...))Execute body with thread-local config overrides.
Overrides take precedence over `set-config!` and defaults,
but plot options still win.
- `(with-config {:theme {:bg "#FFF"}} (plot ...))`(with-data pose data)Supply or replace the top-level dataset on a pose. Useful for building a template once and applying it to different datasets:
(def template (-> (pj/pose)
(pj/pose :x :y {:color :group})
pj/lay-point
(pj/lay-smooth {:stat :linear-model})))
(-> template (pj/with-data my-data))
(-> template (pj/with-data other-data))
At attach time, every keyword column reference in the template's
mapping, layers, sub-poses, and facet options must exist in the
dataset -- otherwise an error is thrown naming the missing columns
and listing what is available. Per-layer / per-sub-pose :data
still overrides the top-level data.
Supply or replace the top-level dataset on a pose.
Useful for building a template once and applying it to different
datasets:
(def template (-> (pj/pose)
(pj/pose :x :y {:color :group})
pj/lay-point
(pj/lay-smooth {:stat :linear-model})))
(-> template (pj/with-data my-data))
(-> template (pj/with-data other-data))
At attach time, every keyword column reference in the template's
mapping, layers, sub-poses, and facet options must exist in the
dataset -- otherwise an error is thrown naming the missing columns
and listing what is available. Per-layer / per-sub-pose `:data`
still overrides the top-level data.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |