NEWS.md

NSCA 0.4.5

A missing condition value no longer moves that condition's scope

Each condition is fitted by the engines on its own complete pairs, but the outcome bounds NSCA measured the admissible region against were taken from every observed outcome. A row with an outcome and no condition value therefore widened the box on one side of the identity and not on the other. On one such dataset admissible_region_share fell from 0.167 to 0.056, the area identity missed by 0.11, and geometry_acceptable -- and with it joint_support -- turned FALSE. With two conditions the effect is not confined to edge cases: missing values in one condition stretched its bounds to the outcome range of the other, and the identity missed by 0.65. Outcome bounds are now taken per condition, from the same pairs and the same scope segment the engines use. Complete data is unaffected.

Smaller fixes

Examples

CRAN review asked that \dontrun{} be kept for code that genuinely cannot run. The one example that used it, nsca_extract(), relied on a fit it never created; it now builds its own model and runs unwrapped.

nsca(), nsca_table(), nsca_results() with its print() and summary() methods, nsca_thresholds() and nsca_plot() with its plot() method had no examples and now have runnable ones. The nsca() example keeps the default 1000 permutations, which take longer than CRAN's five-second budget, so it is wrapped in \donttest{}: it still runs under example() and under R CMD check --as-cran.

nsca_reference()'s example and its list of returned columns had been added to the .Rd file by hand and were missing from the roxygen source, so the next roxygen2::roxygenise() would have dropped them. They are now in the source, and every help page is regenerated from it.

Documentation

NSCA 0.4.4

URL, BugReports and both READMEs pointed at youngchanresearcher/NSCA_condition_analysis. They now point at the package's repository, https://github.com/youngchanresearcher/NSCA. Nothing else changed; the entry for 0.4.3 below had been relabelled 0.4.4 in that release and is restored to its own version here.

NSCA 0.4.3

Authorship and maintainer

Authors@R named a placeholder, NSCA Contributors <maintainer@example.org>, which CRAN rejects: the maintainer must be a real person at a working address. Both are now Young Chan youngchanresearcher@gmail.com.

The package had no inst/CITATION, so citation("NSCA") fell back to the entry R generates from DESCRIPTION. That entry was correct but silent about where the numbers come from. A CITATION file now names NSCA, SCAtools and the NCA method article together, and reads the version from DESCRIPTION.

region was retired but never reachable

0.3.0 renamed the threshold-table column region to threshold_status, and that rename is printed in the table below, in both READMEs and in the package guide. The entry was never added to the legacy map, so nsca_legacy_names() omitted it, nsca_thresholds(legacy = TRUE) did not append a region column, and nsca_extract("region") reported it as an unknown parameter rather than a retired name — the one thing the retirement policy promises. It is now in the map, and the test pins the whole documented mapping rather than a sample of it, so a missing entry cannot pass again.

Documentation

Both READMEs gained the installation section they never had. NSCA needs NCA and SCAtools in place first, and the order matters, which is worth stating where a reader starts.

DESCRIPTION gained URL and BugReports, and both READMEs now link the repository. SCAtools is released separately, so its own link is given too:

URL: https://github.com/youngchanresearcher/NSCA_condition_analysis
BugReports: https://github.com/youngchanresearcher/NSCA_condition_analysis/issues

The interval and the overlap now follow scale (breaking)

nsca_thresholds() reported necessity_threshold and sufficiency_threshold on the requested scale, but necessity_sufficiency_interval and overlap_width were left in actual units on every scale. The three columns print side by side, so a reader who subtracted the two thresholds did not get the interval: on the default "percentage.range" the two thresholds were percentages of the range and the width between them was in the units of X. The behaviour was documented -- the help page said the interval is "expressed in the units of X" and told the reader to normalise -- but the normalised number was the one the package would not produce, and nothing in the table said the row mixed two kinds of column.

Both columns are now reported on scale, so a row can be checked by subtraction. necessity_sufficiency_interval_actual and overlap_width_actual are new and hold the previous values, whatever scale is asked for; the signed threshold_gap_actual is unchanged.

