Software · Founding article · Published 2026-10-23 · Open access, CC-BY 4.0

A Computational Framework for Multivariate Goodness-of-Fit and Two-Sample Testing: MDgof and MD2sample

Wolfgang Rolke1

1 Department of Mathematical Sciences, University of Puerto Rico at Mayagüez, Mayagüez, Puerto Rico 00681, USA · orcid 0000-0002-3514-726X

goodness-of-fittwo-sample testsmultivariate databenchmarkingpower studiesR
doi: https://doi.org/10.68365/jrepstat.2026.8 Vol. 1 (2026) · Art. 8 Version 1 how to cite ↓ html pdf (typeset)
✓ Reproduced

Reproducibility record

verified 2026-10-23
Result
Reported results reproduced. Reported values reproduce, except power estimates of tests with own p-values and the BR row of the first Iris test, which vary between runs (an RNG fault).
✓ measured
Entry point
cd code && quarto render replication_material.Rmd
✓ executed
Environment
R 4.6.0, MDgof 1.2.1, MD2sample 1.4.2, rocker/r-ver:4.6.0, linux/aarch64
✓ measured
Determinism
Seeded.
declared
Archive
a-computational-framework-for-multivariate-goodness-of-fit-and-two-sample-testing-mdgof-and-md2sample-v1-repro.zip · reviewed bundle SHA-256 521d2f115511d50f13ee57e8542c07b664231462e66092597873f6f2323debea · https://doi.org/10.5281/zenodo.23598707
✓ computed
Data
Open.
declared, route checked

Reproducibility package (.zip, 218 KB) · Zenodo doi:10.5281/zenodo.23598707 · SHA-256 309e9b2500155ada13b7e2cf8e783567f0522b273f7b565e0110b38a2af8c31c

$cd code && quarto render replication_material.Rmd
Open review

Peer review record

2 rounds · 3 reviewers · signed Full reviewsHide reviews
2026-10-13Minor revision2026-10-15Revised2026-10-23Accepted

The decision letters, the reports the authors read, their answers and the editor's signed decisions, as written. The reports come from the journal's review engine. The authors could have kept this record private until they confirmed the proof.

Round 1Minor revision3 reviewers · response
Minor revisionConfirmed and signed by Matthias Templ2026-10-13 · orcid 0000-0002-8638-5276
Decision letter2026-10-13The paper presents MDgof and MD2sample, a pair of R packages that put a broad set of multivariate goodness-of-fit and two-sample procedures behind one common interface for testing, parametric-bootstrap calibration, Monte Carlo power estimation, simultaneous inference, and benchmarking, with a slot for user-supplied statistics. It matters to applied analysts choosing among multivariate tests and to methodologists who need established competitors on identical simulation conditions. We would be glad to see the revised version.

Thank you for your submission to JRepStat. We are inviting a minor revision. The reviewers’ points follow, grouped by weight.

The paper presents MDgof and MD2sample, a pair of R packages that put a broad set of multivariate goodness-of-fit and two-sample procedures behind one common interface for testing, parametric-bootstrap calibration, Monte Carlo power estimation, simultaneous inference, and benchmarking, with a slot for user-supplied statistics. It matters to applied analysts choosing among multivariate tests and to methodologists who need established competitors on identical simulation conditions. We would be glad to see the revised version.

How to read this letter. The decision rests on the items marked required. If a required item seems wrong to you, or cannot be done within this paper, say so in your response letter and give the reason. A reasoned decline is a legitimate answer, and the editor weighs it. Reviewers can be mistaken. Items marked recommended or optional are advice from the reviewers. Adopt them, decline them with a sentence, or leave them for later work. None of that counts against the paper. The reviewer reports below carry the same marks. The editor who signs this decision may weigh an item differently in the accompanying message, and that message prevails.

Reproducibility items marked required are the journal’s minimum for a paper that ships code with its results: code and a command that runs it, and by acceptance a captured computational environment (dependencies/lockfile/container) and a signed determinism attestation for the entry-point command. They document what you already have. They do not ask for new analysis.

required (the decision rests on these):

  • [software-quality] In src/TS_cont.cpp, the Aslan-Zech accumulator in the y-y loop guards the logarithm with the wrong index: for(i=0;i<(ny-1);++i) for(j=i+1;j<ny;++j) if(Dz(i,j)>0) tmp(0)=tmp(0)-log(Dz(nx+i,nx+j));. The condition tests Dz(i,j) (an x-x distance) while the term taken is Dz(nx+i,nx+j) (a y-y distance). Please confirm the guard should read Dz(nx+i,nx+j)>0. As written the AZ statistic can take log(0) when a y-y distance is exactly zero, or skip a valid term, which can bias the AZ power figures in Tables and Section 9. Because both observed and simulated statistics use the same code, level is preserved, so the paper’s validity claims stand, but the reported AZ power may shift. Alternatively state that this is the intended computation.
  • [statistical-rigor] The iris and EPL applications (Section 9) report bare p-values and draw conclusions from the 0.05 threshold with no effect size or interval. “setosa does not appear to come from a multivariate normal distribution” and “a significant shift in goal-scoring patterns, which may reflect changes in playing styles, tactics, or league dynamics” are substantive claims resting on p-values alone. Either report a distance or discrepancy magnitude for these two examples, or reword both conclusions to present them as illustrations of the interface rather than findings about iris or the Premier League.
  • [Reviewer 1] The iris conclusion in Section 9.1 rests on the two smallest of three p-values (qK 0.0014, qCvM 0.0000, qAD 0.1043) while Section 4 warns against exactly that. Report the adjusted p-value the package produces, or qualify the conclusion to reflect the disagreement among the three statistics.
  • [Reviewer 1] The EPL example in Section 9.2 drops matches with 5 or more goals (factor levels 0:4 send them to NA) and compares a complete 2020-21 season against a partial 2025-26 season, then reads the result as a change in tactics. State the truncation and the incomplete season, and present the finding as a software illustration rather than a substantive claim about league dynamics.
  • [Reviewer 2] In Section 9.2 the adjusted p value combines four tests (ChiSquare, KS, AZ, CvM) while twosample_test reports eight. State which tests enter the minimum and why, since the claim that the result is not a multiple-testing artifact rests on that set. Replace “significant shift” and “confirms” with the estimates and their resampling uncertainty.
  • [Reviewer 3] The English Premier League example in Section 9.2 reads live CSVs from a URL and sets no seed, so the printed p-values and the adjusted value of 0.018 cannot be reproduced, and the 2025-26 file is a partial, changing season. Ship a frozen snapshot (or give the retrieval date and state the numbers depend on data vintage) and set a seed.
  • [Reviewer 3] The set of methods in the printed output changes across functions without explanation: discrete gof_test returns K, AD, KL, P while discrete gof_power returns KS, K, CvM, AD, TV, KL, H, P, and twosample_test_adjusted_pvalue reports only four of the eight methods from twosample_test. Reconcile these or state in the text why the testing and power/adjustment routines evaluate different method sets.

recommended:

  • [consistency-check] The English Premier League example reports the same tests twice with different p values. twosample_test(dta) in Section 9.2 gives KS = 0.0020, AZ = 0.0042, CvM = 0.0488, while the adjusted-p-value call a few lines later lists KS = 0.0036, AZ = 0.0044, CvM = 0.0508. These are consistent with unseeded Monte Carlo runs, but a reader comparing the two blocks sees conflicting numbers for the same statistics on the same data. Pass a fixed seed to both EPL calls so the individual p values agree across the two outputs, or add one sentence stating that the two calls use independent simulations and so differ by Monte Carlo error.
  • [consistency-check] Table 2 marks Chi-square tests as available for continuous data in MD2sample, but the continuous twosample_test output in Section 5.2 (KS, K, CvM, AD, NN1, NN5, AZ, BF, BG, MMD, FR, NN0, CF1–CF4, Ball) shows no ChiSquare entry, whereas the discrete output in the same section does include ChiSquare. Reconcile the table with the demonstrated default output, or note that the continuous chi-square test is not run by default.
  • [consistency-check] The adjusted-p-value algorithm in Section 4 states “Repeat B(= 1000) times”, while the documented default for the testing routines is B = 5000 (Section 5). This is a presentational mismatch in the stated simulation size rather than a result discrepancy. State the default B used by the adjusted-p-value routines, or mark 1000 as illustrative.
  • [methods-soundness] The text warns that permutation calibration treating a fitted distribution as fixed “can be inappropriate” when parameters are estimated, and provides parametric-bootstrap calibration for gof_test. The hybrid route (generate a Monte Carlo sample from the null and apply a two-sample test) is only illustrated with a simple null. Add one or two sentences stating how hybrid_test and hybrid_power handle an estimated-parameter null, since generating the reference sample from parameters fitted to the observed data induces dependence that a standard permutation two-sample calibration would not reflect.
  • [prose-and-style] The manuscript leans on the “not X but Y” frame several times: “The principal contribution of this work is not any single test statistic, but infrastructure”, “The purpose of including many methods is not to claim that one family dominates the others, but to make procedures available”, “intended not simply as collections of tests but as infrastructure”. State each point directly in its own sentence, for example “The contribution of this work is infrastructure in which existing and newly proposed statistics can be implemented and compared.”
  • [prose-and-style] “the packages provide common infrastructure for applying heterogeneous procedures and, importantly, for comparing them under identical simulation conditions” uses “importantly” as emphasis. Drop the word and let the clause stand.
  • [simulation-design] Monte Carlo error is never reported or acknowledged. Every displayed example uses B=250, and the resulting type I error estimates under the null range from 0 to about 0.13 against a nominal 0.05 (for instance the goodness-of-fit power table first row, and the hybrid power table), which shows the reported figures carry substantial sampling noise. State once that the in-text numbers use a deliberately small B=250 for speed and are illustrative, and that the default B (5000 for tests, 1000 for power) should be used for reported comparisons; a single sentence giving the approximate standard error of a rejection-rate estimate at a given B would let readers judge which differences in the rank tables are real.
  • [simulation-design] The “average rank” summary from run.studies is a reasonable headline metric, but with B=250 and no spread reported the ordering of closely ranked methods (for example qK at 6.3 versus qAD at 6.8 versus qCvM at 7.0) is not distinguishable from Monte Carlo noise. If any comparative ranking is to be read as a finding, report it at the default B with an indication of variability.
  • [software-landscape] Several built-in statistics are the package’s own reimplementations in Rcpp (maximum mean discrepancy in mmdagg.cpp, the Aslan-Zech and Baringhaus-Franz energy statistics and the Biswas-Ghosh statistic in TS_cont.cpp, Friedman-Rafsky in FR.test.R), while others wrap a reference package directly (Ball::bd.test, gTests::g.tests, FNN, spatstat.explore::Kest), where agreement is automatic. For the reimplemented ones, a short table giving the statistic value computed by MDgof/MD2sample and by the corresponding reference package (energy or cramer for the energy and Baringhaus-Franz statistics, a kernel package for MMD) on one shared data set would show the built-in statistics match the established definitions and would strengthen the benchmarking claim.
  • [software-landscape] The MMD, energy, Friedman-Rafsky and Fasano-Franceschini reimplementations are not accompanied by a citation to any existing software implementation of those methods. Adding one citation each for the packages a reader would otherwise reach for (for example cramer for Baringhaus-Franz, a kernel-MMD package, fasano.franceschini.test) would make the relation to existing tools explicit.
  • [software-quality] In R/power_pvals.R the NaN handling if(any(is.nan(TS_sim))) {j=j-1;next} cannot repeat an iteration, since assigning to the loop variable of an R for loop has no effect on iteration. A rejected simulation is therefore dropped rather than re-drawn, so the denominator B no longer matches the number of accumulated statistics for affected rows. Consider a while loop or an explicit resample.
  • [software-quality] In R/twosample_power.R the message block if(max(totaltime)<20 | B<=2*ncores) if(!SuppressMessages) message(...) else if(...) message(...) has a dangling else that binds to the inner if(!SuppressMessages), so the “Using N cores” message prints under the wrong condition. The actual processor counts are set separately below, so only the console message is misleading, but the branch reads as a logic slip.
  • [statistical-rigor] Every p-value and power figure in the paper is a Monte Carlo estimate, and none carries an uncertainty measure. The worked examples use B=250, so a reported power of 0.404 has a standard error near 0.03 and a p-value near 0.05 has a standard error near 0.014. The average-rank summary in Section 7 inherits this noise and could reorder methods on a re-run. Add the Monte Carlo standard error (sqrt(p(1-p)/B)) to the testing and power output, or state it once in the text, so readers can see which differences the simulation size can resolve.
  • [statistical-rigor] The EPL comparison sets the 2020-21 season (played without crowds under pandemic conditions) against 2025-26 and attributes any difference to “playing styles, tactics, or league dynamics”. The season difference is confounded with the crowd-absence effect. Note this confound where the example is introduced, since the interpretation does not follow from the test alone.
  • [writing-quality] The dnull argument is used in the Section 5.1 examples (gof_test(x, pnullC, rnullC, dnull = dnullC, ...)) and drives the extra BB statistic, but it is absent from the bulleted argument list for MDgof::gof_test(). A reader meets dnull in the example with no prior definition. Add a line describing it (the null density function) to that argument list, as is already done for gof_power().
  • [writing-quality] The abbreviations in the printed output (for example qK, qCvM, qAD, FF, RK, EP, BB, BR, KSD, ES, EP33, NN0/NN1/NN5, CF1–CF4) are not mapped to the method names in Tables 1 and 2. A reader cannot reliably connect, say, ES/EP or RK to a row in the table. A short column of output labels in each table, or one legend, would let readers interpret every result block.
  • [visualisation] The density and correlation panels of a default ggpairs matrix can be hard to read at print size. Please confirm the axis tick labels and the panel strip labels remain legible at the final column width, and enlarge the base font if they do not.
  • [Reviewer 2] Add a size study showing the built-in tests hold the nominal level across representative scenarios. The null rows already shown range from about 0.03 to 0.12 (the discrete chi-square P test reaches 0.124), which at B=250 is several Monte Carlo standard errors from 0.05 and warrants a direct check.
  • [Reviewer 2] Pin and archive the exact Football-Data.co.uk files used in Section 9.2. The live URLs update, so the reported counts and p values cannot be reproduced from the article as written.
  • [Reviewer 2] State that the illustrative runs use B=250 and give the resulting Monte Carlo error, so readers do not read the tabulated power values as precise.
  • [Reviewer 3] State whether seeded runs reproduce under parallel execution (maxProcessor greater than 1), since the packages emphasise parallel processing yet every seeded example uses maxProcessor = 1.
  • [Reviewer 3] Report the software environment (R version and versions of mvtnorm, copula, Ball, energy, GGally, Rcpp) alongside the CPU and OS already given in Section 8, so the reproduction is pinned.

