The language-neutral contract every binding implements. Each function has the same name and argument order in every language, and is called as a free function: scale_frames(s) in R and Julia, ns.scale_frames(s) in Python, nestedscales::scale_frames(s) in C++.
Correctness is checked against the R package: specs/goldens/generate.R writes the cases, and every binding replays them.
| Concept | R | Python |
|---|---|---|
| scale | S7 NestedScale
|
frozen dataclass NestedScale
|
| table |
data.frame / lazy arrow |
pyarrow.Table (accepts polars, pandas) |
| character vector | character |
list[str] (a bare str is one element) |
| missing |
NULL / NA
|
None |
| verdict with evidence | logical + attribute offenders / sizes
|
Verdict(ok, offenders, sizes); truthy, == bool
|
| error | stop() |
NestedScalesError (a ValueError) |
| warning | warning() |
NestedScalesWarning |
| summary | summary(x) |
scale_summary(x) |
print(x) |
str(x); repr(x) is the one-line format()
|
|
| subclass hook | S7 generic | functools.singledispatch |
| source / target argument |
source / target
|
source / target
|
named vector argument (rule = c(a = "sum")) |
named vector or list | dict |
| lazy input | arrow Dataset / dtplyr |
pyarrow.dataset.Dataset, polars LazyFrame
|
x[frame, unit] |
[.NestedScale |
x[frame, unit] (__getitem__) |
| dense array |
array with dimnames |
numpy.ndarray; labels are scale_units()
|
Rows of returned tables are sorted by code point in Python and by the session locale in R, so row order is not part of the contract. Code vocabularies (scale_units, children, parents) are in canonical member order everywhere.
Error and warning messages are kept verbatim across bindings.
Missing values follow R: an aggregate over a group containing a missing value is missing, and a target whose sources are only partly present in the data is missing (the partial-source law). A lazy input returns the observed target units only unless collect = TRUE; a materialised result is completed to the full target vocabulary, identifier groups in first-appearance order and target codes in member order, a missing-target row last.
Known differences:
rlang::hash(), which cannot be reproduced outside R, so such names differ between bindings;coverage, parent_totals, a Calendar’s tokens) as bare sequences rounded to 7 digits; the Python reader maps them back onto the declared weights and recomputes coverage from the parent totals, and the Python writer stores named mappings at full precision.Status: R = in the R package; Py = in the Python binding.
Signatures are the agreed ones. Three renames landed in R 0.2.0 with no deprecation aliases (the R package is pre-1.0), matching Python:
from / to became source / target everywhere: frames, scales, crosswalk columns, navigation, and the atom-pair columns. In recast(), the scale argument is scale and the source frame source;clear_* functions take name (registry names or keys) and scope;.ms_* became the backend_* functions below.| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
scale_from_leaftable |
leaftable, frames, key=NULL, weights=NULL, default_weight=NULL, members=NULL, name="", desc="", attributes=NULL, aliases=NULL, ... |
scale | ✓ | ✓ |
scale_example |
scale (7 atoms) | ✓ | ✓ | |
scale_example2 |
scale (4 atoms) | ✓ | ✓ | |
is_valid_frame |
x |
logical per name | ✓ | ✓ |
scale_frames |
x, finest=FALSE |
names, coarsest first | ✓ | ✓ |
scale_units |
x, frame=NULL |
codes at a frame (default: atoms) | ✓ | ✓ |
scale_rank |
x, frame |
1 = coarsest; key = n+1; else missing | ✓ | ✓ |
scale_atom_level |
x |
finest frame, or the key | ✓ | ✓ |
scale_key |
x |
key column name | ✓ | ✓ |
scale_leaftable |
x |
table | ✓ | ✓ |
scale_weights |
x |
weight column names | ✓ | ✓ |
scale_residuals |
x, frame=NULL |
frame → codes, or codes | ✓ | ✓ |
scale_coverage |
x, weight=NULL |
weight → fraction, or one fraction | ✓ | ✓ |
scale_vocab |
x |
dimension words for messages | ✓ | ✓ |
scale_payload_slice |
x, i |
x with payload subset |
✓ | ✓ |
scale_payload_collapse |
x, map |
x with payload collapsed by map (old row → new row, missing = dropped); default identity |
✓ | ✓ |
scale_alias_property |
name |
alias property for subclasses | ✓ | ✓ |
scale_is |
x |
logical | ✓ | ✓ |
scale_class |
the class | ✓ | ✓ | |
summary / scale_summary
|
x |
summary object | ✓ | ✓ |
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
scale_family |
x, parent=NULL, child=NULL |
table parent_frame, parent, child_frame, child
|
✓ | ✓ |
scale_ancestry |
x |
same table, all frame pairs | ✓ | ✓ |
scale_nests |
x, parent, child |
verdict, offenders
|
✓ | ✓ |
scale_crosses |
x, a, b |
verdict, offenders
|
✓ | ✓ |
scale_is_uniform |
x, frame, weight=NULL, tolerance=1e-9 |
verdict, sizes
|
✓ | ✓ |
scale_children |
x, frame, unit, target=NULL |
codes | ✓ | ✓ |
scale_parents |
x, frame, unit, target=NULL |
codes | ✓ | ✓ |
scale_descendants |
x, frame, unit, target=NULL |
table frame, unit
|
✓ | ✓ |
scale_ancestors |
x, frame, unit, target=NULL |
table frame, unit
|
✓ | ✓ |
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
SCALE_RULES |
sum, weighted_mean, mean, copy, sd, share, logshare |
✓ | ✓ | |
register_scale_rule |
param, rule, weight=NULL, scope=NULL |
the entry | ✓ | ✓ |
get_scale_rule |
param, scope=NULL |
entry rule, weight, scope, or missing |
✓ | ✓ |
list_scale_rules |
scope=NULL |
table param, rule, weight, scope
|
✓ | ✓ |
clear_scale_rules |
name=NULL, scope=NULL |
nothing | ✓ | ✓ |
A rule name may be a unique prefix ("weighted"), as with R’s match.arg(). A scoped lookup falls back to the unscoped entry. A value column with no rule (explicit or registered) is an error.
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
scale_atom_pairs |
x, source, target, weight=NULL, ... |
table source, target, w (subclass hook) |
✓ | ✓ |
scale_map |
x, source, target, weight=NULL, by=character(), ... |
crosswalk | ✓ | ✓ |
scale_map_between |
source, target, weight=NULL |
crosswalk | ✓ | ✓ |
register_scale_map |
x, source, target, map |
registry key | ✓ | ✓ |
register_scale_map_between |
source, target, map |
registry key | ✓ | ✓ |
get_scale_map |
x, source, target |
map, or missing | ✓ | ✓ |
get_scale_map_between |
source, target |
map, or missing | ✓ | ✓ |
list_scale_maps |
table key
|
✓ | ✓ | |
clear_scale_maps |
name=NULL |
nothing | ✓ | ✓ |
A crosswalk has columns <source label>, <target label>, n_from, n_overlap, w, w_from (plus any by columns first). n_from and n_overlap are 32-bit integers, w and w_from doubles. A missing target label marks atoms the target does not cover. The split share of sum disaggregation is w / w_from; with no weight column every atom weighs 1, so it equals n_overlap / n_from.
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
recast_scale |
data, x, source=NULL, target, key=NULL, values=NULL, rule=NULL, weight=NULL, na_action="drop", parent=NULL, collect=NULL, diagnostics="auto", missing_sources="na", batch=NULL, batch_by=NULL, ... |
table keyed at target (source for the share rules) |
✓ | ✓ |
recast_crosswalk |
data, map, source, target, key=source, values=NULL, rule=NULL, ids=NULL, by=character(), targets=NULL, missing_sources="na", unit="unit", collect=NULL |
table keyed at target
|
✓ | ✓ |
recast_to_atoms |
data, x, source=NULL, key=NULL, values=NULL, rule=NULL, weight=NULL, attach_weight=TRUE, collect=NULL |
table keyed at the atoms | ✓ | ✓ |
recast_from_atoms |
data, x, target, key=NULL, values=NULL, rule=NULL, weight=NULL, na_rm=FALSE, na_action="drop", collect=NULL |
table keyed at target
|
✓ | ✓ |
recast |
x, scale, ... |
dispatches on the scale | ✓ | ✓ |
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
filter_scale |
x, frame, unit, drop_empty_frames=FALSE |
scale (a sample) | ✓ | ✓ |
prune_scale |
x, frame |
scale | ✓ | ✓ |
scale_share |
x, frame, weight=NULL, within=NULL |
table | ✓ | ✓ |
join_scale |
data, x, key=NULL, frame=NULL, attach=NULL, meta=FALSE, weight=NULL, as_factor=TRUE, collect=NULL, diagnostics="auto" |
data plus attached columns |
✓ | ✓ |
as_scale_array |
data, x, value=NULL, frames=NULL, fill=NA, limit=1e7 |
dense array | ✓ | one scale |
as_scale_table |
a, x, value="value", frames=NULL, drop_na=TRUE |
table | ✓ | one scale |
as_factor = TRUE gives an R factor, and a dictionary column in Python.
Member attributes (a calorific value, an emission factor) and aliases (alternative labels) are long tables on the scale keyed by (frame, unit). Attributes are never weights and are never recast; convert_scale() applies them row by row. A kind travels with every attribute row: property is an invertible ratio, factor is one-way.
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
SCALE_ATTRIBUTE_KINDS |
property, factor |
✓ | ✓ | |
attach_attributes_scale |
x, attributes, source=NULL, kind=NULL |
x with rows appended; an existing (frame, unit, attribute, source) row is an error |
✓ | ✓ |
scale_attributes |
x, attribute=NULL, frame=NULL, source=NULL, inherit=TRUE |
stored rows; with frame= one row per member and attribute, resolved along the hierarchy (origin_frame, origin_unit) |
✓ | ✓ |
attach_aliases_scale |
x, aliases, source=NULL |
x with rows appended |
✓ | ✓ |
scale_aliases |
x, frame=NULL, source=NULL |
alias rows | ✓ | ✓ |
scale_match |
x, labels, frame=NULL, source=NULL |
table label, frame, unit, method, candidates; method = exact, alias, ambiguous, none
|
✓ | ✓ |
convert_scale |
data, x, attribute, from_unit, to_unit, key=NULL, frame=NULL, values=NULL, source=NULL, na_action="error", collect=NULL |
data with value columns converted plus <attribute> and <attribute>.origin_frame
|
✓ | ✓ |
For dimension packages: accept the same inputs as the core verbs and return results in the caller’s type.
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
backend_of |
data, arg="data" |
the backend (errors on an unsupported input) | ✓ | ✓ |
backend_is_lazy |
backend |
logical | ✓ | ✓ |
backend_schema |
data, backend=NULL |
column names and types, no scan | ✓ | ✓ |
backend_scan |
data, backend=NULL, columns=NULL, filter=NULL |
the needed columns and rows | ✓ | ✓ |
backend_pull |
table |
an in-memory table | ✓ | ✓ |
backend_restore |
table, backend, collect=NULL |
the caller’s type | ✓ | ✓ |
backend_bind_rows |
parts |
one table | ✓ | ✓ |
backend_distinct |
data, columns, backend=NULL |
distinct rows, first-appearance order | ✓ | ✓ |
backend_lazy |
data, backend=NULL |
a dplyr carrier for a pipeline (R’s lazy lift; Python has no need for it) | ✓ | — |
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
write_scale_dataset |
data, x, path, partitioning=NULL, compression="zstd", overwrite=FALSE |
path |
✓ | ✓ |
open_scale_dataset |
path |
data (lazy) + scale
|
✓ | ✓ |
scale_dataset_info |
path |
kind, axes, keys, files, bytes | ✓ | ✓ |
The store’s manifest is _scales/manifest.json (one definition header per axis); a legacy manifest.yaml is still read.
A definition is a language-neutral file: a JSON header (schema_version, class, name, key, frames, members, meta, tables, payload) with the leaftable, attribute and alias tables inline (column-wise, typed) or, in a <name>.scale/ bundle directory, as Arrow IPC or CSV files. YAML is accepted as input and optional output. The core ships carriers (fuels and energy carriers with IPCC 2006 defaults) and reads definitions from every registered directory.
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
write_scale |
x, path, format="json", tables="auto", overwrite=FALSE |
path; .json/.yaml = one file, otherwise a bundle directory |
✓ | ✓ |
read_scale |
path, package=NULL, class="stored" |
the scale (a registered subclass, or the core with class="core"); path may be a catalogue id |
✓ | ✓ |
scale_catalog |
table id, name, kind, key, frames, n_atoms, package, path
|
✓ | ✓ | |
register_scale_dir |
path, package=NULL |
registry key | ✓ | ✓ |
list_scale_dirs |
registered directories | ✓ | ✓ | |
clear_scale_dirs |
nothing | ✓ | ✓ | |
register_scale_class |
class |
registry key (qualified class name) | ✓ | ✓ |
list_scale_classes |
registered class names | ✓ | ✓ | |
scale_write_payload |
x, dir, header |
the header with payload entries (subclass hook; default identity) | ✓ | ✓ |
scale_read_payload |
x, dir, header |
the subclass object (subclass hook; default identity) | ✓ | ✓ |
| Function | Arguments | Returns | R | Py |
|---|---|---|---|---|
scale_product |
..., name="", desc="", keys=NULL, joint_weights=NULL |
product | ✓ | ✓ |
scale_axes, product_keys, product_frames, product_size, product_weights, product_coverage
|
x, ... |
accessors | ✓ | ✓ |
scale_is_product, scale_product_class
|
✓ | ✓ | ||
recast_product, join_product, filter_product, product_atoms
|
✓ | — |