Only the reported magnitude moved. The sign is still settled in actual units and carried over, because under convention = "directional" a low-X direction is mirrored: taking the sign from the reported difference would swap "admissible interval" and "frontiers overlap" in exactly the views where the mirroring applies. threshold_status, thresholds_compatible and every component and joint index are untouched.

Under scale = "percentile" the column is a difference of ranks, so it reads as the share of cases between the two thresholds rather than as a width. The help page now says so, and points at "percentage.range" or "sd" for a normalised width.

To restore the old numbers, read the _actual columns or ask for scale = "actual".

NSCA 0.4.2

Three fixes found by the first full R CMD check --as-cran of the 0.4.x line. All three predate 0.4.1. No code, argument, column, default or reported quantity changed.

nsca_table() documentation was malformed (WARNING)

checkRd refused nsca_table.Rd: \emph is not valid inside \code. The formula for balanced_joint_effect had been written to the .Rd as \code{2 \emph{ sqrt(d_nec } d_suf)} -- a markdown converter had read the two multiplication signs in 2 * sqrt(d_nec * d_suf) as emphasis markers and turned the text between them into \emph. The .Rd now carries the formula it was meant to carry. The roxygen source was already correct, so nothing in R/results.R needed changing.

A test used an argument its expectation does not have (ERROR)

test-invariants.R passed info = name to expect_gte() and expect_lte(). The ordered comparisons take label and expected.label, not info, so current testthat raises "unused argument" -- which aborts the whole test_that() block rather than failing one expectation. This is why the suite reported 1 error against 1032 passes: the assertion itself was fine and had never actually run. Both calls now pass label.

The neighbouring expect_true(..., info = ...) calls are correct and are left alone; expect_true() does take info.

README_zh-TW.md moves into inst/docs/ (NOTE)

R CMD check counts any unrecognised top-level file as a NOTE. From inst/ the translation is still shipped and is now reachable from R: file.show(system.file("docs", "README_zh-TW.md", package = "NSCA")).

Not fixed, because it is not the package

The "unable to verify current time" NOTE is R CMD check failing to reach a time server. It says nothing about the package and no package change can remove it. Set _R_CHECK_SYSTEM_CLOCK_ = 0 before checking to skip it.

NSCA 0.4.1

"biconditional" is prose; biconditional stays a column name

The article calls the joint claim necessary-and-sufficient, and uses "biconditional" only in contrast: section 2.1 says each component claim "is a conditional, not a biconditional". Using the same word for the joint claim therefore reads against the article it follows. All prose now says necessary-and-sufficient -- the DESCRIPTION, the roxygen documentation, the code comments, the printed inference notes, and both READMEs.

Nothing was renamed. The biconditional column of nsca_direction_map() keeps its name: it holds the joint statement written out, and the short logical word is the more usable column name. necessary_and_sufficient_statement would be three times as long for the same content, in a data frame whose other columns are one or two words. Every field name, list element and status value is unchanged, including the necessary_and_sufficient element of nsca_results().

OLS is available as a reference line

"ols" is an ordinary least-squares regression of the outcome on the condition, fitted to all the data. It has always been refused as a component, and still is: it summarises central tendency, not an empty space, so it can be neither the necessity nor the sufficiency side. Since 0.4.1 it can be drawn alongside them. Section 6.1 of the framework keeps average-effect and condition analysis side by side rather than merging them into one number, and cites the combined importance-performance map as the precedent for showing two logics on one picture without collapsing them.

Requires SCAtools >= 0.4.1.

NSCA 0.4.0

This release adopts the vocabulary of condition analysis in degree, the framework that places SCA and NSCA beside NCA. Terminology follows the article; the package's own names survive only where the article has nothing to say. Nothing was removed: every retired name still works.

The admissible region and the necessity-sufficiency interval

The framework names the two quantities 0.3.0 had called the data zone.

Two threshold_status values change with them: "determinate" becomes "exact correspondence", the limiting case in which one X value is both the minimum required and the sufficient level, and "data zone" becomes "admissible interval".

