Skip to content

vellumplot 0.6.0

Choose a tag to compare

@schochastics schochastics released this 20 Jul 08:43
· 170 commits to main since this release
  • Sankey styling options. vsankey() / mark_sankey() gain show_values
    (append each node's value to its label), flow_color ("source", "target",
    or "gradient" — a source-to-target colour fade per ribbon), and
    node_width / node_gap to tune the node rectangles.

  • Sankey crossing minimisation. vsankey() now orders the nodes within each
    column with the Sugiyama barycenter heuristic to minimise ribbon crossings
    (previously first-appearance order, which left many avoidable crossings). The
    reordering is deterministic and pure R. A related fix stacks each node's ribbon
    slices to meet the node in the same vertical order as the nodes they connect to,
    removing the remaining crossings within a fan of ribbons.

  • Sunburst rendering fixes. vsunburst() now colours wedges by their
    top-level branch (each branch a distinct hue, lightened with depth) instead of
    a single colour per ring — sibling branches were previously indistinguishable.
    It also starts the first wedge at twelve o'clock and winds clockwise, matching
    the package's pies/roses (coord_polar) rather than starting at three o'clock
    counter-clockwise.

  • Sunburst / radial hierarchies: vsunburst(). A new plot type for
    part-of-whole hierarchies, from a parent listid, parent (NA at the
    root), and value (leaf values; internal nodes sum their children). Depth maps
    to a ring and each node's angular span is its share of its parent's, drawn as
    one batched sector_grob in an aspect-locked, axis-free square panel (mirroring
    vsankey()/vgraph()). inner_radius opens a central hole (a donut/ring
    sunburst); nodes are coloured by depth. mark_sunburst() is the exported layer.
    See vignette("flows-and-hierarchies").

  • Sankey / flow diagrams: vsankey(). A new plot type for layered flows,
    built from a flow list — one row per flow with from, to, and value (the
    ribbon width). Nodes are the union of from/to; a node that is both a source
    and a target makes the diagram multi-stage. vsankey(data, from, to, value)
    returns a ready, axis-free plot (mirroring vgraph()); mark_sankey() is the
    exported layer it adds. The layout is computed R-side (longest-path layering,
    value-proportional node heights and ribbon widths, filled Bézier ribbons) and is
    deterministic. Flows must form a DAG; nodes are coloured from the qualitative
    palette. See vignette("flows-and-hierarchies").

  • Uncertainty marks: mark_halfeye() and mark_interval(). ggdist-style
    slab + interval marks for sample/posterior input (many y rows per categorical
    x). mark_halfeye() draws a one-sided density slab with a point-interval at
    its base — the median (or point = "mean"), a thick inner and thin outer
    equal-tailed quantile interval at the .width probabilities (default
    c(0.66, 0.95)); mark_interval() is the point-interval alone. A natural fit
    for visualising posterior draws (e.g. from \pkg{brms}).

  • coord_radial() and scale_size_area(). coord_radial() is a fuller
    polar system (ggplot2 3.5's name): besides theta/start/direction it takes
    end to sweep only a partial arc (e.g. start = -pi/2, end = pi/2 for a
    semicircular gauge) and inner_radius for a donut hole; with the defaults
    it matches coord_polar(). scale_size_area() maps a value to the marker's
    area (value 0 → size 0), the perceptually honest default for bubble
    charts, with max_size the size of the largest value.

  • Sankey labels stay on-panel. vsankey() now reserves horizontal margin for
    node labels, so the source column's labels (drawn to their left) and the
    terminal column's (drawn to their right) no longer clip at the panel edge.

  • Robustness of the new marks. vsunburst()/mark_sunburst() now reject a
    missing or negative leaf value with a clear message (instead of a cryptic
    downstream error), and mark_sunburst() validates inner_radius like
    vsunburst() and coord_radial() do. mark_halfeye()/mark_interval() skip a
    category with fewer than two finite observations (with a warning) rather than
    drawing an empty interval, and reject a width = argument (a likely typo for
    .width) that was previously ignored silently.

  • Consistent argument names for the hole radius / ridge height. The
    central-hole fraction is now spelled inner_radius everywhere it appears:
    coord_radial(), vsunburst()/mark_sunburst(), and mark_donut() (was
    hole). mark_ridgeline()'s overlap control is now height (was scale,
    which collided with mark_halfeye(scale=), a different quantity).

  • Consistent sketch / blend passthrough. mark_ecdf(), mark_contour(),
    mark_contour_filled(), mark_dotplot(), mark_qq(), and mark_qq_line() now
    accept a per-layer sketch = (their emitters already honoured it), and
    mark_pie() / mark_donut() accept blend =, matching the rest of the mark
    surface.

  • Parameterised position adjustments. New position_nudge(),
    position_jitter(), position_dodge(), position_dodge2(), and
    position_jitterdodge() give a mark's position tunable parameters (a bare
    string like "dodge" still works with the defaults). Adds three adjustments:
    nudge (shift every element by a constant in data units), dodge2 (dodge by
    the groups actually present at each x, filling the band with a padding gap —
    so ragged groupings stay centred), and jitterdodge (jitter points within
    their dodged slot). position_jitter(width=, height=, seed=) and
    position_dodge(width=) expose the previously-fixed jitter/dodge extents. A
    plot using the old string positions is unchanged.

  • Label repulsion: mark_text(repel = TRUE) / mark_label(repel = TRUE).
    Overlapping text labels are moved apart with a force-directed layout
    (ggrepel-style), each keeping a thin leader line back to its point. Because the
    plot size is fixed on the spec, the repulsion is resolved exactly against the
    true rendered panel — a two-pass compile that reads the panel's device geometry
    from vellum::scene_model(), relaxes the label boxes in pixel space, and maps
    the result back — so it needs no approximation and no vellum change, and is
    deterministic under seed. Tunable via box_padding, point_padding,
    min_segment_length, and seed. Limited to a single cartesian panel for now
    (facets / composition / polar error clearly).

  • mark_line(window = ): rolling / cumulative / offset transforms. A line can
    now transform its y per group (over rows ordered by x) before drawing —
    moving mean/sum/median/min/max over a window of k, running
    cumsum/cummean/cummax/cummin, lag/lead shifts, or rank. Pass an op
    name (window = "mean") or a list (window = list(op = "mean", k = 7, align = "right", partial = TRUE)); align is trailing/leading/centred and partial
    fills the edges from the shorter window so the line stays continuous. A plot
    without window is unchanged.

  • Diverging colour scales: scale_color_gradient2() / scale_fill_gradient2().
    A three-point ramp (low--mid--high) centred on midpoint (default 0),
    rescaled about the midpoint (scales::rescale_mid) so the neutral colour sits
    on the chosen value and each side spans as far as the data reaches — the correct
    scale for signed / anomaly data. A diverging continuous colorbar also reports
    midpoint + diverging in its colorbar descriptor, so an interactive host can
    centre a value-range filter on the neutral value.

  • symlog position transform. scale_x_continuous(trans = "symlog") (and
    y) adds a symmetric-log axis: linear through zero, logarithmic in the tails
    (sign(x) · log10(1 + |x|)), so signed data spanning several orders of magnitude
    — including zero and negatives, which log10 cannot show — reads on one axis.
    Breaks sit at zero and signed powers of ten. The transform name flows into the
    panel scales descriptor like the other transforms.

  • New group-region marks: mark_ellipse() and mark_hull(). Both enclose a
    set of (x, y) points in a single region drawn over a scatter — one region per
    group when a color/fill is mapped. mark_ellipse() draws a covariance
    ellipse (type = "t" robust default via \pkg{MASS}, or "norm"/"euclid"),
    following ggplot2's stat_ellipse(); mark_hull() draws the convex hull. Both
    are unfilled boundaries by default (map/set a fill to shade them) and need at
    least 3 points per group. The region's boundary trains the position scales, so
    an ellipse that bulges past the data is not clipped.

  • mark_smooth() gains real smoothing methods. Beyond "lm", the smooth
    mark now fits "loess" (local regression, span =), "glm" (with a family
    via method.args, e.g. logistic — the fit and its ribbon are back-transformed
    from the link scale), "gam" (a penalised smooth, default y ~ s(x); needs
    \pkg{mgcv}), and "rq" (quantile regression at a single method.args$tau;
    needs \pkg{quantreg}, line only, no ribbon). The default method = "auto"
    picks loess for small groups (< 1000 points) and gam for large ones, as in
    ggplot2. New formula, span, and method.args arguments; glm/gam bands
    use a normal interval, lm/loess a t-interval. Previously mark_smooth()
    errored on any method other than "lm". \pkg{mgcv} and \pkg{quantreg} are
    Suggests — a gated method errors clearly if its package is absent.

  • Data panels are emitted as pannable, gridlines tagged. Cartesian data panels
    (including coord_flip and a linear coord_trans) now push a pannable vellum
    viewport, and gridlines carry role = "grid". This is inert for static rendering
    but lets an interactive host (vellumwidget) pan/zoom a panel's marks while its
    clip + axes stay fixed and hide/redraw gridlines — the groundwork for axis-aware
    zoom. Polar / nonlinear-coord_trans panels stay non-pannable. Requires the
    current development vellum.

  • Continuous colorbar filter metadata. A continuous color scale now attaches
    each mark's colour value as filter_value in its element meta, and a colorbar
    descriptor (value domain + orientation) to the gradient-bar grob. Together (via
    vellum::scene_model()) these let a host such as vellumwidget overlay an
    interactive value-range filter on the colorbar. Discrete/binned colour scales are
    unaffected. No change to rendered output.

  • Panels now carry a scales descriptor for interactive hosts. Each cartesian
    data panel's viewport gains a meta$scales record — per axis: type
    (continuous / log10 / discrete / binned / date / datetime), transform, the
    data and native domains, tick breaks + labels, and time_unit for date/datetime
    axes. It surfaces via vellum::scene_model()$panels$meta and lets a host (e.g.
    vellumwidget) map device pixels back to data values — so a brush or a reported
    view can be expressed in data coordinates, not just pixels. Requires
    vellum (>= 0.4.0.9000). Internal: trained position scales now also record their
    transform name and, for date/time axes, a time_unit (previously the
    date/datetime nature was lost after training). No change to rendered output.