optional:

  • [methods-soundness] The continuous multivariate EDF statistics are evaluated only at the observed points along one orientation. The paper already flags this as a computational approximation. A short note on which alternatives this approximation can miss (orientation-dependence of multivariate KS-type statistics) would help users choose among the built-in statistics, and could be deferred to the vignette.
  • [Reviewer 1] A compact summary across the benchmark collection, showing which statistic families lead under which alternative geometries, would give applied readers a basis for a default choice.
  • [Reviewer 2] Note that the adjusted p value nests simulation inside simulation (each resample reruns every test’s own calibration) and say what that costs.

We will correct these in production, and you confirm them in the proof:

  • [writing-quality] In Section 4 the null distribution of the minimum p value is named Fmin in padj = Fmin(pmin,obs) and then called ˆFp in the algorithm bullets (“empirical distribution function ˆFp”, “Apply ˆFp”). The capitalisation of the minimum p value also alternates between Pmin and pmin. Using one symbol throughout removes the apparent discontinuity.

Reviews

Report Reviewer 1recommends minorThe problem this software addresses is real. Multivariate goodness-of-fit and two-sample testing carry a long list of procedures whose power depends on the alternative, the dimension, the sample size, and the treatment of nuisance parameters, and there is no uniformly best test. An analyst who wants to run several procedures, or a methodologist who needs established competitors on matched conditions, currently has to stitch together packages with different data conventions and resampling schemes. Putting these procedures behind one interface, with shared calibration, power, and benchmarking machinery and a slot for user statistics, is useful infrastructure. The framing is honest that the contribution is the environment and not any single statistic. The method coverage is broad and the parametric-bootstrap calibration that re-estimates parameters is the right choice for composite nulls. From the applied side the design is sound and the packages are on CRAN.

Written by the journal's review engine in the role of the application reviewer, and read by the editor before the decision. How the engine works is on the peer review page.

The problem this software addresses is real. Multivariate goodness-of-fit and two-sample testing carry a long list of procedures whose power depends on the alternative, the dimension, the sample size, and the treatment of nuisance parameters, and there is no uniformly best test. An analyst who wants to run several procedures, or a methodologist who needs established competitors on matched conditions, currently has to stitch together packages with different data conventions and resampling schemes. Putting these procedures behind one interface, with shared calibration, power, and benchmarking machinery and a slot for user statistics, is useful infrastructure. The framing is honest that the contribution is the environment and not any single statistic. The method coverage is broad and the parametric-bootstrap calibration that re-estimates parameters is the right choice for composite nulls. From the applied side the design is sound and the packages are on CRAN.

My main concern is in the two real-data demonstrations, which are the paper’s evidence that the framework helps on actual data, and which both state conclusions firmer than the data support. The iris example reports three p-values for multivariate normality of setosa (qK 0.0014, qCvM 0.0000, qAD 0.1043) and concludes the data do not come from a normal distribution. That reads the two small values and sets aside the third, which is the move Section 4 is written to prevent. The package already computes an adjusted p-value for this situation, so the fix is to report it, or to qualify the sentence to acknowledge that one of the three statistics is compatible with normality. As written the application contradicts the paper’s own message.

The EPL example has two treatment issues. The scoring table uses factor levels 0:4, so any match with five or more goals falls to NA and leaves the counts, which removes real matches from both samples without comment. And the 2020-21 season is complete while the 2025-26 season is in progress at the time of writing, so the two samples differ in completeness as well as in era. On that basis the text reads the test as a shift in goal-scoring that may reflect playing style, tactics, or league dynamics. That interpretation is not supported by a test that compares a full season to a partial one on truncated counts. Please state the truncation and the incomplete season, and present the result as an illustration of the discrete two-sample workflow. The paper also uses “significant” and reads p-values against a threshold in this section. Describing the size of the departure and what it is compatible with would fit the rest of the paper better.

Three smaller points would help a reader use the software. The Football-Data source is live, so the Section 9.2 numbers will not reproduce from the URLs given, and a frozen snapshot in the artifacts would fix that. The output columns use abbreviations (qK, qKS, BR, FF, RK, ES, EP, BB, and the two-sample labels) that a reader cannot map to Tables 1 and 2 without guessing, and a short key would remove the guessing. Finally, the set of methods returned is not stable between the test and power routines: the discrete gof_test returned K, AD, KL, P, while the discrete gof_power for the same setup returned KS, K, CvM, AD, TV, KL, H, P, and the continuous case shows a similar gap. A sentence explaining why, or a correction if it is unintended, would prevent confusion.

Numbered suggestions to the authors:

  1. required In Section 9.1, report the adjusted p-value for the iris normality test or qualify the conclusion, since qAD (0.1043) disagrees with qK and qCvM and the current wording selects the two small values against Section 4’s own caution.
  2. required In Section 9.2, state that matches with five or more goals are excluded by the factor levels 0:4 and that the 2025-26 season is incomplete, and present the result as an illustration of the discrete two-sample workflow rather than a claim about tactics or league dynamics. Replace the threshold language with the size of the departure.
  3. recommended Include a frozen CSV snapshot of the Football-Data.co.uk files in the artifacts so the Section 9.2 outputs reproduce.
  4. recommended Add a mapping from the output abbreviations to the method names in Tables 1 and 2.
  5. recommended Explain, or correct, why the returned method set differs between the test and power routines (for example the discrete gof_test returned K, AD, KL, P while the discrete gof_power returned KS, K, CvM, AD, TV, KL, H, P).
  6. optional Add a compact summary across the benchmark collection showing which statistic families lead under which alternative geometries, to guide a default choice.
Report Reviewer 2recommends minorThis is a software paper, and judged as one the statistical design is sound. The framework separates the observed data, the test statistic, and the null-generation mechanism, and that separation is what lets built-in and user-defined statistics share one calibration, power, and benchmarking path. Simulation-based calibration is the right choice for a collection this heterogeneous, because most of these multivariate statistics have no usable finite-sample null distribution, and a resampling calibration is valid for each of them by construction. The parametric-bootstrap handling of estimated parameters, with re-estimation inside the null loop, is the correct treatment and is often skipped in practice. The hybrid route, comparing the observed sample to a Monte Carlo reference through a two-sample statistic, is a reasonable device for models that are easy to simulate but hard to evaluate. On the whole the methods are described accurately and matched to the problem.

Written by the journal's review engine in the role of the methodology reviewer, and read by the editor before the decision. How the engine works is on the peer review page.

This is a software paper, and judged as one the statistical design is sound. The framework separates the observed data, the test statistic, and the null-generation mechanism, and that separation is what lets built-in and user-defined statistics share one calibration, power, and benchmarking path. Simulation-based calibration is the right choice for a collection this heterogeneous, because most of these multivariate statistics have no usable finite-sample null distribution, and a resampling calibration is valid for each of them by construction. The parametric-bootstrap handling of estimated parameters, with re-estimation inside the null loop, is the correct treatment and is often skipped in practice. The hybrid route, comparing the observed sample to a Monte Carlo reference through a two-sample statistic, is a reasonable device for models that are easy to simulate but hard to evaluate. On the whole the methods are described accurately and matched to the problem.