The documentation now states three things the framework is careful about and 0.3.0 was not. The interval is expressed in the units of X, so it is scale-dependent and comparable across studies only after normalisation. It is not a confidence interval and carries no statistical uncertainty. And an estimated interval of zero is consistent with exact correspondence but does not by itself establish it; a small non-zero interval should be called near correspondence only when a substantively justified tolerance has been defined.

Joint support

Evidence for both components is joint support, which is a claim about the content of the joint hypothesis rather than a demand that the components have identical effects or thresholds.

The disjointness check

Section 5.2 of the framework states a consistency check the package did not implement. Whenever the two frontiers do not cross, the floor lies at or below the ceiling throughout the scope, so no point belongs to both expected empty spaces; because each component effect size is that space's share of the same scope, the two shares cannot sum to more than one. Component effect sizes summing above one therefore say the frontiers have crossed and the result needs diagnosis, not that it is unusually strong.

The sum is a proxy for a crossing that is not otherwise available. Here it is available: overlap_share is the area on which the two empty spaces actually overlap, integrated from the reconstructed frontiers. Deciding from the measurement avoids the proxy's failure mode, which is that the sum comes from the engines and the comparison threshold from the geometry, so the two can disagree in the last few digits on exactly the configuration the check exists to bless.

That configuration is the perfect diagonal. A step frontier overlaps itself by construction: between two consecutive observations the upper staircase still holds the earlier value while the lower one has already moved, so the two spaces overlap on every tread, exactly 1 / (n - 1) of the scope summed over the n - 1 treads, and a perfect diagonal attains it. That allowance is subtracted before the comparison. For every smooth frontier the allowance is zero and any overlap at all is a genuine crossing.

frontiers_crossed asks whether the frontiers crossed at all; geometry_acceptable asks whether they crossed by more than geometry.max_overlap. A row can be flagged as crossed and still have acceptable geometry, which is the useful distinction: one says something happened, the other says whether it matters.

Ceiling, floor, and the empty space

The framework distinguishes a boundary, the theoretical line separating an expected empty space from the compatible region, from a frontier, that boundary estimated from data, and names the direction in which Y is bounded a ceiling or a floor. The package estimated the right things but never said which was which, because the argument is called ceilings in every direction.

nsca_table(), nsca_results(), nsca_corners() and nsca_direction_map() now report, in the wording of Table 1:

nsca_thresholds() gains relationship and records the inequality it used.

Boundary is no longer an argument name

nsca_thresholds() renames its boundary argument to inequality, matching SCAtools 0.4.0. A boundary is the theoretical line an empty space is separated by; it has nothing to do with whether a reported rule uses > or >=. The old argument still works and warns.

Retired names still work

Also

NSCA 0.3.0

This release adopts the unified conceptual specification: the reported quantities now follow one geometric identity instead of being defined separately, the vocabulary names the geometry rather than an interpretation, and a statistic of both components can be tested properly for the first time.

The identity everything now follows

The two empty zones and the region between them tile the analytic scope, so by inclusion and exclusion

data_zone_share = 1 - d_nec - d_suf + overlap_share

Every other area quantity is derived from this rather than asserted. Two consequences are new:

New: reconstruction_error

The identity has two computational routes. data_zone_share and overlap_share are integrated from the frontiers this package reconstructs; d_nec and d_suf are reported by the engines from their own internal frontiers. The residual between the routes is now reported per row.

It is the only available check that the reconstruction still matches what the engine actually fitted, and it catches the one failure mode a plot cannot: a wrongly rebuilt frontier still looks like a frontier and still yields plausible areas. Only the identity refuses to close. This was previously asserted on fixtures in the test suite; it now runs on the user's own data.

Renamed columns

Old names remain reachable: nsca_table(legacy = TRUE) appends them as duplicates, nsca_extract() accepts them with a warning, and nsca_legacy_names() returns the mapping.

| retired | current | |---|---| | d_nsca | weakest_effect | | j_nsca | balanced_joint_effect | | determinacy | joint_empty_zone_coverage | | undetermined_share | data_zone_share | | weaker_side | weaker_component | | p_nsca | p_nsca_iut | | undetermined_width (thresholds) | data_zone_width | | region (thresholds) | threshold_status |

