25 Known Limitations
Gaps in the current Plotje release. None produce crashes on canonical inputs; each is documented and tracked for post-alpha work.
(ns plotje-book.known-limitations
(:require
;; Kindly -- notebook rendering protocol
[scicloj.kindly.v4.kind :as kind]))Layout and Visuals
Multi-layer overlays like
(-> data (pj/lay-point ...) (pj/lay-lm ...) (pj/lay-loess ...))do not auto-generate a layer-kind legend to distinguish the two regression curves. Workaround: color each layer explicitly.Histograms, stacked bars, step plots, and other stat-derived marks do not default to a
"count"or"density"y-label.Linear continuous color legends (numeric
:colormapping with:linearscale) label only the endpoint tick marks on the gradient bar. Intermediate values are unlabeled, making it hard to map interior colors back to data values. Log-scaled continuous color and fill legends do carry intermediate ticks.SPLOMs with 6+ variables at the default 600x400 have tight panels. Bump
:width/:heightor pin:panel-width/:panel-height.Horizontal bars from
(pj/coord :flip)render the first row of data at the bottom of the chart, so a dataset sorted descending (natural “top N” order) produces the biggest bar at the bottom. The behavior matches ggplot2’scoord_flip(). Workaround: sort the dataset ascending before plotting, e.g.(tc/order-by data [:value] [:asc]). A future opt-in flag such as(pj/coord :flip {:reverse-categorical true})would spare users the sort.
Marks
Aesthetic-gate vs. mark-consumer asymmetry. Several aesthetics are accepted at the universal pose-mapping gate but consumed only by one or two mark extractors. Setting them on other marks is a silent no-op rather than an error.
Aesthetic Consumed on Silently no-op on :size(column ref)lay-pointevery other lay-* :alpha(column ref)lay-pointevery other lay-* (literal :alpha Nworks on most via:fixed-alpha):shapelay-pointtext, label, lollipop, summary, … numeric (continuous) :colorlay-point,lay-interval-hevery other lay-* (a numeric column on a categorical-color path produces banded palette colors instead of a gradient) tooltip / row-indices plumbing lay-point,lay-interval-hevery other lay-* Workaround: pre-bin or convert the numeric column into a discrete color column where appropriate, or use
lay-pointfor the mark where the aesthetic must vary per row.:alphaonpj/lay-rule-h/pj/lay-rule-vis silently dropped at render time (the rendering path reads:coloronly). Bands honor:alpha. Workaround: use a lighter:colorto simulate the visual effect on rules.pj/lay-rule-hrendered under(pj/coord :flip)becomes a vertical line;pj/lay-rule-vbecomes a horizontal line. The mark name still reflects the unflipped semantics. Add a one-line note in the surrounding prose if the chart’s flipped state is non-obvious.:position :dodgeis ignored at render-time on nine marks includingsummary– onlay-barandlay-summarythe dodge request is dropped at construction; onlay-pointandlay-linethe dodge metadata reaches the plan but no geometric offset is applied. Workaround: pre-compute dodge offsets viatc/group-by.Polar plots for bar-family marks don’t auto-emit category labels – rose charts currently render with zero text.
Stacked bars don’t split positive and negative values; all-positive data works, but mixed-sign data stacks incorrectly.
pj/lay-tile(and the underlying:bin2dstat) requires numeric x and y columns. Passing a categorical axis throws a clear “Stat :bin2d requires a numeric column” error at plan time. The recommended workaround is to render a numeric-indexed grid (1-N integers in place of the categorical column) and pair:breakswith:labelson the axis – see Customization and Troubleshooting for a worked example. For a true categorical axis (binning over labels rather than numeric intervals),pj/lay-value-barwith{:color :value}gives a categorical “heatmap” look.pj/lay-barwith:position :stack(or:fill) is count-only and rejects aycolumn – there is no clean way to render a stacked bar chart of pre-aggregated values (e.g. a “messages per year broken down by tenure bucket” chart where the counts are already computed).pj/lay-areawith:position :stackdoes accept pre-aggregatedy, so the pattern works there. Workarounds: lift the aggregation (expand each row back into count-many duplicate rows so:countsums to the pre-aggregated value), or usepj/lay-areawith:position :stackon a numeric x. A proper fix (lifting the count-only restriction whenyis supplied) is planned.Stack order in
pj/lay-areaandpj/lay-bar(with:position :stack) follows the sort order of the:colorcolumn. There is no:stack-order/:color-orderoption yet, so forcing a specific bottom-to-top order requires prefixing category labels with sort-stable ordinal characters ("01: ...","02: ..."), which leaks into the legend.Annotations are silently skipped under
(pj/coord :polar). A polar rule would need to render as a circle (fixed radius) or spoke (fixed angle); those shapes are not implemented. Use Cartesian or flip coords for annotated plots.Large scatters produce large SVGs (~220 bytes/point). For >10k points, use
:format :bufimgfor raster output.pj/save-png(and the:bufimgraster path generally) truncates the rotated y-axis label after ~6 characters. The SVG path (pj/plot+ Clay GFM, orrsvg-converton the saved SVG) renders the full label. Root cause lives in Membrane’s Java2D backend (the rotated-text bounding box is clipped). Workaround: render to SVG and rasterize externally, or pad:y-labelto stay short. Needs an upstream fix in Membrane.LOESS with confidence bands is O(n^2); subsample above ~5k rows.
Options and Configuration
:panel-sizeis a legacy configuration key from the pre-total-first layout. It now emits a deprecation warning and is ignored. Use:panel-width/:panel-height(total-first escape hatches).The
:widthkey on apj/planresult preserves the user’s original request even when:panel-widthpins the real size – inspect:total-width/:total-heightfor the rendered canvas.pj/plancalled on a plan or on a hiccup value now throws a clear error. Callpj/planonly on poses.
Mixing Keyword and String Column References
- Mapping the same column with a keyword in one place and a string in another (e.g.
(pj/pose ds {:color :group})then(pj/lay-point :x :y {:color "group"})) is not normalized: the scope hierarchy treats them as different keys and the result is a silent empty plot. Workaround: pick one form (keyword or string) and use it consistently within a pose.
ggplot2 Features Not Yet Implemented
The
:fillaesthetic is currently consumed only bylay-tile(and the:bin2doutput beneathlay-density-2d). On filled marks likelay-bar,lay-area, andlay-violin,:colorpaints the interior; there is no separate stroke channel.The
:linetypeaesthetic (ggplot2’saes(linetype=...)for solid vs. dashed lines) is not implemented. Workaround: encode the same distinction via:colorinstead.No
after_stat()analog. ggplot2 idioms likegeom_bar(aes(label=after_stat(count)))andgeom_histogram(aes(y=after_stat(density)))reference computed stat values inside aesthetic mappings; Plotje requires a pre-computed column. Workaround: aggregate the data first viatc/group-by+tc/aggregate, then map the count or density column directly.Theme support is shallow vs. ggplot2: today the theme map carries
:bg,:grid,:font-size, and a small handful of other keys. Named theme presets (theme_minimal,theme_bw,theme_classic), axis text rotation, panel borders, strip text styling, andlegend.positionby coordinate are not yet exposed.Per-layer
data,guides()for per-aesthetic legend control,scale_*_sqrt/reverse/date. All tracked in the backlog.