My main methodological reservation concerns what a benchmarking and calibration framework ought to demonstrate about itself. The central promise is that these tests can be compared on equal terms, and equal terms start with every test holding its stated level. The paper shows null rows inside the power tables rather than a dedicated size study, and some of those entries sit well away from 0.05. The discrete chi-square P statistic reaches 0.124 under the null at B=250, which is far enough from nominal to be worth isolating rather than leaving inside a power example. A short size study run through the existing run.studies machinery would settle this and would strengthen the paper’s own argument about comparability. I have marked it recommended because the simulation calibration is valid by design, so this is confirmation rather than a repair of a claim.

The Section 9.2 application needs attention on two points that bear on a reader’s conclusions. The adjusted p value is reported over four tests while the test table lists eight, and the text does not say which tests feed the minimum or why. Because the adjusted value depends entirely on that set, and because the sentence draws a substantive conclusion about Premier League scoring from it, the reader needs the set stated. The surrounding wording (“significant shift”, “confirms that this result is not an artifact”) reports threshold crossings where it could report the estimates and their resampling spread. The same section relies on live data files that change over time, so the printed numbers are not reproducible as they stand. Pinning the downloaded files would fix that at low cost.

Smaller clarity items would help a reader move between the tables and the printed output. The output uses abbreviations that are not all mapped to the method names, and the set of methods differs between gof_test and gof_power without explanation. Stating the B used in the examples and the attendant Monte Carlo error would keep readers from over-reading the tabulated power. None of these change the framework, and the framework is the contribution.

Numbered suggestions to the authors:

  1. required In Section 9.2, state which tests enter the adjusted p value (the output shows four, the test table shows eight) and why that subset, and replace “significant shift” and “confirms” with the estimates and their resampling uncertainty.
  2. recommended Add a size study across representative scenarios showing the built-in tests attain the nominal level, and comment on the discrete chi-square P entry of 0.124.
  3. recommended Add a mapping from the output abbreviations to the method names in Tables 1 and 2, and explain why gof_test and gof_power report different method sets.
  4. recommended Archive the exact Football-Data.co.uk files used in Section 9.2 so the reported counts and p values can be reproduced.
  5. recommended State that the illustrative runs use B=250 and give the resulting Monte Carlo error.
  6. optional Note the nested-simulation cost of the adjusted p value, where each resample reruns every test’s own calibration.
Report Reviewer 3recommends minorThis is a software paper describing two companion R packages that place a large collection of multivariate goodness-of-fit and two-sample procedures behind a common interface, and route both built-in and user-supplied statistics through the same resampling, power, and benchmarking code. From a software-and-reproducibility standpoint the design is coherent. Separating the data, the statistic, and the null-generation mechanism is the right decomposition, it is what lets one Monte Carlo engine serve every statistic, and the parallel naming across the two packages is a sensible interface choice. The package versions are pinned (MDgof 1.2.0, MD2sample 1.4.0), CRAN distribution is stated, and most examples pass a seed. The contribution as infrastructure is real and the paper presents it clearly.

Written by the journal's review engine in the role of the software reviewer, and read by the editor before the decision. How the engine works is on the peer review page.

This is a software paper describing two companion R packages that place a large collection of multivariate goodness-of-fit and two-sample procedures behind a common interface, and route both built-in and user-supplied statistics through the same resampling, power, and benchmarking code. From a software-and-reproducibility standpoint the design is coherent. Separating the data, the statistic, and the null-generation mechanism is the right decomposition, it is what lets one Monte Carlo engine serve every statistic, and the parallel naming across the two packages is a sensible interface choice. The package versions are pinned (MDgof 1.2.0, MD2sample 1.4.0), CRAN distribution is stated, and most examples pass a seed. The contribution as infrastructure is real and the paper presents it clearly.

My concerns are about whether the results shown can be reproduced and whether the printed output can be interpreted. The clearest reproducibility gap is the English Premier League example in Section 9.2. It reads two CSV files over the network, sets no seed, and the 2025-26 file is an in-progress season. The printed statistics, the per-test p-values, and the combined adjusted value of 0.018 are therefore tied to whatever the website served on the day the author ran it, and a reader following the same code today will get different numbers. For a software paper the fix is cheap: include a frozen copy of the two score tables used, or state the retrieval date and note that the numbers depend on the data vintage, and set a seed on the calls. The iris example in Section 9.1 likewise omits a seed, so its p-values are reproducible only up to Monte Carlo error.

The second issue is interpretive consistency across the interface. The paper’s central claim is a unified workflow, so it matters that the method set in the printed output changes from one function to the next. Discrete gof_test returns K, AD, KL, P, while discrete gof_power returns KS, K, CvM, AD, TV, KL, H, P. The continuous gof_test returns six or seven statistics, while gof_power returns nine. The adjusted-p-value routine reports four of the eight methods that twosample_test ran on the same data, and the text does not say which four enter the minimum or why. One or two sentences explaining the rule (for example, that power and adjustment restrict to methods with a particular calibration path) would resolve this and support the unification claim rather than appearing to undercut it.

Interpretation is also limited by the absence of a key for the output labels. The columns qKS, qK, qCvM, qAD, BB, BR, RK, FF, ES, EP, AZ, BF, BG, the NN variants, and CF1 through CF4 are never mapped to the method names in Tables 1 and 2, so a reader cannot tell which row of the table produced which number. A single mapping table or glossary would make every example readable. Two smaller points round this out. The packages advertise parallel processing, yet every seeded example runs at maxProcessor = 1, so the manuscript does not establish whether a seeded run reproduces under parallel execution, which is the determinism question a user will hit first. And Section 8 reports the CPU and OS but not the R version or the versions of the dependencies (mvtnorm, copula, Ball, energy, GGally, Rcpp), which is what pins a reproduction.

Numbered suggestions to the authors:

  1. required Make the Section 9.2 example reproducible: include a frozen snapshot of the two score tables (or give the retrieval date and state that the numbers depend on data vintage), and set a seed on twosample_test and twosample_test_adjusted_pvalue so the printed p-values and the 0.018 adjusted value can be regenerated.
  2. required Reconcile or document the differing method sets across functions, including the discrete gof_test versus gof_power case (K, AD, KL, P versus KS, K, CvM, AD, TV, KL, H, P) and the four-of-eight methods reported by twosample_test_adjusted_pvalue, stating which methods enter each routine and why.
  3. recommended Add a glossary or table mapping every output abbreviation (qKS, qK, qCvM, qAD, BB, BR, RK, FF, ES, EP, AZ, BF, BG, NN0/NN1/NN5, CF1–CF4, Ball) to the method names in Tables 1 and 2.
  4. recommended State whether a seed reproduces results under parallel execution (maxProcessor greater than 1), given that all seeded examples use a single core.
  5. recommended Add the R version and the versions of mvtnorm, copula, Ball, energy, GGally, and Rcpp to the environment reported in Section 8.
  6. recommended In Section 9.2, note that factor levels 0:4 drop matches with five or more goals, or widen max_goals so no matches are excluded.

Revision

Authors' response2026-10-15This revision was answered under the earlier rule: a short note on what changed, and a reason for each required point the authors declined.

This revision was answered under the earlier rule: a short note on what changed, and a reason for each required point the authors declined.

The authors' response letter (PDF)

Round 2Accepted3 reviewers
AcceptedSigned by Matthias Templ2026-10-23 · orcid 0000-0002-8638-5276

The editor's note

Final acceptance of version 2, after the editor read the preview of the article. The journal’s re-run reproduces the reported values, with one exception: the power estimates of tests that compute their own p-values vary between runs despite the seed, through a fault in the package’s random-number handling (calcTS). One AZ entry, printed as 0.236, reproduces as 0.220 and was corrected in the proof.

Decision letter2026-10-23Congratulations, your manuscript has been accepted for publication in JRepStat. Production begins now.

Congratulations, your manuscript has been accepted for publication in JRepStat. Production begins now.

Where it is not on file yet, we still need the following for production.

  • a valid ORCID iD for every author
  • a captured computational environment (dependencies/lockfile/container)
  • a signed determinism attestation for the entry-point command

Reviews

Report Reviewer 1recommends acceptOf the two required points and the lighter ones I raised, the required points are both met and all but one of the rest are settled.

Written by the journal's review engine in the role of the application reviewer, and read by the editor before the decision. How the engine works is on the peer review page.

Of the two required points and the lighter ones I raised, the required points are both met and all but one of the rest are settled.

The iris example in Section 9.1 now reports the adjusted p-value alongside the individual statistics, frames the disagreement among qAD (0.1077), BR (0.7747) and the two small values as the motivation for the adjustment, and states that a substantive assessment of normality is not the point. The note that the adjusted value of zero is a Monte Carlo estimate at the resolution used is a good addition. This removes the contradiction with Section 4 that I had flagged.

The EPL example in Section 9.2 now states that matches with five or more goals are excluded, that the 2025-26 season was incomplete when collected, and that no substantive conclusion about scoring patterns is drawn. The frozen 25-row frequency table is in the manuscript and the replication material, so the output reproduces without the live source. The threshold language is gone from that section. Both required points are therefore resolved. The abbreviation legend in Section 7 and the computational environment detail in Section 8 also address the recommended items I raised.

One recommended point is still open. The goodness-of-fit test and power routines return different method sets for the same problem and data: in Section 5.1 the discrete gof_test prints K, AD, KL, P, while in Section 6.1 the discrete gof_power for the matching setup prints KS, K, CvM, AD, TV, KL, H, P, and the continuous examples show the same pattern. The response addressed why defaults differ between the test and the adjusted-p-value routines, and Section 4 now documents that, but it does not explain the test-versus-power gap, which a reader comparing the two examples will still notice. This does not hold the paper. One sentence saying the power routine reports the full available set by default, or aligning the two defaults, would close it. I accept the reasoned decline on a cross-benchmark summary of which families lead under which alternatives: the dependence on null, alternative, dimension and sample size makes a compact default recommendation hard to state without misleading, and the discussion already says so.

Numbered suggestions to the authors:

  1. recommended Add one sentence explaining why the goodness-of-fit test and power routines return different method sets for the same problem and data (for example that the power routine reports the full available set by default), or align the default selections so the two examples match.
Report Reviewer 2recommends acceptOf the one required point and five lighter ones I raised, all are met except the size study, which the author declined for a stated reason. The required Section 9.2 point is resolved: the adjusted p value now runs over the stated eight discrete methods via doMethods=“all”, Section 4 lists the default four-method subset and explains why it differs, and the threshold-crossing wording is replaced by the reported p values with the example reframed as a software demonstration.

Written by the journal's review engine in the role of the methodology reviewer, and read by the editor before the decision. How the engine works is on the peer review page.

Of the one required point and five lighter ones I raised, all are met except the size study, which the author declined for a stated reason. The required Section 9.2 point is resolved: the adjusted p value now runs over the stated eight discrete methods via doMethods=“all”, Section 4 lists the default four-method subset and explains why it differs, and the threshold-crossing wording is replaced by the reported p values with the example reframed as a software demonstration.

