vignettes/sorting.qmd
Scope
This article covers the ordering of rows, bars and categories inside a single figure or table produced by makeme().
It does not cover the ordering of chapters, sections or files across a report. That belongs to the scaffolding stage and lives in saros.base — see arrange_section_by in refine_chapter_overview() and Prepare the chapter overview.
As a rule of thumb: saros.base decides which output appears where in the report; saros decides what the output looks like once it is there.
The three controls
Every makeme() call exposes three sorting arguments:
| Argument | Controls | Default |
|---|---|---|
sort_dep_by |
The order of the dependent variables (the rows/bars) | ".variable_position" |
sort_indep_by |
The order of the independent variable’s categories (the groups/facets) | ".factor_order" |
descend / descend_indep
|
The direction of each of the above |
TRUE / FALSE
|
makeme(
data = ex_survey,
dep = b_1:b_3,
type = "cat_table_html",
sort_dep_by = ".top",
descend = TRUE
)Ordered factors take precedence
If a dependent variable is an ordered factor, its level order defines the base order and sort_dep_by will not override it — the levels are the authoritative scale. descend still applies, reversing the display order while preserving the inherent ordering.
Unordered factors are reordered freely by sort_dep_by.
The same holds for the independent variable: an ordered indep factor takes precedence over sort_indep_by, and is reversed only when descend_indep = TRUE.
sort_dep_by: ordering the dependent variables
Structural keys
These ignore the responses and sort on the variables themselves.
| Key | Orders by |
|---|---|
".variable_position" |
Position in the supplied data frame (the default) |
".variable_label" |
The variable labels, alphabetically |
".variable_name" |
The variable names, alphabetically |
Response-pattern keys
These sort on where the mass of the responses sits. They are the usual choice for “show me the items respondents were most positive about”.
| Key | Orders by |
|---|---|
".top" |
Proportion in the highest category |
".upper" |
Summed proportion above the middle category |
".mid_upper" |
Summed proportion including and above the middle category |
".mid_lower" |
Summed proportion including and below the middle category |
".lower" |
Summed proportion below the middle category |
".bottom" |
Proportion in the lowest category |
# The items with the largest share in the top category first
makeme(
data = ex_survey,
dep = b_1:b_3,
type = "cat_plot_html",
sort_dep_by = ".top"
)Spread
".range" sorts by how unevenly the answers are distributed within each variable: the difference between the largest and the smallest category proportion.
- A variable where one category dominates has a large range.
- A variable whose answers spread evenly across all categories has a range near zero.
It is a measure of consensus, not of direction. A large range tells you the responses concentrated in one category, but nothing about which category — ".top" and ".bottom" are the keys for that. Reach for ".range" when the question is “which items did respondents converge on, and which split them?” rather than “which items scored highest”.
# Most concentrated items first (one category dominates)
makeme(
data = ex_survey,
dep = b_1:b_3,
type = "cat_plot_html",
sort_dep_by = ".range",
descend = TRUE
)
# Most evenly split items first
makeme(
data = ex_survey,
dep = b_1:b_3,
type = "cat_plot_html",
sort_dep_by = ".range",
descend = FALSE
)".range" works on the proportions, so it applies to the categorical output types. Types that never compute proportions (the int_* types, for example) ignore it rather than reordering.
Direct column keys
These sort on a computed summary column, when the output type provides it.
| Key | Orders by |
|---|---|
".count" |
Cell count |
".proportion" |
Proportion |
".mean" |
Mean of the ordinal category codes |
".median" |
Median of the ordinal category codes |
".sum_value" |
Summed value |
Note that ".mean" and ".median" are computed over the factor’s level codes (1, 2, 3, …), not over the category labels, which are normally text. This is deliberate: it is the usual way to summarize an ordinal survey scale.
Custom category keys
Passing a character vector of category labels sorts on those categories. This is the escape hatch when none of the named keys expresses the ordering you want.
Be aware that the two forms sort on different bases:
- A single label orders by that category’s
.count. -
Several labels order by the summed
.sum_value, which sums the proportions — or the counts whendata_label = "count".
When the dependent variables have equal numbers of respondents this makes no difference. When they do not, adding a second label can change the ordering of the variables you had already sorted, because the basis switched from counts to proportions.
sort_indep_by: ordering the independent categories
| Key | Orders by |
|---|---|
".factor_order" (or NULL) |
The indep factor’s own level order (the default) |
".variable_label" |
The category labels, alphabetically |
".top", ".upper", ".mid_upper", ".mid_lower", ".lower", ".bottom"
|
The same response-pattern aggregates as for sort_dep_by
|
".count" |
Cell count |
".count_per_indep_group" |
Total valid responses in each independent group |
".mean", ".median", ".sum_value"
|
The corresponding summary column |
character() |
Summed proportions of the named categories |
".count_per_indep_group" is the key for ordering groups by size rather than by response pattern — putting the largest organization, region or team first regardless of how it answered.
makeme(
data = ex_survey,
dep = b_1:b_3,
indep = x1_sex,
type = "cat_plot_html",
sort_indep_by = ".count_per_indep_group",
descend_indep = TRUE
)Independent ordering is computed per dependent variable, so the group order may legitimately differ between the rows of a single figure.
Direction
descend reverses sort_dep_by; descend_indep reverses sort_indep_by.
Note the asymmetry in the defaults: descend defaults to TRUE (largest first, which is what you normally want for a response-pattern sort), while descend_indep defaults to FALSE.
When a key is not recognized
Sort keys are validated against a whitelist. Passing a key that is neither a recognized .-prefixed option nor a category present in the data raises an error that lists both sets:
makeme(
data = ex_survey,
dep = b_1:b_3,
type = "cat_table_html",
sort_dep_by = ".nonexistent"
)
#> Error:
#> x Invalid `sort_by`: .nonexistent
#> i `sort_by` must be either NULL (no sorting), a single string from the set
#> options `.top`, `.upper`, `.mid_upper`, `.lower`, `.mid_lower`, `.bottom`,
#> `.range`, `.variable_name`, `.variable_position`, `.variable_label`, ...
#> or all valid categories in the data (`Not at all`, `A bit`, and `A lot`).Misspelling a category label produces the same error, so the valid category names are always visible at the point of failure.
Two caveats are worth knowing:
- A key that is whitelisted but whose column the output type never computed reports that separately, naming the columns that can be sorted on.
- Keys that do not apply to an output type at all (for instance a proportion-based key on an
int_*type) are ignored rather than rejected, so check the result if a sort appears to have had no effect.
Setting a sort order once for the whole report
Rather than repeating the arguments in every chunk, set them once with global_settings_set():
global_settings_set(
fn_name = "makeme",
new = list(
sort_dep_by = ".top",
descend = TRUE
)
)Every later makeme() call in the document inherits these unless it overrides them explicitly.