Changelog
Source:NEWS.md
smartcor 1.2.0
Version 1.2.0 aligns the R and Python packages with the accompanying paper (Harshvardhan and Ranjan, 2026). The changes below apply to both languages unless a section says otherwise.
New selection rules
Method selection now follows Table 3 and Figure 4 of the paper. Every pair starts from the Step 0 diagnostics described below. Default selections change for eight of the ten type pairs:
- Continuous + continuous: Spearman is selected when the outer-fence rule flags either variable. Otherwise
test_linearity()decides between Pearson and Spearman as before. When Pearson is selected and Mardia’s test rejects bivariate normality, the rationale says that the interval and p-value are approximate. - Continuous + binary: was always point-biserial. Now rank-biserial when the within-group residuals have outliers or fail the Jarque-Bera test; otherwise biserial when the LR test does not reject latent normality, and point-biserial when it does.
- Binary + binary:
"auto"still gives phi andTRUEtetrachoric.assume_latent_normal = FALSEnow gives Yule’s Q instead of phi. - Continuous + ordinal: the continuous variable must pass the outer-fence rule and the Jarque-Bera test before polyserial is considered. The fallback is Kendall’s tau when the ordinal variable has 5 or fewer categories and Spearman otherwise (previously always Spearman).
- Ordinal + ordinal: when the LR test rejects, the fallback is Kendall’s tau if either variable has 5 or fewer categories and Spearman otherwise (previously always Kendall’s tau).
- Binary + ordinal: was always rank-biserial. Now polychoric when the LR test does not reject latent normality, and rank-biserial otherwise.
- Continuous + categorical: was Cramer’s V on the binned continuous variable. Now the correlation ratio eta-squared, or Kruskal-Wallis eta-squared (H) when the within-group residuals have outliers or fail the Jarque-Bera test.
- Ordinal + categorical: was Cramer’s V. Now Freeman’s theta, which keeps the order of the ordinal variable.
Binary + categorical and categorical + categorical still use Cramer’s V. Count variables follow the continuous rules everywhere, so count pairs change in the same way as the continuous ones.
assume_latent_normal = TRUE or FALSE now fixes only the latent-normality node; the outlier and Jarque-Bera checks still run. In "auto" mode a failed LR test resolves to “not assumed”.
Diagnostics
- New
detect_outliers()applies the outer-fence rule (values below Q1 - 3 IQR or above Q3 + 3 IQR, type-7 quartiles). It returns asmartcor_outliersobject with the fences, quartiles, flagged values and their positions, skewness, and excess kurtosis. When the IQR is 0 the rule is marked as degenerate and flags nothing. -
test_bivariate_normality()now chooses the test from the variable types: Mardia’s test (viaMVN::mardia()) for two continuous variables, the Jarque-Bera test (viamoments::jarque.test()) plus the LR chi-square test for continuous-ordinal and continuous-binary pairs, and the LR test alone for ordinal and binary pairs. Categorical variables give an error. A contingency table passed asxstill runs the LR test. The result gainstest,components,null_hypothesis, andinterpretation; with several components the overallp.valueis the smallest one, and the test rejects if any component rejects. -
test_linearity()now returns asmartcor_lintestobject with a print method. New fields:p_y_on_xandp_x_on_y, eachmax(min(p_bp, p_lb, p_bds), p_reset)for one direction;p.value, the larger of the two, which is belowalphaexactly when linearity is rejected;direction, the direction whose component p-values are reported;null_hypothesis; andinterpretation. - The BDS p-value in
test_linearity()is now the minimum over the distance thresholds. It was the mean, which is not a p-value. Some linearity decisions change as a result. - New argument
alpha = 0.05onsmart_cor(),smart_cormat(),smart_cor_df(), andcompare_methods(), passed to every diagnostic test.
Linearity diagnostics and method selection
- Added
test_linearity()in both languages. It removes incomplete pairs by default, regresses each variable on the other, and orders the residuals by the regressor, averaging residuals at tied values. Diagnostics include Box-Pierce, Ljung-Box, BDS, Breusch-Pagan, and RESET, alongside Pearson, Spearman, and Kendall correlations. - Automatic
smart_cor()calls for continuous/count pairs without outliers run these diagnostics atalpha. A direction rejects linearity when a residual structure test and RESET both reject. Spearman is selected only when both directions reject; otherwise the rule retains Pearson, with the inference recommendation taken from a direction that does not reject. The model behind the test assumes errors independent of the regressor, which can hold in only one direction, so requiring both directions keeps skewed margins from being read as nonlinearity. Explicitmethodarguments bypass the diagnostic rule. - Results include
linearity_test. Inconclusive diagnostics retain Pearson and explain why. Non-rejection does not establish linearity. The decision does not depend on argument order or row order. - When the rule recommends robust/bootstrap Pearson inference,
bootstrap = "auto"now uses a paired percentile-bootstrap confidence interval. The p-value remains the standard Pearson test; it is not a robust p-value. Setbootstrap = FALSEin R orbootstrap=Falsein Python to disable resampling.
New estimators
Four estimators bring the total to 18 methods:
-
biserial: the biserial correlation, fitted as a polyserial correlation with a two-category variable (see the polyserial note below). Wald p-value and interval on the Fisher-z scale. If the fit fails, point-biserial is reported and named in the result. -
eta_squared: the correlation ratio, SS_between / SS_total. One-way ANOVA F p-value and a noncentral-F interval, mapped to eta-squared as ineffectsize::eta_squared(). -
kw_eta_squared: Kruskal-Wallis eta-squared, (H - k + 1) / (n - k). Kruskal-Wallis p-value and a percentile bootstrap interval. -
freemans_theta: Freeman’s theta for a nominal and an ordinal variable. Permutation p-value (n_permshuffles of the ordinal variable) and a percentile bootstrap interval.
Polyserial and biserial now default to polycor::polyserial()’s own two-step estimator, with std.err = TRUE for the interval and p-value. New argument polyserial_ml = FALSE on smart_cor(), smart_cormat(), smart_cor_df(), and compare_methods(); TRUE, which we recommend, fits the full maximum-likelihood estimate with a strict tolerance and a safe-start retry, as earlier versions did.
available_methods() and compare_methods() now list the methods of Table 1 of the paper for each pair. The duplicate Pearson row for continuous-binary pairs is gone. For continuous-categorical pairs, compare_methods() bins the continuous variable only for Cramer’s V.
The two-letter codes used in the method heatmaps are Bs (biserial), E2 (eta-squared), EH (Kruskal-Wallis eta-squared), and Ft (Freeman’s theta). The clash between tetrachoric and Tschuprow’s T is fixed: they are now Tc and Ts.
Results
-
smart_cor()results gaindiagnostics, a list withoutliers,normality(Mardia or Jarque-Bera),latent(the LR test), andlinearity. Entries that did not run areNULL.normality_testandlinearity_testare kept for compatibility. -
print.smartcor()shows one line per diagnostic that ran, under a bold green Diagnostics header. -
tidy()gains four columns,outlier_flag,normality_p,latent_p, andlinearity_p(24 columns in all). They areNAwhen a test did not run. -
smart_cormat()keeps its return structure. Its stored pairs dropdiagnosticsbut keep the four summary fields. Diagonal method labels follow the type andassume_latent_normal. -
detect_type()classifies any variable with exactly two distinct values as binary, including a two-level ordered factor.
Optional positive-definite matrix adjustment
- Added
nearPDtosmart_cormat()andsmart_cor_df(). It remains off by default (FALSEin R,Falsein Python). - Explicit enablement adjusts the matrix while retaining a unit diagonal. R uses
Matrix::nearPD(..., corr = TRUE); Python implements the corresponding Higham/Dykstra adjustment. -
smart_cormat()retainsoriginal_correlationsand adjustment diagnostics. Methods, pairwise details, and tidy/long output still describe the original estimates and inference. The adjustment does not refit those estimates. - The adjustment works with the new measures in the matrix (eta-squared and Freeman’s theta lie in [0, 1]).
- Missing or infinite matrix entries must be resolved before adjustment. Use
smart_cormat()rather than the two-tablesmart_cor_df()wrapper when you need the original matrix and adjustment diagnostics.
Python
- Polychoric, polyserial, and biserial estimates are now full joint maximum-likelihood fits in Python, as
polycorcomputes them in R; R restarts thepolycorfit from its own solution with a tight tolerance so both languages reach the same optimum. Polychoric estimates at the boundary (|rho| >= 0.995, a table that separates the variables) fall back to Kendall’s tau in both languages. - Permutation p-values (Theil’s U, Freeman’s theta) are computed even with
bootstrap=Falsein Python, as in R. - Python implements the same rules, diagnostics, estimators, method codes, and output fields.
detect_outliers()returns anOutlierResultwith 0-basedindex. Mardia’s test uses the formulas ofMVN::mardia()and the Jarque-Bera test usesscipy.stats.jarque_bera.TIDY_COLUMNSlists the same 24 columns in the same order as R. -
_select_method()is now a function of the diagnostics and follows the same criteria as R. - Fixed leftovers from the package rename: doctests use
files("pysmartcor"), the install hint ispip install pysmartcor[viz], thelicense-filespath inpyproject.tomlpoints tosrc/pysmartcor/LICENSES/, and docstrings refer topysmartcor.results.
Dependencies and validation
- Added R dependencies
fNonlinear,lmtest,Matrix,MVN (>= 6.0), andmomentsto Imports, andeffectsizeandrstatixto Suggests (used in tests). Added Python dependencystatsmodels. - Added tests for type detection, the outer-fence rule, Mardia and Jarque-Bera against their reference packages, the classical estimators against
cor.test()andpolycor, the type-aware normality test, the linearity fields, every selection branch with itsTRUE/FALSEoverrides, the four new estimators against reference implementations,compare_methods()rows, tidy columns, and matrix adjustment with mixed measures. Cross-language comparisons cover the diagnostics and adjustment on the tested inputs; they do not establish parity for every package function. Seeded bootstrap and permutation results can differ across languages.
smartcor 1.0.1
CRAN release: 2026-08-09
CRAN resubmission after reviewer feedback. No statistical results change.
- The Description field now cites the accompanying paper: Harshvardhan and Ranjan (2026) doi:10.48550/arXiv.2607.22285.
- The
ggcor_heatmap()/ggcor_method_heatmap()example is no longer wrapped in\dontrun{}; it uses only bundled data and runs during checks. - The internal seed helper no longer touches the global environment directly; it delegates to
withr::with_seed()(withr added to Imports). Seeded results are unchanged, and a seeded call still restores the caller’s.Random.seed. -
graphicsandutilsare now declared in Imports (both were already imported in NAMESPACE). -
inst/CITATION, the README, and the documentation now cite the published arXiv preprint (arXiv:2607.22285) instead of “work in progress”.
smartcor 1.0.0
First public release.
-
smart_cor()detects the types of two variables and selects an appropriate correlation method (Pearson, Spearman, Kendall’s tau-b, polychoric, polyserial, tetrachoric, point-biserial, rank-biserial, Goodman-Kruskal gamma, or phi), with analytic inference on the Fisher-z scale and optional bootstrap. -
smart_cormat()andsmart_cor_df()extend automatic selection to full mixed-type data frames and correlation matrices. -
compare_methods()reports all applicable estimators for a pair side by side. -
test_bivariate_normality()implements the likelihood-ratio chi-square test for bivariate normality (Joreskog 2005);smart_cor()runs it automatically before reporting polychoric/polyserial estimates and falls back to Kendall’s tau-b when it rejects. -
detect_type()andavailable_methods()expose the type-detection and method-lookup machinery. - Heatmap visualizations:
ggcor_heatmap(),ggcor_method_heatmap(), andplot_cor_heatmap(). -
tidy()method for pipeline-friendly output.