On the declined size study, the reasoning convinces me. The simulation calibration is valid by construction for every procedure that draws its p value from the null simulation, so the null rows are confirmation rather than a claim the paper rests on. The one genuine outlier, the discrete chi-square P entry at 0.124, is the procedure that relies on a classical distributional approximation, and the revision now names it as the exception and adds the Monte Carlo-variation caveat. Since the item was recommended, not required, the decline does not hold the paper.

The remaining recommended items are in place: the output-abbreviation legend, the pinned goal frequency table in the replication material and in the manuscript body, the B=250 statement with its Monte Carlo standard error in Section 6, and the serial two-stage clarification of the adjusted p value that corrects my own nested-simulation framing. Nothing is open.

Numbered suggestions to the authors:

Report Reviewer 3recommends acceptOf the two required points and four lighter ones I raised, all are met. The Premier League example now embeds the 25-row frequency table directly in the text (goals_premier_league), sets seed = 1234, states the retrieval context, and notes the exclusion of matches with five or more goals, so the printed statistics and the 0.013 adjusted value regenerate from the manuscript alone. The iris example now carries a seed as well. Section 4 explains that method availability depends on data type and dimension, documents the default doMethods subsets against doMethods = "all", and the iris example uses "all" so the test and adjusted routines evaluate the same five methods. The abbreviation legend at the end of Section 7, the parallel-reproducibility note on the seed argument, and the expanded Section 8 environment (R 4.6.0, the Windows build, and the six dependency versions) all land cleanly.

Written by the journal's review engine in the role of the software reviewer, and read by the editor before the decision. How the engine works is on the peer review page.

Of the two required points and four lighter ones I raised, all are met. The Premier League example now embeds the 25-row frequency table directly in the text (goals_premier_league), sets seed = 1234, states the retrieval context, and notes the exclusion of matches with five or more goals, so the printed statistics and the 0.013 adjusted value regenerate from the manuscript alone. The iris example now carries a seed as well. Section 4 explains that method availability depends on data type and dimension, documents the default doMethods subsets against doMethods = "all", and the iris example uses "all" so the test and adjusted routines evaluate the same five methods. The abbreviation legend at the end of Section 7, the parallel-reproducibility note on the seed argument, and the expanded Section 8 environment (R 4.6.0, the Windows build, and the six dependency versions) all land cleanly.

One residual observation, carrying no ask: the discrete gof_power output still lists eight statistics (KS, K, CvM, AD, TV, KL, H, P) while the discrete gof_test example above it reports four (K, AD, KL, P). The new Section 4 text covers the test-versus-adjusted default difference and the dimension dependence, which is what I asked for, so this does not hold the paper. If a future revision touches Section 5.1, a half-sentence on why the power default is wider than the test default for discrete data would close the last interpretive gap. The version as it stands is reproducible from its own text and the replication material, and the interface is readable.

Numbered suggestions to the authors: (none)

Peer review record (PDF) · How open review works

Abstract

Multivariate goodness-of-fit and nonparametric two-sample testing comprise a large collection of procedures whose relative performance can depend strongly on the alternative distribution, dimension, sample size, and treatment of nuisance parameters. Systematic comparison is difficult when methods are implemented through different interfaces and require separate simulation code. We present a unified computational framework for implementing, comparing, benchmarking, and extending multivariate goodness-of-fit and two-sample procedures, implemented in the MDgof and MD2sample packages for R. The framework supports continuous and discrete data, simple and composite null hypotheses, simulation-based inference, adjusted p values for simultaneous testing, power estimation, user-defined test statistics, and large collections of benchmark alternatives. A parallel application programming interface is used across the two packages so that workflows learned for one testing problem transfer directly to the other. Computationally intensive components use Rcpp and parallel processing where appropriate. The resulting framework provides reusable statistical-computing infrastructure for applied testing and for reproducible Monte Carlo comparison of existing and newly proposed multivariate procedures.

1 Introduction

Multivariate goodness-of-fit and nonparametric two-sample testing are fundamental statistical problems. In the goodness-of-fit problem, observations are compared with a specified probability model; in the two-sample problem, two independent samples are compared without specifying their common distribution under the null hypothesis. Both problems arise in many scientific settings and have generated a large literature of procedures based on empirical distribution functions, nearest neighbors, graphs, kernels, energy-type distances, characteristic functions, and multivariate depth; see, for example, Thas (2010) and Rayner et al. (2009).

The practical difficulty is not a shortage of tests but the absence of a uniformly best procedure. Power can change substantially with the form of the alternative, dimension, sample size, and whether nuisance parameters are estimated. Consequently, an applied analyst may wish to compare several procedures, while a methodological researcher proposing a new statistic must evaluate it against established competitors over many alternatives. When implementations are distributed among packages with different interfaces, data conventions, output formats, and resampling schemes, such comparisons require substantial additional programming and can be difficult to reproduce.

The MDgof and MD2sample packages (Rolke, 2026b, 2026a) address this computational problem through a common framework for multivariate goodness-of-fit and two-sample testing. The principal contribution is a common computational framework in which existing and newly proposed statistics can be implemented, calibrated, compared, and benchmarked under common conditions. The framework supports continuous and discrete data, simple and composite null hypotheses, hybrid goodness-of-fit problems based on simulated reference samples, simultaneous inference, Monte Carlo power studies, and systematic benchmarking. The computational contribution is the integration of these tasks in a single extensible workflow, so that calibration and comparative simulation need not be reimplemented separately for each statistic.

Four design features are central. First, built-in procedures are exposed through common testing and power interfaces rather than unrelated method-specific functions. Second, user-defined statistics can be inserted into the same resampling and benchmarking machinery used by the built-in procedures. Third, the packages provide collections of simulation scenarios so that competing procedures can be evaluated under identical alternatives and sample sizes. Fourth, MDgof and MD2sample deliberately use parallel names for analogous user-facing operations. This is an interface-design choice: a user who has learned the workflow for one testing problem can transfer that knowledge to the other without learning a second vocabulary. Simultaneous attachment of both packages is not required; namespace-qualified calls such as MDgof::gof_test() and MD2sample::twosample_test() remain unambiguous.

Computationally intensive components use Rcpp (Eddelbuettel et al., 2024) and parallel processing where appropriate. The packages also give explicit attention to multivariate discrete data, for which general-purpose software is less extensive than for continuous data. This makes it possible to use the same overall computational strategy for continuous observations, discrete count representations, and simulation-based hybrid problems.

The present work builds on the computational approach developed for the univariate goodness-of-fit and two-sample problems in the R packages Rgof and R2sample (Rolke, 2025). MDgof and MD2sample extend this approach to multivariate problems, for which the available test statistics, data representations, and computational requirements are substantially different. The four packages nevertheless share a similar workflow for testing, power estimation, user-defined procedures, and comparative simulation.

The remainder of the paper is organized around the computational framework. Section 2 defines the statistical settings handled by the software. Section 3 summarizes the implemented procedures and their relation to existing software. Sections 4–7 describe simultaneous inference, the common testing interface, extensibility, power estimation, and benchmarking. Section 8 discusses computational performance, Section 9 gives representative applications, and the final section summarizes the role of the framework in methodological development and comparative simulation.

2 Statistical problems and computational setting

For multivariate goodness-of-fit, let 𝑿1,…,𝑿n\mathbf{X}_1,\ldots,\mathbf{X}_n be observations from an unknown distribution FF. The null hypothesis is H0:F=F0,H_0:F=F_0, where F0F_0 may be completely specified or may contain parameters estimated from the data. For multivariate two-sample testing, let 𝑿1,…,𝑿n∼F\mathbf{X}_1,\ldots,\mathbf{X}_n\sim F and 𝒀1,…,𝒀m∼G\mathbf{Y}_1,\ldots,\mathbf{Y}_m\sim G be independent samples. The null hypothesis is H0:F=G.H_0:F=G. The packages support both continuous multivariate observations and discrete data represented through support points and their observed counts.

A useful intermediate case is a goodness-of-fit problem in which evaluation of F0F_0 is difficult or unavailable but simulation from the model is feasible. A Monte Carlo sample from the fitted or specified null model can then be compared with the observed sample using a two-sample statistic. We refer to this as the hybrid problem. It is particularly useful for models in which generation is straightforward but high-dimensional distribution-function or likelihood calculations are computationally prohibitive.

When parameters of F0F_0 are estimated from the observed data, calibration must reproduce the estimation step. A permutation calibration that treats the fitted distribution as fixed can be inappropriate. The framework therefore allows the user to provide an estimator and a null-data generator, yielding a parametric-bootstrap calibration in which parameters are re-estimated as required. This same separation between the test statistic and the data-generating mechanism is important for the power and benchmarking routines described below.

From a computational perspective, the three settings share the same basic pipeline: data→test statistic(s)→null calibration→p-value(s)→power and benchmarking.\text{data}\longrightarrow \text{test statistic(s)}\longrightarrow \text{null calibration}\longrightarrow p\text{-value(s)}\longrightarrow \text{power and benchmarking}. The software is organized around this pipeline. Differences among goodness-of-fit, two-sample, continuous, discrete, and hybrid problems are handled primarily through the data representation and null-generation mechanism rather than through unrelated user workflows.

3 Implemented procedures and relation to existing software

The framework contains a broad collection of classical and recent multivariate procedures. Tables 1 and 2 summarize the methods currently available for goodness-of-fit and two-sample analysis, respectively. The purpose of including many methods is not to claim that one family dominates the others, but to make procedures based on different principles available under a common computational interface.

Table 1: Goodness-of-fit methods implemented in MDgof.
MDgof
Method Continuous Discrete
Chi-square tests Yes (2D only) Yes (2D only)
Kolmogorov-Smirnov Yes Yes
Kuiper Yes Yes
Cramer-von Mises Yes Yes
Anderson-Darling Yes Yes
Bickel-Breiman Yes No
Bakshaev-Rudzkis Yes No
Fasano-Franceschini Yes No
Ripley Yes No
Total variation No Yes
Kullback–Leibler No Yes
Hellinger No Yes
Table 2: Two-sample methods implemented in MD2sample.
MD2sample
Method Continuous Discrete
Chi-square tests Yes (2D only) Yes (2D only)
Kolmogorov-Smirnov Yes Yes
Kuiper Yes Yes
Cramer-von Mises Yes Yes
Anderson-Darling Yes Yes
nearest-neighbor Yes Yes
Aslan-Zech Yes Yes
Biswas-Ghosh Yes No
Baringhaus-Franz Yes Yes
Maximum mean discrepancy Yes No
Friedman–Rafsky Yes No
Simple 1-Nearest Neighbor Yes No
Chen-Friedman Yes No
Ball divergence Yes No

