All notable changes to this project will be documented in this file. This change log follows the conventions of keepachangelog.com.
This release mostly follows the label-positioning topic thread. Many thanks to @behrica, @timothypratley, @phronmophobic, and @generateme for the fruitful work over that thread.
pj/frames reports where a plot's panels sit on the canvas: per panel, the :panel-box and :drawing-area rectangles in drawing units, plus the :canvas once for the plot. A composite reports every cell in canvas coordinates. Plain data throughout. - thanks, @timothypratleypj/to-drawing and pj/to-data map between data space and drawing space for one panel of pj/frames. Both take a single x and y, or a dataset with :x and :y columns, and answer in kind. pj/to-data throws under :polar, which has no inverse; a panel's :invertible? says which case it is.:offset-x and :offset-y shift a layer by a number of drawing units, after the scales run -- the distance a label needs to clear its point. A nudge cannot: it shifts by a data amount, and throws on a categorical axis. Every layer type takes them, and a numeric domain widens to keep an offset label whole. - thanks, @behrica:x and :y may be given as a value, not only as a column. Both as values draws one mark: (pj/lay-text pose {:x 6.5 :y 3.5 :text "mean"}). One value beside a column repeats for every row, so {:x 33 :y :revenue} labels each row at one fixed x. The value counts toward the axis domains, as ggplot2's annotate() does. - thanks, @behrica:in names the space a layer's :x and :y are in: :data (default) or :drawing-area, which measures drawing units from the top left of the panel background. (pj/lay-text pose {:in :drawing-area :x 12 :y 12 :text "n = 150"}) puts a note in the corner without touching the domains.pj/lay-rule-* and pj/lay-band-* warn that they do not accept :in, instead of taking it and doing nothing. They are placed from data values only.:group no longer discards a fixed :color. (pj/lay-line data :x :y {:group :country :color "#d0d0d0"}) draws one line per country, all in that grey; before, each line took a different palette color.{:tooltip true} and {:brush true} work in the browser again. Since 0.7.0 the Scittle scripts reached the page with " where they wrote ", and a script element's content is raw text in HTML, so nothing decoded them back and neither interaction ran.:width and :height, which is a screen pixel only at the plot's natural size on a standard-resolution display.4-beta23 -> 4-beta25, brought in by Plotje: pj/plot asks for :kind/hiccup2, which 4-beta23 does not define.(pj/options {:legend-position :top}) draws its legend inside the image, instead of above the top edge where nothing is visible.:not-applicable labels a tick, legend entry or facet strip as not applicable. String categories are untouched. - thanks, @timothypratleyCost-Benefit Ratio, not Cost Benefit Ratio. Keyword and symbol names are unchanged. - thanks, @timothypratleypj/lay-* arity rejects a non-column position, as pj/pose already did. Breaking: (pj/lay-point data 0 1 {}) no longer plots integer column names -- rename them first with (tc/rename-columns ds [:x :y]).pj/scale is never widened. (Closes #18) - thanks, @behrica:fit-text-domain (default true) turns that widening off.:shape produces a legend. One column driving both :color and :shape gives one merged legend, as in ggplot2; two different columns give two legends. (Closes #4) - thanks, @timothypratleypj/shape-symbols, the marker symbols a categorical :shape mapping draws with, in assignment order.:circle, :square, :triangle, :diamond, :triangle-down, :plus, :cross. More categories than symbols repeats one, which now warns.pj/scale reads :shape: :domain sets the category and legend order, and new :values supplies the symbols -- (pj/scale pose :shape {:domain ["gold" "silver"] :values [:diamond :cross]}). An undrawable symbol is rejected, as is :values on any other channel.:shape-label, titling the shape legend as :color-label and :size-label do for theirs. Naming one half of a merged legend splits it in two.pj/svg-summary no longer counts legend symbols as plot data.{:position :dodge}. (Closes #13) - thanks, @behricapj/lay-text and pj/lay-label accept {:stat :count}, labelling each bar of a counting pj/lay-bar: (-> data (pj/lay-bar :species) (pj/lay-label {:stat :count})). pj/lay-point and pj/lay-line plot those counts too. (Closes #14) - thanks, @behrica:x-tick-angle rotates the tick labels in PNG output, not only in SVG.:y-label renders in full in PNG output, where it was cut off after about six characters -- the missing rotation above, not the upstream Membrane bug it was listed as.:label mark is gone; :text and :label are two layer types drawn by the same :text mark, so a label layer reports :mark :text in a plan. pj/lay-label and (pj/lay :label ...) are unchanged. Marks drop from 22 to 21; the 25 layer types are unchanged.:defaults map on the registry entry, which the call site overrides: :label is :text with {:box true}.:box true gives a white rounded panel with a thin border, and a map sets its properties -- {:box {:corner-radius 8}}, or 0 for square corners. (Closes #19, with :thousands-separator below; the arrows and out-of-panel text in that issue's image are #17 and #18) - thanks, @behricageom_label), where it was square.pj/svg-summary reports label background boxes as :label-boxes, one per label, instead of counting them as :tiles.pj/lay-text and pj/lay-label accept :font-weight (:normal or :bold) and :font-style (:normal or :italic), independent and combining, in SVG and PNG. A numeric CSS weight such as 700, or CSS :oblique, is rejected -- Java2D draws neither. (Closes #21) - thanks, @behricapj/svg-summary reports :bold-texts and :italic-texts.clipPath ids are derived from the clip region's geometry instead of a counter shared across the whole JVM, so rendering the same plot twice produces the same bytes.:thousands-separator too, and a whole-valued legend number drops its trailing .0 -- a size legend for a count reads 100,000, not 100,000.0.pj/layer-option-docs describes every layer option. Fourteen were missing, among them :x, :y, :data, :mark and :stat.pj/scale :values on a channel other than :shape now names :palette as the way to choose the colors a categorical :color mapping draws with.:thousands-separator groups the digits of large numbers in numeric tick labels and in text taken from a column, so (pj/options {:thousands-separator ","}) draws 462,389. Any string works. Off by default, and never applied to category names, legend entries or facet strips. Part of #19 - thanks, @behrica(pj/lay-bar :x :y {:x-label "sales"}) adds Plot options belong in pj/options: [:x-label]. The pointer works in every direction, and under :strict the same text appears in the thrown exception. - thanks, @timothypratleypj/lay-point docstring called its trailing map "aesthetic opts"; it holds layer options generally.R&D, or a title "Q1 <profit> & loss", was read as markup. pj/plot now asks for :kind/hiccup2. A saved SVG was never affected. (PR #33) - thanks, @timothypratleypj/lay-density on a column spanning 4.3 to 7.9 drew an axis from 2.1 to 10.1 and a pj/lay-rug beside it covered only the middle of the plot. The curve now starts and ends with the rug, and the axis matches what ggplot2's geom_density() produces for the same data. (Closes #23) - thanks, @behricapj/lay-violin and pj/lay-ridgeline estimate through the same kernel density, so they are bounded by their category's values too -- each body now ends where that category's data ends instead of tapering into a long needle past it. This matches ggplot2, which trims violins by default (geom_violin(trim = TRUE)).pj/lay-density, pj/lay-violin and pj/lay-ridgeline accept :trim, choosing which values each group's curve is estimated over, after ggplot2's argument of the same name and with its per-geom defaults. A density is untrimmed: every group is estimated across the whole layer, so grouped curves share one interval and each falls away to nothing rather than being cut off at its own group's extremes. {:trim true} estimates each group over its own values instead. A violin or ridgeline is trimmed, so each body ends at its category's values; {:trim false} extends it by three bandwidths on each side. This changes the appearance of a grouped pj/lay-density, which previously behaved as {:trim true}.nrd0, the bandwidth rule R's density() and ggplot2 use; the previous default smoothed about 18% wider, drawing every curve slightly flatter and broader than the same data in ggplot2. Affects pj/lay-density, pj/lay-violin and pj/lay-ridgeline. Passing an explicit :bandwidth is unchanged.pj/lay-violin and pj/lay-ridgeline keep 80, where the extra points make no visible difference.pj/plot, pj/save, pj/draft, pj/plan, and pj/membrane now accept raw data directly, giving it a default mapping first exactly as pj/pose does. Previously (pj/plot some-dataset) (data not wrapped in pj/pose) rendered a blank figure; it now renders the same inferred default as (pj/plot (pj/pose some-dataset)).pj/infer-mapping: given a pose that carries data but no mapping (the bare leaf pj/->pose produces), it attaches a default position/color mapping from the first 1-3 columns; it is a no-op on any pose that already has a mapping, has layers, is composite, or has 4+ columns. This is the step the terminal shortcuts apply after pj/->pose, exposed so pipeline-minded users can build their own chains ((-> data pj/->pose pj/infer-mapping pj/pose->draft pj/draft->plan)).[1 4 1 5 6] -- is now accepted as plot data and read as a single column named :value (previously only {:column [values]} maps or sequences of row-maps were accepted). Combined with the default-mapping change, (pj/plot [1 4 1 5 6]) renders a histogram.pj/save now returns the written file as a java.io.File carrying :kind/image metadata (instead of the path string), so evaluating a pj/save call in a notebook also displays 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 yields the path string. (PR #29) - thanks, @timothypratleyplotje-plot as a class on svg output (PR #28) - thanks, @timothypratleypj/svg-summary counts square markers. A :square marker draws as a rounded rectangle of radius 0, which fell between the summary's point test and its tile test, so (pj/lay-point :sepal-length :sepal-width {:shape :species}) on a 150-row dataset summarized as 100 marks. Squares now count in :points alongside circles, which also brings their colors and opacities into :colors and :alphas; :sizes still reports only positive radii.pj/lay-density and pj/lay-area accept an opt-in outline on the curve: :stroke (outline color) with optional :stroke-width. The fill still comes from :color, so (pj/lay-density :x {:color "lightblue" :stroke "black"}) draws a light-blue area with a black outline. The outline strokes only the top curve, not the baseline. Without :stroke the appearance is unchanged. (Closes #11) - thanks, @behricapj/lay-line, pj/lay-step, pj/lay-smooth, the reference lines pj/lay-rule-h / pj/lay-rule-v, and a density/area outline accept :stroke-dash, either a named preset (:dashed, :dotted, :solid) or a raw [dash gap ...] pixel pattern ({:stroke-dash [6 3]}). Renders through both the SVG and PNG (Java2D) paths. (Closes #12) - thanks, @behricapj/svg-summary reports :dashed-lines (count of polylines carrying a stroke-dasharray) and :dash-patterns (the distinct stroke-dasharray strings), for asserting that a dashed line, rule, or area outline rendered dashed and with which pattern.pj/lay-value-bar is removed. pj/lay-bar now covers both cases: with x only it counts each category (as before), and with a y column it uses the y value as the bar height (the former pj/lay-value-bar). The stat is inferred from whether a y column is present and is overridable with {:stat :count} or {:stat :identity}. To migrate, replace (pj/lay-value-bar data :x :y) with (pj/lay-bar data :x :y). This also lifts the previous "stacked bars reject pre-aggregated counts" limitation -- pj/lay-bar with {:position :stack} and a y column now stacks pre-computed values. - thanks, @timothypratleypj/lay-bar value bars now accept the categorical axis on either x or y: (pj/lay-bar :value :category) with a categorical y draws horizontal bars directly, no pj/coord :flip needed (matching how pj/lay-boxplot auto-orients). Plain and dodged horizontal bars are supported; stacked/filled horizontal bars still need (pj/coord :flip).pj/lay-bar with two numeric or temporal axes now draws a bar at each x position -- a numeric-position or time-series bar chart ((pj/lay-bar :month :revenue)), which previously errored. Bar width defaults to 0.9 of the smallest gap between adjacent x values; set it with {:bar-width n}. Grouped numeric bars currently overlap rather than dodge.pj/lay-bar's categorical-x error now points to the {:x-type :categorical} override and (pj/coord :flip), matching the guidance other categorical-axis marks already give.pj/valid-membrane? and pj/explain-membrane validate a membrane against its Malli schema, mirroring the existing pj/valid-plan? / pj/explain-plan pair for plans.pj/options -- notably :theme, but also :palette -- now flow through the explicit pj/draft->membrane and pj/draft->plot steps, not only through the pj/plot / pj/membrane shortcuts. These steps default their options to the draft's own options (any options passed explicitly override per key), so a theme set before drafting is no longer dropped at the membrane stage. (Closes #20) - thanks, @behricapj/lay-line reference line drawn beyond a narrowed pj/scale domain, say -- is masked at the panel edge instead of painting across neighbouring panels in a pj/arrange or facet layout. A narrowed :domain acts as a view window (like ggplot2's coord_cartesian): the data is kept, only the view is bounded. (Closes #16) - thanks, @behricapj/options accepts :x-tick-angle to rotate x-axis tick labels (in degrees; -45 is a common diagonal), so dense or long categorical labels stay readable instead of overlapping. :x-tick-label-pad overrides the vertical space reserved below the panel for the angled labels. The rotation flows through pj/save (SVG and PNG) as well as the notebook pj/plot path. Long labels can still run past the left plot edge (see Known Limitations). (PR #6) - thanks, @tombaryspj/scale accepts :n-ticks on a categorical axis to thin a crowded axis to about that many evenly-spaced tick labels, instead of labelling every category ((pj/scale :x {:n-ticks 8})). An alternative to rotating the labels for dense categorical axes. (PR #25) - thanks, @behricapj/scale :breaks and :labels now work on a categorical axis, not just numeric ones. On a discrete axis :breaks selects which categories get a tick (each matched to a category by its displayed label) and :labels relabels them; a break naming no category is dropped with a warning (an error under :strict). Previously the categorical branch ignored both. When both :breaks and :n-ticks are given, explicit :breaks win and no thinning is applied. (Closes #22) - thanks, @behricapj/lay-* call renders on top of the previous one -- instead of being reordered by position type. A pj/lay-text or pj/lay-label added after a bar now reads on top of it rather than being hidden underneath.pj/lay-text and pj/lay-label accept :align-x (:left/:center/:right) and :align-y (:top/:center/:bottom) to set which part of the label sits on the data point -- e.g. :align-x :right places a value label inside a bar's end. Defaults :left/:center preserve the previous placement.:nudge-x/:nudge-y on a categorical axis now raise a clear error pointing to :align-x/:align-y (and :jitter/:position :dodge). Nudge is a data-space shift and applies only to numeric or temporal axes.pj/lay-* with different x/y columns from the existing pose now produces a two-panel composite instead of throwing.pj/lay-* would create a new panel using columns that don't exist in the data, the error now fires at the lay call with a clear message, instead of later during pj/plan or pj/plot.:data but the pose's x/y columns are missing from it, the error now names where the missing column came from and suggests two fixes: rename the column to match, or set a different x/y on the layer.pj/scale :y :log now works on histograms and categorical bar charts. (Closes #5) - thanks, @harold.java.util.Locale/ROOT, so plots render correctly on JVMs whose default locale uses comma as the decimal separator (Czech, German, etc.). (PR #3) - thanks, @tombaryspj/scale accepts :labels paired with :breaks -- render numeric tick positions with custom text (e.g. days of the week 1-7 labelled "Mon"-"Sun" on a tile heatmap). Length must match :breaks; :labels without :breaks throws.PlotjeMembrane record implementing the Membrane UI protocols (IOrigin, IBounds, IChildren), so Plotje plots compose with hand-built Membrane elements. Width and height read via (membrane.ui/width m)/(height m); title rides as :plotje/title. Replaces the prior metadata-tagged-vector contract.pj/membrane? predicateCan you improve this documentation?Edit on GitHub
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 |