Resolve line type(s) inconsistencies - #683
Merged
Merged
Conversation
The free-facet branch selected y-axis tick labels by listing eligible
plot types by name. Any type outside that list lost its `at` values
while keeping the `labels` inherited from the shared axis arguments,
which axis() rejects outright:
Error in axis(...) : 'labels' is supplied and not 'at'
Gate on `!is.null(ylabs)` instead, matching the fixed-scale branch
above (#677). Named `ylabs` means the type placed categories on the
y-axis, which is the property the code actually depends on. The old
`isTRUE(flip)` condition was a poor proxy for it, since flipping is
only one of the ways categories reach the y-axis: a plain `type = "p"`
with a factor y variable hit the same error without any flip.
Refs #679
align_layer() mapped each row of an added layer onto the base layer's axis by indexing the layer's own x positions with the looked-up original positions. That treats a per-row lookup as a permutation. It only coincided with the correct result when the layer's rows arrived in ascending order, which is the common case of one sorted row per category. Assign the lookup directly instead. Rows that arrived in any other order were previously permuted, and repeated categories collapsed onto a single x position. Refs #679
Levels are set in data order so the snapshot is independent of how a type orders its categories, and stays valid either way should that ordering change. Refs #679
Both layers use the same plot type, and the disagreement between them comes from the levels each data frame declares. Keeping the two types identical means the test does not depend on any type's category ordering, which is still under discussion. The added layer's rows deliberately arrive out of ascending order, so that a position-based mapping cannot accidentally agree with a category-based one. Refs #679
Line types (type = "l", "b", "h", ... via type_lines()) now place categorical data exactly like type_points() does: categories follow the factor levels rather than their order of appearance in the data, so an explicit factor(x, levels = ...) is honoured and layered point/ line types land on the same categories. Factor y variables are also handled now, instead of falling back to a numeric axis for every line type except "p". To reorder on the fly, type_points(), type_lines(), type_errorbar(), and type_pointrange() gain an `xlevels` argument, extending the convention already established by type_barplot(), type_spineplot(), and type_ridge() (ylevels). Accepted values are a character vector of level names, numeric level indexes (e.g. 3:1), or the new keyword "data" (first appearance in the data), which the three existing types now also accept. The argument only affects categorical variables and is forwarded automatically from the top-level tinyplot() call. type_errorbar() and type_pointrange() default to xlevels = "data", preserving their existing behaviour (typically coefficient plots, where the row order of the data is intentional) while making it overridable via xlevels = NULL. The level-reordering logic that was previously copy-pasted across barplot/spineplot/ridge is consolidated into a shared sanitize_xlevels() helper, which all seven types now use. Ridge additionally gains the unknown-level warning the other types had. Closes #679
Line types (type = "l", "b", "h", ... via type_lines()) now place categorical data exactly like type_points() does: categories follow the factor levels rather than their order of appearance in the data, so an explicit factor(x, levels = ...) is honoured and layered point/ line types land on the same categories. Factor y variables are also handled now, instead of falling back to a numeric axis for every line type except "p". To reorder on the fly, type_points(), type_lines(), type_errorbar(), and type_pointrange() gain an `xlevels` argument, extending the convention already established by type_barplot(), type_spineplot(), and type_ridge() (ylevels). Accepted values are a character vector of level names, numeric level indexes (e.g. 3:1), or the new keyword "asis", which takes the categories in the order that they appear in the data (cf. the `as.is` argument of read.table()). The three existing types accept the keyword too. The argument only affects categorical variables and is forwarded automatically from the top-level tinyplot() call. type_errorbar() and type_pointrange() default to xlevels = "asis", preserving their existing behaviour (typically coefficient plots, where the row order of the data is intentional) while making it overridable via xlevels = NULL. The level-reordering logic that was previously copy-pasted across barplot/spineplot/ridge is consolidated into a shared sanitize_xlevels() helper, which all seven types now use. Ridge additionally gains the unknown-level warning the other types had. Closes #679
There was a problem hiding this comment.
Pull request overview
Resolves categorical-axis inconsistencies across plot types and layers.
Changes:
- Adds shared
xlevels/ylevelsordering, including"asis". - Preserves categorical labels for line plots, flipped axes, and free facets.
- Corrects categorical layer alignment and adds snapshot coverage.
Reviewed changes
Copilot reviewed 19 out of 33 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
R/type_spineplot.R |
Uses shared level ordering. |
R/type_ridge.R |
Adds "asis" level ordering. |
R/type_points.R |
Adds xlevels. |
R/type_pointrange.R |
Adds configurable ordering. |
R/type_lines.R |
Aligns categorical axes and labels. |
R/type_errorbar.R |
Exposes xlevels. |
R/type_barplot.R |
Uses shared level ordering. |
R/sanitize_xlevels.R |
Implements shared ordering logic. |
R/facet.R |
Fixes free-facet categorical labels. |
R/align_layer.R |
Corrects per-row category alignment. |
NEWS.md |
Documents changes. |
man/type_spineplot.Rd |
Updates spineplot documentation. |
man/type_ridge.Rd |
Updates ridge documentation. |
man/type_points.Rd |
Documents xlevels. |
man/type_lines.Rd |
Documents categorical axes. |
man/type_errorbar.Rd |
Documents interval-type ordering. |
man/type_barplot.Rd |
Documents "asis". |
inst/tinytest/test-type_pointrange.R |
Tests pointrange ordering. |
inst/tinytest/test-type_lines.R |
Tests line-axis behavior. |
inst/tinytest/test-tinyplot_add.R |
Tests layer alignment. |
inst/tinytest/test-facet.R |
Tests free-facet labels. |
inst/tinytest/_tinysnapshot/type_points_xlevels_idx.svg |
Point-order snapshot. |
inst/tinytest/_tinysnapshot/type_lines_xlevels_asis.svg |
Appearance-order snapshot. |
inst/tinytest/_tinysnapshot/type_lines_layer_h_p.svg |
Layer alignment snapshot. |
inst/tinytest/_tinysnapshot/type_lines_flip_labels.svg |
Flipped-label snapshot. |
inst/tinytest/_tinysnapshot/type_lines_explicit_levels.svg |
Explicit-level snapshot. |
inst/tinytest/_tinysnapshot/type_lines_categorical_y.svg |
Categorical-y snapshot. |
inst/tinytest/_tinysnapshot/type_lines_categorical_points.svg |
Point baseline snapshot. |
inst/tinytest/_tinysnapshot/type_lines_categorical_lines.svg |
Line consistency snapshot. |
inst/tinytest/_tinysnapshot/tinyplot_add_layer_category_alignment.svg |
Added-layer snapshot. |
inst/tinytest/_tinysnapshot/pointrange_xlevels_null.svg |
Factor-order snapshot. |
inst/tinytest/_tinysnapshot/facet_free_categorical_yaxis.svg |
Free-facet snapshot. |
altdoc/pkgdown.yml |
Updates documentation-build metadata. |
Files not reviewed (3)
- man/type_barplot.Rd: Generated file
- man/type_errorbar.Rd: Generated file
- man/type_lines.Rd: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #679
HU @zeileis
Problem 1
As we discussed in the issue thread, I opted to order non-numeric
xdata by (coerced) factor levels, thus ensuring better consistency withtype_points(), etc. Using your running example from the original issue, where I first plottyoe_pointsas a foil.However, note that users can override via an explicit
xlevels = "asis"argument, which orders the x values according to first appearance in the data. This works for both lines and points (and also other types liketype_spineplotwith a preexistingxlevelsargument).Created on 2026-08-17 with reprex v2.1.1
Problem 2
Character labels are now preserved correctly with flipped axes. As an aside: the solution turned out to be a nice example where I could cut a lot of conditional type checking code (thus fragile) and just put in a simpler general label check. Again, using your example from the original issue, first with
type_pointsas a foil.