The implemented statistics span several methodological families. Empirical-distribution-function procedures include multivariate versions or approximations to Kolmogorov–Smirnov, Kuiper, Cramér–von Mises, and Anderson–Darling statistics (Anderson, 1962; Anderson & Darling, 1952; Cramér, 1928; Kolmogorov, 1933; Kuiper, 1960; Mises, 1928; Pettitt, 1976; Smirnov, 1939). For continuous multivariate goodness-of-fit, the empirical-distribution-function statistics are evaluated at the observed data points; this avoids the ndn^d evaluations required by a full search over coordinate combinations, but it should be regarded as a computational approximation rather than the exact multivariate extension.

Other goodness-of-fit procedures use nearest-neighbor distances, density estimation, Rosenblatt-type transformations, spatial statistics, or discrepancies between observed and expected discrete counts (Bakshaev & Rudzkis, 2015; Bickel & Breiman, 1983; Fasano & Franceschini, 1987; Ripley, 1976). The two-sample collection includes nearest-neighbor, distance-based, kernel, graph, and Ball-divergence procedures, including methods of Aslan & Zech (2005), Baringhaus & Franz (2004), Biswas & Ghosh (2014), Gretton et al. (2012), Friedman & Rafsky (1979), Chen & Friedman (2017), and Pan et al. (2018). These established statistics serve both as practical testing procedures and as reference competitors for newly proposed methods.

4 Simultaneous inference and adjusted pp values

Because no single multivariate test has uniformly high power, an analyst may apply several procedures and use the smallest individual pp value as evidence against the common null hypothesis. The unadjusted minimum is not itself a valid level-α\alpha test because the individual statistics are dependent and the probability of at least one small pp value exceeds α\alpha.

The packages therefore calibrate the minimum pp value by resampling under the joint null hypothesis. If PminP_{\min} denotes the minimum of the individual pp values and H0H_0 is true, let FminF_{\min} be its null distribution function. The combined adjusted value is padj=Fmin(pmin⁡,obs).p_{\mathrm{adj}} = F_{\min}(p_{\min,\mathrm{obs}}). Because FminF_{\min} is generally unavailable analytically, it is estimated from resampled data generated under the same null model. The computational algorithm is:

  • Generate a new data set under the null hypothesis, run the desired tests and record the smallest pp value.

  • Repeat this step for the number of replications specified by the second component of B.

  • Use the empirical distribution function F̂min\hat{F}_{\min} of the simulated minimum pp values to estimate FminF_{\min}.

  • Apply F̂min\hat{F}_{\min} to the observed minimum pp value pmin⁡,obsp_{\min,\mathrm{obs}} to obtain the adjusted pp value.

For an example, see (Rolke, 2025). For a general discussion of pp value adjustments based on resampling see (Westfall & Young, 1993).

MDgof::gof_test_adjusted_pvalue() and MD2sample::twosample_test_adjusted_pvalue() compute these adjusted pp values. Both functions have the default argument B = c(5000, 1000). The first component specifies the number of simulation replications used to estimate the individual pp values, and the second specifies the number used to estimate the null distribution function of their minimum. The two stages are performed serially rather than by nesting the full individual calibration inside every second-stage replication; the computational cost is therefore associated with the two successive stages rather than their product. Both provide a doMethods argument for specifying the tests to combine; doMethods="all" includes every method available for the given data type and dimension. The default selections are smaller subsets. For example, for discrete data MD2sample::twosample_test_adjusted_pvalue() combines Chi-square, Kolmogorov–Smirnov, Aslan–Zech, and Cramér–von Mises by default, whereas MD2sample::twosample_test() reports eight tests. For four-dimensional continuous data, the default MDgof::gof_test() selection reports qK, qCvM, and qAD. Specifying doMethods="all" in both testing routines also includes qKS and BR, allowing the same five methods to be compared. Method availability depends on data type and dimension; in particular, the chi-square implementations are limited to two dimensions. See Section 5 for the testing interfaces. For an example using this adjustment in simultaneous confidence bands, see (Aldor-Noiman et al., 2013).

5 Computational framework, testing, and extensibility

The public interfaces separate three components that are often intertwined in method-specific implementations: the observed data, the statistic to be evaluated, and the mechanism used to generate or resample data under the null hypothesis. This separation is what allows the same computational machinery to serve built-in and user-defined statistics.

The two packages intentionally expose analogous workflows with parallel naming conventions. This reduces the learning cost for users moving between goodness-of-fit and two-sample problems. The packages need not be attached simultaneously; examples in this paper use namespace-qualified calls when the package identity is useful.

A user-defined statistic is supplied through TS; auxiliary objects or functions required by that statistic can be passed through TSextra. Once supplied, the statistic can participate in testing, power estimation, and benchmarking without requiring the user to implement separate Monte Carlo loops. This is the principal extensibility mechanism of the framework.

5.1 Goodness-of-fit

The routine to carry out a goodness-of-fit hypothesis test is MDgof::gof_test(). Its arguments are

  • x: a data set, either the continuous outcomes or the counts in the discrete case. In the continuous data case it is a matrix with each row representing an observation. In the discrete case it is a matrix with three columns, the values of x, the values of y and the corresponding counts

  • pnull: a function to calculate values for the cdf.

  • rnull: a function to generate new data under the null hypothesis.

  • dnull: a function evaluating the density under the null hypothesis, when required by a test such as BB.

  • phat=function(x) -99: a function to estimate parameters, or -99 if null hypothesis is simple and no parameters are estimated.

  • TS: routine to calculate test statistics other than those included.

  • TSextra: a list passed to TS.

  • rate=0, the rate of the Poisson random variable if the sample size was random.

  • nbins=c(5, 5): number of bins in x and y directions to use in chi-square tests.

  • Ranges=matrix(c(-Inf, Inf, -Inf, Inf), 2, 2): range of continuous random variables.

  • minexpcount=5: required minimal expected counts for chi-square tests.

  • maxProcessor: number of cores to use for parallel processing, 1 means no parallel programming.

  • doMethods: vector with names of methods to include.

  • B=5000: number of simulation runs.

  • seed=NULL: if provided, initializes the simulations for reproducibility. For a fixed parallel configuration, the seed is also passed to the worker processes. Changing maxProcessor can change the simulated results.

  • ReturnTSextra = FALSE, returns a list for TSextra, needed for some checks.

TSextra is a list object that allows the creator of a new method to provide their test statistic with needed additional information. As an example, say a new method requires a routine that finds the median of each variable. This can be done with

TSextra = list(med = function(x) apply(x, 2, median))

The formats of the routines pnull and rnull have to be as follows. If no parameters are estimated pnull is a function of one variable (the data) and rnull has no arguments. We will use as an example the standard normal distribution for continuous data.

pnullC = function (x) {
    f = function(x) mvtnorm::pmvnorm(rep(-Inf, length(x)), x)
    if (!is.matrix(x))
        return(f(x))
    apply(x, 1, f)
}
rnullC = function() mvtnorm::rmvnorm(100, c(0, 0))

Notice that the function pnull should accept both a vector and a matrix.

Here is an example run:

set.seed(1234)
x = rnullC()
gof_test(x, pnullC, rnullC, B = 250, maxProcessor = 1, seed = 1234)
$statistics
     qK    qCvM     qAD      FF      RK      EP
 0.1740  0.2940  2.1220  0.4610  0.0495 18.1000

$p.values
    qK   qCvM    qAD     FF     RK     EP
0.1920 0.0440 0.2320 0.7240 0.8480 0.1538

If the density is known it can be added and several more tests run:

dnullC = function (x) {
    f = function(x) mvtnorm::dmvnorm(x)
    if (!is.matrix(x))
        return(f(x))
    apply(x, 1, f)
}
gof_test(x, pnullC, rnullC, dnull = dnullC, B = 250, maxProcessor = 1, seed = 1234)
$statistics
      qK     qCvM      qAD       FF       RK       EP       BB
 0.17400  0.29400  2.12200  0.46100  0.04950 18.10000  0.00647

$p.values
    qK   qCvM    qAD     FF     RK     EP     BB
0.1920 0.0440 0.2320 0.7240 0.8480 0.1538 0.8360

As discussed before, one can also run tests by generating a second data set under the null hypothesis, and then running a two-sample test. This is easy to do with MDgof::hybrid_test(). In the following run we will generate MC data sets twice the size of the actual data:

MDgof::hybrid_test(x, rnullC, nMC = 2, B = 250, maxProcessor = 1, seed = 1234)
Data is assumed to be continuous
MD2sample two-sample tests
Data: continuous  | n.x = 100  | n.y = 200  | dimension = 2  | B = 250

 method statistic p.value
     KS   0.13000   0.372
      K   0.22500   0.132
    CvM   0.17710   0.232
     AD   1.11680   0.292
    NN1   1.18000   0.012
    NN5   2.30000   0.000
     AZ   0.06003   0.368
     BF   0.25570   0.452
     BG   0.07833   0.728
    MMD   0.01627   0.340

For the case of discrete data the formats of pnull and rnull are the same.

In the case where parameters are estimated pnull has to be a function of two arguments, the data and the estimated parameters, and rnull a function of the estimated parameters. As an example we consider the case of a discrete random vector of Binomial random variables. Under the null hypothesis we have X∼Bin(10,p),Y|X=x∼Bin(5,0.5+x/100)X\sim Bin(10, p), Y|X=x\sim Bin(5, 0.5+x/100). Instead we will generate data from X∼Bin(10,p),Y|X=x∼Bin(5,K+x/100)X\sim Bin(10, p), Y|X=x\sim Bin(5, K+x/100), so for K≠0.5K\ne 0.5 the null hypothesis is false

pnullD = function(x, p) {
  f = function(x) sum(dbinom(0:x[1], 10, p[1])*pbinom(x[2], 5, 0.5+0:x[1]/100))
  if(!is.matrix(x)) x = rbind(x)
  apply(x, 1, f)
}
rnullD = function(p = 0.5) {
  x = stats::rbinom(1000, 10, p)
  y = stats::rbinom(1000, 5, 0.5+x/100)
  MDgof::sq2rec(table(x, y))
}
raltD = function(K = 0.5) {
  x = stats::rbinom(1000, 10, 0.5)
  y = stats::rbinom(1000, 5, K+x/100)
  MDgof::sq2rec(table(x, y))
}

phatD = function(x) {
  tx = tapply(x[, 3], x[, 1], sum)
  mean(rep(as.numeric(names(tx)), times = tx))/10
}

These routines make use of MDgof::sq2rec(), which transforms discrete data as generated by the R function table() into the format required by MDgof::gof_test(). A simple example is as follows:

x = stats::rbinom(1000, 2, 0.5)
y = stats::rbinom(1000, 2, 0.7+x/100)
head(cbind(x, y))
     x y
[1,] 1 2
[2,] 0 1
[3,] 2 2
[4,] 0 1
[5,] 0 1
[6,] 1 1
MDgof::sq2rec(table(x, y))
      [,1] [,2] [,3]
 [1,]    0    0   30
 [2,]    1    0   38
 [3,]    2    0   13
 [4,]    0    1  107
 [5,]    1    1  216
 [6,]    2    1   96
 [7,]    0    2  121
 [8,]    1    2  244
 [9,]    2    2  135