determinacy had to go: it named an interpretation the number does not earn, since a constant outcome scores 1 on it. The region between the frontiers is the data zone — the region observations are still admitted in, which is not the claim that any were observed there, and the documentation now says so explicitly. The threshold_status value "undetermined" is correspondingly now "data zone".

New: p_weakest_perm and the shared permutation sequence

shared.test.rep replaces the engines' two separate permutation runs with one sequence driving both. Each replication shuffles the outcome once, refits both engines on that same shuffled frame, and records d_nec, d_suf and their minimum together.

This is what makes a p-value for min(d_nec, d_suf) meaningful. An intersection-union rule combines two p-values and needs no assumption about how they co-vary, but the minimum is a statistic of both components and its null distribution depends on exactly that co-variation. Two independent permutation runs would discard the constraint that the two empty zones are disjoint subsets of one scope, and produce a null distribution no dataset could have generated.

When the shared sequence is used, p_nec and p_suf come from it too, so all four p-values are mutually consistent; p_source reports which regime produced them. The p-values carry the Phipson-Smyth correction, so none can be zero.

p_weakest_perm does not replace p_nsca_iut, and the documentation now gives the sharp reason rather than the vague one. Its null is random pairing alone, which is a proper subset of the union null "not necessary or not sufficient": that union contains states, such as necessary-but-not-sufficient, which are not random pairing at all. Calibrating against random pairing therefore carries no level-alpha guarantee over the union. It is a sensitivity analysis; the intersection-union rule remains the decision.

New: the support decision

necessity_supported, sufficiency_supported, geometry_acceptable and nsca_supported are reported separately, so a failure says which of the three requirements failed rather than collapsing into one boolean.

New: balance

balanced_joint_effect penalises asymmetry but does not measure it: (0.50, 0.125) and (0.25, 0.25) both score 0.50. balance is the geometric mean over the arithmetic mean, which is 1 exactly when the two components are equal. The name of the index promised something only this column delivers.

Documentation is now generated from one source

Earlier versions carried hand-written .Rd files alongside roxygen comments. Running devtools::document() silently replaced the .Rd files with roxygen output and lost their content, and without Roxygen: list(markdown = TRUE) the result rendered literal backticks and [function()] links on every help page. The roxygen blocks are now the single source, the shipped .Rd files are generated from them and carry the roxygen2 header, and markdown is enabled.

Also

NSCA 0.2.0

The doubling is normalisation, not a new assumption. The two empty zones are disjoint subsets of one scope box, so d_nec + d_suf <= 1, and by AM-GM sqrt(d_nec * d_suf) <= (d_nec + d_suf)/2 <= 0.5. The index therefore spans [0, 1] and reaches 1 exactly when both components equal 0.5, which is the single-line case where the two frontiers coincide and the band closes. It is left unclamped so that the step frontier's 1/n self-overlap stays visible.

This is a correction to how earlier versions framed the choice. d_nsca was described as though the minimum were the uniquely correct joint effect size and the alternatives were mistakes. They are not: they are the same family at different degrees of compensation, and nothing in the geometry of two empty zones fixes the degree. The documentation now names the parameter instead of hiding a choice inside a formula.

The other thing called a horizontal line — a flat frontier over a filled scope — needs no special rule and gets none. There is no empty zone on either side, so every index is correctly zero.

NSCA 0.1.2

It exists because d_nsca and the band answer different questions and neither dominates the other. d_nsca is depressed by curvature: a perfect bijection such as Y = X^3 scores 0.25 because the sufficiency zone it leaves really is small. determinacy is curvature-neutral and reaches 1 for Y = X and Y = X^3 alike, but it is inflated at the low end, where a loose scatter already scores about 0.2.

determinacy is not an effect size and the documentation says so. A constant outcome closes the band completely and scores 1 while satisfying neither component. The permutation screen is what rules that out, not the index.

NSCA 0.1.1

NSCA 0.1.0

First release.

Design decisions worth knowing



Try the NSCA package in your browser

Any scripts or data that you put into this service are public.

NSCA documentation built on Oct. 10, 2026, 5:08 p.m.