set.seed(1234)
x = rnullD(0.57)
MDgof::gof_test(x, pnullD, rnullD, phat = phatD, B = 250, maxProcessor = 1, seed = 1234)
$statistics
       K       AD       KL        P
0.048100 0.001270 0.042600 89.900000

$p.values
    K    AD    KL     P
0.104 0.116 0.016 0.072

5.2 Two-sample

The routine to carry out a two-sample hypothesis test is MD2sample::twosample_test(). Its arguments are

  • x: a data set, either the continuous outcomes or the counts in the discrete case. It could also be a matrix with columns vals_x, vals_y, x and y, indicating discrete data.

  • y: a data set, either the continuous outcomes or the counts in the discrete case.

  • vals_x: values of a discrete rv.

  • vals_y: values of a discrete rv.

  • TS: routine to calculate test statistics other than those included.

  • TSextra: a list passed to TS.

  • B=5000: number of simulation runs.

  • seed=NULL: can be used for reproducibility.

  • nbins=c(5, 5): number of bins in x and y directions to use in chi-square tests.

  • Ranges=matrix(c(-Inf, Inf, -Inf, Inf), 2, 2): range of continuous random variables.

  • minexpcount=5: required minimal expected counts for chi-square tests.

  • maxProcessor: number of cores to use for parallel processing, 1 means no parallel programming.

  • DoTransform=TRUE: should continuous data be transformed to unit interval?

  • samplingmethod="Binomial": sampling method for discrete data

  • doMethods="all": include all methods available for the data type and dimension.

  • SuppressMessages=FALSE, should informative messages be printed?

  • rnull: routine to generate new data, for parametric bootstrap.

  • LargeSampleOnly=FALSE: run only methods with large sample theory? Used for very large data sets.

As an example for continuous data, we will generate data from a uniform distribution in 5 dimensions

set.seed(1234)
x = matrix(runif(500), ncol = 5)
y = matrix(runif(500), ncol = 5)
MD2sample::twosample_test(x, y, B = 250, maxProcessor = 1, seed = 1234)
$statistics
       KS         K       CvM        AD       NN1       NN5
 0.100000  0.190000  0.034400  0.981400  0.890000  5.110000
       AZ        BF        BG       MMD        FR       NN0
 0.097470  0.622400  2.924600  0.024200  0.568300  0.220000
      CF1       CF2       CF3       CF4      Ball
 0.568300  1.484000 -0.568000  1.077500  0.012600

$p.values
    KS      K    CvM     AD    NN1    NN5     AZ     BF     BG
0.6040 0.3120 0.3280 0.5200 0.8960 0.2680 0.0400 0.0320 0.0320
   MMD     FR    NN0    CF1    CF2    CF3    CF4   Ball
0.0600 0.7151 0.8644 0.7151 0.4762 0.7151 0.4051 0.0236

For a discrete two-sample example, we generate two sets of counts over four categories, representing the combinations of two binary variables. The second sample uses different category probabilities.

set.seed(1234)
x = table(sample(1:4, size = 1000, replace = TRUE))
y = table(sample(1:4, size = 1000, replace = TRUE,
                 prob = c(1, 1.5, 1, 1)))
vals_x = rep(1:2, 2)
vals_y = rep(1:2, each = 2)
MD2sample::twosample_test(x, y, vals_x, vals_y,
                          B = 250, seed = 1234)
$statistics
       KS        K      CvM       AD       NN       AZ       BF ChiSquare
  0.08400  0.13200  1.41150  5.95920 10.07200  0.03334  0.08924  19.76900

$p.values
       KS        K      CvM       AD       NN       AZ       BF ChiSquare
   0.0000   0.0000   0.0000   0.0000   0.0000   0.0000   0.0000    0.0002

6 Monte Carlo power estimation and user-defined procedures

The illustrative power calculations in this section use B=250B=250 Monte Carlo replications to keep computation times reasonable. For an estimated rejection probability π̂\hat{\pi}, the Monte Carlo standard error is approximately π̂(1−π̂)/B\sqrt{\hat{\pi}(1-\hat{\pi})/B}. With B=250B=250, this is at most approximately 0.032 and is approximately 0.014 when the rejection probability is 0.05. The tabulated values illustrate the software and should not be interpreted as precise power estimates; larger simulation sizes are appropriate for substantive power comparisons.

6.1 Goodness-of-fit

The routine to carry out power estimation for the goodness-of-fit methods is MDgof::gof_power(). Its arguments are again pnull, rnull, phat, dnull, TS, TSextra, Ranges, nbins, minexpcount, rate, maxProcessor, seed and B=1000, as in MDgof::gof_test(). Also the formats of these functions are the same. In addition we have

  • ralt: a function to generate data under the alternative hypothesis. It should have one argument.

  • param_alt: a vector of values passed to ralt.

  • With.p.value=FALSE: does the user supplied test routine TS find its own pp values, or should simulation be used?

  • alpha=0.05: type I error probability.

As an example we will again use the case described above, testing whether the data comes from a multivariate standard normal distribution. As an alternative we will use a multivariate normal with correlation:

raltC = function(s) {
  mvtnorm::rmvnorm(100, c(0, 0), sigma = matrix(c(1, s, s, 1), 2, 2))
}
MDgof::gof_power(pnullC, rnullC, raltC, c(0, 0.5), B = 250, maxProcessor = 1, seed = 1234)
      qKS    qK  qCvM   qAD    BR    FF    RK    ES    EP
0   0.052 0.080 0.032 0.048 0.084 0.084 0.068 0.072 0.084
0.5 0.404 0.208 0.576 0.624 0.088 0.252 0.148 0.700 0.816

Again we can run the hybrid tests instead:

MDgof::hybrid_power(rnullC, raltC, c(0, 0.5), B = 250, maxProcessor = 1, seed = 1234)
        KS      K    CvM     AD    NN1    NN5     AZ     BF
0   0.088 0.060 0.076 0.076 0.072 0.052 0.048 0.056
0.5 0.116 0.136 0.196 0.276 0.248 0.260 0.220 0.188
        BG    MMD     FR    NN0    CF1    CF2    CF3    CF4
0   0.052 0.060 0.032 0.092 0.032 0.044 0.032 0.036
0.5 0.040 0.284 0.164 0.148 0.164 0.104 0.164 0.116
      Ball    ES    EP
0   0.048 0.056 0.064
0.5 0.080 0.416 0.316

MDgof::gof_test() and MDgof::gof_power() allow the user to also run their own tests. As an example, MDgof includes the routine newTS(), which runs a simple chi-square test with 9 bins and both equal space and equal probability bins. We also need to include the arguments TSextra, which is a list of items passed to the testing routine. In this example it tells newTS() to run the version for continuous data and without estimation, and then return a pp value.

TSextra = list(Continuous = TRUE,
             WithEstimation = FALSE,
             Withpvalue = TRUE)
MDgof::gof_power(pnullC, rnullC, raltC, c(0, 0.5),
                 TS = newTS, TSextra = TSextra, With.p.value = TRUE,
                 B = 250, maxProcessor = 1, seed = 1234)
     ES33  EP33
0   0.056 0.048
0.5 0.596 0.860

Here ES33 and EP33 are labels for the user-defined statistics supplied through TS; they are not built-in methods.

The discrete examples below report different method sets because gof_test() and gof_power() use different default method selections; the doMethods argument permits an explicit selection.

Next a power estimation for the discrete data case:

MDgof::gof_power(pnullD, rnullD, raltD, c(0.5, 0.53), phat = phatD, B = 250, maxProcessor = 1, seed = 1234)
       KS     K   CvM    AD    TV    KL     H     P
0.5  0.024 0.012 0.020 0.024 0.068 0.068 0.060 0.124
0.53 0.928 0.844 0.928 0.936 0.536 0.556 0.468 0.372

6.2 Two-sample

For the two-sample problem we have the routine MD2sample::twosample_power(). Its arguments are the same as MD2sample::twosample_test(), except

  • f: a function that generates either a list with two data sets for continuous data, or a single matrix for discrete data.

  • ... either one or two vectors of values to be passed to f.

  • alpha=0.05: type I error probability.

As an example for continuous data, we consider the following power study: one data set has a uniform distribution on the unit square in 5 dimensions. The other comes from a Frank copula with parameter θ\theta. Note that if θ=0\theta=0, the null hypothesis is true

f = function (theta = 0, d = 2) {
    x = matrix(stats::runif(500), ncol = 5)
    if (theta == 0)
      return(list(x = x, y = matrix(runif(500), ncol = 5)))
    cop = copula::frankCopula(theta, 5)
    list(x = x, y = copula::rCopula(100, cop))
}
MD2sample::twosample_power(f, c(0, 3.5), B = 250, maxProcessor = 1, seed = 1234)
        KS      K    CvM     AD    NN1    NN5     AZ     BF
0   0.052 0.056 0.040 0.036 0.056 0.048 0.052 0.052
3.5 0.872 0.596 0.988 0.988 0.912 0.992 1.000 0.988
        BG    MMD     FR    NN0    CF1    CF2    CF3    CF4
0   0.056 0.044 0.044 0.052 0.044 0.032 0.044 0.052
3.5 0.060 1.000 0.936 0.544 0.936 0.924 0.936 0.916
      Ball
0   0.032
3.5 0.948

As with the goodness-of-fit routines, the user again can run other tests not included in MD2sample.

7 Benchmarking and comparative simulation

A central purpose of the framework is to compare the power of competing methods, including methods supplied by the user, under common Monte Carlo conditions. In both packages this functionality is provided by run.studies(). By fixing the data-generating scenarios, sample sizes, type I error level, and simulation size across procedures, the framework removes a substantial source of incidental variation from comparative studies.

Both packages include a large number of case studies, that is, distributions for the null hypothesis and for an alternative hypothesis. They can be found in the routine case.studies(). The two packages provide 140 distinct case names, corresponding to 250 study configurations when continuous and discrete versions, parameter estimation, and five-dimensional cases are counted separately. Both packages include vignettes with detailed descriptions of the studies. For a listing one can run

MDgof::case.studies(Continuous = TRUE,
                    WithEstimation = TRUE,
                    Dim = 5,
                    ReturnCaseNames = TRUE)[1:10]
 [1] "uniform.uniform-diagonal-n" "uniform.uniform-diagonal-b"
 [3] "normal-ind.normal-corA"     "normal-ind.normal-corB"
 [5] "t-ind.t-cor"                "uniform.Frank"
 [7] "uniform.Clayton"            "uniform.Gumbel"
 [9] "uniform.Joe"                "mix.uniform.Clayton"

The names are chosen to give some idea of the type of study. For example, in the first study the null hypothesis specifies independent uniform data, where the actual data is uniform with some data on the main diagonal in such a way that the marginals are still all uniforms. Many of the studies are based on copulas using the R package copula.

Let’s say we want to compare the power of the included test MDgof::newTS() with those included in the package for the case of continuous data without parameter estimation. We can run

TSextra = list(Continuous = TRUE, WithEstimation = FALSE, Withpvalue = TRUE)
res = MDgof::run.studies(TS = newTS, TSextra = TSextra, With.p.value = TRUE, B = 250, maxProcessor = 1, seed = 1234)
Average rank of each test across studies:
ES33 EP33  qKS   qK qCvM  qAD   BB   BR  KSD   FF   RK   ES   EP
10.0  9.3  6.7  6.3  7.0  6.8  2.0  5.1  5.2  8.2  5.4  9.6  9.3

res contains the detailed power estimates. The summary reports the average rank of each method across the selected scenarios, with larger values indicating better average performance.

Arguments common to MDgof::run.studies() and MD2sample::run.studies() are:

  • study: number(s) or name(s) of studies to run. If missing all are run.

  • Continuous=TRUE: is data continuous?

  • TS: new test statistic (optional).

  • TSextra: list passed to TS (optional).

  • With.p.value = FALSE, does new test routine find pp values?

  • alpha=0.05: type I error probability for tests.

  • param_alt: parameter vectors for alternative hypothesis. This should be a vector or a matrix. If missing, the values from the included studies are used.

  • nsample: sample size(s). This should be one number, or two for MD2sample if different sample sizes are needed.

  • SuppressMessages = FALSE: should informative messages be printed?

  • B = 1000: simulation sample size.

  • maxProcessor: number of processors to use. If missing, the number of processors in the computer minus 1 is used. If =1, no parallel processing is used.

Arguments specific to MDgof::run.studies():

  • WithEstimation = FALSE: should case studies with parameter estimation be run?

  • Hybrid = FALSE: should two-sample methods be used?

  • nMC5 = FALSE: if Hybrid=TRUE, should the MC data set have the same size as the real data, or be 5 times as large?

  • Dim = 2: should 2-D case studies be run, or 5-D?

As an example, the package MDgof includes the routine newTS(). This runs a chi-square test with a 3 by 3 grid of bins, either equal space or equal probability. To see how these tests perform for continuous data without parameter estimation, we can run

TSextra = list(Continuous = TRUE, WithEstimation = FALSE, Withpvalue = TRUE)
res = MDgof::run.studies(TS = MDgof::newTS, TSextra = TSextra, With.p.value = TRUE, B = 250, maxProcessor = 1, seed = 1234)
Average rank of each test across studies:
ES33 EP33  qKS   qK qCvM  qAD   BB   BR  KSD   FF   RK   ES   EP
10.0  9.3  6.7  6.3  7.0  6.8  2.0  5.1  5.2  8.2  5.4  9.6  9.3

The run.studies() routines can be used to

  • rerun the studies with the included tests.

  • rerun the studies with the included tests, but with changed parameters for the alternatives, a different type I error probability and/or different sample sizes.

The method labels in the output abbreviate the procedures in Tables 1 and 2: KS (Kolmogorov–Smirnov), K (Kuiper), CvM (Cramér–von Mises), AD (Anderson–Darling), NN (nearest-neighbor), AZ (Aslan–Zech), BF (Baringhaus–Franz), BG (Biswas–Ghosh), MMD (maximum mean discrepancy), FR (Friedman–Rafsky), CF (Chen–Friedman), and BR (Bakshaev–Rudzkis). The q-prefixed labels identify goodness-of-fit EDF statistics; additional labels are defined by the respective package documentation.

8 Computational performance

All computations in this article were performed on an Intel Core i9-14900F system with 32 GB RAM running Windows 11 Home (build 26200), using R version 4.6.0 (2026-04-24 ucrt). The package versions were mvtnorm 1.4.2, copula 1.1.7, Ball 1.3.13, energy 1.7.12, GGally 2.4.0, and Rcpp 1.1.1-1.1. The versions of the packages presented here were MDgof 1.2.1 and MD2sample 1.4.2. The latter includes the corrected cross-sample distance guard in the Aslan–Zech statistic, which tests Dz(i,nx+j)>0 before evaluating log(Dz(i,nx+j)). To illustrate the computational costs, we include two examples.

8.1 Goodness-of-fit, continuous data

Here the example is the first of the case studies. The null hypothesis specifies a uniform distribution in two dimensions with 1000 observations:

tmp = MDgof::case.studies(1, nsample = 1000)
x = tmp$rnull()
tp = proc.time()
a = MDgof::gof_test(x, tmp$pnull, tmp$rnull, dnull = tmp$dnull, B = 250, maxProcessor = 1, seed = 1234)
proc.time()-tp
   user  system elapsed
  43.28    0.92   45.78

The elapsed time was therefore about 46 seconds.

8.2 Two-sample, continuous data

Both data sets come from a uniform distribution in 5 dimensions with 100 observations:

x = matrix(runif(500), ncol = 5)
y = matrix(runif(500), ncol = 5)
tp = proc.time()
a = MD2sample::twosample_test(x, y, B = 250, maxProcessor = 1, seed = 1234)
Data is assumed to be continuous
proc.time()-tp
   user  system elapsed
   5.65    0.44    6.16

The elapsed time was therefore about 6 seconds.

9 Applications

The following examples illustrate the use of MDgof and MD2sample with real datasets. Their purpose is to demonstrate the software interface, including parameter estimation and adjusted pp-values, rather than to provide comprehensive statistical analyses of the underlying scientific questions. The full code and fixed data used for these examples are included in the replication material.

9.1 Fisher’s iris data

As an illustration with continuous data, consider Fisher’s iris data (Fisher, 1936). We use the 50 observations on the setosa variety to demonstrate a goodness-of-fit test of a four-dimensional multivariate normal distribution, with its mean vector and covariance matrix estimated from the data. Figure 1 shows a scatterplot matrix.

setosa = iris3[, , 1]
p = GGally::ggpairs(
  setosa,
  progress = FALSE
) +
  ggplot2::theme_bw(base_size = 13) +
  ggplot2::theme(
    axis.text = ggplot2::element_text(size = 10),
    strip.text = ggplot2::element_text(size = 12),
    strip.text.x = ggplot2::element_text(size = 12),
    strip.text.y = ggplot2::element_text(size = 12)
  )
print(p)
Fig 1: Scatterplot matrix of the setosa variety of Fisher’s iris data.

We specify the null cumulative distribution function, density, simulation routine, and parameter estimator:

pnull = function (x, p) {
    f = function(x)
       mvtnorm::pmvnorm(rep(-Inf, length(x)), x,
              mean = p[1:4],
              sigma = matrix(p[5:20], nrow = 4))
    if (!is.matrix(x)) return(f(x))
    apply(x, 1, f)
}
dnull = function (x, p) {
    f = function(x)
       mvtnorm::dmvnorm(x,
              mean = p[1:4],
              sigma = matrix(p[5:20], nrow = 4))
    if (!is.matrix(x)) return(f(x))
    apply(x, 1, f)
}
rnull = function(p) mvtnorm::rmvnorm(50,
              mean = p[1:4],
              sigma = matrix(p[5:20], nrow = 4))
phat = function(x) c(apply(x, 2, mean), cov(x))

The tests can then be run as follows, using a fixed random seed:

MDgof::gof_test(setosa, pnull, rnull, B = 1000,
        dnull = dnull, phat = phat, seed = 1234,
        doMethods = "all", maxProcessor = 1)
MDgof multivariate goodness-of-fit tests
Data: continuous  | n = 50  | dimension = 4  | B = 1000

 method statistic p.value
    qKS     0.268   0.000
     qK     0.278   0.000
   qCvM     0.748   0.000
    qAD     8.419   0.083
     BR     1.309   0.564

The individual tests give different results, which motivates illustrating the adjusted pp-value procedure. We use the same five methods in the adjusted test by specifying doMethods = "all":

MDgof::gof_test_adjusted_pvalue(setosa, pnull, rnull,
        B = 1000, dnull = dnull, phat = phat,
        seed = 1234, doMethods = "all", maxProcessor = 1)
p values of individual tests:
qKS : 0
qK : 0
qCvM : 0
qAD : 0.083
BR : 0.875

adjusted p value of combined tests: 0

  qKS    qK  qCvM   qAD    BR Min p
0.000 0.000 0.000 0.083 0.875 0.000

The adjusted pp-value is reported as zero at the simulation resolution used here; this is a Monte Carlo estimate, not an assertion that the probability is exactly zero. The example illustrates both the different responses of individual test statistics and the use of the adjusted pp-value routine. A substantive assessment of multivariate normality in these measurements is not the purpose of this example.

9.2 Goals in the English Premier League

For an illustration with discrete bivariate data, we compare the joint frequencies of goals scored by home and away teams in the English Premier League in 2020–21 and in the available portion of 2025–26. The original match results were obtained from Football-Data.co.uk (Football-Data.co.uk, 2026). The latter season was incomplete when the data were collected, so the comparison is intended only as a demonstration of the software, not as an assessment of changes in football tactics or scoring patterns.

For simplicity, the example uses the categories 0 through 4 goals for each team. Matches in which either team scored five or more goals are excluded, so the analysis applies only to the retained matches. To make the results independent of future changes to the source website, the 25-row frequency table is included directly in the replication material. It has columns vals_x and vals_y for home and away goals, and x and y for the corresponding frequencies in the two seasons.

goals_premier_league <- cbind(
  vals_x = rep(0:4, times = 5),
  vals_y = rep(0:4, each = 5),
  x = c(30,33,26,14,6, 38,40,27,13,4,
        23,33,7,7,3, 14,18,7,6,2, 3,4,3,2,0),
  y = c(27,22,29,27,2, 31,47,37,20,6,
        15,27,26,11,3, 10,9,10,3,2, 2,4,1,2,1)
)

We apply the eight tests available by default for these discrete data:

MD2sample::twosample_test(goals_premier_league,
                          B = 1000, seed = 1234)
MD2sample two-sample tests
Data: discrete  | n.x = 363  | n.y = 374  | dimension = 2  | B = 1000

    method statistic p.value
        KS    0.1314  0.0040
         K    0.1827  0.0020
       CvM    0.4884  0.0410
        AD    2.3511  0.0530
        NN    3.0350  0.0400
        AZ    0.2111  0.0040
        BF    0.3149  0.0040
 ChiSquare   33.1600  0.0445

To illustrate simultaneous inference using the complete collection of eight discrete-data methods, we specify doMethods = "all" in twosample_test_adjusted_pvalue. Without this option, the routine uses four methods by default: Chi-square, Kolmogorov–Smirnov, Aslan–Zech, and Cramér–von Mises.

MD2sample::twosample_test_adjusted_pvalue(
    goals_premier_league, B = 1000, seed = 1234,
    doMethods = "all")
p values of individual tests:
KS: 0.004
K: 0.002
CvM: 0.041
AD: 0.053
NN: 0.04
AZ: 0.004
BF: 0.004
ChiSquare: 0.0445
adjusted p value of combined tests: 0.013

The adjusted pp-value for these eight tests is 0.013. The example demonstrates the use of the discrete two-sample procedures and their combined calibration. Because the seasons have different coverage and the frequency table excludes high-scoring matches, no substantive conclusion about changes in Premier League goal-scoring patterns is drawn.

10 Discussion

Because the relative power of the procedures depends on the null distribution, the alternative, the dimension, and the sample size, the benchmark results should not be interpreted as general rankings. The simulation facilities provided by the packages allow users to compare procedures under scenarios relevant to their applications.

Multivariate goodness-of-fit and two-sample testing present a computational comparison problem as much as an implementation problem. The large number of available procedures reflects the fact that sensitivity to departures from the null varies with the geometry of the alternative, dimension, sample size, and treatment of nuisance parameters. Consequently, providing another isolated implementation of a test does not by itself solve the practical problem faced by analysts or the evaluation problem faced by researchers developing new methodology.

The contribution of MDgof and MD2sample is a common computational environment in which heterogeneous procedures can be applied, calibrated, compared, and benchmarked. Built-in statistics cover several methodological families and both continuous and discrete data. More importantly, user-defined statistics can enter the same testing, Monte Carlo power, simultaneous-inference, and benchmarking workflows. The deliberately parallel interfaces of the two packages make the computational structure of goodness-of-fit and two-sample studies similar from the user’s perspective even though their null-generation mechanisms differ.

The benchmark collections are not intended to identify a universally best test. Rather, they provide reproducible scenarios in which the strengths and weaknesses of competing statistics can be studied under common conditions. This is especially useful when a new statistic is proposed: the researcher can compare it with established procedures without writing a separate simulation infrastructure for every competitor.

There are also limitations. Some multivariate empirical-distribution-function statistics require computational approximations, and the cost of resampling-based calibration can be substantial for large samples or extensive benchmark grids. The framework therefore uses compiled code and parallel processing where appropriate, but computational burden remains method dependent. Future development can extend the collection of benchmark alternatives, incorporate additional testing families, and improve computational strategies for large or high-dimensional data.

In this sense, MDgof and MD2sample provide infrastructure for reproducible methodological comparison. They provide a common environment in which established and newly proposed multivariate goodness-of-fit and two-sample procedures can be implemented, evaluated, and compared under controlled simulation conditions.

Statements and Declarations

Funding. No funds, grants, or other support was received.

Competing interests. The author has no relevant financial or non-financial interests to disclose.

Generative AI use. OpenAI ChatGPT (GPT-5.6 Sol) was used during manuscript preparation for language and organizational assistance, consistency checking, and software coding and debugging assistance. All resulting text, code, numerical results, and references were reviewed and verified by the author, who takes full responsibility for the content of the manuscript.

Data and Code Availability

Data availability. The Fisher iris data are included with R. The English Premier League data used in Section 9.2 are publicly available from Football-Data.co.uk. The remaining numerical examples and simulation studies use data generated by the code and the MDgof and MD2sample packages described in the article.

Code availability. The MDgof and MD2sample packages are publicly available from CRAN. Code needed to reproduce the numerical examples is given in the article, and the benchmark-generating routines and case studies are included in the packages.

References

  • Aldor-Noiman, S., Brown, L. D., Buja, A., Rolke, W., & Stine, R. A. (2013). The Power to See: A New Graphical Test of Normality. The American Statistician, 67(4), 249–260. https://doi.org/10.1080/00031305.2013.847865 ↩
  • Anderson, T. W. (1962). On the Distribution of the Two-Sample Cramer-von Mises Criterion. The Annals of Mathematical Statistics, 33(3), 1148–1159. https://doi.org/10.1214/aoms/1177704477 ↩
  • Anderson, T. W., & Darling, D. A. (1952). Asymptotic Theory of Certain “Goodness of Fit” Criteria Based on Stochastic Processes. The Annals of Mathematical Statistics, 23(2), 193–212. https://doi.org/10.1214/aoms/1177729437 ↩
  • Aslan, B., & Zech, G. (2005). New test for the multivariate two-sample problem based on the concept of minimum energy. Journal of Statistical Computation and Simulation, 75(2), 109–119. https://doi.org/10.1080/00949650410001661440 ↩
  • Bakshaev, A., & Rudzkis, R. (2015). Multivariate goodness-of-fit tests based on kernel density estimators. Nonlinear Analysis: Modelling and Control, 20(4), 585–602. https://doi.org/10.15388/na.2015.4.9 ↩
  • Baringhaus, L., & Franz, C. (2004). On a new multivariate two-sample test. Journal of Multivariate Analysis, 88(1), 190–206. https://doi.org/10.1016/s0047-259x(03)00079-4 ↩
  • Bickel, P. J., & Breiman, L. (1983). Sums of Functions of Nearest Neighbor Distances, Moment Bounds, Limit Theorems and a Goodness of Fit Test. The Annals of Probability, 11(1), 185–214. https://doi.org/10.1214/aop/1176993668 ↩
  • Biswas, M., & Ghosh, A. K. (2014). A nonparametric two-sample test applicable to high dimensional data. Journal of Multivariate Analysis, 123, 160–171. https://doi.org/10.1016/j.jmva.2013.09.004 ↩
  • Chen, H., & Friedman, J. H. (2017). A New Graph-Based Two-Sample Test for Multivariate and Object Data. Journal of the American Statistical Association, 112(517), 397–409. https://doi.org/10.1080/01621459.2016.1147356 ↩
  • Cramér, H. (1928). On the composition of elementary errors: First paper: Mathematical deductions. Scandinavian Actuarial Journal, 1928(1), 13–74. https://doi.org/10.1080/03461238.1928.10416862 ↩
  • Eddelbuettel, D., François, R., Allaire, J., Ushey, K., Kou, Q., Russell, N., Ucar, I., Bates, D., & Chambers, J. (2024). Rcpp: Seamless r and c++ integration [R package version 1.1.1-1.1]. https://CRAN.R-project.org/package=Rcpp ↩
  • Fasano, G., & Franceschini, A. (1987). A multidimensional version of the Kolmogorov–Smirnov test. Monthly Notices of the Royal Astronomical Society, 225(1), 155–170. https://doi.org/10.1093/mnras/225.1.155 ↩
  • Fisher, R. A. (1936). The use of multiple measurements in taxonomic problems. Annals of Eugenics, 7(2), 179–188. https://doi.org/10.1111/j.1469-1809.1936.tb02137.x ↩
  • Football-Data.co.uk. (2026). Historical football match results data archive. ↩
  • Franz, C. (2024). Cramer: Multivariate nonparametric cramer-test for the two-sample-problem [R package version 0.9-4]. https://CRAN.R-project.org/package=cramer ↩
  • Friedman, J. H., & Rafsky, L. C. (1979). Multivariate Generalizations of the Wald-Wolfowitz and Smirnov Two-Sample Tests. The Annals of Statistics, 7(4), 697–717. https://doi.org/10.1214/aos/1176344722 ↩
  • Gretton, A., Sejdinovic, D., Strathmann, H., Balakrishnan, S., Pontil, M., Fukumizu, K., & Sriperumbudur, B. K. (2012). Optimal kernel choice for large scale two sample tests. Advances in Neural Information Processing Systems (NIPS), 1205–1213. ↩
  • Karatzoglou, A., Smola, A., Hornik, K., & Zeileis, A. (2004). kernlab - An S4 Package for Kernel Methods in R. Journal of Statistical Software, 11(9). https://doi.org/10.18637/jss.v011.i09 ↩
  • Kolmogorov, A. N. (1933). Sulla determinazione empirica di una legge di distribuzione. Giornale Dell’Instituto Italiano Degli Attuari, 4, 83–91. ↩
  • Kuiper, N. H. (1960). Tests concerning random points on a circle. Indagationes Mathematicae (Proceedings), 63, 38–47. https://doi.org/10.1016/s1385-7258(60)50006-0 ↩
  • von Mises, R. (1928). Wahrscheinlichkeit Statistik und Wahrheit. Springer Berlin Heidelberg. https://doi.org/10.1007/978-3-662-36230-3 ↩
  • Pan, W., Tian, Y., Wang, X., & Zhang, H. (2018). Ball Divergence: Nonparametric two sample test. The Annals of Statistics, 46(3), 1109–1137. https://doi.org/10.1214/17-aos1579 ↩
  • Pettitt, A. N. (1976). A two-sample anderson-darling rank statistic. Biometrika, 63(1), 161–168. ↩
  • Puritz, C., Ness-Cohn, E., & Braun, R. (2023). fasano.franceschini.test: An Implementation of a Multivariate KS Test in R. The R Journal, 15(3), 159–171. https://doi.org/10.32614/rj-2023-067 ↩
  • Rayner, J. C. W., Thas, O., & Best, D. J. (2009). Smooth tests of goodness of fit. Wiley. ↩
  • Ripley, B. D. (1976). The second-order analysis of stationary point processes. Journal of Applied Probability, 13(2), 255–266. https://doi.org/10.2307/3212829 ↩
  • Rizzo, M. L., & Székely, G. J. (2024). Energy: E-statistics: Multivariate inference via the energy of data [R package version 1.7-12]. https://CRAN.R-project.org/package=energy ↩
  • Rolke, W. (2025). Rgof and R2sample: Testing and Benchmarking for the Univariate Goodness-of-Fit and Two-Sample Problems. The R Journal, 17(3), 138–161. https://doi.org/10.32614/rj-2025-027 ↩ 1 2
  • Rolke, W. (2026a). MD2sample: Various methods for the two-sample problem in D > 1 dimensions [R package version 1.4.2]. https://doi.org/10.32614/CRAN.package.MD2sample
  • Rolke, W. (2026b). MDgof: Various methods for the goodness-of-fit problem in D > 1 dimensions [R package version 1.2.1]. https://doi.org/10.32614/CRAN.package.MDgof
  • Smirnov, N. V. (1939). Estimate of deviation between empirical distribution functions in two independent samples. Bulletin of Moscow University, 2, 3–16. ↩
  • Thas, O. (2010). Comparing distributions. Springer. ↩
  • Westfall, P. H., & Young, S. S. (1993). Resampling-based multiple testing: Examples and methods for p-value adjustment. John Wiley & Sons. ↩

Cite this article

Rolke, W. (2026). A Computational Framework for Multivariate Goodness-of-Fit and Two-Sample Testing: MDgof and MD2sample. Journal of Reproducible Statistics, 1, 8. https://doi.org/10.68365/jrepstat.2026.8

doi: https://doi.org/10.68365/jrepstat.2026.8

Downloads

PDF ePub JATS XML .bib .ris .csl.json Reproducibility package (.zip) 309e9b2500155ada13b7e2cf8e783567f0522b273f7b565e0110b38a2af8c31c

https://doi.org/10.68365/jrepstat.2026.8 also supports content negotiation, so reference managers can fetch the citation from the DOI directly. The exports above work without it.