effectsize/0000755000175000017500000000000014174255623012544 5ustar nileshnilesheffectsize/MD50000644000175000017500000002252314174255623013060 0ustar nileshnileshcbc0a5ed5f344933ba3e4fce0d9dd1f5 *DESCRIPTION 099e4edb8ca0f5e9ef11c014d23a134f *NAMESPACE b2e6f61a5aab40070786ca2b2e24ccdd *NEWS.md 01b2f489e04d5d6442162defa20f91b8 *R/cohens_d.R f6976de528c3ee6461ca1f2c1a15fd60 *R/common_language.R 0e0a968d5f6be1b0223f25a783916595 *R/convert_between_OR_to_RR.R b15cd43f382bab5bd0faddda78fbe747 *R/convert_between_anova.R 24323040090f75669a09960d834fba39 *R/convert_between_common_language.R bee8980593a13ade70163742ae7efaff *R/convert_between_d_to_r.R 2a30afec4929761886622639a3963f41 *R/convert_between_odds_to_probs.R a6ce9ea3a7f614f966ce43dec3ad07c5 *R/convert_stat_chisq.R e4c410e79ad10ee6665121bc310cfb0f *R/convert_stat_to_anova.R 05afb364a23923124806502ae6e4eda7 *R/convert_stat_to_d.R 4fd5d8be884df09d3cabf6d33f56d21e *R/convert_stat_to_r.R f9532fed0067086679f5f91a9e6f1e7a *R/datasets.R 1252c527a0e6f0177701180840562747 *R/docs_extra.R 826574f2e4c0365b2b85bb76cc5f7de3 *R/effectsize.BFBayesFactor.R 8bee72a747b2a5d42030656c816fb4b7 *R/effectsize.R 11b0ae51d3bbf4a745886c5d1f095b16 *R/effectsize.htest.R bc5b919201e2b827dcf3c79d880d60bf *R/equivalence_test.R 2fb67162e569afc50c905f16fd19bd47 *R/eta_squared.R ae1e46dc3e881cfef17356e876106c6f *R/eta_squared_posterior.R 173963a6b034f49b1c8e7367b13e4ba5 *R/format_standardize.R df1975e28901df998aa63e70680beb25 *R/interpret.R a8c322518e847a7912cae825b1c86f1b *R/interpret_bf.R fc544de54e7799027784f0e115d38e66 *R/interpret_cfa_fit.R e0063e6506c3dc04e1f18aa25f72bbd9 *R/interpret_cohens_d.R 58afc6770454de08379a4567f47122d2 *R/interpret_cohens_g.R 12ee7cb0e5bd2f7458b5d1106091c744 *R/interpret_direction.R 7e0acaaa58c45ce83c7f0840ebe0a29e *R/interpret_ess_rhat.R 3277d9c806eeb7b4cef2db931270907c *R/interpret_icc.R 1d9a18c30a9a96883a5aaaca0fcce04a *R/interpret_kendalls_w.R 14e9bcc6d4104838b33b5212a4f17f52 *R/interpret_oddsratio.R 9e0815bf9545b7b1b3d422b916157ee5 *R/interpret_omega_squared.R 94327a87c74eae4f4eb57f5634ce2b2f *R/interpret_p.R d9a00a1859baa6cf3bb207afd4289936 *R/interpret_pd.R 87a04f151fc62173ad523c1b20e2db1e *R/interpret_r.R 7f5e57a15238b79248d033a6215005ac *R/interpret_r2.R f17d915e61ef3da14a3b265d871618c8 *R/interpret_rope.R 6098458813288f8c5d12e2c597b75d9b *R/interpret_vif.R 7d96473a3f73e9f1118a0ebc7626ae30 *R/is_effectsize_name.R e10d77e149ad426514e32f33e5e7f249 *R/plot.R 7cb0ab17ad94424c269e517655ae2d65 *R/print.effectsize_table.R 21f6070a2538e034834c67fed154dad0 *R/print.rules.R d23022f90bd5d5915b3b26372b5b4eb9 *R/rank_effectsizes.R 742e87a8ff5f70daa1cb7b872c076363 *R/reexports.R c955f235e68d5cc978d11021c7d33d17 *R/sd_pooled.R 793bc5ca14a7d9a3bb1f2fcda811f71b *R/standardize.models.R 788eca9022e50638d0051613e2c32303 *R/standardize_info.R f78738723c2b152119b1cbacb0931e96 *R/standardize_parameters.R eb9c654cad70ffb6426c22e9c7d8ed41 *R/utils_interpret.R d4421edbe4b61dc4491f6604c8a0e2e4 *R/utils_ncp_ci.R 46c674326b7d511a4bf1d2946f6f358b *R/utils_standardize.R e2547de3eedbb168b27e621c9459c04d *R/utils_validate_input_data.R 27f76a4e55802fb816b51dac500eaf26 *R/utils_values_aov.R 77d31b0535eec792147705587ea29b72 *R/xtab.R 569697a105740903a855e7c53223c90f *R/zzz_deprecated.R f2651a276cb9baa330f9d19791c34190 *README.md eb3cc7ff32582ab3faca7d4c7170d99f *build/partial.rdb 0b258c581b01f38eae417677f920c9de *build/vignette.rds 8b1c7f8c59bcf2e497e5d93998db3cc0 *data/hardlyworking.rda 58ca1b1838bfaa214a67ecf99f232667 *inst/CITATION 21b7119b4fbc99010aa4fbe94018bc40 *inst/WORDLIST 6ec0638e3a8fe6e9f3b11e213ae91cc4 *inst/doc/anovaES.R b96ef4200c7d361783276c82e5012dfb *inst/doc/anovaES.Rmd 4be624ea0c87bd1dfbd9ee256a244960 *inst/doc/anovaES.html 1b1ffd1ab72c36f1a44823801d65d01c *inst/doc/bayesian_models.R 36a16877046b89c0a4a017652ee80a27 *inst/doc/bayesian_models.Rmd f6125a37061486be697c1b3f6389c02d *inst/doc/bayesian_models.html a13fbc74da35eb1736740caabb4a08ef *inst/doc/convert.R 1901ee48abff7feb13f4255e86f0442f *inst/doc/convert.Rmd 18292602333b9795685e8cf5a4fb8119 *inst/doc/convert.html 5e249d65e31e4322b42093350ce37922 *inst/doc/effectsize.R ce06740dece0d87994d4921bc444fcc6 *inst/doc/effectsize.Rmd dfe22d03d23e9a3266b45fe18fce2a71 *inst/doc/effectsize.html 8206ae5ccbdef2838a0ccc30cc45aa74 *inst/doc/effectsize_API.R 7c6e4b8d6ff953e99f7989e93f122fdc *inst/doc/effectsize_API.Rmd 626c116a4256cd6d7ba3e480d9d6dfad *inst/doc/effectsize_API.html 4e6de3c3a69503fafde5e01eb41e0000 *inst/doc/from_test_statistics.R 307fe86d0948a781de24e71ce66d419a *inst/doc/from_test_statistics.Rmd 0cd7a05d130cae5ad8f2c97cbaf0120f *inst/doc/from_test_statistics.html c2955cb1cff18e49a42037c4ecaa40d0 *inst/doc/interpret.R c4d496c4f99f73bad5f61d2107d78096 *inst/doc/interpret.Rmd 80bb87fdd7429d5d8ffcae8f3d557328 *inst/doc/interpret.html e12f4bf7d676a8a35d92b7778ecaae0d *inst/doc/simple_htests.R a89fbb3a41364dddcd507117c033a29b *inst/doc/simple_htests.Rmd ccd773ba12c164f3bbc3b9cb7ec639f9 *inst/doc/simple_htests.html 418fec6eb9303de2e082cf83bb86295f *inst/doc/standardize_parameters.R 499f96481c20c9174a1a2bfb6f02fb1a *inst/doc/standardize_parameters.Rmd 9971d975c5820b19abbe6856f8fefec9 *inst/doc/standardize_parameters.html 3456aef81783cb0278705ea9edf283d8 *man/F_to_eta2.Rd 9fad61eeb16a230189121f220d1174b5 *man/chisq_to_phi.Rd 73e62c754323d3c82745c27a713f52c4 *man/cles.Rd 75d8e1052c3d9a08d4b441f9fff1c3a4 *man/cohens_d.Rd 5b85da5f26667b41001ef2fb1a3bfdc0 *man/d_to_cles.Rd 60d29e0727612d07af71c8ae9f3d6dc1 *man/d_to_r.Rd 2adcb487056048bdc15dcb1b774057ec *man/effectsize.Rd a9503b0dec81f80a648b74fe5cc61b31 *man/effectsize_API.Rd 0ac93fc5f3a7f91f3de8cef74381f943 *man/effectsize_CIs.Rd bc71f851805e67afbb0030e9667b9123 *man/effectsize_deprecated.Rd 1e3ddbe542d689d0a0974cccc3953aae *man/equivalence_test.effectsize_table.Rd 4ef7bb57328626d18591d762c3970400 *man/es_info.Rd a2ad3d7fa1ea138b8a503d6bf5d19cb0 *man/eta2_to_f2.Rd 80890c63770962b22ddce0185d046708 *man/eta_squared.Rd 5da312135d19a711a76500956d2ccc7b *man/figures/logo.png 4738d1af3bead430dc565dc259f98e9e *man/format_standardize.Rd 31be60aec05f52d4ed7267f790dc80cd *man/hardlyworking.Rd 5c698faf1ebe4aa5a38799f5c83bdf44 *man/interpret.Rd 23b9d5102c8581c313ba2da6bd76baa3 *man/interpret_bf.Rd 26910463ca90081580dc2cc018cd719c *man/interpret_cohens_d.Rd bcc35b483ce6ac6e0a0802222e3aa79b *man/interpret_cohens_g.Rd 8bfa70673966be91a0401cafcf2e71be *man/interpret_direction.Rd 50952b8e17dd5357e047c3dc016eafd5 *man/interpret_ess.Rd a478e0f833ff19f84f4423ae66f01708 *man/interpret_gfi.Rd a20d7b6c60e0c262510ff5d040dbef56 *man/interpret_icc.Rd 19dbfc04e74e26e6a27443a59917a068 *man/interpret_kendalls_w.Rd 436149611a2577376c1370f88716e472 *man/interpret_oddsratio.Rd ff2fb8dd1a9dcce4f71886ac05fa193b *man/interpret_omega_squared.Rd b0deef3d8bf1fef83fdd6a42148fb57b *man/interpret_p.Rd e1d160168718a337bbb249f0a4bf2c97 *man/interpret_pd.Rd de9c820db9d52b29d1d1923c76865ee5 *man/interpret_r.Rd e0be342132e325dded9a4076c18cc97e *man/interpret_r2.Rd 896abd8f694917edc70f2384e873d72d *man/interpret_rope.Rd eaecc96fd2b31c3482513d32483e78cc *man/interpret_vif.Rd d020214611d356e09d1a585debb25c0a *man/is_effectsize_name.Rd dcd6b1ea701967d2a39d5f3fa9dc759e *man/odds_to_probs.Rd b8dc4236d06d8992feef3ae93759db3e *man/oddsratio_to_riskratio.Rd dc45b71eccc3ae1b8a6ca4716076e9c3 *man/phi.Rd bd217e38bed4b54de32b81429f4efc3f *man/print.effectsize_table.Rd dc97cb038e378e2cc96cad32955d7581 *man/rank_biserial.Rd 6269933a9c3721efb301e4dd0ecfd0e0 *man/reexports.Rd 2074e61fb77948aff7138470ad73ebc9 *man/rules.Rd 07d87f0360ffd4fb5c81579a1afd3726 *man/sd_pooled.Rd 9835fef4e63a880b7e5c46da762155c4 *man/standardize.default.Rd 688dc4cd4083450f5fc736ba3729ff5c *man/standardize_info.Rd 416fb810c0de9d595a5365d0f4d7517e *man/standardize_parameters.Rd 08397915523916a51aec820fa6b93bec *man/t_to_r.Rd dbd9bab057163c01c320d0b9d3bb9620 *tests/spelling.R 89df14737372bc2c692782993b96dd08 *tests/testthat.R 0bcd8594ce929f851ca09d040d302c9c *tests/testthat/test-convert_between.R 5b7387a54bd382d4bda49d5f98d2e799 *tests/testthat/test-convert_statistic.R 0f9de7d0d197ba76df7a1d7ee3a22529 *tests/testthat/test-effectsize.R 7c5d49b0e516108fc2a68c3bce873495 *tests/testthat/test-equivalence_test.R 4643711a29b5fb543e1e8df697da30f1 *tests/testthat/test-eta_squared.R 00d73c3fe6b29e42de0e008c42c1a987 *tests/testthat/test-eta_squared_posterior.R 82a18efe6a5dbfb631d9f7baf0b32613 *tests/testthat/test-format_standardize.R 5694a9326313560d47e2873e1d378777 *tests/testthat/test-helpers.R 07f58cda18ca41be187ac90aebf0d8b6 *tests/testthat/test-interpret.R 8964c3752a560f719c2b901f18b01e46 *tests/testthat/test-printing.R 80787079a89d1b1c21077a980f173c35 *tests/testthat/test-rankES.R 9ae76bfb4d1a76d6a4ecc01820d72d8c *tests/testthat/test-sd_pooled.R 1c685528903d7414f2a08c96fec5fb95 *tests/testthat/test-standardize_models.R 8f31a009ff61e6f609dc12172b092d83 *tests/testthat/test-standardize_parameters.R 5594d9ca66e7adb19eb6c18d9ae732f6 *tests/testthat/test-standardized_differences.R e2b2c3885b988f20cf57ea0ab4f84e65 *tests/testthat/test-xtab.R b96ef4200c7d361783276c82e5012dfb *vignettes/anovaES.Rmd 9ca941f5f2faa90c7d7c0729e22bc376 *vignettes/apa.csl 36a16877046b89c0a4a017652ee80a27 *vignettes/bayesian_models.Rmd 6eec2032057159a0bfe0df4c60f9887e *vignettes/bibliography.bib 1901ee48abff7feb13f4255e86f0442f *vignettes/convert.Rmd ce06740dece0d87994d4921bc444fcc6 *vignettes/effectsize.Rmd 7c6e4b8d6ff953e99f7989e93f122fdc *vignettes/effectsize_API.Rmd 307fe86d0948a781de24e71ce66d419a *vignettes/from_test_statistics.Rmd c4d496c4f99f73bad5f61d2107d78096 *vignettes/interpret.Rmd a89fbb3a41364dddcd507117c033a29b *vignettes/simple_htests.Rmd 499f96481c20c9174a1a2bfb6f02fb1a *vignettes/standardize_parameters.Rmd effectsize/NEWS.md0000644000175000017500000003476014174206665013656 0ustar nileshnilesh# effectsize 0.6.0.1 *This is a patch release.* ## Bug fixes - `interpret.performance_lavaan()` now works without attaching `effectsize` ( #410 ). - `eta_squared()` now fully support multi-variate `car` ANOVAs (class `Anova.mlm`; #406 ). # effectsize 0.6.0 ## Breaking Changes - `pearsons_c()` effect size column name changed to `Pearsons_c` for consistency. ## New features ### New API See [*Support functions for model extensions* vignette](https://easystats.github.io/effectsize/articles/effectsize_API.html). ### Other features - `eta_squared()` family now supports `afex::mixed()` models. - `cles()` for estimating common language effect sizes. - `rb_to_cles()` for converting rank-biserial correlation to Probability of superiority. ## Changes - `effectsize()` for `BayesFactor` objects returns the same standardized output as for `htest`. ## Bug fixes - `eta_squared()` for MLM return effect sizes in the correct order of the responses. - `eta_squared()` family no longer fails when CIs fail due to non-finite *F*s / degrees of freedom. - `standardize()` for multivariate models standardizes the (multivariate) response. - `standardize()` for models with offsets standardizes offset variables according to `include_response` and `two_sd` ( #396 ). - `eta_squared()`: fixed a bug that caused `afex_aov` models with more than 2 within-subject factors to return incorrect effect sizes for the lower level factors ( #389 ). # effectsize 0.5.0 ## Breaking Changes - `cramers_v()` correctly does not work with 1-dimentional tables (for goodness-of-fit tests). - `interpret_d()`, `interpret_g()`, and `interpret_delta()` are now `interpret_cohens_d()`, `interpret_hedges_g()`, and `interpret_glass_delta()`. - `interpret_parameters()` was removed. Use `interpret_r()` instead (with caution!). - Phi, Cohen's *w*, Cramer's *V*, ANOVA effect sizes, rank Epsilon squared, Kendall's *W* - CIs default to 95% one-sided CIs (`alternative = "greater"`). (To restore previous behavior, set `ci = .9, alternative = "two.sided"`.) - `adjust()`, `change_scale()`, `normalize()`, `ranktransform()`, `standardize()` (data), and `unstandardize()` have moved to the new [`{datawizard}`](https://easystats.github.io/datawizard/) package! ## New features - `pearsons_c()` (and `chisq_to_pearsons_c()`) for estimating Pearson's contingency coefficient. - `interpret_vif()` for interpretation of *variance inflation factors*. - `oddsratio_to_riskratio()` can now convert OR coefficients to RR coefficients from a logistic GLM(M). - All effect-size functions gain an `alternative` argument which can be used to make one- or two-sided CIs. - `interpret()` now accepts as input the results from `cohens_d()`, `eta_squared()`, `rank_biserial()`, etc. - `interpret_pd()` for the interpretation of the [*Probability of Direction*](https://easystats.github.io/bayestestR/reference/p_direction.html). ## Bug fixes - `kendalls_w()` CIs now correctly bootstrap samples from the raw data (previously the rank-transformed data was sampled from). - `cohens_d()`, `sd_pooled()` and `rank_biserial()` now properly respect when `y` is a grouping character vector. - `effectsize()` for Chi-squared test of goodness-of-fit now correctly respects non-uniform expected probabilities ( #352 ). ## Changes - `interpret_bf()` now accepts *`log(BF)`* as input. # effectsize 0.4.5 ## New features - `eta_squared()` family now indicate the type of sum-of-squares used. - `rank_biserial()` estimates CIs using the normal approximation (previously used bootstrapping). - `hedges_g()` now used exact bias correction (thanks to @mdelacre for the suggestion!) - `glass_delta()` now estimates CIs using the NCP method based on Algina et al (2006). ## Bug fixes - `eta_squared()` family returns correctly returns the type 2/3 effect sizes for mixed ANOVAs fit with `afex`. - `cohens_d()` family now correctly deals with missing factor levels ( #318 ) - `cohens_d()` / `hedges_g()` minor fix for CI with unequal variances. ## Changes - `mad_pooled()` (the robust version of `sd_pooled()`) now correctly pools the the two samples. # effectsize 0.4.4-1 ## New features - `standardize_parameters()` + `eta_sqaured()` support `tidymodels` (when that the underlying model is supported; #311 ). - `cohens_d()` family now supports `Pairs()` objects as input. - `standardize_parameters()` gains the `include_response` argument (default to `TRUE`) ( #309 ). ## Bug fixes - `kendalls_w()` now actually returns correct effect size. Previous estimates were incorrect, and based on transposing the groups and blocks. # effectsize 0.4.4 `effectsize` now supports `R >= 3.4`. ## New features - `standardize_parameters()` now supports bootstrapped estimates (from `parameters::bootstrap_model()` and `parameters::bootstrap_parameters()`). - `unstandardize()` which will reverse the effects of `standardize()`. - `interpret_kendalls_w()` to interpret Kendall's coefficient of concordance. - `eta_squared()` family of functions can now also return effect sizes for the intercept by setting `include_intercept = TRUE` ( #156 ). ## Bug fixes - `standardize()` can now deal with dates ( #300 ). # effectsize 0.4.3 ## Breaking Changes - `oddsratio()` and `riskratio()` - order of groups has been changed (the *first* groups is now the **treatment group**, and the *second* group is the **control group**), so that effect sizes are given as *treatment over control* (treatment / control) (previously was reversed). This is done to be consistent with other functions in R and in `effectsize`. ## New features - `cohens_h()` effect size for comparing two independent proportions. - `rank_biserial()`, `cliffs_delta()`, `rank_epsilon_squared()` and `kendalls_w()` functions for effect sizes for rank-based tests. - `adjust()` gains `keep_intercept` argument to keep the intercept. - `eta_squared()` family of functions supports `Anova.mlm` objects (from the `car` package). - `effectsize()`: - supports Cohen's *g* for McNemar's test. - Extracts OR from Fisher's Exact Test in the 2x2 case. - `eta2_to_f2()` / `f2_to_eta2()` to convert between two types of effect sizes for ANOVA ( #240 ). - `cohens_d()` family of functions gain `mu` argument. ## Bug fixes - `adjust()` properly works when `multilevel = TRUE`. - `cohens_d()` family / `sd_pooled()` now properly fails when given a missing column name. ## Changes - `effectsize()` for `htest` objects now tries first to extract the data used for testing, and computed the effect size directly on that data. - `cohens_d()` family / `sd_pooled()` now respect any transformations (e.g. `I(log(x) - 3) ~ factor(y)`) in a passed formula. - `eta_squared()` family of functions gains a `verbose` argument. - `verbose` argument more strictly respected. - `glass_delta()` returns CIs based on the bootstrap. # effectsize 0.4.1 ## Breaking Changes - `cohens_d()` and `glass_delta()`: The `correction` argument has been deprecated, in favor of it being correctly implemented in `hedges_g()` ( #222 ). - `eta_squared_posterior()` no longer uses `car::Anova()` by default. ## New features - `effectsize()` gains `type = ` argument for specifying which effect size to return. - `eta_squared_posterior()` can return a generalized Eta squared. - `oddsratio()` and `riskratio()` functions for 2-by-2 contingency tables. - `standardize()` gains support for `mediation::mediate()` models. - `eta_squared()` family available for `manova` objects. ## Changes - `eta_squared()` family of functions returns non-partial effect size for one-way between subjects design (#180). ## Bug fixes - `hedges_g()` correctly implements the available bias correction methods ( #222 ). - Fixed width of CI for Cohen's *d* and Hedges' *g* when using *non*-pooled SD. # effectsize 0.4.0 ## Breaking Changes - `standardize_parameters()` for multi-component models (such as zero-inflated) now returns the unstandardized parameters in some cases where standardization is not possible (previously returned `NA`s). - Column name changes: - `eta_squared()` / `F_to_eta2` families of function now has the `Eta2` format, where previously was `Eta_Sq`. - `cramers_v` is now `Cramers_v` ## New features - `effectsize()` added support for `BayesFactor` objects (Cohen's *d*, Cramer's *v*, and *r*). - `cohens_g()` effect size for paired contingency tables. - Generalized Eta Squared now available via `eta_squared(generalized = ...)`. - `eta_squared()`, `omega_squared()` and `epsilon_squared()` fully support `aovlist`, `afex_aov` and `mlm` (or `maov`) objects. - `standardize_parameters()` can now return Odds ratios / IRRs (or any exponentiated parameter) by setting `exponentiate = TRUE`. - Added `cohens_f_squared()` and `F_to_f2()` for Cohen's *f*-squared. - `cohens_f()` / `cohens_f_squared()`can be used to estimate Cohen's *f* for the R-squared change between two models. - `standardize()` and `standardize_info()` work with weighted models / data ( #82 ). - Added `hardlyworking` (simulated) dataset, for use in examples. - `interpret_*` ( #131 ): - `interpret_omega_squared()` added `"cohen1992"` rule. - `interpret_p()` added *Redefine statistical significance* rules. - `oddsratio_to_riskratio()` for converting OR to RR. ## Changes - CIs for Omega-/Epsilon-squared and Adjusted Phi/Cramer's V return 0s instead of negative values. - `standardize()` for data frames gains the `remove_na` argument for dealing with `NA`s ( #147 ). - `standardize()` and `standardize_info()` now (and by extension, `standardize_parameters()`) respect the weights in weighted models when standardizing ( #82 ). - Internal changes to `standardize_parameters()` (reducing co-dependency with `parameters`) - argument `parameters` has been dropped. ## Bug fixes - `ranktransform(sign = TURE)` correctly (doesn't) deal with zeros. - `effectsize()` for `htest` works with Spearman and Kendall correlations ( #165 ). - `cramers_v()` and `phi()` now work with goodness-of-fit data ( #158 ) - `standardize_parameters()` for post-hoc correctly standardizes transformed outcome. - Setting `two_sd = TRUE` in `standardize()` and `standardize_parameters()` (correctly) on uses 2-SDs of the predictors (and not the response). - `standardize_info()` / `standardize_parameters(method = "posthoc")` work for zero-inflated models ( #135 ) - `standardize_info(include_pseudo = TRUE)` / `standardize_parameters(method = "pseudo")` are less sensitive in detecting between-group variation of within-group variables. - `interpret_oddsratio()` correctly treats extremely small odds the same as treats extremely large ones. # effectsize 0.3.3 ## New features - `standardize_parameters(method = "pseudo")` returns pseudo-standardized coefficients for (G)LMM models. - `d_to_common_language()` for common language measures of standardized differences (a-la Cohen's d). ## Changes - `r_to_odds()` family is now deprecated in favor of `r_to_oddsratio()`. - `interpret_odds()` is now deprecated in favor of `interpret_oddsratio()` ## Bug fixes - `phi()` and `cramers_v()` did not respect the CI argument ( #111 ). - `standardize()` / `standardize_parameters()` properly deal with transformed data in the model formula ( #113 ). - `odds_to_probs()` was mis-treating impossible odds (NEVER TELL ME THE ODDS! #123 ) # effectsize 0.3.2 ## New features - `eta_squared_posterior()` for estimating Eta Squared for Bayesian models. - `eta_squared()`, `omega_squared()` and `epsilon_squared()` now works with - `ols` / `rms` models. - `effectsize()` for class `htest` supports `oneway.test(...)`. ## Bug fixes - Fix minor miss-calculation of Chi-squared for 2*2 table with small samples ( #102 ). - Fixed miss-calculation of signed rank in `ranktransform()` ( #87 ). - Fixed bug in `standardize()` for standard objects with non-standard class-attributes (like vectors of class `haven_labelled` or `vctrs_vctr`). - Fix `effectsize()` for one sample `t.test(...)` ( #95 ; thanks to pull request by @mutlusun ) # effectsize 0.3.1 ## New features - `standardize_parameters()` now returns CIs ( #72 ) - `eta_squared()`, `omega_squared()` and `epsilon_squared()` now works with - `gam` models. - `afex` models. - `lme` and `anova.lme` objects. - New function `equivalence_test()` for effect sizes. - New plotting methods in the `see` package. # effectsize 0.3.0 ## New features - New general purpose `effectsize()` function. - Effectsize for differences have CI methods, and return a data frame. - Effectsize for ANOVA all have CI methods, and none are based on bootstrapping. - New effect sizes for contingency tables (`phi()` and `cramers_v()`). - `chisq_to_phi()` / `cramers_v()` functions now support CIs (via the ncp method), and return a data frame. - `F_to_eta2()` family of functions now support CIs (via the ncp method), and return a data frame. - `t_to_d()` and `t_to_r()` now support CIs (via the ncp method), and return a data frame. - `standardize()` for model-objects has a default-method, which usually accepts all models. Exception for model-objects that do not work will be added if missing. - `standardize.data.frame()` gets `append` and `suffix` arguments, to add (instead of replace) standardized variables to the returned data frame. - `eta_squared()`, `omega_squared()` and `epsilon_squared()` now works - output from `parameters::model_parameters()`. - `mlm` models. ## Bug fixes - Fix `cohens_d()`'s dealing with formula input (#44). - `sd_pooled()` now returns the... pooled sd (#44). ## Changes - In `t_to_d()`, argument `pooled` is now `paired`. # effectsize 0.2.0 ## Bug fixes - `standardize.data.frame()` did not work when variables had missing values. - Fixed wrong computation in `standardize()` when `two_sd = TRUE`. - Fixed bug with missing column names in `standardize_parameters()` for models with different components (like count and zero-inflation). # effectsize 0.1.1 ## Changes - News are hidden in an air of mystery... # effectsize 0.1.0 ## New features - `standardize_parameters()` and `standardize()` now support models from packages *brglm*, *brglm2*, *mixor*, *fixest*, *cgam*, *cplm*, *cglm*, *glmmadmb* and *complmrob*. ## Bug fixes - Fix CRAN check issues. effectsize/DESCRIPTION0000644000175000017500000000707114174255623014257 0ustar nileshnileshType: Package Package: effectsize Title: Indices of Effect Size and Standardized Parameters Version: 0.6.0.1 Authors@R: c(person(given = "Mattan S.", family = "Ben-Shachar", role = c("aut", "cre"), email = "matanshm@post.bgu.ac.il", comment = c(ORCID = "0000-0002-4287-4801", Twitter = "@mattansb")), person(given = "Dominique", family = "Makowski", role = "aut", email = "dom.makowski@gmail.com", comment = c(ORCID = "0000-0001-5375-9967", Twitter = "@Dom_Makowski")), person(given = "Daniel", family = "Lüdecke", role = "aut", email = "d.luedecke@uke.de", comment = c(ORCID = "0000-0002-8895-3206", Twitter = "@strengejacke")), person(given = "Indrajeet", family = "Patil", role = "ctb", email = "patilindrajeet.science@gmail.com", comment = c(ORCID = "0000-0003-1995-6531", Twitter = "@patilindrajeets")), person(given = "Brenton M.", family = "Wiernik", role = "ctb", email = "brenton@wiernik.org", comment = c(ORCID = "0000-0001-9560-6336", Twitter = "@bmwiernik")), person(given = "Ken", family = "Kelley", role = "ctb"), person(given = "David", family = "Stanley", role = "ctb"), person(given = "Jessica", family = "Burnett", role = "rev", email = "jburnett@usgs.gov", comment = c(ORCID = "0000-0002-0896-5099")), person(given = "Johannes", family = "Karreth", role = "rev", email = "jkarreth@ursinus.edu", comment = c(ORCID = "0000-0003-4586-7153"))) Maintainer: Mattan S. Ben-Shachar Description: Provide utilities to work with indices of effect size and standardized parameters for a wide variety of models (see list of supported models using the function 'insight::supported_models()'), allowing computation of and conversion between indices such as Cohen's d, r, odds, etc. License: GPL-3 URL: https://easystats.github.io/effectsize/ BugReports: https://github.com/easystats/effectsize/issues/ Depends: R (>= 3.4) Imports: bayestestR (>= 0.11.5), insight (>= 0.15.0), parameters (>= 0.16.0), performance (>= 0.8.0), datawizard (>= 0.2.2), stats, utils Suggests: correlation (>= 0.6.1), see (>= 0.6.4), afex, BayesFactor, boot, brms, car, covr, emmeans, gamm4, knitr, lavaan, lm.beta, lme4, lmerTest, MASS, mediation, mgcv, pscl, rmarkdown, rms, rstanarm, rstantools, spelling, testthat, tidymodels VignetteBuilder: knitr Encoding: UTF-8 Language: en-US RoxygenNote: 7.1.2 Config/testthat/edition: 3 Config/testthat/parallel: true NeedsCompilation: no Packaged: 2022-01-26 09:28:38 UTC; matta Author: Mattan S. Ben-Shachar [aut, cre] (, @mattansb), Dominique Makowski [aut] (, @Dom_Makowski), Daniel Lüdecke [aut] (, @strengejacke), Indrajeet Patil [ctb] (, @patilindrajeets), Brenton M. Wiernik [ctb] (, @bmwiernik), Ken Kelley [ctb], David Stanley [ctb], Jessica Burnett [rev] (), Johannes Karreth [rev] () Repository: CRAN Date/Publication: 2022-01-26 14:32:51 UTC effectsize/README.md0000644000175000017500000002161014173772201014016 0ustar nileshnilesh # effectsize [![DOI](https://joss.theoj.org/papers/10.21105/joss.02815/status.svg/)](https://doi.org/10.21105/joss.02815) [![downloads](https://cranlogs.r-pkg.org/badges/effectsize)](https://cran.r-project.org/package=effectsize/) [![total](https://cranlogs.r-pkg.org/badges/grand-total/effectsize)](https://cran.r-project.org/package=effectsize/) [![status](https://tinyverse.netlify.com/badge/effectsize/)](https://CRAN.R-project.org/package=effectsize/) ***Significant is just not enough\!*** The goal of this package is to provide utilities to work with indices of effect size and standardized parameters, allowing computation and conversion of indices such as Cohen’s *d*, *r*, odds-ratios, etc. ## Installation [![CRAN](https://www.r-pkg.org/badges/version/effectsize)](https://cran.r-project.org/package=effectsize/) [![effectsize status badge](https://easystats.r-universe.dev/badges/effectsize/)](https://easystats.r-universe.dev/) [![R-check](https://github.com/easystats/effectsize/workflows/R-check/badge.svg/)](https://github.com/easystats/effectsize/actions/) [![pkgdown](https://github.com/easystats/effectsize/workflows/pkgdown/badge.svg/)](https://github.com/easystats/effectsize/actions/) [![Codecov test coverage](https://codecov.io/gh/easystats/effectsize/branch/main/graph/badge.svg/)](https://app.codecov.io/gh/easystats/effectsize?branch=main/) Run the following to install the stable release of **effectsize** from CRAN: ``` r install.packages("effectsize") ``` Or you can install the latest development version `0.6.0.1` from [*R-universe*](https://easystats.r-universe.dev): ``` r install.packages("effectsize", repos = "https://easystats.r-universe.dev/") ``` ## Documentation [![Documentation](https://img.shields.io/badge/documentation-effectsize-orange.svg?colorB=E91E63/)](https://easystats.github.io/effectsize/) [![Blog](https://img.shields.io/badge/blog-easystats-orange.svg?colorB=FF9800/)](https://easystats.github.io/blog/posts/) [![Features](https://img.shields.io/badge/features-effectsize-orange.svg?colorB=2196F3/)](https://easystats.github.io/effectsize/reference/index.html) Click on the buttons above to access the package [**documentation**](https://easystats.github.io/effectsize/) and the [**easystats blog**](https://easystats.github.io/blog/posts/), and check-out these vignettes: - **Effect Sizes** - [**Parameter and Model Standardization**](https://easystats.github.io/effectsize/articles/standardize_parameters.html) - [**ANOVA Effect Sizes**](https://easystats.github.io/effectsize/articles/anovaES.html) - [**Effect Sizes in Bayesian Models**](https://easystats.github.io/effectsize/articles/bayesian_models.html) - [**For Simple Hypothesis Tests**](https://easystats.github.io/effectsize/articles/simple_htests.html) - **Effect Sizes Conversion** - [**Between Effect Sizes**](https://easystats.github.io/effectsize/articles/convert.html) - [**Effect Size from Test Statistics**](https://easystats.github.io/effectsize/articles/from_test_statistics.html) - [**Automated Interpretation of Indices of Effect Size**](https://easystats.github.io/effectsize/articles/interpret.html) # Features This package is focused on indices of effect size. Check out the package website for [**a full list of features and functions** provided by `effectsize`](https://easystats.github.io/effectsize/reference/index.html). ``` r library(effectsize) ``` ## Effect Size Computation ### Standardized Differences (Cohen’s *d*, Hedges’ *g*, Glass’ *delta*) The package provides functions to compute indices of effect size. ``` r cohens_d(mpg ~ am, data = mtcars) ## Cohen's d | 95% CI ## -------------------------- ## -1.48 | [-2.27, -0.67] ## ## - Estimated using pooled SD. hedges_g(mpg ~ am, data = mtcars) ## Hedges' g | 95% CI ## -------------------------- ## -1.44 | [-2.21, -0.65] ## ## - Estimated using pooled SD. glass_delta(mpg ~ am, data = mtcars) ## Glass' delta | 95% CI ## ----------------------------- ## -1.17 | [-1.93, -0.39] ``` `effectsize` also provides effect sizes for *contingency tables*, *rank tests*, and more… ### ANOVAs (Eta2, Omega2, …) ``` r model <- aov(mpg ~ factor(gear), data = mtcars) eta_squared(model) ## # Effect Size for ANOVA ## ## Parameter | Eta2 | 95% CI ## ---------------------------------- ## factor(gear) | 0.43 | [0.18, 1.00] ## ## - One-sided CIs: upper bound fixed at (1). omega_squared(model) ## # Effect Size for ANOVA ## ## Parameter | Omega2 | 95% CI ## ------------------------------------ ## factor(gear) | 0.38 | [0.14, 1.00] ## ## - One-sided CIs: upper bound fixed at (1). epsilon_squared(model) ## # Effect Size for ANOVA ## ## Parameter | Epsilon2 | 95% CI ## -------------------------------------- ## factor(gear) | 0.39 | [0.14, 1.00] ## ## - One-sided CIs: upper bound fixed at (1). ``` And more… ### Regression Models (Standardized Parameters) Importantly, `effectsize` also provides [advanced methods](https://easystats.github.io/effectsize/articles/standardize_parameters.html) to compute standardized parameters for regression models. ``` r m <- lm(rating ~ complaints + privileges + advance, data = attitude) standardize_parameters(m) ## # Standardization method: refit ## ## Parameter | Coefficient (std.) | 95% CI ## ------------------------------------------------ ## (Intercept) | -9.57e-16 | [-0.22, 0.22] ## complaints | 0.85 | [ 0.58, 1.13] ## privileges | -0.04 | [-0.33, 0.24] ## advance | -0.02 | [-0.26, 0.22] ``` Also, models can be re-fit with standardized data: ``` r standardize(m) ## ## Call: ## lm(formula = rating ~ complaints + privileges + advance, data = data_std) ## ## Coefficients: ## (Intercept) complaints privileges advance ## -9.57e-16 8.55e-01 -4.35e-02 -2.19e-02 ``` ## Effect Size Conversion The package also provides ways of converting between different effect sizes. ``` r d_to_r(d = 0.2) ## [1] 0.0995 oddsratio_to_riskratio(2.6, p0 = 0.4) ## [1] 1.59 ``` And for recovering effect sizes from test statistics. ``` r F_to_d(15, df = 1, df_error = 60) ## d | 95% CI ## ------------------- ## 1.00 | [0.46, 1.53] F_to_r(15, df = 1, df_error = 60) ## r | 95% CI ## ------------------- ## 0.45 | [0.22, 0.61] F_to_eta2(15, df = 1, df_error = 60) ## Eta2 (partial) | 95% CI ## ----------------------------- ## 0.20 | [0.07, 1.00] ## ## - One-sided CIs: upper bound fixed at (1). ``` ## Effect Size Interpretation The package allows for an automated interpretation of different indices. ``` r interpret_r(r = 0.3) ## [1] "large" ## (Rules: funder2019) ``` Different sets of “rules of thumb” are implemented ([**guidelines are detailed here**](https://easystats.github.io/effectsize/articles/interpret.html)) and can be easily changed. ``` r interpret_cohens_d(d = 0.45, rules = "cohen1988") ## [1] "small" ## (Rules: cohen1988) interpret_cohens_d(d = 0.45, rules = "gignac2016") ## [1] "moderate" ## (Rules: gignac2016) ``` ### Citation In order to cite this package, please use the following citation: - Ben-Shachar M, Lüdecke D, Makowski D (2020). effectsize: Estimation of Effect Size Indices and Standardized Parameters. *Journal of Open Source Software*, *5*(56), 2815. doi: 10.21105/joss.02815 Corresponding BibTeX entry: @Article{, title = {{e}ffectsize: Estimation of Effect Size Indices and Standardized Parameters}, author = {Mattan S. Ben-Shachar and Daniel Lüdecke and Dominique Makowski}, year = {2020}, journal = {Journal of Open Source Software}, volume = {5}, number = {56}, pages = {2815}, publisher = {The Open Journal}, doi = {10.21105/joss.02815}, url = {https://doi.org/10.21105/joss.02815} } # Contributing and Support If you have any questions regarding the the functionality of the package, you may either contact us via email or also [file an issue](https://github.com/easystats/effectsize/issues/). Anyone wishing to contribute to the package by adding functions, features, or in another way, please follow [this guide](https://github.com/easystats/effectsize/blob/main/.github/CONTRIBUTING.md/) and our [code of conduct](https://github.com/easystats/effectsize/blob/main/.github/CODE_OF_CONDUCT.md/). effectsize/man/0000755000175000017500000000000014174160416013312 5ustar nileshnilesheffectsize/man/rules.Rd0000644000175000017500000000230714132466117014736 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret.R \name{rules} \alias{rules} \alias{is.rules} \title{Interpretation Grid} \usage{ rules(values, labels = NULL, name = NULL, right = TRUE) is.rules(x) } \arguments{ \item{values}{Vector of reference values (edges defining categories or critical values).} \item{labels}{Labels associated with each category. If \code{NULL}, will try to infer it from \code{values} (if it is a named vector or a list), otherwise, will return the breakpoints.} \item{name}{Name of the set of rules (stored as a 'rule_name' attribute).} \item{right}{logical, for threshold-type rules, indicating if the thresholds themselves should be included in the interval to the right (lower values) or in the interval to the left (higher values).} \item{x}{An arbitrary R object.} } \description{ Create a container for interpretation rules of thumb. Usually used in conjunction with \link{interpret}. } \examples{ rules(c(0.05), c("significant", "not significant"), right = FALSE) rules(c(0.2, 0.5, 0.8), c("small", "medium", "large")) rules(c("small" = 0.2, "medium" = 0.5), name = "Cohen's Rules") } \seealso{ interpret } effectsize/man/standardize_parameters.Rd0000644000175000017500000002434514170065645020350 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/standardize_parameters.R \name{standardize_parameters} \alias{standardize_parameters} \alias{standardize_posteriors} \title{Parameters standardization} \usage{ standardize_parameters( model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, parameters, ... ) standardize_posteriors( model, method = "refit", robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ... ) } \arguments{ \item{model}{A statistical model.} \item{method}{The method used for standardizing the parameters. Can be \code{"refit"} (default), \code{"posthoc"}, \code{"smart"}, \code{"basic"} or \code{"pseudo"}. See 'Details'.} \item{ci}{Confidence Interval (CI) level} \item{robust}{Logical, if \code{TRUE}, centering is done by subtracting the median from the variables and dividing it by the median absolute deviation (MAD). If \code{FALSE}, variables are standardized by subtracting the mean and dividing it by the standard deviation (SD).} \item{two_sd}{If \code{TRUE}, the variables are scaled by two times the deviation (SD or MAD depending on \code{robust}). This method can be useful to obtain model coefficients of continuous parameters comparable to coefficients related to binary predictors, when applied to \strong{the predictors} (not the outcome) (Gelman, 2008).} \item{include_response}{If \code{TRUE} (default), the response value will also be standardized. If \code{FALSE}, only the predictors will be standardized. For GLMs the response value will never be standardized (see \emph{Generalized Linear Models} section).} \item{verbose}{Toggle warnings and messages on or off.} \item{parameters}{Deprecated.} \item{...}{For \code{standardize_parameters()}, arguments passed to \link[parameters:model_parameters]{parameters::model_parameters}, such as: \itemize{ \item \code{ci_method}, \code{centrality} for Mixed models and Bayesian models... \item \code{exponentiate}, ... \item etc. }} } \value{ A data frame with the standardized parameters (\verb{Std_*}, depending on the model type) and their CIs (\code{CI_low} and \code{CI_high}). Where applicable, standard errors (SEs) are returned as an attribute (\code{attr(x, "standard_error")}). } \description{ Compute standardized model parameters (coefficients). } \section{Standardization Methods:}{ \itemize{ \item \strong{refit}: This method is based on a complete model re-fit with a standardized version of the data. Hence, this method is equal to standardizing the variables before fitting the model. It is the "purest" and the most accurate (Neter et al., 1989), but it is also the most computationally costly and long (especially for heavy models such as Bayesian models). This method is particularly recommended for complex models that include interactions or transformations (e.g., polynomial or spline terms). The \code{robust} (default to \code{FALSE}) argument enables a robust standardization of data, i.e., based on the \code{median} and \code{MAD} instead of the \code{mean} and \code{SD}. \strong{See \code{\link[=standardize]{standardize()}} for more details.} \itemize{ \item \strong{Note} that \code{standardize_parameters(method = "refit")} may not return the same results as fitting a model on data that has been standardized with \code{standardize()}; \code{standardize_parameters()} used the data used by the model fitting function, which might not be same data if there are missing values. see the \code{remove_na} argument in \code{standardize()}. } \item \strong{posthoc}: Post-hoc standardization of the parameters, aiming at emulating the results obtained by "refit" without refitting the model. The coefficients are divided by the standard deviation (or MAD if \code{robust}) of the outcome (which becomes their expression 'unit'). Then, the coefficients related to numeric variables are additionally multiplied by the standard deviation (or MAD if \code{robust}) of the related terms, so that they correspond to changes of 1 SD of the predictor (e.g., "A change in 1 SD of \code{x} is related to a change of 0.24 of the SD of \code{y}). This does not apply to binary variables or factors, so the coefficients are still related to changes in levels. This method is not accurate and tend to give aberrant results when interactions are specified. \item \strong{basic}: This method is similar to \code{method = "posthoc"}, but treats all variables as continuous: it also scales the coefficient by the standard deviation of model's matrix' parameter of factors levels (transformed to integers) or binary predictors. Although being inappropriate for these cases, this method is the one implemented by default in other software packages, such as \code{\link[lm.beta:lm.beta]{lm.beta::lm.beta()}}. \item \strong{smart} (Standardization of Model's parameters with Adjustment, Reconnaissance and Transformation - \emph{experimental}): Similar to \code{method = "posthoc"} in that it does not involve model refitting. The difference is that the SD (or MAD if \code{robust}) of the response is computed on the relevant section of the data. For instance, if a factor with 3 levels A (the intercept), B and C is entered as a predictor, the effect corresponding to B vs. A will be scaled by the variance of the response at the intercept only. As a results, the coefficients for effects of factors are similar to a Glass' delta. \item \strong{pseudo} (\emph{for 2-level (G)LMMs only}): In this (post-hoc) method, the response and the predictor are standardized based on the level of prediction (levels are detected with \code{\link[performance:check_heterogeneity_bias]{performance::check_heterogeneity_bias()}}): Predictors are standardized based on their SD at level of prediction (see also \code{\link[datawizard:demean]{datawizard::demean()}}); The outcome (in linear LMMs) is standardized based on a fitted random-intercept-model, where \code{sqrt(random-intercept-variance)} is used for level 2 predictors, and \code{sqrt(residual-variance)} is used for level 1 predictors (Hoffman 2015, page 342). A warning is given when a within-group varialbe is found to have access between-group variance. } } \section{Transformed Variables}{ When the model's formula contains transformations (e.g. \code{y ~ exp(X)}) \code{method = "refit"} will give different results compared to \code{method = "basic"} (\code{"posthoc"} and \code{"smart"} do not support such transformations): While \code{"refit"} standardizes the data \emph{prior} to the transformation (e.g. equivalent to \code{exp(scale(X))}), the \code{"basic"} method standardizes the transformed data (e.g. equivalent to \code{scale(exp(X))}). \cr\cr See the \emph{Transformed Variables} section in \code{\link[=standardize.default]{standardize.default()}} for more details on how different transformations are dealt with when \code{method = "refit"}. } \section{Confidence Intervals}{ The returned confidence intervals are re-scaled versions of the unstandardized confidence intervals, and not "true" confidence intervals of the standardized coefficients (cf. Jones & Waller, 2015). } \section{Generalized Linear Models}{ Standardization for generalized linear models (GLM, GLMM, etc) is done only with respect to the predictors (while the outcome remains as-is, unstandardized) - maintaining the interpretability of the coefficients (e.g., in a binomial model: the exponent of the standardized parameter is the OR of a change of 1 SD in the predictor, etc.) } \section{Dealing with Factors}{ \code{standardize(model)} or \code{standardize_parameters(model, method = "refit")} do \emph{not} standardized categorical predictors (i.e. factors) / their dummy-variables, which may be a different behaviour compared to other R packages (such as \pkg{lm.beta}) or other software packages (like SPSS). To mimic such behaviours, either use \code{standardize_parameters(model, method = "basic")} to obtain post-hoc standardized parameters, or standardize the data with \code{datawizard::standardize(data, force = TRUE)} \emph{before} fitting the model. } \examples{ library(effectsize) model <- lm(len ~ supp * dose, data = ToothGrowth) standardize_parameters(model, method = "refit") \donttest{ standardize_parameters(model, method = "posthoc") standardize_parameters(model, method = "smart") standardize_parameters(model, method = "basic") # Robust and 2 SD standardize_parameters(model, robust = TRUE) standardize_parameters(model, two_sd = TRUE) model <- glm(am ~ cyl * mpg, data = mtcars, family = "binomial") standardize_parameters(model, method = "refit") standardize_parameters(model, method = "posthoc") standardize_parameters(model, method = "basic", exponentiate = TRUE) } \donttest{ if (require("lme4")) { m <- lmer(mpg ~ cyl + am + vs + (1 | cyl), mtcars) standardize_parameters(m, method = "pseudo", ci_method = "satterthwaite") } \dontrun{ if (require("rstanarm")) { model <- stan_glm(rating ~ critical + privileges, data = attitude, refresh = 0) standardize_posteriors(model, method = "refit") standardize_posteriors(model, method = "posthoc") standardize_posteriors(model, method = "smart") head(standardize_posteriors(model, method = "basic")) } } } } \references{ \itemize{ \item Hoffman, L. (2015). Longitudinal analysis: Modeling within-person fluctuation and change. Routledge. \item Jones, J. A., & Waller, N. G. (2015). The normal-theory and asymptotic distribution-free (ADF) covariance matrix of standardized regression coefficients: theoretical extensions and finite sample behavior. Psychometrika, 80(2), 365-378. \item Neter, J., Wasserman, W., & Kutner, M. H. (1989). Applied linear regression models. \item Gelman, A. (2008). Scaling regression inputs by dividing by two standard deviations. Statistics in medicine, 27(15), 2865-2873. } } \seealso{ Other standardize: \code{\link{standardize.default}()}, \code{\link{standardize_info}()} Other effect size indices: \code{\link{cles}()}, \code{\link{cohens_d}()}, \code{\link{effectsize.BFBayesFactor}()}, \code{\link{eta_squared}()}, \code{\link{phi}()}, \code{\link{rank_biserial}()} } \concept{effect size indices} \concept{standardize} effectsize/man/interpret_rope.Rd0000644000175000017500000000252114170072560016640 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_rope.R \name{interpret_rope} \alias{interpret_rope} \title{Interpret Bayesian diagnostic indices} \usage{ interpret_rope(rope, ci = 0.9, rules = "default") } \arguments{ \item{rope}{Value or vector of percentages in ROPE.} \item{ci}{The Credible Interval (CI) probability, corresponding to the proportion of HDI, that was used. Can be \code{1} in the case of "full ROPE".} \item{rules}{A character string (see details) or a custom set of \code{\link[=rules]{rules()}}.} } \description{ Interpretation of Bayesian indices of percentage in ROPE. } \section{Rules}{ \itemize{ \item Default \itemize{ \item For CI < 1 \itemize{ \item \strong{Rope = 0} - Significant \item \strong{0 < Rope < 1} - Undecided \item \strong{Rope = 1} - Negligible } \item For CI = 1 \itemize{ \item \strong{Rope < 0.01} - Significant \item \strong{0.01 < Rope < 0.025} - Probably significant \item \strong{0.025 < Rope < 0.975} - Undecided \item \strong{0.975 < Rope < 0.99} - Probably negligible \item \strong{Rope > 0.99} - Negligible } } } } \examples{ interpret_rope(0, ci = 0.9) interpret_rope(c(0.005, 0.99), ci = 1) } \references{ \href{https://easystats.github.io/bayestestR/articles/guidelines.html}{BayestestR's reporting guidelines} } effectsize/man/cles.Rd0000644000175000017500000001070514170065645014536 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/common_language.R \name{cles} \alias{cles} \alias{common_language} \alias{cohens_u3} \alias{p_superiority} \alias{p_overlap} \title{Estimate Common Language Effect Sizes (CLES)} \usage{ cles( x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", parametric = TRUE, verbose = TRUE, iterations = 200, ... ) common_language( x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", parametric = TRUE, verbose = TRUE, iterations = 200, ... ) cohens_u3(...) p_superiority(...) p_overlap(...) } \arguments{ \item{x}{A formula, a numeric vector, or a character name of one in \code{data}.} \item{y}{A numeric vector, a grouping (character / factor) vector, a or a character name of one in \code{data}. Ignored if \code{x} is a formula.} \item{data}{An optional data frame containing the variables.} \item{mu}{a number indicating the true value of the mean (or difference in means if you are performing a two sample test).} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"two.sided"} (default, two-sided CI), \code{"greater"} or \code{"less"} (one-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{parametric}{Use parametric estimation (see \code{\link[=cohens_d]{cohens_d()}}) or non-parametric estimation (see \code{\link[=rank_biserial]{rank_biserial()}}).} \item{verbose}{Toggle warnings and messages on or off.} \item{iterations}{The number of bootstrap replicates for computing confidence intervals. Only applies when \code{ci} is not \code{NULL} and \code{parametric = FALSE}.} \item{...}{Arguments passed to or from other methods.} } \value{ A data frame containing the common language effect sizes (and optionally their CIs). } \description{ \code{cohens_u3()}, \code{p_superiority()}, and \code{p_overlap()} give only one of the CLESs. } \details{ These measures of effect size present group differences in probabilistic terms: \itemize{ \item \strong{Probability of superiority} is the probability that, when sampling an observation from each of the groups at random, that the observation from the second group will be larger than the sample from the first group. \item \strong{Cohen's U3} is the proportion of the second group that is smaller than the median of the first group. \item \strong{Overlap} (OVL) is the proportional overlap between the distributions. (When \code{parametric = FALSE}, \code{\link[bayestestR:overlap]{bayestestR::overlap()}} is used.) } For unequal group sizes, it is recommended to use the non-parametric based CLES (\code{parametric = FALSE}). } \section{Confidence Intervals (CIs)}{ For parametric CLES, the CIs are transformed CIs for Cohen's \emph{d} (\code{\link[=d_to_cles]{d_to_cles()}}). For non-parametric (\code{parametric = FALSE}) CLES, the CI of \emph{Pr(superiority)} is a transformed CI of the rank-biserial correlation (\code{\link[=rb_to_cles]{rb_to_cles()}}), while for Cohen's \emph{U3} and the Overlap coefficient the confidence intervals are bootstrapped (requires the \code{boot} package). } \examples{ cles(mpg ~ am, data = mtcars) set.seed(4) cles(mpg ~ am, data = mtcars, parametric = FALSE) \dontrun{ ## Individual CLES p_superiority(extra ~ group, data = sleep) cohens_u3(extra ~ group, data = sleep, parametric = FALSE) p_overlap(extra ~ group, data = sleep) } } \references{ \itemize{ \item Cohen, J. (1977). Statistical power analysis for the behavioral sciences. New York: Routledge. \item Reiser, B., & Faraggi, D. (1999). Confidence intervals for the overlapping coefficient: the normal equal variance case. Journal of the Royal Statistical Society, 48(3), 413-418. \item Ruscio, J. (2008). A probability-based measure of effect size: robustness to base rates and other factors. Psychological methods, 13(1), 19–30. } } \seealso{ \code{\link[=d_to_cles]{d_to_cles()}} \code{\link[=sd_pooled]{sd_pooled()}} Other effect size indices: \code{\link{cohens_d}()}, \code{\link{effectsize.BFBayesFactor}()}, \code{\link{eta_squared}()}, \code{\link{phi}()}, \code{\link{rank_biserial}()}, \code{\link{standardize_parameters}()} } \concept{effect size indices} effectsize/man/interpret_kendalls_w.Rd0000644000175000017500000000206214132466117020021 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_kendalls_w.R \name{interpret_kendalls_w} \alias{interpret_kendalls_w} \title{Interpret Kendall's coefficient of concordance} \usage{ interpret_kendalls_w(w, rules = "landis1977") } \arguments{ \item{w}{Value or vector of Kendall's coefficient of concordance.} \item{rules}{Can be \code{"landis1977"} (default) or a custom set of \code{\link[=rules]{rules()}}.} } \description{ Interpret Kendall's coefficient of concordance } \section{Rules}{ \itemize{ \item Landis & Koch (1977) (\code{"landis1977"}; default) \itemize{ \item \strong{0.00 <= w < 0.20} - Slight agreement \item \strong{0.20 <= w < 0.40} - Fair agreement \item \strong{0.40 <= w < 0.60} - Moderate agreement \item \strong{0.60 <= w < 0.80} - Substantial agreement \item \strong{w >= 0.80} - Almost perfect agreement } } } \references{ \itemize{ \item Landis, J. R., & Koch G. G. (1977). The measurement of observer agreement for categorical data. Biometrics, 33:159-74. } } effectsize/man/standardize.default.Rd0000644000175000017500000001063614174170437017546 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/standardize.models.R \name{standardize.default} \alias{standardize.default} \alias{standardize_models} \alias{standardize.models} \title{Re-fit a model with standardized data} \usage{ \method{standardize}{default}( x, robust = FALSE, two_sd = FALSE, weights = TRUE, verbose = TRUE, include_response = TRUE, ... ) } \arguments{ \item{x}{A statistical model.} \item{robust}{Logical, if \code{TRUE}, centering is done by subtracting the median from the variables and dividing it by the median absolute deviation (MAD). If \code{FALSE}, variables are standardized by subtracting the mean and dividing it by the standard deviation (SD).} \item{two_sd}{If \code{TRUE}, the variables are scaled by two times the deviation (SD or MAD depending on \code{robust}). This method can be useful to obtain model coefficients of continuous parameters comparable to coefficients related to binary predictors, when applied to \strong{the predictors} (not the outcome) (Gelman, 2008).} \item{weights}{If \code{TRUE} (default), a weighted-standardization is carried out.} \item{verbose}{Toggle warnings and messages on or off.} \item{include_response}{If \code{TRUE} (default), the response value will also be standardized. If \code{FALSE}, only the predictors will be standardized. \itemize{ \item Note that for GLMs and models with non-linear link functions, the response value will not be standardized, to make re-fitting the model work. \item If the model contains an \code{\link[stats:offset]{stats::offset()}}, the offset variable(s) will be standardized only if the response is standardized. If \code{two_sd = TRUE}, offsets are standardized by one-sd (similar to the response). \item (For \code{mediate} models, the \code{include_response} refers to the outcome in the y model; m model's response will always be standardized when possible). }} \item{...}{Arguments passed to or from other methods.} } \value{ A statistical model fitted on standardized data } \description{ Performs a standardization of data (z-scoring) using \code{\link[datawizard:standardize]{datawizard::standardize()}} and then re-fits the model to the standardized data. \cr\cr Standardization is done by completely refitting the model on the standardized data. Hence, this approach is equal to standardizing the variables \emph{before} fitting the model and will return a new model object. This method is particularly recommended for complex models that include interactions or transformations (e.g., polynomial or spline terms). The \code{robust} (default to \code{FALSE}) argument enables a robust standardization of data, based on the \code{median} and the \code{MAD} instead of the \code{mean} and the \code{SD}. } \section{Generalized Linear Models}{ Standardization for generalized linear models (GLM, GLMM, etc) is done only with respect to the predictors (while the outcome remains as-is, unstandardized) - maintaining the interpretability of the coefficients (e.g., in a binomial model: the exponent of the standardized parameter is the OR of a change of 1 SD in the predictor, etc.) } \section{Dealing with Factors}{ \code{standardize(model)} or \code{standardize_parameters(model, method = "refit")} do \emph{not} standardized categorical predictors (i.e. factors) / their dummy-variables, which may be a different behaviour compared to other R packages (such as \pkg{lm.beta}) or other software packages (like SPSS). To mimic such behaviours, either use \code{standardize_parameters(model, method = "basic")} to obtain post-hoc standardized parameters, or standardize the data with \code{datawizard::standardize(data, force = TRUE)} \emph{before} fitting the model. } \section{Transformed Variables}{ When the model's formula contains transformations (e.g. \code{y ~ exp(X)}) the transformation effectively takes place after standardization (e.g., \code{exp(scale(X))}). Since some transformations are undefined for none positive values, such as \code{log()} and \code{sqrt()}, the releven variables are shifted (post standardization) by \code{Z - min(Z) + 1} or \code{Z - min(Z)} (respectively). } \examples{ model <- lm(Infant.Mortality ~ Education * Fertility, data = swiss) coef(standardize(model)) } \seealso{ Other standardize: \code{\link{standardize_info}()}, \code{\link{standardize_parameters}()} } \concept{standardize} effectsize/man/chisq_to_phi.Rd0000644000175000017500000001332014170065645016255 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_stat_chisq.R \name{chisq_to_phi} \alias{chisq_to_phi} \alias{chisq_to_cohens_w} \alias{chisq_to_cramers_v} \alias{chisq_to_pearsons_c} \alias{phi_to_chisq} \title{Conversion Chi-Squared to Phi or Cramer's V} \usage{ chisq_to_phi( chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", adjust = FALSE, ... ) chisq_to_cohens_w( chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", adjust = FALSE, ... ) chisq_to_cramers_v( chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", adjust = FALSE, ... ) chisq_to_pearsons_c( chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", ... ) phi_to_chisq(phi, n, ...) } \arguments{ \item{chisq}{The Chi-squared statistic.} \item{n}{Total sample size.} \item{nrow, ncol}{The number of rows/columns in the contingency table (ignored for Phi when \code{adjust=FALSE} and \code{CI=NULL}).} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"greater"} (default) or \code{"less"} (one-sided CI), or \code{"two.sided"} (default, two-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{adjust}{Should the effect size be bias-corrected? Defaults to \code{FALSE}.} \item{...}{Arguments passed to or from other methods.} \item{phi}{The Phi statistic.} } \value{ A data frame with the effect size(s), and confidence interval(s). See \code{\link[=cramers_v]{cramers_v()}}. } \description{ Convert between Chi square (\eqn{\chi^2}), Cramer's V, phi (\eqn{\phi}), Cohen's \emph{w} and Pearson's \emph{C} for contingency tables or goodness of fit. } \details{ These functions use the following formulae: \cr \deqn{\phi = \sqrt{\chi^2 / n}}{phi = sqrt(\chi^2 / n)} \cr \deqn{Cramer's V = \phi / \sqrt{min(nrow,ncol)-1}}{Cramer's V = \phi / sqrt(min(nrow,ncol)-1)} \cr \deqn{Pearson's C = \sqrt{\chi^2 / (\chi^2 + n)}}{Pearson's C = sqrt(\chi^2 / (\chi^2 + n))} \cr\cr For adjusted versions, see Bergsma, 2013. } \note{ Cohen's \emph{w} is equivalent to \emph{Phi}. } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \examples{ contingency_table <- as.table(rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477))) # chisq.test(contingency_table) #> #> Pearson's Chi-squared test #> #> data: contingency_table #> X-squared = 41.234, df = 4, p-value = 2.405e-08 chisq_to_phi(41.234, n = sum(contingency_table), nrow = nrow(contingency_table), ncol = ncol(contingency_table) ) chisq_to_cramers_v(41.234, n = sum(contingency_table), nrow = nrow(contingency_table), ncol = ncol(contingency_table) ) chisq_to_pearsons_c(41.234, n = sum(contingency_table), nrow = nrow(contingency_table), ncol = ncol(contingency_table) ) } \references{ \itemize{ \item Cumming, G., & Finch, S. (2001). A primer on the understanding, use, and calculation of confidence intervals that are based on central and noncentral distributions. Educational and Psychological Measurement, 61(4), 532-574. \item Bergsma, W. (2013). A bias-correction for Cramer's V and Tschuprow's T. Journal of the Korean Statistical Society, 42(3), 323-328. } } \seealso{ Other effect size from test statistic: \code{\link{F_to_eta2}()}, \code{\link{t_to_d}()} } \concept{effect size from test statistic} effectsize/man/interpret_pd.Rd0000644000175000017500000000242614132466117016305 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_pd.R \name{interpret_pd} \alias{interpret_pd} \title{Interpret Probability of Direction (pd)} \usage{ interpret_pd(pd, rules = "default", ...) } \arguments{ \item{pd}{Value or vector of probabilities of direction.} \item{rules}{Can be \code{"default"}, \code{"makowski2019"} or a custom set of \code{\link[=rules]{rules()}}.} \item{...}{Not directly used.} } \description{ Interpret Probability of Direction (pd) } \section{Rules}{ \itemize{ \item Default (i.e., equivalent to p-values) \itemize{ \item \strong{pd <= 0.975} - not significant \item \strong{pd > 0.975} - significant } \item Makowski et al. (2019) (\code{"makowski2019"}) \itemize{ \item \strong{pd <= 0.95} - uncertain \item \strong{pd > 0.95} - possibly existing \item \strong{pd > 0.97} - likely existing \item \strong{pd > 0.99} - probably existing \item \strong{pd > 0.999} - certainly existing } } } \examples{ interpret_pd(.98) interpret_pd(c(.96, .99), rules = "makowski2019") } \references{ \itemize{ \item Makowski, D., Ben-Shachar, M. S., Chen, S. H., \& Lüdecke, D. (2019). Indices of effect existence and significance in the Bayesian framework. Frontiers in psychology, 10, 2767. } } effectsize/man/effectsize_deprecated.Rd0000644000175000017500000000152714170065645020121 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zzz_deprecated.R \name{effectsize_deprecated} \alias{effectsize_deprecated} \alias{interpret_d} \alias{interpret_g} \alias{interpret_delta} \alias{interpret_parameters} \title{Deprecated functions} \usage{ interpret_d(...) interpret_g(...) interpret_delta(...) interpret_parameters(...) } \arguments{ \item{...}{Arguments to the deprecated function.} } \description{ Deprecated functions } \details{ \itemize{ \item \code{interpret_d} is now \code{\link{interpret_cohens_d}}. \item \code{interpret_g} is now \code{\link{interpret_hedges_g}}. \item \code{interpret_delta} is now \code{\link{interpret_glass_delta}}. \item \code{interpret_parameters} for \emph{standardized parameters} was incorrect. Use \code{\link{interpret_r}} instead. } } effectsize/man/interpret_icc.Rd0000644000175000017500000000230114132466117016430 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_icc.R \name{interpret_icc} \alias{interpret_icc} \title{Interpret Intraclass Correlation Coefficient (ICC)} \usage{ interpret_icc(icc, rules = "koo2016", ...) } \arguments{ \item{icc}{Value or vector of Intraclass Correlation Coefficient (ICC) values.} \item{rules}{Can be \code{"koo2016"} (default) or custom set of \code{\link[=rules]{rules()}}.} \item{...}{Not used for now.} } \description{ The value of an ICC lies between 0 to 1, with 0 indicating no reliability among raters and 1 indicating perfect reliability. } \section{Rules}{ \itemize{ \item Koo (2016) (\code{"koo2016"}; default) \itemize{ \item \strong{ICC < 0.50} - Poor reliability \item \strong{0.5 <= ICC < 0.75} - Moderate reliability \item \strong{0.75 <= ICC < 0.9} - Good reliability \item **ICC >= 0.9 ** - Excellent reliability } } } \examples{ interpret_icc(0.6) interpret_icc(c(0.4, 0.8)) } \references{ \itemize{ \item Koo, T. K., \& Li, M. Y. (2016). A guideline of selecting and reporting intraclass correlation coefficients for reliability research. Journal of chiropractic medicine, 15(2), 155-163. } } effectsize/man/eta_squared.Rd0000644000175000017500000003220714170065645016106 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/eta_squared.R, R/eta_squared_posterior.R \name{eta_squared} \alias{eta_squared} \alias{omega_squared} \alias{epsilon_squared} \alias{cohens_f} \alias{cohens_f_squared} \alias{eta_squared_posterior} \title{Effect size for ANOVA} \usage{ eta_squared( model, partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ... ) omega_squared( model, partial = TRUE, ci = 0.95, alternative = "greater", verbose = TRUE, ... ) epsilon_squared( model, partial = TRUE, ci = 0.95, alternative = "greater", verbose = TRUE, ... ) cohens_f( model, partial = TRUE, ci = 0.95, alternative = "greater", squared = FALSE, verbose = TRUE, model2 = NULL, ... ) cohens_f_squared( model, partial = TRUE, ci = 0.95, alternative = "greater", squared = TRUE, verbose = TRUE, model2 = NULL, ... ) eta_squared_posterior( model, partial = TRUE, generalized = FALSE, ss_function = stats::anova, draws = 500, verbose = TRUE, ... ) } \arguments{ \item{model}{A model, ANOVA object, or the result of \code{parameters::model_parameters}.} \item{partial}{If \code{TRUE}, return partial indices.} \item{generalized}{If TRUE, returns generalized Eta Squared, assuming all variables are manipulated. Can also be a character vector of observed (non-manipulated) variables, in which case generalized Eta Squared is calculated taking these observed variables into account. For \code{afex_aov} model, when \code{generalized = TRUE}, the observed variables are extracted automatically from the fitted model, if they were provided then.} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"greater"} (default) or \code{"less"} (one-sided CI), or \code{"two.sided"} (default, two-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{verbose}{Toggle warnings and messages on or off.} \item{...}{Arguments passed to or from other methods. \itemize{ \item Can be \code{include_intercept = TRUE} to include the effect size for the intercept. \item For Bayesian models, arguments passed to \code{ss_function}. }} \item{squared}{Return Cohen's \emph{f} or Cohen's \emph{f}-squared?} \item{model2}{Optional second model for Cohen's f (/squared). If specified, returns the effect size for R-squared-change between the two models.} \item{ss_function}{For Bayesian models, the function used to extract sum-of-squares. Uses \code{\link[=anova]{anova()}} by default, but can also be \code{car::Anova()} for simple linear models.} \item{draws}{For Bayesian models, an integer indicating the number of draws from the posterior predictive distribution to return. Larger numbers take longer to run, but provide estimates that are more stable.} } \value{ A data frame with the effect size(s) between 0-1 (\code{Eta2}, \code{Epsilon2}, \code{Omega2}, \code{Cohens_f} or \code{Cohens_f2}, possibly with the \code{partial} or \code{generalized} suffix), and their CIs (\code{CI_low} and \code{CI_high}). \cr\cr For \code{eta_squared_posterior()}, a data frame containing the ppd of the Eta squared for each fixed effect, which can then be passed to \code{\link[bayestestR:describe_posterior]{bayestestR::describe_posterior()}} for summary stats. A data frame containing the effect size values and their confidence intervals. } \description{ Functions to compute effect size measures for ANOVAs, such as Eta- (\eqn{\eta}), Omega- (\eqn{\omega}) and Epsilon- (\eqn{\epsilon}) squared, and Cohen's f (or their partialled versions) for ANOVA tables. These indices represent an estimate of how much variance in the response variables is accounted for by the explanatory variable(s). \cr\cr When passing models, effect sizes are computed using the sums of squares obtained from \code{anova(model)} which might not always be appropriate. See details. } \details{ For \code{aov}, \code{aovlist} and \code{afex_aov} models, and for \code{anova} objects that provide Sums-of-Squares, the effect sizes are computed directly using Sums-of-Squares (for \code{mlm} / \code{maov} models, effect sizes are computed for each response separately). For all other model, effect sizes are approximated via test statistic conversion of the omnibus \emph{F} statistic provided by the appropriate \code{anova()} method (see \code{\link[=F_to_eta2]{F_to_eta2()}} for more details.) \subsection{Type of Sums of Squares}{ The sums of squares (or \emph{F} statistics) used for the computation of the effect sizes is based on those returned by \code{anova(model)} (whatever those may be - for \code{aov} and \code{aovlist} these are \emph{type-1} sums of squares; for \code{lmerMod} (and \code{lmerModLmerTest}) these are \emph{type-3} sums of squares). Make sure these are the sums of squares you are interested in; You might want to pass the result of \code{car::Anova(mode, type = 2)} or \code{type = 3} instead of the model itself, or use the \code{afex} package to fit ANOVA models. \cr\cr For type 3 sum of squares, it is generally recommended to fit models with \emph{\code{contr.sum} factor weights} and \emph{centered covariates}, for sensible results. See examples and the \code{afex} package. } \subsection{Un-Biased Estimate of Eta}{ Both \emph{\strong{Omega}} and \emph{\strong{Epsilon}} are unbiased estimators of the population's \emph{\strong{Eta}}, which is especially important is small samples. But which to choose? \cr\cr Though Omega is the more popular choice (Albers \& Lakens, 2018), Epsilon is analogous to adjusted R2 (Allen, 2017, p. 382), and has been found to be less biased (Carroll & Nordholm, 1975). \cr\cr (Note that for Omega- and Epsilon-squared it is possible to compute a negative number; even though this doesn't make any practical sense, it is recommended to report the negative number and not a 0.) } \subsection{Cohen's f}{ Cohen's f can take on values between zero, when the population means are all equal, and an indefinitely large number as standard deviation of means increases relative to the average standard deviation within each group. \cr\cr When comparing two models in a sequential regression analysis, Cohen's f for R-square change is the ratio between the increase in R-square and the percent of unexplained variance. \cr\cr Cohen has suggested that the values of 0.10, 0.25, and 0.40 represent small, medium, and large effect sizes, respectively. } \subsection{Eta Squared from Posterior Predictive Distribution}{ For Bayesian models (fit with \code{brms} or \code{rstanarm}), \code{eta_squared_posterior()} simulates data from the posterior predictive distribution (ppd) and for each simulation the Eta Squared is computed for the model's fixed effects. This means that the returned values are the population level effect size as implied by the posterior model (and not the effect size in the sample data). See \code{\link[rstantools:posterior_predict]{rstantools::posterior_predict()}} for more info. } } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \examples{ \donttest{ data(mtcars) mtcars$am_f <- factor(mtcars$am) mtcars$cyl_f <- factor(mtcars$cyl) model <- aov(mpg ~ am_f * cyl_f, data = mtcars) (eta2 <- eta_squared(model)) # More types: eta_squared(model, partial = FALSE) eta_squared(model, generalized = "cyl_f") omega_squared(model) epsilon_squared(model) cohens_f(model) if (require(see)) plot(eta2) model0 <- aov(mpg ~ am_f + cyl_f, data = mtcars) # no interaction cohens_f_squared(model0, model2 = model) ## Interpretation of effect sizes ## ------------------------------------- interpret_omega_squared(0.10, rules = "field2013") interpret_eta_squared(0.10, rules = "cohen1992") interpret_epsilon_squared(0.10, rules = "cohen1992") interpret(eta2, rules = "cohen1992") # Recommended: Type-3 effect sizes + effects coding # ------------------------------------------------- if (require(car, quietly = TRUE)) { contrasts(mtcars$am_f) <- contr.sum contrasts(mtcars$cyl_f) <- contr.sum model <- aov(mpg ~ am_f * cyl_f, data = mtcars) model_anova <- car::Anova(model, type = 3) eta_squared(model_anova) } # afex takes care of both type-3 effects and effects coding: if (require(afex)) { data(obk.long, package = "afex") model <- aov_car(value ~ treatment * gender + Error(id / (phase)), data = obk.long, observed = "gender" ) eta_squared(model) epsilon_squared(model) omega_squared(model) eta_squared(model, partial = FALSE) epsilon_squared(model, partial = FALSE) omega_squared(model, partial = FALSE) eta_squared(model, generalized = TRUE) # observed vars are pulled from the afex model. } ## Approx. effect sizes for mixed models ## ------------------------------------- if (require(lmerTest, quietly = TRUE)) { model <- lmer(mpg ~ am_f * cyl_f + (1 | vs), data = mtcars) omega_squared(model) } ## Bayesian Models (PPD) ## --------------------- \dontrun{ if (require(rstanarm) && require(bayestestR) && require(car)) { fit_bayes <- stan_glm(mpg ~ factor(cyl) * wt + qsec, data = mtcars, family = gaussian(), refresh = 0 ) es <- eta_squared_posterior(fit_bayes, ss_function = car::Anova, type = 3 ) bayestestR::describe_posterior(es) # compare to: fit_freq <- lm(mpg ~ factor(cyl) * wt + qsec, data = mtcars ) aov_table <- car::Anova(fit_freq, type = 3) eta_squared(aov_table) } } } } \references{ \itemize{ \item Albers, C., \& Lakens, D. (2018). When power analyses based on pilot data are biased: Inaccurate effect size estimators and follow-up bias. Journal of experimental social psychology, 74, 187-195. \item Allen, R. (2017). Statistics and Experimental Design for Psychologists: A Model Comparison Approach. World Scientific Publishing Company. \item Carroll, R. M., & Nordholm, L. A. (1975). Sampling Characteristics of Kelley's epsilon and Hays' omega. Educational and Psychological Measurement, 35(3), 541-554. \item Kelley, T. (1935) An unbiased correlation ratio measure. Proceedings of the National Academy of Sciences. 21(9). 554-559. \item Olejnik, S., & Algina, J. (2003). Generalized eta and omega squared statistics: measures of effect size for some common research designs. Psychological methods, 8(4), 434. \item Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis. Psychological Methods, 9, 164-182. } } \seealso{ \code{\link[=F_to_eta2]{F_to_eta2()}} Other effect size indices: \code{\link{cles}()}, \code{\link{cohens_d}()}, \code{\link{effectsize.BFBayesFactor}()}, \code{\link{phi}()}, \code{\link{rank_biserial}()}, \code{\link{standardize_parameters}()} } \concept{effect size indices} effectsize/man/phi.Rd0000644000175000017500000002125614170065645014373 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xtab.R \name{phi} \alias{phi} \alias{cohens_w} \alias{cramers_v} \alias{pearsons_c} \alias{oddsratio} \alias{riskratio} \alias{cohens_h} \alias{cohens_g} \title{Effect size for contingency tables} \usage{ phi(x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ...) cohens_w(x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ...) cramers_v(x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ...) pearsons_c( x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ... ) oddsratio(x, y = NULL, ci = 0.95, alternative = "two.sided", log = FALSE, ...) riskratio(x, y = NULL, ci = 0.95, alternative = "two.sided", log = FALSE, ...) cohens_h(x, y = NULL, ci = 0.95, alternative = "two.sided", ...) cohens_g(x, y = NULL, ci = 0.95, alternative = "two.sided", ...) } \arguments{ \item{x}{a numeric vector or matrix. \code{x} and \code{y} can also both be factors.} \item{y}{a numeric vector; ignored if \code{x} is a matrix. If \code{x} is a factor, \code{y} should be a factor of the same length.} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"greater"} (two-sided CI; default for Cramer's \emph{V}, phi (\eqn{\phi}), and Cohen's \emph{w}), \code{"two.sided"} (default for OR, RR, Cohen's \emph{h} and Cohen's \emph{g}) or \code{"less"} (one-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{adjust}{Should the effect size be bias-corrected? Defaults to \code{FALSE}.} \item{...}{Arguments passed to \code{\link[stats:chisq.test]{stats::chisq.test()}}, such as \code{p}. Ignored for \code{cohens_g()}.} \item{log}{Take in or output the log of the ratio (such as in logistic models).} } \value{ A data frame with the effect size (\code{Cramers_v}, \code{phi} (possibly with the suffix \verb{_adjusted}), \code{Odds_ratio}, \code{Risk_ratio} (possibly with the prefix \code{log_}), \code{Cohens_h}, or \code{Cohens_g}) and its CIs (\code{CI_low} and \code{CI_high}). } \description{ Compute Cramer's \emph{V}, phi (\eqn{\phi}), Cohen's \emph{w} (an alias of phi), Pearson's contingency coefficient, Odds ratios, Risk ratios, Cohen's \emph{h} and Cohen's \emph{g} for contingency tables or goodness-of-fit. See details. } \details{ Cramer's \emph{V}, phi (\eqn{\phi}) and Pearson's \emph{C} are effect sizes for tests of independence in 2D contingency tables. For 2-by-k tables, Cramer's \emph{V} and phi are identical, and are equal to the simple correlation between two dichotomous variables, ranging between 0 (no dependence) and 1 (perfect dependence). For larger tables, Cramer's \emph{V} or Pearson's \emph{C} should be used, as they are bounded between 0-1, whereas phi can be larger than 1 (upper bound is \verb{sqrt(min(nrow, ncol) - 1))}). \cr\cr For goodness-of-fit in 1D tables Pearson's \emph{C} or phi can be used. Phi has no upper bound (can be arbitrarily large, depending on the expected distribution), while Pearson's \emph{C} is bounded between 0-1. \cr\cr For 2-by-2 contingency tables, Odds ratios, Risk ratios and Cohen's \emph{h} can also be estimated. Note that these are computed with each \strong{column} representing the different groups, and the first column representing the treatment group and the second column baseline (or control). Effects are given as \code{treatment / control}. If you wish you use rows as groups you must pass a transposed table, or switch the \code{x} and \code{y} arguments. \cr\cr Cohen's \emph{g} is an effect size for dependent (paired) contingency tables ranging between 0 (perfect symmetry) and 0.5 (perfect asymmetry) (see \code{\link[stats:mcnemar.test]{stats::mcnemar.test()}}). } \section{Confidence Intervals for Cohen's g, OR, RR and Cohen's h}{ For Cohen's \emph{g}, confidence intervals are based on the proportion (\eqn{P = g + 0.5}) confidence intervals returned by \code{\link[stats:prop.test]{stats::prop.test()}} (minus 0.5), which give a good close approximation. \cr\cr For Odds ratios, Risk ratios and Cohen's \emph{h}, confidence intervals are estimated using the standard normal parametric method (see Katz et al., 1978; Szumilas, 2010). \cr\cr See \emph{Confidence (Compatibility) Intervals (CIs)}, \emph{CIs and Significance Tests}, and \emph{One-Sided CIs} sections for \emph{phi}, Cohen's \emph{w}, Cramer's \emph{V} and Pearson's \emph{C}. } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \examples{ M <- matrix(c(150, 100, 165, 130, 50, 65, 35, 10, 2, 55, 40, 25), nrow = 4, dimnames = list( Music = c("Pop", "Rock", "Jazz", "Classic"), Study = c("Psych", "Econ", "Law"))) M # Note that Phi is not bound to [0-1], but instead # the upper bound for phi is sqrt(min(nrow, ncol) - 1) phi(M) cramers_v(M) pearsons_c(M) ## 2-by-2 tables ## ------------- RCT <- matrix(c(71, 30, 50, 100), nrow = 2, byrow = TRUE, dimnames = list( Diagnosis = c("Sick", "Recovered"), Group = c("Treatment", "Control"))) RCT # note groups are COLUMNS oddsratio(RCT) oddsratio(RCT, alternative = "greater") riskratio(RCT) cohens_h(RCT) ## Dependent (Paired) Contingency Tables ## ------------------------------------- Performance <- matrix(c(794, 150, 86, 570), nrow = 2, dimnames = list( "1st Survey" = c("Approve", "Disapprove"), "2nd Survey" = c("Approve", "Disapprove"))) Performance cohens_g(Performance) } \references{ \itemize{ \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Katz, D. J. S. M., Baptista, J., Azen, S. P., & Pike, M. C. (1978). Obtaining confidence intervals for the risk ratio in cohort studies. Biometrics, 469-474. \item Szumilas, M. (2010). Explaining odds ratios. Journal of the Canadian academy of child and adolescent psychiatry, 19(3), 227. } } \seealso{ \code{\link[=chisq_to_phi]{chisq_to_phi()}} for details regarding estimation and CIs. Other effect size indices: \code{\link{cles}()}, \code{\link{cohens_d}()}, \code{\link{effectsize.BFBayesFactor}()}, \code{\link{eta_squared}()}, \code{\link{rank_biserial}()}, \code{\link{standardize_parameters}()} } \concept{effect size indices} effectsize/man/F_to_eta2.Rd0000644000175000017500000002263014170072560015404 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_stat_to_anova.R \name{F_to_eta2} \alias{F_to_eta2} \alias{t_to_eta2} \alias{F_to_epsilon2} \alias{t_to_epsilon2} \alias{F_to_eta2_adj} \alias{t_to_eta2_adj} \alias{F_to_omega2} \alias{t_to_omega2} \alias{F_to_f} \alias{t_to_f} \alias{F_to_f2} \alias{t_to_f2} \title{Convert test statistics (F, t) to indices of \strong{partial} variance explained (\strong{partial} Eta / Omega / Epsilon squared and Cohen's f)} \usage{ F_to_eta2(f, df, df_error, ci = 0.95, alternative = "greater", ...) t_to_eta2(t, df_error, ci = 0.95, alternative = "greater", ...) F_to_epsilon2(f, df, df_error, ci = 0.95, alternative = "greater", ...) t_to_epsilon2(t, df_error, ci = 0.95, alternative = "greater", ...) F_to_eta2_adj(f, df, df_error, ci = 0.95, alternative = "greater", ...) t_to_eta2_adj(t, df_error, ci = 0.95, alternative = "greater", ...) F_to_omega2(f, df, df_error, ci = 0.95, alternative = "greater", ...) t_to_omega2(t, df_error, ci = 0.95, alternative = "greater", ...) F_to_f( f, df, df_error, ci = 0.95, alternative = "greater", squared = FALSE, ... ) t_to_f(t, df_error, ci = 0.95, alternative = "greater", squared = FALSE, ...) F_to_f2( f, df, df_error, ci = 0.95, alternative = "greater", squared = TRUE, ... ) t_to_f2(t, df_error, ci = 0.95, alternative = "greater", squared = TRUE, ...) } \arguments{ \item{df, df_error}{Degrees of freedom of numerator or of the error estimate (i.e., the residuals).} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"greater"} (default) or \code{"less"} (one-sided CI), or \code{"two.sided"} (default, two-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{...}{Arguments passed to or from other methods.} \item{t, f}{The t or the F statistics.} \item{squared}{Return Cohen's \emph{f} or Cohen's \emph{f}-squared?} } \value{ A data frame with the effect size(s) between 0-1 (\code{Eta2_partial}, \code{Epsilon2_partial}, \code{Omega2_partial}, \code{Cohens_f_partial} or \code{Cohens_f2_partial}), and their CIs (\code{CI_low} and \code{CI_high}). (Note that for \eqn{\omega_p^2}{Omega2p} and \eqn{\epsilon_p^2}{Epsilon2p} it is possible to compute a negative number; even though this doesn't make any practical sense, it is recommended to report the negative number and not a 0). } \description{ These functions are convenience functions to convert F and t test statistics to \strong{partial} Eta- (\eqn{\eta}), Omega- (\eqn{\omega}) Epsilon- (\eqn{\epsilon}) squared (an alias for the adjusted Eta squared) and Cohen's f. These are useful in cases where the various Sum of Squares and Mean Squares are not easily available or their computation is not straightforward (e.g., in liner mixed models, contrasts, etc.). For test statistics derived from \code{lm} and \code{aov} models, these functions give exact results. For all other cases, they return close approximations. \cr See \href{https://easystats.github.io/effectsize/articles/from_test_statistics.html}{Effect Size from Test Statistics vignette.} } \details{ These functions use the following formulae: \cr \deqn{\eta_p^2 = \frac{F \times df_{num}}{F \times df_{num} + df_{den}}}{\eta^2_p = F * df1 / (F * df1 + df2)} \cr \deqn{\epsilon_p^2 = \frac{(F - 1) \times df_{num}}{F \times df_{num} + df_{den}}}{\epsilon^2_p = (F - 1) * df1 / (F * df1 + df2)} \cr \deqn{\omega_p^2 = \frac{(F - 1) \times df_{num}}{F \times df_{num} + df_{den} + 1}}{\omega^2_p=(F - 1) * df1 / (F * df1 + df2 + 1)} \cr \deqn{f_p = \sqrt{\frac{\eta_p^2}{1-\eta_p^2}}}{f = \eta^2 / (1 - \eta^2)} \cr\cr For \emph{t}, the conversion is based on the equality of \eqn{t^2 = F} when \eqn{df_{num}=1}{df1 = 1}. \subsection{Choosing an Un-Biased Estimate}{ Both Omega and Epsilon are unbiased estimators of the population Eta. But which to choose? Though Omega is the more popular choice, it should be noted that: \enumerate{ \item The formula given above for Omega is only an approximation for complex designs. \item Epsilon has been found to be less biased (Carroll & Nordholm, 1975). } } } \note{ Adjusted (partial) Eta-squared is an alias for (partial) Epsilon-squared. } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \examples{ \donttest{ if (require("afex")) { data(md_12.1) aov_ez("id", "rt", md_12.1, within = c("angle", "noise"), anova_table = list(correction = "none", es = "pes") ) } # compare to: (etas <- F_to_eta2( f = c(40.72, 33.77, 45.31), df = c(2, 1, 2), df_error = c(18, 9, 18) )) if (require(see)) plot(etas) if (require("lmerTest")) { # for the df_error fit <- lmer(extra ~ group + (1 | ID), sleep) # anova(fit) # #> Type III Analysis of Variance Table with Satterthwaite's method # #> Sum Sq Mean Sq NumDF DenDF F value Pr(>F) # #> group 12.482 12.482 1 9 16.501 0.002833 ** # #> --- # #> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 F_to_eta2(16.501, 1, 9) F_to_omega2(16.501, 1, 9) F_to_epsilon2(16.501, 1, 9) F_to_f(16.501, 1, 9) } ## Use with emmeans based contrasts ## -------------------------------- if (require(emmeans)) { warp.lm <- lm(breaks ~ wool * tension, data = warpbreaks) jt <- joint_tests(warp.lm, by = "wool") F_to_eta2(jt$F.ratio, jt$df1, jt$df2) } } } \references{ \itemize{ \item Albers, C., & Lakens, D. (2018). When power analyses based on pilot data are biased: Inaccurate effect size estimators and follow-up bias. Journal of experimental social psychology, 74, 187-195. \doi{10.31234/osf.io/b7z4q} \item Carroll, R. M., & Nordholm, L. A. (1975). Sampling Characteristics of Kelley's epsilon and Hays' omega. Educational and Psychological Measurement, 35(3), 541-554. \item Cumming, G., & Finch, S. (2001). A primer on the understanding, use, and calculation of confidence intervals that are based on central and noncentral distributions. Educational and Psychological Measurement, 61(4), 532-574. \item Friedman, H. (1982). Simplified determinations of statistical power, magnitude of effect and research sample sizes. Educational and Psychological Measurement, 42(2), 521-526. \doi{10.1177/001316448204200214} \item Mordkoff, J. T. (2019). A Simple Method for Removing Bias From a Popular Measure of Standardized Effect Size: Adjusted Partial Eta Squared. Advances in Methods and Practices in Psychological Science, 2(3), 228-232. \doi{10.1177/2515245919855053} \item Morey, R. D., Hoekstra, R., Rouder, J. N., Lee, M. D., & Wagenmakers, E. J. (2016). The fallacy of placing confidence in confidence intervals. Psychonomic bulletin & review, 23(1), 103-123. \item Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis. Psychological Methods, 9, 164-182. } } \seealso{ \code{\link[=eta_squared]{eta_squared()}} for more details. Other effect size from test statistic: \code{\link{chisq_to_phi}()}, \code{\link{t_to_d}()} } \concept{effect size from test statistic} effectsize/man/reexports.Rd0000644000175000017500000000151414174211516015633 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reexports.R \docType{import} \name{reexports} \alias{reexports} \alias{equivalence_test} \alias{standardize} \alias{ranktransform} \alias{adjust} \alias{change_scale} \alias{normalize} \alias{unstandardize} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{bayestestR}{\code{\link[bayestestR]{equivalence_test}}} \item{datawizard}{\code{\link[datawizard]{adjust}}, \code{\link[datawizard:data_rescale]{change_scale}}, \code{\link[datawizard]{normalize}}, \code{\link[datawizard]{ranktransform}}, \code{\link[datawizard]{standardize}}, \code{\link[datawizard:standardize]{unstandardize}}} }} effectsize/man/equivalence_test.effectsize_table.Rd0000644000175000017500000000755714170261701022447 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/equivalence_test.R \name{equivalence_test.effectsize_table} \alias{equivalence_test.effectsize_table} \title{Test for Practical Equivalence} \usage{ \method{equivalence_test}{effectsize_table}( x, range = "default", rule = c("classic", "cet", "bayes"), ... ) } \arguments{ \item{x}{An effect size table, such as returned by \code{\link[=cohens_d]{cohens_d()}}, \code{\link[=eta_squared]{eta_squared()}}, \code{\link[=F_to_r]{F_to_r()}}, etc.} \item{range}{The range of practical equivalence of an effect. For one-sides CIs, a single value can be proved for the lower / upper bound to test against (but see more details below). For two-sided CIs, a single value is duplicated to \code{c(-range, range)}. If \code{"default"}, will be set to \verb{[-.1, .1]}.} \item{rule}{How should acceptance and rejection be decided? See details.} \item{...}{Arguments passed to or from other methods.} } \value{ A data frame with the results of the equivalence test. } \description{ Perform a \strong{Test for Practical Equivalence} for indices of effect size. } \details{ The CIs used in the equivalence test are the ones in the provided effect size table. For results equivalent (ha!) to those that can be obtained using the TOST approach (e.g., Lakens, 2017), appropriate CIs should be extracted using the function used to make the effect size table (\code{cohens_d}, \code{eta_squared}, \code{F_to_r}, etc), with \code{alternative = "two.sided"}. See examples. \subsection{The Different Rules}{ \itemize{ \item \code{"classic"} - \strong{the classic method}: \itemize{ \item If the CI is completely within the ROPE - \emph{Accept H0} \item Else, if the CI does not contain 0 - \emph{Reject H0} \item Else - \emph{Undecided} } \item \code{"cet"} - \strong{conditional equivalence testing}: \itemize{ \item If the CI does not contain 0 - \emph{Reject H0} \item Else, If the CI is completely within the ROPE - \emph{Accept H0} \item Else - \emph{Undecided} } \item \code{"bayes"} - \strong{The Bayesian approach}, as put forth by Kruschke: \itemize{ \item If the CI does is completely outside the ROPE - \emph{Reject H0} \item Else, If the CI is completely within the ROPE - \emph{Accept H0} \item Else - \emph{Undecided} } } } } \examples{ \donttest{ model <- aov(mpg ~ hp + am * factor(cyl), data = mtcars) es <- eta_squared(model, ci = 0.9, alternative = "two.sided") equivalence_test(es, range = 0.30) # TOST RCT <- matrix(c(71, 101, 50, 100), nrow = 2) OR <- oddsratio(RCT, alternative = "greater") equivalence_test(OR, range = 1) ds <- t_to_d( t = c(0.45, -0.65, 7, -2.2, 2.25), df_error = c(675, 525, 2000, 900, 1875), ci = 0.9, alternative = "two.sided" # TOST ) # Can also plot if (require(see)) plot(equivalence_test(ds, range = 0.2)) if (require(see)) plot(equivalence_test(ds, range = 0.2, rule = "cet")) if (require(see)) plot(equivalence_test(ds, range = 0.2, rule = "bayes")) } } \references{ \itemize{ \item Campbell, H., & Gustafson, P. (2018). Conditional equivalence testing: An alternative remedy for publication bias. PLOS ONE, 13(4), e0195145. https://doi.org/10.1371/journal.pone.0195145 \item Kruschke, J. K. (2014). Doing Bayesian data analysis: A tutorial with R, JAGS, and Stan. Academic Press \item Kruschke, J. K. (2018). Rejecting or accepting parameter values in Bayesian estimation. Advances in Methods and Practices in Psychological Science, 1(2), 270-280. doi: 10.1177/2515245918771304 \item Lakens, D. (2017). Equivalence Tests: A Practical Primer for t Tests, Correlations, and Meta-Analyses. Social Psychological and Personality Science, 8(4), 355–362. https://doi.org/10.1177/1948550617697177 } } \seealso{ For more details, see \code{\link[bayestestR:equivalence_test]{bayestestR::equivalence_test()}}. } effectsize/man/interpret_cohens_d.Rd0000644000175000017500000000522614170065645017470 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_cohens_d.R \name{interpret_cohens_d} \alias{interpret_cohens_d} \alias{interpret_hedges_g} \alias{interpret_glass_delta} \title{Interpret standardized differences} \usage{ interpret_cohens_d(d, rules = "cohen1988", ...) interpret_hedges_g(g, rules = "cohen1988") interpret_glass_delta(delta, rules = "cohen1988") } \arguments{ \item{d, g, delta}{Value or vector of effect size values.} \item{rules}{Can be \code{"cohen1988"} (default), \code{"gignac2016"}, \code{"sawilowsky2009"}, \code{"lovakov2021"} or a custom set of \code{\link[=rules]{rules()}}.} \item{...}{Not directly used.} } \description{ Interpretation of standardized differences using different sets of rules of thumb. } \section{Rules}{ Rules apply to equally to positive and negative \emph{d} (i.e., they are given as absolute values). \itemize{ \item Cohen (1988) (\code{"cohen1988"}; default) \itemize{ \item \strong{d < 0.2} - Very small \item \strong{0.2 <= d < 0.5} - Small \item \strong{0.5 <= d < 0.8} - Medium \item \strong{d >= 0.8} - Large } \item Sawilowsky (2009) (\code{"sawilowsky2009"}) \itemize{ \item \strong{d < 0.1} - Tiny \item \strong{0.1 <= d < 0.2} - Very small \item \strong{0.2 <= d < 0.5} - Small \item \strong{0.5 <= d < 0.8} - Medium \item \strong{0.8 <= d < 1.2} - Large \item \strong{1.2 <= d < 2} - Very large \item \strong{d >= 2} - Huge } \item Lovakov & Agadullina (2021) (\code{"lovakov2021"}) \itemize{ \item \strong{d < 0.15} - Very small \item \strong{0.15 <= d < 0.36} - Small \item \strong{0.36 <= d < 0.65} - Medium \item \strong{d >= 0.65} - Large } \item Gignac & Szodorai (2016) (\code{"gignac2016"}, based on the \code{\link[=d_to_r]{d_to_r()}} conversion, see \code{\link[=interpret_r]{interpret_r()}}) \itemize{ \item \strong{d < 0.2} - Very small \item \strong{0.2 <= d < 0.41} - Small \item \strong{0.41 <= d < 0.63} - Moderate \item \strong{d >= 0.63} - Large } } } \examples{ interpret_cohens_d(.02) interpret_cohens_d(c(.5, .02)) interpret_cohens_d(.3, rules = "lovakov2021") } \references{ \itemize{ \item Lovakov, A., & Agadullina, E. R. (2021). Empirically Derived Guidelines for Effect Size Interpretation in Social Psychology. European Journal of Social Psychology. \item Gignac, G. E., & Szodorai, E. T. (2016). Effect size guidelines for individual differences researchers. Personality and individual differences, 102, 74-78. \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Sawilowsky, S. S. (2009). New effect size rules of thumb. } } effectsize/man/interpret_vif.Rd0000644000175000017500000000124114170065645016463 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_vif.R \name{interpret_vif} \alias{interpret_vif} \title{Interpret the Variance Inflation Factor (VIF)} \usage{ interpret_vif(vif, rules = "default") } \arguments{ \item{vif}{Value or vector of VIFs.} \item{rules}{Can be \code{"default"} or a custom set of \code{\link[=rules]{rules()}}.} } \description{ Interpret VIF index of multicollinearity. } \section{Rules}{ \itemize{ \item Default \itemize{ \item \strong{VIF < 5} - Low \item \strong{5 <= VIF < 10} - Moderate \item \strong{VIF >= 10} - High } } } \examples{ interpret_vif(c(1.4, 30.4)) } effectsize/man/interpret_r.Rd0000644000175000017500000000605614132466117016146 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_r.R \name{interpret_r} \alias{interpret_r} \alias{interpret_phi} \alias{interpret_cramers_v} \alias{interpret_rank_biserial} \title{Interpret correlation coefficient} \usage{ interpret_r(r, rules = "funder2019") interpret_phi(r, rules = "funder2019") interpret_cramers_v(r, rules = "funder2019") interpret_rank_biserial(r, rules = "funder2019") } \arguments{ \item{r}{Value or vector of correlation coefficient.} \item{rules}{Can be \code{"funder2019"} (default), \code{"gignac2016"}, \code{"cohen1988"}, \code{"evans1996"}, \code{"lovakov2021"} or a custom set of \code{\link[=rules]{rules()}}.} } \description{ Interpret correlation coefficient } \note{ As \eqn{\phi}{\phi} can be larger than 1 - it is recommended to compute and interpret Cramer's \emph{V} instead. } \section{Rules}{ Rules apply positive and negative \emph{r} alike. \itemize{ \item Funder & Ozer (2019) (\code{"funder2019"}; default) \itemize{ \item \strong{r < 0.05} - Tiny \item \strong{0.05 <= r < 0.1} - Very small \item \strong{0.1 <= r < 0.2} - Small \item \strong{0.2 <= r < 0.3} - Medium \item \strong{0.3 <= r < 0.4} - Large \item \strong{r >= 0.4} - Very large } \item Gignac & Szodorai (2016) (\code{"gignac2016"}) \itemize{ \item \strong{r < 0.1} - Very small \item \strong{0.1 <= r < 0.2} - Small \item \strong{0.2 <= r < 0.3} - Moderate \item \strong{r >= 0.3} - Large } \item Cohen (1988) (\code{"cohen1988"}) \itemize{ \item \strong{r < 0.1} - Very small \item \strong{0.1 <= r < 0.3} - Small \item \strong{0.3 <= r < 0.5} - Moderate \item \strong{r >= 0.5} - Large } \item Lovakov & Agadullina (2021) (\code{"lovakov2021"}) \itemize{ \item \strong{r < 0.12} - Very small \item \strong{0.12 <= r < 0.24} - Small \item \strong{0.24 <= r < 0.41} - Moderate \item \strong{r >= 0.41} - Large } \item Evans (1996) (\code{"evans1996"}) \itemize{ \item \strong{r < 0.2} - Very weak \item \strong{0.2 <= r < 0.4} - Weak \item \strong{0.4 <= r < 0.6} - Moderate \item \strong{0.6 <= r < 0.8} - Strong \item \strong{r >= 0.8} - Very strong } } } \examples{ interpret_r(.015) interpret_r(c(.5, -.02)) interpret_r(.3, rules = "lovakov2021") } \references{ \itemize{ \item Lovakov, A., & Agadullina, E. R. (2021). Empirically Derived Guidelines for Effect Size Interpretation in Social Psychology. European Journal of Social Psychology. \item Funder, D. C., & Ozer, D. J. (2019). Evaluating effect size in psychological research: sense and nonsense. Advances in Methods and Practices in Psychological Science. \item Gignac, G. E., & Szodorai, E. T. (2016). Effect size guidelines for individual differences researchers. Personality and individual differences, 102, 74-78. \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Evans, J. D. (1996). Straightforward statistics for the behavioral sciences. Thomson Brooks/Cole Publishing Co. } } \seealso{ Page 88 of APA's 6th Edition. } effectsize/man/sd_pooled.Rd0000644000175000017500000000276414132466117015563 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sd_pooled.R \name{sd_pooled} \alias{sd_pooled} \alias{mad_pooled} \title{Pooled Standard Deviation} \usage{ sd_pooled(x, y = NULL, data = NULL, verbose = TRUE) mad_pooled(x, y = NULL, data = NULL, constant = 1.4826, verbose = TRUE) } \arguments{ \item{x}{A formula, a numeric vector, or a character name of one in \code{data}.} \item{y}{A numeric vector, a grouping (character / factor) vector, a or a character name of one in \code{data}. Ignored if \code{x} is a formula.} \item{data}{An optional data frame containing the variables.} \item{verbose}{Toggle warnings and messages on or off.} \item{constant}{scale factor.} } \value{ Numeric, the pooled standard deviation. } \description{ The Pooled Standard Deviation is a weighted average of standard deviations for two or more groups, \emph{assumed to have equal variance}. It represents the common deviation among the groups, around each of their respective means. } \details{ The standard version is calculated as: \deqn{\sqrt{\frac{\sum (x_i - \bar{x})^2}{n_1 + n_2 - 2}}}{sqrt(sum(c(x - mean(x), y - mean(y))^2) / (n1 + n2 - 2))} The robust version is calculated as: \deqn{1.4826 \times Median(|\left\{x - Median_x,\,y - Median_y\right\}|)}{mad(c(x - median(x), y - median(y)), constant = 1.4826)} } \examples{ sd_pooled(mpg ~ am, data = mtcars) mad_pooled(mtcars$mpg, factor(mtcars$am)) } \seealso{ \code{\link[=cohens_d]{cohens_d()}} } effectsize/man/eta2_to_f2.Rd0000644000175000017500000000322314170065645015531 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_between_anova.R \name{eta2_to_f2} \alias{eta2_to_f2} \alias{eta2_to_f} \alias{f2_to_eta2} \alias{f_to_eta2} \title{Convert between ANOVA effect sizes} \usage{ eta2_to_f2(es) eta2_to_f(es) f2_to_eta2(f2) f_to_eta2(f) } \arguments{ \item{es}{Any measure of variance explained such as Eta-, Epsilon-, Omega-, or R-Squared, partial or otherwise. See details.} \item{f, f2}{Cohen's \emph{f} or \emph{f}-squared.} } \description{ Convert between ANOVA effect sizes } \details{ Any measure of variance explained can be converted to a corresponding Cohen's \emph{f} via: \cr\cr \deqn{f^2 = \frac{\eta^2}{1 - \eta^2}} \cr\cr \deqn{\eta^2 = \frac{f^2}{1 + f^2}} \cr\cr If a partial Eta-Squared is used, the resulting Cohen's \emph{f} is a partial-Cohen's \emph{f}; If a less biased estimate of variance explained is used (such as Epsilon- or Omega-Squared), the resulting Cohen's \emph{f} is likewise a less biased estimate of Cohen's \emph{f}. } \references{ \itemize{ \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis. Psychological Methods, 9, 164-182. } } \seealso{ \code{\link[=eta_squared]{eta_squared()}} for more details. Other convert between effect sizes: \code{\link{d_to_cles}()}, \code{\link{d_to_r}()}, \code{\link{odds_to_probs}()}, \code{\link{oddsratio_to_riskratio}()} } \concept{convert between effect sizes} effectsize/man/interpret_cohens_g.Rd0000644000175000017500000000263014170065645017467 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_cohens_g.R \name{interpret_cohens_g} \alias{interpret_cohens_g} \title{Interpret Cohen's g} \usage{ interpret_cohens_g(g, rules = "cohen1988", ...) } \arguments{ \item{g}{Value or vector of effect size values.} \item{rules}{Can be \code{"cohen1988"} (default) or a custom set of \code{\link[=rules]{rules()}}.} \item{...}{Not directly used.} } \description{ Interpret Cohen's g } \note{ "\emph{Since \strong{g} is so transparently clear a unit, it is expected that workers in any given substantive area of the behavioral sciences will very frequently be able to set relevant [effect size] values without the proposed conventions, or set up conventions of their own which are suited to their area of inquiry.}" - Cohen, 1988, page 147. } \section{Rules}{ Rules apply to equally to positive and negative \emph{g} (i.e., they are given as absolute values). \itemize{ \item Cohen (1988) (\code{"cohen1988"}; default) \itemize{ \item \strong{d < 0.05} - Very small \item \strong{0.05 <= d < 0.15} - Small \item \strong{0.15 <= d < 0.25} - Medium \item \strong{d >= 0.25} - Large } } } \examples{ interpret_cohens_g(.02) interpret_cohens_g(c(.3, .15)) } \references{ \itemize{ \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. } } effectsize/man/format_standardize.Rd0000644000175000017500000000345714132466117017473 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/format_standardize.R \name{format_standardize} \alias{format_standardize} \title{Transform a standardized vector into character} \usage{ format_standardize( x, reference = x, robust = FALSE, digits = 1, protect_integers = TRUE, ... ) } \arguments{ \item{x}{A standardized numeric vector.} \item{reference}{The reference vector from which to compute the mean and SD.} \item{robust}{Logical, if \code{TRUE}, centering is done by subtracting the median from the variables and dividing it by the median absolute deviation (MAD). If \code{FALSE}, variables are standardized by subtracting the mean and dividing it by the standard deviation (SD).} \item{digits}{Number of digits for rounding or significant figures. May also be \code{"signif"} to return significant figures or \code{"scientific"} to return scientific notation. Control the number of digits by adding the value as suffix, e.g. \code{digits = "scientific4"} to have scientific notation with 4 decimal places, or \code{digits = "signif5"} for 5 significant figures (see also \code{\link[=signif]{signif()}}).} \item{protect_integers}{Should integers be kept as integers (i.e., without decimals)?} \item{...}{Other arguments to pass to \code{\link[insight:format_value]{insight::format_value()}} such as \code{digits}, etc.} } \description{ Transform a standardized vector into character, e.g., \code{c("-1 SD", "Mean", "+1 SD")}. } \examples{ format_standardize(c(-1, 0, 1)) format_standardize(c(-1, 0, 1, 2), reference = rnorm(1000)) format_standardize(c(-1, 0, 1, 2), reference = rnorm(1000), robust = TRUE) format_standardize(standardize(mtcars$wt), digits = 1) format_standardize(standardize(mtcars$wt, robust = TRUE), digits = 1) } effectsize/man/effectsize_API.Rd0000644000175000017500000000272314170072560016423 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/docs_extra.R, R/eta_squared.R \name{effectsize_API} \alias{effectsize_API} \alias{.es_aov_simple} \alias{.es_aov_strata} \alias{.es_aov_table} \title{\code{effectsize} API} \usage{ .es_aov_simple( aov_table, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE ) .es_aov_strata( aov_table, DV_names, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE ) .es_aov_table( aov_table, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE ) } \arguments{ \item{aov_table}{Input data frame} \item{type}{Which effect size to compute?} \item{partial, generalized, ci, alternative, verbose}{See \code{\link[=eta_squared]{eta_squared()}}.} \item{include_intercept}{Should the intercept (\code{(Intercept)}) be included?} \item{DV_names}{A character vector with the names of all the predictors, including the grouping variable (e.g., \code{"Subject"}).} } \description{ Read the \href{https://easystats.github.io/effectsize/articles/effectsize_API.html}{\emph{Support functions for model extensions}} vignette. } effectsize/man/interpret_p.Rd0000644000175000017500000000214514132466117016137 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_p.R \name{interpret_p} \alias{interpret_p} \title{Interpret p-values} \usage{ interpret_p(p, rules = "default") } \arguments{ \item{p}{Value or vector of p-values.} \item{rules}{Can be \code{"default"}, \code{"rss"} (for \emph{Redefine statistical significance} rules) or custom set of \code{\link[=rules]{rules()}}.} } \description{ Interpret p-values } \section{Rules}{ \itemize{ \item Default \itemize{ \item \strong{p >= 0.05} - Not significant \item \strong{p < 0.05} - Significant } \item Benjamin et al. (2018) (\code{"rss"}) \itemize{ \item \strong{p >= 0.05} - Not significant \item \strong{0.005 <= p < 0.05} - Suggestive \item \strong{p < 0.005} - Significant } } } \examples{ interpret_p(c(.5, .02, 0.001)) interpret_p(c(.5, .02, 0.001), rules = "rss") } \references{ \itemize{ \item Benjamin, D. J., Berger, J. O., Johannesson, M., Nosek, B. A., Wagenmakers, E. J., Berk, R., ... & Cesarini, D. (2018). Redefine statistical significance. Nature Human Behaviour, 2(1), 6-10. } } effectsize/man/interpret_bf.Rd0000644000175000017500000000421114132466117016263 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_bf.R \name{interpret_bf} \alias{interpret_bf} \title{Interpret Bayes Factor (BF)} \usage{ interpret_bf( bf, rules = "jeffreys1961", log = FALSE, include_value = FALSE, protect_ratio = TRUE, exact = TRUE ) } \arguments{ \item{bf}{Value or vector of Bayes factor (BF) values.} \item{rules}{Can be \code{"jeffreys1961"} (default), \code{"raftery1995"} or custom set of \code{\link[=rules]{rules()}} (for the \emph{absolute magnitude} of evidence).} \item{log}{Is the \code{bf} value \code{log(bf)}?} \item{include_value}{Include the value in the output.} \item{protect_ratio}{Should values smaller than 1 be represented as ratios?} \item{exact}{Should very large or very small values be reported with a scientific format (e.g., 4.24e5), or as truncated values (as "> 1000" and "< 1/1000").} } \description{ Interpret Bayes Factor (BF) } \details{ Argument names can be partially matched. } \section{Rules}{ Rules apply to BF as ratios, so BF of 10 is as extreme as a BF of 0.1 (1/10). \itemize{ \item Jeffreys (1961) (\code{"jeffreys1961"}; default) \itemize{ \item \strong{BF = 1} - No evidence \item \strong{1 < BF <= 3} - Anecdotal \item \strong{3 < BF <= 10} - Moderate \item \strong{10 < BF <= 30} - Strong \item \strong{30 < BF <= 100} - Very strong \item \strong{BF > 100} - Extreme. } \item Raftery (1995) (\code{"raftery1995"}) \itemize{ \item \strong{BF = 1} - No evidence \item \strong{1 < BF <= 3} - Weak \item \strong{3 < BF <= 20} - Positive \item \strong{20 < BF <= 150} - Strong \item \strong{BF > 150} - Very strong } } } \examples{ interpret_bf(1) interpret_bf(c(5, 2)) } \references{ \itemize{ \item Jeffreys, H. (1961), Theory of Probability, 3rd ed., Oxford University Press, Oxford. \item Raftery, A. E. (1995). Bayesian model selection in social research. Sociological methodology, 25, 111-164. \item Jarosz, A. F., & Wiley, J. (2014). What are the odds? A practical guide to computing and reporting Bayes factors. The Journal of Problem Solving, 7(1), } \enumerate{ \item } } effectsize/man/interpret_gfi.Rd0000644000175000017500000001250214170065645016446 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_cfa_fit.R \name{interpret_gfi} \alias{interpret_gfi} \alias{interpret_agfi} \alias{interpret_nfi} \alias{interpret_nnfi} \alias{interpret_cfi} \alias{interpret_rmsea} \alias{interpret_srmr} \alias{interpret_rfi} \alias{interpret_ifi} \alias{interpret_pnfi} \alias{interpret.lavaan} \alias{interpret.performance_lavaan} \title{Interpret of indices of CFA / SEM goodness of fit} \usage{ interpret_gfi(x, rules = "default") interpret_agfi(x, rules = "default") interpret_nfi(x, rules = "byrne1994") interpret_nnfi(x, rules = "byrne1994") interpret_cfi(x, rules = "default") interpret_rmsea(x, rules = "default") interpret_srmr(x, rules = "default") interpret_rfi(x, rules = "default") interpret_ifi(x, rules = "default") interpret_pnfi(x, rules = "default") \method{interpret}{lavaan}(x, ...) \method{interpret}{performance_lavaan}(x, ...) } \arguments{ \item{x}{vector of values, or an object of class \code{lavaan}.} \item{rules}{Can be \code{"default"} or custom set of \code{\link[=rules]{rules()}}.} \item{...}{Currently not used.} } \description{ Interpretation of indices of fit found in confirmatory analysis or structural equation modelling, such as RMSEA, CFI, NFI, IFI, etc. } \details{ \subsection{Indices of fit}{ \itemize{ \item \strong{Chisq}: The model Chi-squared assesses overall fit and the discrepancy between the sample and fitted covariance matrices. Its p-value should be > .05 (i.e., the hypothesis of a perfect fit cannot be rejected). However, it is quite sensitive to sample size. \item \strong{GFI/AGFI}: The (Adjusted) Goodness of Fit is the proportion of variance accounted for by the estimated population covariance. Analogous to R2. The GFI and the AGFI should be > .95 and > .90, respectively. \item \strong{NFI/NNFI/TLI}: The (Non) Normed Fit Index. An NFI of 0.95, indicates the model of interest improves the fit by 95\\% relative to the null model. The NNFI (also called the Tucker Lewis index; TLI) is preferable for smaller samples. They should be > .90 (Byrne, 1994) or > .95 (Schumacker & Lomax, 2004). \item \strong{CFI}: The Comparative Fit Index is a revised form of NFI. Not very sensitive to sample size (Fan, Thompson, & Wang, 1999). Compares the fit of a target model to the fit of an independent, or null, model. It should be > .90. \item \strong{RMSEA}: The Root Mean Square Error of Approximation is a parsimony-adjusted index. Values closer to 0 represent a good fit. It should be < .08 or < .05. The p-value printed with it tests the hypothesis that RMSEA is less than or equal to .05 (a cutoff sometimes used for good fit), and thus should be not significant. \item \strong{RMR/SRMR}: the (Standardized) Root Mean Square Residual represents the square-root of the difference between the residuals of the sample covariance matrix and the hypothesized model. As the RMR can be sometimes hard to interpret, better to use SRMR. Should be < .08. \item \strong{RFI}: the Relative Fit Index, also known as RHO1, is not guaranteed to vary from 0 to 1. However, RFI close to 1 indicates a good fit. \item \strong{IFI}: the Incremental Fit Index (IFI) adjusts the Normed Fit Index (NFI) for sample size and degrees of freedom (Bollen's, 1989). Over 0.90 is a good fit, but the index can exceed 1. \item \strong{PNFI}: the Parsimony-Adjusted Measures Index. There is no commonly agreed-upon cutoff value for an acceptable model for this index. Should be > 0.50. } See the documentation for \code{\link[lavaan:fitmeasures]{fitmeasures()}}. } \subsection{What to report}{ For structural equation models (SEM), Kline (2015) suggests that at a minimum the following indices should be reported: The model \strong{chi-square}, the \strong{RMSEA}, the \strong{CFI} and the \strong{SRMR}. } } \note{ When possible, it is recommended to report dynamic cutoffs of fit indices. See https://dynamicfit.app/cfa/. } \examples{ interpret_gfi(c(.5, .99)) interpret_agfi(c(.5, .99)) interpret_nfi(c(.5, .99)) interpret_nnfi(c(.5, .99)) interpret_cfi(c(.5, .99)) interpret_rmsea(c(.07, .04)) interpret_srmr(c(.5, .99)) interpret_rfi(c(.5, .99)) interpret_ifi(c(.5, .99)) interpret_pnfi(c(.5, .99)) # Structural Equation Models (SEM) if (require("lavaan")) { structure <- " ind60 =~ x1 + x2 + x3 dem60 =~ y1 + y2 + y3 dem60 ~ ind60 " model <- lavaan::sem(structure, data = PoliticalDemocracy) interpret(model) } } \references{ \itemize{ \item Awang, Z. (2012). A handbook on SEM. Structural equation modeling. \item Byrne, B. M. (1994). Structural equation modeling with EQS and EQS/Windows. Thousand Oaks, CA: Sage Publications. \item Tucker, L. R., \& Lewis, C. (1973). The reliability coefficient for maximum likelihood factor analysis. Psychometrika, 38, 1-10. \item Schumacker, R. E., \& Lomax, R. G. (2004). A beginner's guide to structural equation modeling, Second edition. Mahwah, NJ: Lawrence Erlbaum Associates. \item Fan, X., B. Thompson, \& L. Wang (1999). Effects of sample size, estimation method, and model specification on structural equation modeling fit indexes. Structural Equation Modeling, 6, 56-83. \item Kline, R. B. (2015). Principles and practice of structural equation modeling. Guilford publications. } } effectsize/man/es_info.Rd0000644000175000017500000000056014170065645015230 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/is_effectsize_name.R \docType{data} \name{es_info} \alias{es_info} \title{List of effect size names} \format{ An object of class \code{data.frame} with 40 rows and 6 columns. } \usage{ es_info } \description{ Can always add more info here if need be... } \keyword{internal} effectsize/man/is_effectsize_name.Rd0000644000175000017500000000111714132466117017424 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/is_effectsize_name.R \name{is_effectsize_name} \alias{is_effectsize_name} \alias{get_effectsize_name} \alias{get_effectsize_label} \title{Checks if character is of a supported effect size} \usage{ is_effectsize_name(x, ignore_case = TRUE) get_effectsize_name(x, ignore_case = TRUE) get_effectsize_label(x, ignore_case = TRUE) } \arguments{ \item{x}{A character, or a vector.} \item{ignore_case}{Should case of input be ignored?} } \description{ For use by other functions and packages. } effectsize/man/d_to_cles.Rd0000644000175000017500000000422314170065645015541 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_between_common_language.R \name{d_to_cles} \alias{d_to_cles} \alias{convert_d_to_common_language} \alias{d_to_common_language} \alias{rb_to_cles} \alias{rb_to_common_language} \alias{convert_rb_to_common_language} \title{Convert Standardized Mean Difference to Common Language Effect Sizes} \usage{ d_to_cles(d) rb_to_cles(rb) } \arguments{ \item{d, rb}{A numeric value of Cohen's d / rank-biserial correlation \emph{or} the output from \code{\link[=cohens_d]{cohens_d()}} / \code{\link[=rank_biserial]{rank_biserial()}}.} } \value{ A list of \verb{Cohen's U3}, \code{Overlap}, \code{Pr(superiority)}, a numeric vector of \code{Pr(superiority)}, or a data frame, depending on the input. } \description{ Convert Standardized Mean Difference to Common Language Effect Sizes } \details{ This function use the following formulae for Cohen's \emph{d}: \deqn{Pr(superiority) = \Phi(d/\sqrt{2})}{Pr(superiority) = pnorm(d / sqrt(2))} \cr \deqn{Cohen's U_3 = \Phi(d)}{U3 = pnorm(d)} \cr \deqn{Overlap = 2 \times \Phi(-|d|/2)}{Overlap = 2 * pnorm(-abs(d) / 2)} \cr And the following for the rank-biserial correlation: \deqn{Pr(superiority) = (r_{rb} + 1)/2}{Pr(superiority) = (rb + 1)/2} } \note{ These calculations assume that the populations have equal variance and are normally distributed. } \references{ \itemize{ \item Cohen, J. (1977). Statistical power analysis for the behavioral sciences. New York: Routledge. \item Reiser, B., & Faraggi, D. (1999). Confidence intervals for the overlapping coefficient: the normal equal variance case. Journal of the Royal Statistical Society, 48(3), 413-418. \item Ruscio, J. (2008). A probability-based measure of effect size: robustness to base rates and other factors. Psychological methods, 13(1), 19–30. } } \seealso{ \code{\link[=cohens_d]{cohens_d()}}, \code{\link[=rank_biserial]{rank_biserial()}} Other convert between effect sizes: \code{\link{d_to_r}()}, \code{\link{eta2_to_f2}()}, \code{\link{odds_to_probs}()}, \code{\link{oddsratio_to_riskratio}()} } \concept{convert between effect sizes} effectsize/man/odds_to_probs.Rd0000644000175000017500000000311714170065645016447 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_between_odds_to_probs.R \name{odds_to_probs} \alias{odds_to_probs} \alias{convert_odds_to_probs} \alias{odds_to_probs.data.frame} \alias{probs_to_odds} \alias{convert_probs_to_odds} \alias{probs_to_odds.data.frame} \title{Convert between Odds and Probabilities} \usage{ odds_to_probs(odds, log = FALSE, ...) \method{odds_to_probs}{data.frame}(odds, log = FALSE, select = NULL, exclude = NULL, ...) probs_to_odds(probs, log = FALSE, ...) \method{probs_to_odds}{data.frame}(probs, log = FALSE, select = NULL, exclude = NULL, ...) } \arguments{ \item{odds}{The \emph{Odds} (or \code{log(odds)} when \code{log = TRUE}) to convert.} \item{log}{Take in or output log odds (such as in logistic models).} \item{...}{Arguments passed to or from other methods.} \item{select}{When a data frame is passed, character or list of of column names to be transformed.} \item{exclude}{When a data frame is passed, character or list of column names to be excluded from transformation.} \item{probs}{Probability values to convert.} } \value{ Converted index. } \description{ Convert between Odds and Probabilities } \examples{ odds_to_probs(3) odds_to_probs(1.09, log = TRUE) probs_to_odds(0.95) probs_to_odds(0.95, log = TRUE) } \seealso{ \code{\link[stats:Logistic]{stats::plogis()}} Other convert between effect sizes: \code{\link{d_to_cles}()}, \code{\link{d_to_r}()}, \code{\link{eta2_to_f2}()}, \code{\link{oddsratio_to_riskratio}()} } \concept{convert between effect sizes} effectsize/man/interpret_direction.Rd0000644000175000017500000000057414132466117017664 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_direction.R \name{interpret_direction} \alias{interpret_direction} \title{Interpret direction} \usage{ interpret_direction(x) } \arguments{ \item{x}{Numeric value.} } \description{ Interpret direction } \examples{ interpret_direction(.02) interpret_direction(c(.5, -.02)) } effectsize/man/oddsratio_to_riskratio.Rd0000644000175000017500000000301614170065645020366 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_between_OR_to_RR.R \name{oddsratio_to_riskratio} \alias{oddsratio_to_riskratio} \alias{riskratio_to_oddsratio} \title{Convert between Odds ratios and Risk ratios} \usage{ oddsratio_to_riskratio(OR, p0, log = FALSE, ...) riskratio_to_oddsratio(RR, p0, log = FALSE) } \arguments{ \item{OR, RR}{Risk ratio of \code{p1/p0} or Odds ratio of \code{odds(p1)/odds(p0)}, possibly log-ed. \code{OR} can also be a logistic regression model.} \item{p0}{Baseline risk} \item{log}{Take in or output the log of the ratio (such as in logistic models).} \item{...}{Arguments passed to and from other methods.} } \value{ Converted index, or if \code{OR} is a logistic regression model, a parameter table with the converted indices. } \description{ Convert between Odds ratios and Risk ratios } \examples{ p0 <- 0.4 p1 <- 0.7 (OR <- probs_to_odds(p1) / probs_to_odds(p0)) (RR <- p1 / p0) riskratio_to_oddsratio(RR, p0 = p0) oddsratio_to_riskratio(OR, p0 = p0) m <- glm(am ~ factor(cyl), data = mtcars, family = binomial()) oddsratio_to_riskratio(m) } \references{ Grant, R. L. (2014). Converting an odds ratio to a range of plausible relative risks for better communication of research findings. Bmj, 348, f7450. } \seealso{ Other convert between effect sizes: \code{\link{d_to_cles}()}, \code{\link{d_to_r}()}, \code{\link{eta2_to_f2}()}, \code{\link{odds_to_probs}()} } \concept{convert between effect sizes} effectsize/man/effectsize.Rd0000644000175000017500000001244414170065645015741 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/effectsize.BFBayesFactor.R, R/effectsize.R, % R/effectsize.htest.R \name{effectsize.BFBayesFactor} \alias{effectsize.BFBayesFactor} \alias{effectsize} \alias{effectsize.aov} \alias{effectsize.htest} \title{Effect Size} \usage{ \method{effectsize}{BFBayesFactor}(model, type = NULL, verbose = TRUE, test = NULL, ...) effectsize(model, ...) \method{effectsize}{aov}(model, type = NULL, ...) \method{effectsize}{htest}(model, type = NULL, verbose = TRUE, ...) } \arguments{ \item{model}{An object of class \code{htest}, or a statistical model. See details.} \item{type}{The effect size of interest. See details.} \item{verbose}{Toggle off warnings.} \item{test}{The indices of effect existence to compute. Character (vector) or list with one or more of these options: \code{"p_direction"} (or \code{"pd"}), \code{"rope"}, \code{"p_map"}, \code{"equivalence_test"} (or \code{"equitest"}), \code{"bayesfactor"} (or \code{"bf"}) or \code{"all"} to compute all tests. For each "test", the corresponding \pkg{bayestestR} function is called (e.g. \code{\link[bayestestR:rope]{rope()}} or \code{\link[bayestestR:p_direction]{p_direction()}}) and its results included in the summary output.} \item{...}{Arguments passed to or from other methods. See details.} } \value{ A data frame with the effect size (depending on input) and and its CIs (\code{CI_low} and \code{CI_high}). } \description{ This function tries to return the best effect-size measure for the provided input model. See details. } \details{ \itemize{ \item For an object of class \code{htest}, data is extracted via \code{\link[insight:get_data]{insight::get_data()}}, and passed to the relevant function according to: \itemize{ \item A \strong{t-test} depending on \code{type}: \code{"cohens_d"} (default), \code{"hedges_g"}, or \code{"cles"}. \item A \strong{Chi-squared tests of independence or goodness-of-fit}, depending on \code{type}: \code{"cramers_v"} (default), \code{"phi"}, \code{"cohens_w"}, \code{"pearsons_c"}, \code{"cohens_h"}, \code{"oddsratio"}, or \code{"riskratio"}. \item A \strong{One-way ANOVA test}, depending on \code{type}: \code{"eta"} (default), \code{"omega"} or \code{"epsilon"} -squared, \code{"f"}, or \code{"f2"}. \item A \strong{McNemar test} returns \emph{Cohen's g}. \item A \strong{Wilcoxon test} depending on \code{type}: returns "\code{rank_biserial}" correlation (default) or \code{"cles"}. \item A \strong{Kruskal-Wallis test} returns \emph{rank Epsilon squared}. \item A \strong{Friedman test} returns \emph{Kendall's W}. (Where applicable, \code{ci} and \code{alternative} are taken from the \code{htest} if not otherwise provided.) } \item For an object of class \code{BFBayesFactor}, using \code{\link[bayestestR:describe_posterior]{bayestestR::describe_posterior()}}, \itemize{ \item A \strong{t-test} depending on \code{type}: "cohens_d"\verb{(default) or}"cles"`. \item A \strong{correlation test} returns \emph{r}. \item A \strong{contingency table test}, depending on \code{type}: \code{"cramers_v"} (default), \code{"phi"}, \code{"cohens_w"}, \code{"pearsons_c"}, \code{"cohens_h"}, \code{"oddsratio"}, or \code{"riskratio"}. \item A \strong{proportion test} returns \emph{p}. } \item Objects of class \code{anova}, \code{aov}, or \code{aovlist}, depending on \code{type}: \code{"eta"} (default), \code{"omega"} or \code{"epsilon"} -squared, \code{"f"}, or \code{"f2"}. \item Other objects are passed to \code{\link[=standardize_parameters]{standardize_parameters()}}. } \strong{For statistical models it is recommended to directly use the listed functions, for the full range of options they provide.} } \examples{ ## Hypothesis Testing ## ------------------ contingency_table <- as.table(rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477))) Xsq <- chisq.test(contingency_table) effectsize(Xsq) effectsize(Xsq, type = "phi") Tt <- t.test(1:10, y = c(7:20), alternative = "less") effectsize(Tt) Aov <- oneway.test(extra ~ group, data = sleep, var.equal = TRUE) effectsize(Aov) effectsize(Aov, type = "omega") Wt <- wilcox.test(1:10, 7:20, mu = -3, alternative = "less") effectsize(Wt) effectsize(Wt, type = "cles") ## Bayesian Hypothesis Testing ## --------------------------- \donttest{ if (require(BayesFactor)) { bf_prop <- proportionBF(3, 7, p = 0.3) effectsize(bf_prop) bf_corr <- correlationBF(attitude$rating, attitude$complaints) effectsize(bf_corr) data(raceDolls) bf_xtab <- contingencyTableBF(raceDolls, sampleType = "poisson", fixedMargin = "cols") effectsize(bf_xtab) effectsize(bf_xtab, type = "oddsratio") bf_ttest <- ttestBF(sleep$extra[sleep$group==1], sleep$extra[sleep$group==2], paired = TRUE, mu = -1) effectsize(bf_ttest) } } ## Models and Anova Tables ## ----------------------- fit <- lm(mpg ~ factor(cyl) * wt + hp, data = mtcars) effectsize(fit) anova_table <- anova(fit) effectsize(anova_table) effectsize(anova_table, type = "epsilon") } \seealso{ Other effect size indices: \code{\link{cles}()}, \code{\link{cohens_d}()}, \code{\link{eta_squared}()}, \code{\link{phi}()}, \code{\link{rank_biserial}()}, \code{\link{standardize_parameters}()} } \concept{effect size indices} effectsize/man/interpret_r2.Rd0000644000175000017500000000407514132466117016227 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_r2.R \name{interpret_r2} \alias{interpret_r2} \title{Interpret coefficient of determination (R2)} \usage{ interpret_r2(r2, rules = "cohen1988") } \arguments{ \item{r2}{Value or vector of R2 values.} \item{rules}{Can be \code{"cohen1988"} (default), \code{"falk1992"}, \code{"chin1998"}, \code{"hair2011"}, or custom set of \code{\link[=rules]{rules()}}].} } \description{ Interpret coefficient of determination (R2) } \section{Rules}{ \subsection{For Linear Regression}{ \itemize{ \item Cohen (1988) (\code{"cohen1988"}; default) \itemize{ \item \strong{R2 < 0.02} - Very weak \item \strong{0.02 <= R2 < 0.13} - Weak \item \strong{0.13 <= R2 < 0.26} - Moderate \item \strong{R2 >= 0.26} - Substantial } \item Falk & Miller (1992) (\code{"falk1992"}) \itemize{ \item \strong{R2 < 0.1} - Negligible \item \strong{R2 >= 0.1} - Adequate } } } \subsection{For PLS / SEM R-Squared of \emph{latent} variables}{ \itemize{ \item Chin, W. W. (1998) (\code{"chin1998"}) \itemize{ \item \strong{R2 < 0.19} - Very weak \item \strong{0.19 <= R2 < 0.33} - Weak \item \strong{0.33 <= R2 < 0.67} - Moderate \item \strong{R2 >= 0.67} - Substantial } \item Hair et al. (2011) (\code{"hair2011"}) \itemize{ \item \strong{R2 < 0.25} - Very weak \item \strong{0.25 <= R2 < 0.50} - Weak \item \strong{0.50 <= R2 < 0.75} - Moderate \item \strong{R2 >= 0.75} - Substantial } } } } \examples{ interpret_r2(.02) interpret_r2(c(.5, .02)) } \references{ \itemize{ \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Falk, R. F., & Miller, N. B. (1992). A primer for soft modeling. University of Akron Press. \item Chin, W. W. (1998). The partial least squares approach to structural equation modeling. Modern methods for business research, 295(2), 295-336. \item Hair, J. F., Ringle, C. M., & Sarstedt, M. (2011). PLS-SEM: Indeed a silver bullet. Journal of Marketing theory and Practice, 19(2), 139-152. } } effectsize/man/hardlyworking.Rd0000644000175000017500000000117614132466117016473 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/datasets.R \docType{data} \name{hardlyworking} \alias{hardlyworking} \title{Workers' salary and other information} \format{ A data frame with 500 rows and 5 variables: \describe{ \item{salary}{Salary, in Shmekels} \item{xtra_hours}{Number of overtime hours (on average, per week)} \item{n_comps}{Number of compliments given to the boss (observed over the last week)} \item{age}{Age in years} \item{seniority}{How many years with the company} } } \description{ A sample (simulated) dataset, used in tests and some examples. } \keyword{data} effectsize/man/cohens_d.Rd0000644000175000017500000001776014170261701015371 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cohens_d.R \name{cohens_d} \alias{cohens_d} \alias{hedges_g} \alias{glass_delta} \title{Effect size for differences} \usage{ cohens_d( x, y = NULL, data = NULL, pooled_sd = TRUE, mu = 0, paired = FALSE, ci = 0.95, alternative = "two.sided", verbose = TRUE, ... ) hedges_g( x, y = NULL, data = NULL, pooled_sd = TRUE, mu = 0, paired = FALSE, ci = 0.95, alternative = "two.sided", verbose = TRUE, ..., correction ) glass_delta( x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", verbose = TRUE, ..., iterations ) } \arguments{ \item{x}{A formula, a numeric vector, or a character name of one in \code{data}.} \item{y}{A numeric vector, a grouping (character / factor) vector, a or a character name of one in \code{data}. Ignored if \code{x} is a formula.} \item{data}{An optional data frame containing the variables.} \item{pooled_sd}{If \code{TRUE} (default), a \code{\link[=sd_pooled]{sd_pooled()}} is used (assuming equal variance). Else the mean SD from both groups is used instead.} \item{mu}{a number indicating the true value of the mean (or difference in means if you are performing a two sample test).} \item{paired}{If \code{TRUE}, the values of \code{x} and \code{y} are considered as paired. This produces an effect size that is equivalent to the one-sample effect size on \code{x - y}.} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"two.sided"} (default, two-sided CI), \code{"greater"} or \code{"less"} (one-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{verbose}{Toggle warnings and messages on or off.} \item{...}{Arguments passed to or from other methods.} \item{iterations, correction}{deprecated.} } \value{ A data frame with the effect size ( \code{Cohens_d}, \code{Hedges_g}, \code{Glass_delta}) and their CIs (\code{CI_low} and \code{CI_high}). } \description{ Compute effect size indices for standardized differences: Cohen's \emph{d}, Hedges' \emph{g} and Glass’s \emph{delta} (\eqn{\Delta}). (This function returns the \strong{population} estimate.) \cr\cr Both Cohen's \emph{d} and Hedges' \emph{g} are the estimated the standardized difference between the means of two populations. Hedges' \emph{g} provides a bias correction (using the exact method) to Cohen's \emph{d} for small sample sizes. For sample sizes > 20, the results for both statistics are roughly equivalent. Glass’s \emph{delta} is appropriate when the standard deviations are significantly different between the populations, as it uses only the \emph{second} group's standard deviation. } \details{ Set \code{pooled_sd = FALSE} for effect sizes that are to accompany a Welch's \emph{t}-test (Delacre et al, 2021). } \note{ The indices here give the population estimated standardized difference. Some statistical packages give the sample estimate instead (without applying Bessel's correction). } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \examples{ \donttest{ data(mtcars) mtcars$am <- factor(mtcars$am) # Two Independent Samples ---------- (d <- cohens_d(mpg ~ am, data = mtcars)) # Same as: # cohens_d("mpg", "am", data = mtcars) # cohens_d(mtcars$mpg[mtcars$am=="0"], mtcars$mpg[mtcars$am=="1"]) # More options: cohens_d(mpg ~ am, data = mtcars, pooled_sd = FALSE) cohens_d(mpg ~ am, data = mtcars, mu = -5) cohens_d(mpg ~ am, data = mtcars, alternative = "less") hedges_g(mpg ~ am, data = mtcars) glass_delta(mpg ~ am, data = mtcars) # One Sample ---------- cohens_d(wt ~ 1, data = mtcars) # same as: # cohens_d("wt", data = mtcars) # cohens_d(mtcars$wt) # More options: cohens_d(wt ~ 1, data = mtcars, mu = 3) hedges_g(wt ~ 1, data = mtcars, mu = 3) # Paired Samples ---------- data(sleep) cohens_d(Pair(extra[group == 1], extra[group == 2]) ~ 1, data = sleep) # same as: # cohens_d(sleep$extra[sleep$group == 1], sleep$extra[sleep$group == 2], paired = TRUE) # More options: cohens_d(Pair(extra[group == 1], extra[group == 2]) ~ 1, data = sleep, mu = -1) hedges_g(Pair(extra[group == 1], extra[group == 2]) ~ 1, data = sleep) # Interpretation ----------------------- interpret_cohens_d(-1.48, rules = "cohen1988") interpret_hedges_g(-1.48, rules = "sawilowsky2009") interpret_glass_delta(-1.48, rules = "gignac2016") # Or: interpret(d, rules = "sawilowsky2009") # Common Language Effect Sizes d_to_cles(1.48) # Or: print(d, append_CLES = TRUE) } } \references{ \itemize{ \item Algina, J., Keselman, H. J., & Penfield, R. D. (2006). Confidence intervals for an effect size when variances are not equal. Journal of Modern Applied Statistical Methods, 5(1), 2. \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Delacre, M., Lakens, D., Ley, C., Liu, L., & Leys, C. (2021, May 7). Why Hedges’ g*s based on the non-pooled standard deviation should be reported with Welch's t-test. https://doi.org/10.31234/osf.io/tu6mp \item Hedges, L. V. & Olkin, I. (1985). Statistical methods for meta-analysis. Orlando, FL: Academic Press. \item Hunter, J. E., & Schmidt, F. L. (2004). Methods of meta-analysis: Correcting error and bias in research findings. Sage. } } \seealso{ \code{\link[=d_to_cles]{d_to_cles()}} \code{\link[=sd_pooled]{sd_pooled()}} Other effect size indices: \code{\link{cles}()}, \code{\link{effectsize.BFBayesFactor}()}, \code{\link{eta_squared}()}, \code{\link{phi}()}, \code{\link{rank_biserial}()}, \code{\link{standardize_parameters}()} } \concept{effect size indices} effectsize/man/rank_biserial.Rd0000644000175000017500000002052614170065645016417 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rank_effectsizes.R \name{rank_biserial} \alias{rank_biserial} \alias{cliffs_delta} \alias{rank_epsilon_squared} \alias{kendalls_w} \title{Effect size for non-parametric (rank sum) tests} \usage{ rank_biserial( x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", paired = FALSE, verbose = TRUE, ..., iterations ) cliffs_delta( x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", verbose = TRUE, ... ) rank_epsilon_squared( x, groups, data = NULL, ci = 0.95, alternative = "greater", iterations = 200, ... ) kendalls_w( x, groups, blocks, data = NULL, ci = 0.95, alternative = "greater", iterations = 200, verbose = TRUE, ... ) } \arguments{ \item{x}{Can be one of: \itemize{ \item A numeric vector, or a character name of one in \code{data}. \item A formula in to form of \code{DV ~ groups} (for \code{rank_biserial()} and \code{rank_epsilon_squared()}) or \code{DV ~ groups | blocks} (for \code{kendalls_w()}; See details for the \code{blocks} and \code{groups} terminology used here). \item A list of vectors (for \code{rank_epsilon_squared()}). \item A matrix of \verb{blocks x groups} (for \code{kendalls_w()}). See details for the \code{blocks} and \code{groups} terminology used here. }} \item{y}{An optional numeric vector of data values to compare to \code{x}, or a character name of one in \code{data}. Ignored if \code{x} is not a vector.} \item{data}{An optional data frame containing the variables.} \item{mu}{a number indicating the value around which (a-)symmetry (for one-sample or paired samples) or shift (for independent samples) is to be estimated. See \link[stats:wilcox.test]{stats::wilcox.test}.} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"two.sided"} (two-sided CI; default for rank-biserial correlation and Cliff's \emph{delta}), \code{"greater"} (default for rank epsilon squared and Kendall's \emph{W}) or \code{"less"} (one-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{paired}{If \code{TRUE}, the values of \code{x} and \code{y} are considered as paired. This produces an effect size that is equivalent to the one-sample effect size on \code{x - y}.} \item{verbose}{Toggle warnings and messages on or off.} \item{...}{Arguments passed to or from other methods.} \item{iterations}{The number of bootstrap replicates for computing confidence intervals. Only applies when \code{ci} is not \code{NULL}. (Deprecated for \code{rank_biserial()}).} \item{groups, blocks}{A factor vector giving the group / block for the corresponding elements of \code{x}, or a character name of one in \code{data}. Ignored if \code{x} is not a vector.} } \value{ A data frame with the effect size (\code{r_rank_biserial}, \code{rank_epsilon_squared} or \code{Kendalls_W}) and its CI (\code{CI_low} and \code{CI_high}). } \description{ Compute the rank-biserial correlation (\eqn{r_{rb}}{r_rb}), Cliff's \emph{delta} (\eqn{\delta}), rank epsilon squared (\eqn{\varepsilon^2}{\epsilon^2}), and Kendall's \emph{W} effect sizes for non-parametric (rank sum) tests. } \details{ The rank-biserial correlation is appropriate for non-parametric tests of differences - both for the one sample or paired samples case, that would normally be tested with Wilcoxon's Signed Rank Test (giving the \strong{matched-pairs} rank-biserial correlation) and for two independent samples case, that would normally be tested with Mann-Whitney's \emph{U} Test (giving \strong{Glass'} rank-biserial correlation). See \link[stats:wilcox.test]{stats::wilcox.test}. In both cases, the correlation represents the difference between the proportion of favorable and unfavorable pairs / signed ranks (Kerby, 2014). Values range from \code{-1} (\emph{all} values of the second sample are larger than \emph{all} the values of the first sample) to \code{+1} (\emph{all} values of the second sample are smaller than \emph{all} the values of the first sample). Cliff's \emph{delta} is an alias to the rank-biserial correlation in the two sample case. \cr\cr The rank epsilon squared is appropriate for non-parametric tests of differences between 2 or more samples (a rank based ANOVA). See \link[stats:kruskal.test]{stats::kruskal.test}. Values range from 0 to 1, with larger values indicating larger differences between groups. \cr\cr Kendall's \emph{W} is appropriate for non-parametric tests of differences between 2 or more dependent samples (a rank based rmANOVA), where each \code{group} (e.g., experimental condition) was measured for each \code{block} (e.g., subject). This measure is also common as a measure of reliability of the rankings of the \code{groups} between raters (\code{blocks}). See \link[stats:friedman.test]{stats::friedman.test}. Values range from 0 to 1, with larger values indicating larger differences between groups / higher agreement between raters. \subsection{Ties}{ When tied values occur, they are each given the average of the ranks that would have been given had no ties occurred. No other corrections have been implemented yet. } } \section{Confidence Intervals}{ Confidence intervals for the rank-biserial correlation (and Cliff's \emph{delta}) are estimated using the normal approximation (via Fisher's transformation). Confidence intervals for rank Epsilon squared, and Kendall's \emph{W} are estimated using the bootstrap method (using the \code{{boot}} package). } \examples{ \donttest{ data(mtcars) mtcars$am <- factor(mtcars$am) mtcars$cyl <- factor(mtcars$cyl) # Rank Biserial Correlation # ========================= # Two Independent Samples ---------- (rb <- rank_biserial(mpg ~ am, data = mtcars)) # Same as: # rank_biserial("mpg", "am", data = mtcars) # rank_biserial(mtcars$mpg[mtcars$am=="0"], mtcars$mpg[mtcars$am=="1"]) # More options: rank_biserial(mpg ~ am, data = mtcars, mu = -5) print(rb, append_CLES = TRUE) # One Sample ---------- rank_biserial(wt ~ 1, data = mtcars, mu = 3) # same as: # rank_biserial("wt", data = mtcars, mu = 3) # rank_biserial(mtcars$wt, mu = 3) # Paired Samples ---------- dat <- data.frame(Cond1 = c(1.83, 0.5, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.3), Cond2 = c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29)) (rb <- rank_biserial(Pair(Cond1, Cond2) ~ 1, data = dat, paired = TRUE)) # same as: # rank_biserial(dat$Cond1, dat$Cond2, paired = TRUE) interpret_rank_biserial(0.78) interpret(rb, rules = "funder2019") # Rank Epsilon Squared # ==================== rank_epsilon_squared(mpg ~ cyl, data = mtcars) # Kendall's W # =========== dat <- data.frame(cond = c("A", "B", "A", "B", "A", "B"), ID = c("L", "L", "M", "M", "H", "H"), y = c(44.56, 28.22, 24, 28.78, 24.56, 18.78)) (W <- kendalls_w(y ~ cond | ID, data = dat, verbose = FALSE)) interpret_kendalls_w(0.11) interpret(W, rules = "landis1977") } } \references{ \itemize{ \item Cureton, E. E. (1956). Rank-biserial correlation. Psychometrika, 21(3), 287-290. \item Glass, G. V. (1965). A ranking variable analogue of biserial correlation: Implications for short-cut item analysis. Journal of Educational Measurement, 2(1), 91-95. \item Kendall, M.G. (1948) Rank correlation methods. London: Griffin. \item Kerby, D. S. (2014). The simple difference formula: An approach to teaching nonparametric correlation. Comprehensive Psychology, 3, 11-IT. \item King, B. M., & Minium, E. W. (2008). Statistical reasoning in the behavioral sciences. John Wiley & Sons Inc. \item Cliff, N. (1993). Dominance statistics: Ordinal analyses to answer ordinal questions. Psychological bulletin, 114(3), 494. \item Tomczak, M., & Tomczak, E. (2014). The need to report effect size estimates revisited. An overview of some recommended measures of effect size. } } \seealso{ Other effect size indices: \code{\link{cles}()}, \code{\link{cohens_d}()}, \code{\link{effectsize.BFBayesFactor}()}, \code{\link{eta_squared}()}, \code{\link{phi}()}, \code{\link{standardize_parameters}()} } \concept{effect size indices} effectsize/man/standardize_info.Rd0000644000175000017500000000400114170065645017123 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/standardize_info.R \name{standardize_info} \alias{standardize_info} \title{Get Standardization Information} \usage{ standardize_info( model, robust = FALSE, two_sd = FALSE, include_pseudo = FALSE, ... ) } \arguments{ \item{model}{A statistical model.} \item{robust}{Logical, if \code{TRUE}, centering is done by subtracting the median from the variables and dividing it by the median absolute deviation (MAD). If \code{FALSE}, variables are standardized by subtracting the mean and dividing it by the standard deviation (SD).} \item{two_sd}{If \code{TRUE}, the variables are scaled by two times the deviation (SD or MAD depending on \code{robust}). This method can be useful to obtain model coefficients of continuous parameters comparable to coefficients related to binary predictors, when applied to \strong{the predictors} (not the outcome) (Gelman, 2008).} \item{include_pseudo}{(For (G)LMMs) Should Pseudo-standardized information be included?} \item{...}{Arguments passed to or from other methods.} } \value{ A data frame with information on each parameter (see \link[parameters:parameters_type]{parameters::parameters_type}), and various standardization coefficients for the post-hoc methods (see \code{\link[=standardize_parameters]{standardize_parameters()}}) for the predictor and the response. } \description{ This function extracts information, such as the deviations (SD or MAD) from parent variables, that are necessary for post-hoc standardization of parameters. This function gives a window on how standardized are obtained, i.e., by what they are divided. The "basic" method of standardization uses. } \examples{ model <- lm(mpg ~ ., data = mtcars) standardize_info(model) standardize_info(model, robust = TRUE) standardize_info(model, two_sd = TRUE) } \seealso{ Other standardize: \code{\link{standardize.default}()}, \code{\link{standardize_parameters}()} } \concept{standardize} effectsize/man/interpret_oddsratio.Rd0000644000175000017500000000371414170065645017676 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_oddsratio.R \name{interpret_oddsratio} \alias{interpret_oddsratio} \title{Interpret Odds ratio} \usage{ interpret_oddsratio(OR, rules = "chen2010", log = FALSE, ...) } \arguments{ \item{OR}{Value or vector of (log) odds ratio values.} \item{rules}{Can be "\verb{chen2010"} (default), \code{"cohen1988"} (through transformation to standardized difference, see \code{\link[=oddsratio_to_d]{oddsratio_to_d()}}) or custom set of \code{\link[=rules]{rules()}}.} \item{log}{Are the provided values log odds ratio.} \item{...}{Currently not used.} } \description{ Interpret Odds ratio } \section{Rules}{ Rules apply to OR as ratios, so OR of 10 is as extreme as a OR of 0.1 (1/10). \itemize{ \item Chen et al. (2010) (\code{"chen2010"}; default) \itemize{ \item \strong{OR < 1.68} - Very small \item \strong{1.68 <= OR < 3.47} - Small \item \strong{3.47 <= OR < 6.71} - Medium \item **OR >= 6.71 ** - Large } \item Cohen (1988) (\code{"cohen1988"}, based on the \code{\link[=oddsratio_to_d]{oddsratio_to_d()}} conversion, see \code{\link[=interpret_cohens_d]{interpret_cohens_d()}}) \itemize{ \item \strong{OR < 1.44} - Very small \item \strong{1.44 <= OR < 2.48} - Small \item \strong{2.48 <= OR < 4.27} - Medium \item **OR >= 4.27 ** - Large } } } \examples{ interpret_oddsratio(1) interpret_oddsratio(c(5, 2)) } \references{ \itemize{ \item Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. \item Chen, H., Cohen, P., & Chen, S. (2010). How big is a big odds ratio? Interpreting the magnitudes of odds ratios in epidemiological studies. Communications in Statistics-Simulation and Computation, 39(4), 860-864. \item Sánchez-Meca, J., Marín-Martínez, F., & Chacón-Moscoso, S. (2003). Effect-size indices for dichotomized outcomes in meta-analysis. Psychological methods, 8(4), 448. } } effectsize/man/effectsize_CIs.Rd0000644000175000017500000002217214170065645016476 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/docs_extra.R \name{effectsize_CIs} \alias{effectsize_CIs} \title{Confidence (Compatibility) Intervals} \description{ More information regarding Confidence (Compatibiity) Intervals and how they are computed in \emph{effectsize}. } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \section{One-Sided CIs}{ Typically, CIs are constructed as two-tailed intervals, with an equal proportion of the cumulative probability distribution above and below the interval. CIs can also be constructed as \emph{one-sided} intervals, giving only a lower bound or upper bound. This is analogous to computing a 1-tailed \emph{p} value or conducting a 1-tailed hypothesis test. \cr\cr Significance tests conducted using CIs (whether a value is inside the interval) and using \emph{p} values (whether p < alpha for that value) are only guaranteed to agree when both are constructed using the same number of sides/tails. \cr\cr Most effect sizes are not bounded by zero (e.g., \emph{r}, \emph{d}, \emph{g}), and as such are generally tested using 2-tailed tests and 2-sided CIs. \cr\cr Some effect sizes are strictly positive--they do have a minimum value, of 0. For example, \eqn{R^2}, \eqn{\eta^2}, and other variance-accounted-for effect sizes, as well as Cramer's \emph{V} and multiple \emph{R}, range from 0 to 1. These typically involve \emph{F}- or \eqn{\chi^2}-statistics and are generally tested using \emph{1-tailed} tests which test whether the estimated effect size is \emph{larger} than the hypothesized null value (e.g., 0). In order for a CI to yield the same significance decision it must then by a \emph{1-sided} CI, estimating only a lower bound. This is the default CI computed by \emph{effectsize} for these effect sizes, where \code{alternative = "greater"} is set. \cr\cr This lower bound interval indicates the smallest effect size that is not significantly different from the observed effect size. That is, it is the minimum effect size compatible with the observed data, background model assumptions, and \eqn{\alpha} level. This type of interval does not indicate a maximum effect size value; anything up to the maximum possible value of the effect size (e.g., 1) is in the interval. \cr\cr One-sided CIs can also be used to test against a maximum effect size value (e.g., is \eqn{R^2} significantly smaller than a perfect correlation of 1.0?) can by setting \code{alternative = "less"}. This estimates a CI with only an \emph{upper} bound; anything from the minimum possible value of the effect size (e.g., 0) up to this upper bound is in the interval. \cr\cr We can also obtain a 2-sided interval by setting \code{alternative = "two-sided"}. These intervals can be interpreted in the same way as other 2-sided intervals, such as those for \emph{r}, \emph{d}, or \emph{g}. \cr\cr An alternative approach to aligning significance tests using CIs and 1-tailed \emph{p} values that can often be found in the literature is to construct a 2-sided CI at a lower confidence level (e.g., 100(1-2\eqn{\alpha})\% = 100 - 2*5\% = 90\%. This estimates the lower bound and upper bound for the above 1-sided intervals simultaneously. These intervals are commonly reported when conducting \strong{equivalence tests}. For example, a 90\% 2-sided interval gives the bounds for an equivalence test with \eqn{\alpha} = .05. However, be aware that this interval does not give 95\% coverage for the underlying effect size parameter value. For that, construct a 95\% 2-sided CI.\if{html}{\out{
}}\preformatted{data("hardlyworking") fit <- lm(salary ~ n_comps + age, data = hardlyworking) eta_squared(fit) # default, ci = 0.95, alternative = "greater" }\if{html}{\out{
}}\preformatted{## # Effect Size for ANOVA (Type I) ## ## Parameter | Eta2 (partial) | 95\% CI ## ----------------------------------------- ## n_comps | 0.21 | [0.16, 1.00] ## age | 0.10 | [0.06, 1.00] ## ## - One-sided CIs: upper bound fixed at (1). }\if{html}{\out{
}}\preformatted{eta_squared(fit, alternative = "less") # Test is eta is smaller than some value }\if{html}{\out{
}}\preformatted{## # Effect Size for ANOVA (Type I) ## ## Parameter | Eta2 (partial) | 95\% CI ## ----------------------------------------- ## n_comps | 0.21 | [0.00, 0.26] ## age | 0.10 | [0.00, 0.14] ## ## - One-sided CIs: lower bound fixed at (0). }\if{html}{\out{
}}\preformatted{eta_squared(fit, alternative = "two.sided") # 2-sided bounds for alpha = .05 }\if{html}{\out{
}}\preformatted{## # Effect Size for ANOVA (Type I) ## ## Parameter | Eta2 (partial) | 95\% CI ## ----------------------------------------- ## n_comps | 0.21 | [0.15, 0.27] ## age | 0.10 | [0.06, 0.15] }\if{html}{\out{
}}\preformatted{eta_squared(fit, ci = 0.9, alternative = "two.sided") # both 1-sided bounds for alpha = .05 }\if{html}{\out{
}}\preformatted{## # Effect Size for ANOVA (Type I) ## ## Parameter | Eta2 (partial) | 90\% CI ## ----------------------------------------- ## n_comps | 0.21 | [0.16, 0.26] ## age | 0.10 | [0.06, 0.14] } } \section{CI Does Not Contain the Estimate}{ For very large sample sizes or effect sizes, the width of the CI can be smaller than the tolerance of the optimizer, resulting in CIs of width 0. This can also result in the estimated CIs excluding the point estimate. For example:\if{html}{\out{
}}\preformatted{t_to_d(80, df_error = 4555555) }\if{html}{\out{
}}\preformatted{## d | 95\% CI ## ------------------- ## 0.07 | [0.08, 0.08] } In these cases, consider an alternative optimizer, or an alternative method for computing CIs, such as the bootstrap. } \references{ Bauer, P., & Kieser, M. (1996). A unifying approach for confidence intervals and testing of equivalence and difference. \emph{Biometrika, 83}(4), 934-–937. \doi{10.1093/biomet/83.4.934} Rafi, Z., & Greenland, S. (2020). Semantic and cognitive tools to aid statistical science: Replace confidence and significance by compatibility and surprise. \emph{BMC Medical Research Methodology, 20}(1), Article 244. \doi{10.1186/s12874-020-01105-9} Schweder, T., & Hjort, N. L. (2016). \emph{Confidence, likelihood, probability: Statistical inference with confidence distributions.} Cambridge University Press. \doi{10.1017/CBO9781139046671} Steiger, J. H. (2004). Beyond the \emph{F} test: Effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis. \emph{Psychological Methods, 9}(2), 164--182. \doi{10.1037/1082-989x.9.2.164} Xie, M., & Singh, K. (2013). Confidence distribution, the frequentist distribution estimator of a parameter: A review. \emph{International Statistical Review, 81}(1), 3–-39. \doi{10.1111/insr.12000} } effectsize/man/interpret_ess.Rd0000644000175000017500000000351714132466117016476 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_ess_rhat.R \name{interpret_ess} \alias{interpret_ess} \alias{interpret_rhat} \title{Interpret Bayesian diagnostic indices} \usage{ interpret_ess(ess, rules = "burkner2017") interpret_rhat(rhat, rules = "vehtari2019") } \arguments{ \item{ess}{Value or vector of Effective Sample Size (ESS) values.} \item{rules}{A character string (see \emph{Rules}) or a custom set of \code{\link[=rules]{rules()}}.} \item{rhat}{Value or vector of Rhat values.} } \description{ Interpretation of Bayesian diagnostic indices, such as Effective Sample Size (ESS) and Rhat. } \section{Rules}{ \subsection{ESS}{ \itemize{ \item Bürkner, P. C. (2017) (\code{"burkner2017"}; default) \itemize{ \item \strong{ESS < 1000} - Insufficient \item \strong{ESS >= 1000} - Sufficient } } } \subsection{Rhat}{ \itemize{ \item Vehtari et al. (2019) (\code{"vehtari2019"}; default) \itemize{ \item \strong{Rhat < 1.01} - Converged \item \strong{Rhat >= 1.01} - Failed } \item Gelman & Rubin (1992) (\code{"gelman1992"}) \itemize{ \item \strong{Rhat < 1.1} - Converged \item \strong{Rhat >= 1.1} - Failed } } } } \examples{ interpret_ess(1001) interpret_ess(c(852, 1200)) interpret_rhat(1.00) interpret_rhat(c(1.5, 0.9)) } \references{ \itemize{ \item Bürkner, P. C. (2017). brms: An R package for Bayesian multilevel models using Stan. Journal of Statistical Software, 80(1), 1-28. \item Gelman, A., & Rubin, D. B. (1992). Inference from iterative simulation using multiple sequences. Statistical science, 7(4), 457-472. \item Vehtari, A., Gelman, A., Simpson, D., Carpenter, B., & Bürkner, P. C. (2019). Rank-normalization, folding, and localization: An improved Rhat for assessing convergence of MCMC. arXiv preprint arXiv:1903.08008. } } effectsize/man/t_to_r.Rd0000644000175000017500000001601514170072560015070 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_stat_to_d.R, R/convert_stat_to_r.R \name{t_to_d} \alias{t_to_d} \alias{z_to_d} \alias{F_to_d} \alias{t_to_r} \alias{z_to_r} \alias{F_to_r} \title{Convert test statistics (t, z, F) to effect sizes of differences (Cohen's d) or association (\strong{partial} r)} \usage{ t_to_d( t, df_error, paired = FALSE, ci = 0.95, alternative = "two.sided", pooled, ... ) z_to_d(z, n, paired = FALSE, ci = 0.95, alternative = "two.sided", pooled, ...) F_to_d( f, df, df_error, paired = FALSE, ci = 0.95, alternative = "two.sided", ... ) t_to_r(t, df_error, ci = 0.95, alternative = "two.sided", ...) z_to_r(z, n, ci = 0.95, alternative = "two.sided", ...) F_to_r(f, df, df_error, ci = 0.95, alternative = "two.sided", ...) } \arguments{ \item{t, f, z}{The t, the F or the z statistics.} \item{paired}{Should the estimate account for the t-value being testing the difference between dependent means?} \item{ci}{Confidence Interval (CI) level} \item{alternative}{a character string specifying the alternative hypothesis; Controls the type of CI returned: \code{"two.sided"} (default, two-sided CI), \code{"greater"} or \code{"less"} (one-sided CI). Partial matching is allowed (e.g., \code{"g"}, \code{"l"}, \code{"two"}...). See \emph{One-Sided CIs} in \link{effectsize_CIs}.} \item{pooled}{Deprecated. Use \code{paired}.} \item{...}{Arguments passed to or from other methods.} \item{n}{The number of observations (the sample size).} \item{df, df_error}{Degrees of freedom of numerator or of the error estimate (i.e., the residuals).} } \value{ A data frame with the effect size(s)(\code{r} or \code{d}), and their CIs (\code{CI_low} and \code{CI_high}). } \description{ These functions are convenience functions to convert t, z and F test statistics to Cohen's d and \strong{partial} r. These are useful in cases where the data required to compute these are not easily available or their computation is not straightforward (e.g., in liner mixed models, contrasts, etc.). \cr See \href{https://easystats.github.io/effectsize/articles/from_test_statistics.html}{Effect Size from Test Statistics vignette.} } \details{ These functions use the following formulae to approximate \emph{r} and \emph{d}: \cr\cr \deqn{r_{partial} = t / \sqrt{t^2 + df_{error}}} \cr\cr \deqn{r_{partial} = z / \sqrt{z^2 + N}} \cr\cr \deqn{d = 2 * t / \sqrt{df_{error}}} \cr\cr \deqn{d_z = t / \sqrt{df_{error}}} \cr\cr \deqn{d = 2 * z / \sqrt{N}} The resulting \code{d} effect size is an \emph{approximation} to Cohen's \emph{d}, and assumes two equal group sizes. When possible, it is advised to directly estimate Cohen's \emph{d}, with \code{\link[=cohens_d]{cohens_d()}}, \code{emmeans::eff_size()}, or similar functions. } \section{Confidence (Compatibility) Intervals (CIs)}{ Unless stated otherwise, confidence (compatibility) intervals (CIs) are estimated using the noncentrality parameter method (also called the "pivot method"). This method finds the noncentrality parameter ("\emph{ncp}") of a noncentral \emph{t}, \emph{F}, or \eqn{\chi^2} distribution that places the observed \emph{t}, \emph{F}, or \eqn{\chi^2} test statistic at the desired probability point of the distribution. For example, if the observed \emph{t} statistic is 2.0, with 50 degrees of freedom, for which cumulative noncentral \emph{t} distribution is \emph{t} = 2.0 the .025 quantile (answer: the noncentral \emph{t} distribution with \emph{ncp} = .04)? After estimating these confidence bounds on the \emph{ncp}, they are converted into the effect size metric to obtain a confidence interval for the effect size (Steiger, 2004). \cr\cr For additional details on estimation and troubleshooting, see \link{effectsize_CIs}. } \section{CIs and Significance Tests}{ "Confidence intervals on measures of effect size convey all the information in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) intervals and p values are complementary summaries of parameter uncertainty given the observed data. A dichotomous hypothesis test could be performed with either a CI or a p value. The 100 (1 - \eqn{\alpha})\% confidence interval contains all of the parameter values for which \emph{p} > \eqn{\alpha} for the current data and model. For example, a 95\% confidence interval contains all of the values for which p > .05. \cr\cr Note that a confidence interval including 0 \emph{does not} indicate that the null (no effect) is true. Rather, it suggests that the observed data together with the model and its assumptions combined do not provided clear evidence against a parameter value of 0 (same as with any other value in the interval), with the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no effect, additional judgments about what parameter values are "close enough" to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, 1996). } \examples{ ## t Tests res <- t.test(1:10, y = c(7:20), var.equal = TRUE) t_to_d(t = res$statistic, res$parameter) t_to_r(t = res$statistic, res$parameter) t_to_r(t = res$statistic, res$parameter, alternative = "less") res <- with(sleep, t.test(extra[group == 1], extra[group == 2], paired = TRUE)) t_to_d(t = res$statistic, res$parameter, paired = TRUE) t_to_r(t = res$statistic, res$parameter) t_to_r(t = res$statistic, res$parameter, alternative = "greater") \donttest{ ## Linear Regression model <- lm(rating ~ complaints + critical, data = attitude) (param_tab <- parameters::model_parameters(model)) (rs <- t_to_r(param_tab$t[2:3], param_tab$df_error[2:3])) if (require(see)) plot(rs) # How does this compare to actual partial correlations? if (require("correlation")) { correlation::correlation(attitude[, c(1, 2, 6)], partial = TRUE)[1:2, c(2, 3, 7, 8)] } } } \references{ \itemize{ \item Friedman, H. (1982). Simplified determinations of statistical power, magnitude of effect and research sample sizes. Educational and Psychological Measurement, 42(2), 521-526. \doi{10.1177/001316448204200214} \item Wolf, F. M. (1986). Meta-analysis: Quantitative methods for research synthesis (Vol. 59). Sage. \item Rosenthal, R. (1994) Parametric measures of effect size. In H. Cooper and L.V. Hedges (Eds.). The handbook of research synthesis. New York: Russell Sage Foundation. \item Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis. Psychological Methods, 9, 164-182. \item Cumming, G., & Finch, S. (2001). A primer on the understanding, use, and calculation of confidence intervals that are based on central and noncentral distributions. Educational and Psychological Measurement, 61(4), 532-574. } } \seealso{ Other effect size from test statistic: \code{\link{F_to_eta2}()}, \code{\link{chisq_to_phi}()} } \concept{effect size from test statistic} effectsize/man/figures/0000755000175000017500000000000014170065645014762 5ustar nileshnilesheffectsize/man/figures/logo.png0000644000175000017500000004363514170065645016443 0ustar nileshnileshPNG  IHDRxb]esRGBgAMA a pHYsodG2IDATx^|[>H3ǎNIHwK,(e/Ph% ';x!kٲdJrlr|XW<]g+GP1t*l G@^_ &b,… |JD5W(XX_*GH\ \.^Hܐ5;1m 3כ of&Beˊ`"֏ nH:)!;/ kq&3.1N6^,&bYOIuŎa*AMWq t]),#yj~c7IP NPcUZX)'g5Q܀Rv^"(FZ~ ^p0U"H#`vqJ#4.JI!z¯N0iI\J$=Lo5{*#&>]aCy_`"n%y$:I-ӵ)DseBړӽi<s ͉M$&׸^8.`"ǎ3} ݃ËJ3#9@^+da1F;Kv ց5`A+V0ѧ\oD0{<ۓ,Tt[=IS5”Lc_Ao ,v+-v KN-Ÿ1x|cp8p"m9Tu}y!HG>z%Zsl².1;LDn"]aŢT?+!wo5>" mK GbX1>;@:/|dfǿ7z/؈zWӅsbX˪Ր(h=^@臓7Y#N7ǿNTN7l YLFC-`nক3OGٽ~ϝaR+#wF0k:S[|dxa¿(-7EuָC.\_F}"mލaĸ]sIӒA X ENe}A8 e\RJ}XGELNvCxϱ񱾱X\F ݦnZvޔ!%"82z޷M"yDhx8UK]`!by@Zygm쫙\dⰊ\U۠Iτb9qnᅧ8oC|'vVrd QޖQ(oDw=`=^!VQ)Zuꊺh,J&ƚO_`?!*g~1&5B0;>>,'Z& %]몲T /I. #xOxw :˜CE$E!7]^v Dh]X/>1^ƥsp'7G^(9%!@[[XL]pEFl3AeB ž '՛Ln q5a@z׻">uGڄ d|yku6ϲɽXc+FƑZp*@G>v;rx]nN.@> :A5#} ~l}8+.\FMkA-._I0jݿq:fYWU;\,Xvs^ij;#tsp@?#r{z鈚F8"+IsgIJ`0l>?|M3Ep\- G)F>R xơV}bҔJ!t;w3p>9pJk­}nUZ#8xc& ewPڃym7BpaCmC5BuX{z=꨷Mf3ӜHcSZscRS꠽sg<ɿQL7 $U*}BDρh[_ׄqBٱqJzlq""CGSvtTǻ n\n$"X(4tl \(4ͫ(Ƀy\!'J3cy nf%&R <-}n&5DwHZR8`<~hdJ #{{I:,Grk|#wY`KWu՝(s^\*_+T щUQ?l eY|#څ:lYydΟvck6Q>_L~C8(!>-YR\<fCPIފ]vMkt%}>&>8geryԥݜzC)U"ϐK|zrZ7CT!5 i79@S 7l3Ç~ߓm 6 .ֈ쯳cذab՗pvF1Yæ_G֔70b8xACHx4[QzjxTB[G,7j=!'ԍ}dS4B!FmQ +bVcÛ"-6>Df,~eQ+6 &~rTW6\o%CQNG|A9 e>bd|& O" }W^|伝Gc!5,}g "#ϤdvoM=`e =5T9qtIqoCf~ډCx|cݻӝ#55Y\qks^Y؂gư ?cJD,l z/.Az~=`jxFl$$$󃞅yM`ں\?ȆWu`S,Y("AV\3HHOOMRaAպ\[e an>LEFX5>s}%Efv&w3ř6m~r$ Ż4?S DZR w Uwqka|؀ !|$4ZT@itɒ1}`_^2`k0p %s,>%=\`-DK˙ȎŽ;œ|Y)6m(*d:9VT?\ &N,|~ƍx뭷>]vw#)!=^^#CӸ%w/쓁ó35}D{y~V/^xԽת*1C0Eؿb¼p'Dpu\?oe2 z1N+}PEȫRzDžE}œO>ɓ'ǐ!Cݗ8q; W`1`5;% ^!&hrˬONcƜ{ ݏ?_ h O[w)WEͨĈwэEj6/[dxh|:AJmy"eidDGtlZH_DJ{f8ͨ1`f˦a?`H^J98;2)JoC=`=Z|}#]N1)aupt_,i ܌,YJ{JQP?'L%X|LᅨP]>n/Bڈ0he}t>vtvr3F7zB|ˤsGW<^~ MMMXK_Q?$[\-OᾇkGTGX,|郸z'Fe?ͥ^}`o:\\S凟֜DL߿{,YO& nHf7AzA&s WV7Ih/6]['r-K1 eU=DDM |/~.e=rȚKePUI Ą6`ʸ  ^M g &4wcُ& )c8KIaMH’ !:]S6oӅ=*j ufOȍ|dgGګƢxu{TjyX5Z^Eа@=[渏x >*0>j|o=cAmhB=.~^XL[ɼaS 鷵 006i !W=v"9HJ|{W1cjk]K؂1ɓGGBS;={6n^21ԥI#B1b|3 `U|T'6l_x;VX ]%,dEH*BݥPC٩eyĉOYUV^n4ɥI%뵸c:,|{;CoDИ`'_Z6z ٵk3V^g#0@* 8܀J,2"Kx/X?7MQ9\>DEp #^x6#NVLELH!YR gp|M{}jA!H% ??'Io"iz?k7kd6Ү*4`dRy (8Nb7Yj)s`I" "Ʒ~nsٌK},;eb=%k:xG><reD =2MbXnsp݄X K*"O&_xZ -.]*maZWq _Vq*cBkgWÔ)Sܘ;5WG߯M[O.S4-9]@XGYnI$p lp!!!رkv`¤k0zh5mpql5~wG:2^Ianr=VX>t b|؉";^t[@Mm__ƍ#1y HnEo@`!%F8RvY86iK5.<& aעZkP#6A'!?($&AP ݋uxɿcD`DZf| BqC 5qDrpyp"C7diW ]OD62>KYî7=ύ&hCw{ GJ`=t!EP!M鼝"CxNLZ UD<"e$`-Ȍ@Bf xjpѣ_7Ңx`8IUݟ[ s FE,D0kqXsL:*p[=kvk75TZ7 W eM FhcTqzH19}Jj%5T!l=᯶) Gƚotn{Ec>X_YgW!A:)S5i w#&OvƼD05獪|tY@\L04j&od&OScXcV$K0m?,#u׮mW.G~+}btz:X?߂>uwb_j&(",i``HP8!WW!h L[ւ +o MI޵n#oS&&$ |\O@%ZBq x,9A谇;1bxU8|@W=t grfK臏N8> 3csZ|A 9^()×_E  iF%(oBF1cS08lf'܌J>TNJ"gm`RT ,?a@z4ץUKi"?$dp(t u,|hĄۆ<'4w?$R)DzEM0.Ǒ3d# P܂>ɫDeM|= GR86&L0݅H0cOR j|${P3j%AUKtyqhnlēt#7`ON &T-nFzM 5\7t7+51o-z'zk5ckC" F&QN~-\Nؠq&}'GFyя>0z%}mAG`J5) {.yFMMt2Md~I~ݯÑ^j@csn(2 >H"SOS1c`= ވJr6_&ugJ\Oo%}Wbpw+tNoUCKJ jWaj"Ob܈:SSIHmtEDM QR*9ʟqU uB\fӢF2Acb TG/L4$$9 z>B YX但X_zXv4 ,b#%V""Z N}7y%fV>N.$f(d Bh\,.rƌH!KCD D^]Kr'@] #rH~zbx%^0?Vlu(*@g:qPTK܈/Z .%z6 PODuEa ʤ2)-q8Y!! T((i7͡No :$"WnVsqu }fX Tľ= -ņl Fb.ufHŽSukST9<UkXS>,HkX 5hԐDC2 e@Rn DHLI4CCFD('VPvзMاk7-^7VXBbC8t)H ptɊ3${vㇷ_ CbH(gQI.uNIsJKQcÄ1m1֊<^v 3n<ޗF&M!LZLD"| vabɲڐ'R+R AWA8UkvGCT'G24_/b-c!8r h@3T j)ܷ6Zۈ*+K,И$P5@QTYf=|OBuS yW&B++[Q[RcLi] ˆ1l̡1uW<bWc'/,;#pEq(+D \ ՎptrVŵ' G zv[{} 'NUaP?hvA#"JB\ 6hCLmh0뉻0Q//:AG:rEJ8 L2ub?ٔDVO/8:XlͪØ~h.Ɂ50lۂcR5 RJ JJPEa8I !>q##u.r jY#Ue?*l/V߀sO39Tɷ\Xl`K ^UyfZʉ%9h<j1Ta12TZ`!Qh; sn$<@^NK '#l7ǸG¸A$10`26I9 "d䙰_22az&ڦV͂\%XCHM-zKϏόD BRarR$ i1tL>'207!bHgat=l'3iЗRe9HP h.r@mqwISvK_ɬH7}n 'MLKC/O dIzJ bu˶_U+dP\ze2c(3,FyH| %.DGv/ec}p03weF CV)]ġj!'N0d]˖e2e>YGnÓ'tCzCΓ;0a";Li>uv|F~ D͜G{ s&(kMzC蠱XQ@DY:0)h3cԈqk\ErjM`'K9 Hѐ_'Qgt ]Wv֒ЦC}qۼa8rM7,RbFFh݅VL#WH  E؃D=|zC| cZ~>qg:T &ZY`T"BN*h,KhlKN~|pШ{k.V_愑 T K~_y&P N*O\<]zm4٣Lw<޸ !AZ7g&dYطO ?L] "!68b' *5&+$Sޒ Rh`Eiؠќ{H8\O쀭8wDtrzx,ey0FͺϠ:y"W=B'ڎUl#Mr;|4t 5 ;B Uߨ1Xx(jVʯ(AHmD|5'rG:One2(~p{*#J`&!Ȳ61r֎'? `BXKmD.Tu*ٕplPтSEE@xjPEnhPFe*Z)F4(JG CA3EZ( AYr EёɍB̤qy+(o.&&6+|N^EMu +I ;#yV#QZC+ ``ݶ| 4Vfz>qϭCQ /_ț_~v6j&]iy/eRP; Y2~ y$76#@%ԽR݊z#bD L !ф}W!a @, Y $8(ŢM!6 GA]~k*D?ӕ0/@%GJSgo0X267 ÀLh` k\k"tٱ<D#ZqIpdSpz f_{sB7Yb1vT"& S6lAHW"i\yPoE(b@0I~g][+:L(4Z \UѽH!R*V烚B8V r5<}aI Ȼ {~% )p.% NS hݠkBV!X0aW.aUJj+Y/#O.Dp;Rpdcs ۊGY=_)I}*TW:-<PRACI<ʥUϼQy^0vKv|:9 h* b)Xy* 7P36k__GoHW9&W||pXK)i(@]j l-F?D&ǣn"aTXWuhZhVy_lUD`Q&6F&ۑ܁l!(srz@rKɌ,_&mj="wt1̴@=l#8T&&E 5sM4䫂"8pď i 퀯S TZ7O/%(Rm*8[q&D*=bӿxtH 0] JISp-l۠:Ҿ`Y 5~ щ@j4V`2(4Ch٭t!1$5ן@: uןE(׵AZ D(/ nn(qYZ(];oO*L{2AG! : j#,(*UjAt 5PWKbPXCT JDǏEi#1([X XQ<xP!lc[sRihvVC < Lij\a5kjJa⢙>;@q[},Ik>ct,4ءvZ.xOфc$c7ҋbjQM'Hf4*7WBd+[d=D29t}#!J*l DGR'-PYQs14/S59֜VyʙŇDnޤZÏْ-GjK9DCEQFKj׫j'VIB=y>"$6l+<+E\ϐ'kqv^ J)El57Y;@ǿsIz90Ymrmx"^fa>e{a!N׉| :Pb#F=4 VZmrCw$CgBJ1Qh'G\:J EriF8^ ,9G`z?! k;q%W8R'8_ȣ^,@) ѡ~G6xz-},1Dl&RyKZHџR$DK$e4`eIƧ'2U-a'Q0#NaR {'wA䰡 u8 Lj$)R FG[[#ŰISG㻍-E QDRt]GuR̂FLՄ¢&ͥVE./zZrLp˒]ZD=CX;ݶHuXm@=ٜf |^brNO򺓈;q*Şٚ΅m‹wD짘=8ɱ(;$6 ٭!n;,>R J%S UT= "Zc-8^'AkK6`w긋_$yV=Αo}E7}nzn Ǫxj WPw9H~ko=6o*.1 0I#b]SqeJD$OP^FF"pl BDJm0-049ÇRe<,WȡP*EAEXpP4 ' 9 #_Y=FC}8A5ǰ&մ]",H3p))!J^=URӏ 6:c h؟-w$gǸ $"G_!D8. W G_?$F_~ν|҈zxa9JD(A.CX+Cs4ԷwלA2y? v9y?)yoG^$o{ 7hL?`5&(y;H$"kt`hnI8nJARP$Ǐd(`{12ϕ˅z*3wĄb *q 46hmF+sfWI$bBM7p-M$&F&KghA8^V"]uƾJjv` Iiۑ&]twZFh^4$Og9M\*l_!yp)11C >Tk)!ndv_5¾ՊHBz{h[IDFÄ~߁4_@=ǡ>NxUv >WuճE8tru"RC⽹.%ȥ/MDD*?{}[DX1UFʏ{:g7RQW@h[gr-s4p,%me">xߪYZc%xxHu?ۂkP Մw,#y47vi^"Gx~a^ uȉuH RP[;=ou (!oI#b7^ZCD'SÞSN`%:/3K#T)N+nε6ӡ/?$;x\P W1D4o}x OS4X=Ifiϲ3q[SV"%[ax`2?$y49֒D2IBU}~/Ɇb\@i\ _Ѽ$wx '3}Q6NLduƮ&9$@-wB")h[GL_5pÍjk ';D.@:~SgG$H> ʢcZlϲUҌ[o@S]Ń_-j~#K8x/$^sHpGaO׭4bNn⟈-. = Y_'.x=#Xt@/ [0gjK""Kk""dyP d7ScX娷|o@xyi*OXkD,O]V, f=$q{Ԡ Aض*!( p])XK#y-j.C\{@DzΝ9:WJaO^I+*l5Cb"im/.{= {Qa3 /;P) TwSL\قW4إm>%ȑ.= -6'krM/W-E^xi$/ݿUeG0HHx덤j|I݃gMK:pa9V/$yu-a{@D3/9h69;^&h)6'o0 ;3gGwiI^U)X+o.^=W "ZI/f'$Ku7+W Ѽ$⳻npsGDg7ƭ$ކ@xOvjP\{@D.wH0 y艸 fɬf?CĞ.9螹?.F(SIENDB`effectsize/man/interpret.Rd0000644000175000017500000000365414132466117015626 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret.R \name{interpret} \alias{interpret} \alias{interpret.numeric} \alias{interpret.effectsize_table} \title{Generic function for interpretation} \usage{ interpret(x, ...) \method{interpret}{numeric}(x, rules, name = attr(rules, "rule_name"), ...) \method{interpret}{effectsize_table}(x, rules, ...) } \arguments{ \item{x}{Vector of value break points (edges defining categories), or a data frame of class \code{effectsize_table}.} \item{...}{Currently not used.} \item{rules}{Set of \code{\link[=rules]{rules()}}. When \code{x} is a data frame, can be a name of an established set of rules.} \item{name}{Name of the set of rules (stored as a 'rule_name' attribute).} } \value{ \itemize{ \item For numeric input: A character vector of interpertations. \item For data frames: the \code{x} input with an additional \code{Interpretation} column. } } \description{ Interpret a value based on a set of rules. See \code{\link[=rules]{rules()}}. } \examples{ rules_grid <- rules(c(0.01, 0.05), c("very significant", "significant", "not significant")) interpret(0.001, rules_grid) interpret(0.021, rules_grid) interpret(0.08, rules_grid) interpret(c(0.01, 0.005, 0.08), rules_grid) interpret(c(0.35, 0.15), c("small" = 0.2, "large" = 0.4), name = "Cohen's Rules") interpret(c(0.35, 0.15), rules(c(0.2, 0.4), c("small", "medium", "large"))) # ---------- d <- cohens_d(mpg ~ am, data = mtcars) interpret(d, rules = "cohen1988") d <- glass_delta(mpg ~ am, data = mtcars) interpret(d, rules = "gignac2016") interpret(d, rules = rules(1, c("tiny", "yeah okay"))) m <- lm(formula = wt ~ am * cyl, data = mtcars) eta2 <- eta_squared(m) interpret(eta2, rules = "field2013") X <- chisq.test(mtcars$am, mtcars$cyl == 8) interpret(oddsratio(X), rules = "chen2010") interpret(cramers_v(X), "lovakov2021") } \seealso{ rules } effectsize/man/d_to_r.Rd0000644000175000017500000000555114170065645015061 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/convert_between_d_to_r.R \name{d_to_r} \alias{d_to_r} \alias{convert_d_to_r} \alias{r_to_d} \alias{convert_r_to_d} \alias{oddsratio_to_d} \alias{convert_oddsratio_to_d} \alias{logoddsratio_to_d} \alias{convert_logoddsratio_to_d} \alias{d_to_oddsratio} \alias{convert_d_to_oddsratio} \alias{oddsratio_to_r} \alias{convert_oddsratio_to_r} \alias{logoddsratio_to_r} \alias{convert_logoddsratio_to_r} \alias{r_to_oddsratio} \alias{convert_r_to_oddsratio} \title{Convert between \emph{d}, \emph{r} and \emph{Odds ratio}} \usage{ d_to_r(d, ...) r_to_d(r, ...) oddsratio_to_d(OR, log = FALSE, ...) logoddsratio_to_d(OR, log = TRUE, ...) d_to_oddsratio(d, log = FALSE, ...) oddsratio_to_r(OR, log = FALSE, ...) logoddsratio_to_r(OR, log = TRUE, ...) r_to_oddsratio(r, log = FALSE, ...) } \arguments{ \item{d}{Standardized difference value (Cohen's d).} \item{...}{Arguments passed to or from other methods.} \item{r}{Correlation coefficient r.} \item{OR}{\emph{Odds ratio} values in vector or data frame.} \item{log}{Take in or output the log of the ratio (such as in logistic models).} } \value{ Converted index. } \description{ Enables a conversion between different indices of effect size, such as standardized difference (Cohen's d), correlation r or (log) odds ratios. } \details{ Conversions between \emph{d} and \emph{OR} or \emph{r} is done through these formulae. \itemize{ \item \eqn{d = \frac{2 * r}{\sqrt{1 - r^2}}}{d = 2 * r / sqrt(1 - r^2)} \item \eqn{r = \frac{d}{\sqrt{d^2 + 4}}}{r = d / sqrt(d^2 + 4)} \item \eqn{d = \frac{\log(OR)\times\sqrt{3}}{\pi}}{d = log(OR) * sqrt(3) / pi} \item \eqn{log(OR) = d * \frac{\pi}{\sqrt(3)}}{log(OR) = d * pi / sqrt(3)} } The conversion from \emph{d} to \emph{r} assumes equally sized groups. The resulting \emph{r} is also called the binomial effect size display (BESD; Rosenthal et al., 1982). } \examples{ r_to_d(0.5) d_to_oddsratio(1.154701) oddsratio_to_r(8.120534) d_to_r(1) r_to_oddsratio(0.4472136, log = TRUE) oddsratio_to_d(1.813799, log = TRUE) } \references{ \itemize{ \item Sánchez-Meca, J., Marín-Martínez, F., & Chacón-Moscoso, S. (2003). Effect-size indices for dichotomized outcomes in meta-analysis. Psychological methods, 8(4), 448. \item Borenstein, M., Hedges, L. V., Higgins, J. P. T., & Rothstein, H. R. (2009). Converting among effect sizes. Introduction to meta-analysis, 45-49. \item Rosenthal, R., & Rubin, D. B. (1982). A simple, general purpose display of magnitude of experimental effect. Journal of educational psychology, 74(2), 166. } } \seealso{ Other convert between effect sizes: \code{\link{d_to_cles}()}, \code{\link{eta2_to_f2}()}, \code{\link{odds_to_probs}()}, \code{\link{oddsratio_to_riskratio}()} } \concept{convert between effect sizes} effectsize/man/print.effectsize_table.Rd0000644000175000017500000000307414170065645020242 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/plot.R, R/print.effectsize_table.R \name{plot.effectsize_table} \alias{plot.effectsize_table} \alias{plot.equivalence_test_effectsize} \alias{print.effectsize_table} \alias{format.effectsize_table} \alias{print.effectsize_difference} \title{Methods for \code{effectsize} tables} \usage{ \method{plot}{effectsize_table}(x, ...) \method{plot}{equivalence_test_effectsize}(x, ...) \method{print}{effectsize_table}(x, digits = 2, ...) \method{format}{effectsize_table}(x, digits = 2, ...) \method{print}{effectsize_difference}(x, digits = 2, append_CLES = FALSE, ...) } \arguments{ \item{x}{Object to print.} \item{...}{Arguments passed to or from other functions.} \item{digits}{Number of digits for rounding or significant figures. May also be \code{"signif"} to return significant figures or \code{"scientific"} to return scientific notation. Control the number of digits by adding the value as suffix, e.g. \code{digits = "scientific4"} to have scientific notation with 4 decimal places, or \code{digits = "signif5"} for 5 significant figures (see also \code{\link[=signif]{signif()}}).} \item{append_CLES}{Should the Common Language Effect Sizes be printed as well? Only applicable to Cohen's \emph{d}, Hedges' \emph{g} for independent samples of equal variance (pooled sd) or for the rank-biserial correlation for independent samples (See \code{\link[=d_to_cles]{d_to_cles()}})} } \description{ Printing, formatting and plotting methods for \code{effectsize} tables. } effectsize/man/interpret_omega_squared.Rd0000644000175000017500000000324414170302654020512 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/interpret_omega_squared.R \name{interpret_omega_squared} \alias{interpret_omega_squared} \alias{interpret_eta_squared} \alias{interpret_epsilon_squared} \title{Interpret ANOVA effect size} \usage{ interpret_omega_squared(es, rules = "field2013", ...) interpret_eta_squared(es, rules = "field2013", ...) interpret_epsilon_squared(es, rules = "field2013", ...) } \arguments{ \item{es}{Value or vector of eta / omega / epsilon squared values.} \item{rules}{Can be \code{"field2013"} (default), \code{"cohen1992"} or custom set of \code{\link[=rules]{rules()}}.} \item{...}{Not used for now.} } \description{ Interpret ANOVA effect size } \section{Rules}{ \itemize{ \item Field (2013) (\code{"field2013"}; default) \itemize{ \item \strong{ES < 0.01} - Very small \item \strong{0.01 <= ES < 0.06} - Small \item \strong{0.16 <= ES < 0.14} - Medium \item **ES >= 0.14 ** - Large } \item Cohen (1992) (\code{"cohen1992"}) applicable to one-way anova, or to \emph{partial} eta / omega / epsilon squared in multi-way anova. \itemize{ \item \strong{ES < 0.02} - Very small \item \strong{0.02 <= ES < 0.13} - Small \item \strong{0.13 <= ES < 0.26} - Medium \item \strong{ES >= 0.26} - Large } } } \examples{ interpret_eta_squared(.02) interpret_eta_squared(c(.5, .02), rules = "cohen1992") } \references{ \itemize{ \item Field, A (2013) Discovering statistics using IBM SPSS Statistics. Fourth Edition. Sage:London. \item Cohen, J. (1992). A power primer. Psychological bulletin, 112(1), 155. } } \seealso{ https://imaging.mrc-cbu.cam.ac.uk/statswiki/FAQ/effectSize/ } effectsize/vignettes/0000755000175000017500000000000014174212106014541 5ustar nileshnilesheffectsize/vignettes/simple_htests.Rmd0000644000175000017500000002256714170072533020110 0ustar nileshnilesh--- title: "Effect Sizes for Simple Hypothesis Tests" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, conversion] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Effect Sizes for Simple Hypothesis Tests} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r setup, include=FALSE} library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 3) pkgs <- c("effectsize", "BayesFactor") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(7) ``` This vignette provides a short review of effect sizes for common hypothesis tests (in **`R`** these are usually achieved with various `*.test()` functions). ```{r} library(effectsize) library(BayesFactor) ``` In most cases, the effect sizes can be automagically extracted from the `htest` object via the `effectsize()` function. ## Standardized Differences For *t*-tests, it is common to report an effect size representing a standardized difference between the two compared samples' means. These measures range from $-\infty$ to $+\infty$, with negative values indicating the second group's mean is larger (and vice versa). ### Two Independent Samples For two independent samples, the difference between the means is standardized based on the pooled standard deviation of both samples (assumed to be equal in the population): ```{r} t.test(mpg ~ am, data = mtcars, var.equal = TRUE) cohens_d(mpg ~ am, data = mtcars) ``` Hedges' *g* provides a bias correction for small sample sizes ($N < 20$). ```{r} hedges_g(mpg ~ am, data = mtcars) ``` If variances cannot be assumed to be equal, it is possible to get estimates that are not based on the pooled standard deviation: ```{r} t.test(mpg ~ am, data = mtcars, var.equal = FALSE) cohens_d(mpg ~ am, data = mtcars, pooled_sd = FALSE) hedges_g(mpg ~ am, data = mtcars, pooled_sd = FALSE) ``` In cases where the differences between the variances are substantial, it is also common to standardize the difference based only on the standard deviation of one of the groups (usually the "control" group); this effect size is known as Glass' $\Delta$ (delta) (Note that the standard deviation is taken from the *second* sample). ```{r} glass_delta(mpg ~ am, data = mtcars) ``` For a one-sided hypothesis, it is also possible to construct one-sided confidence intervals: ```{r} t.test(mpg ~ am, data = mtcars, var.equal = TRUE, alternative = "less") cohens_d(mpg ~ am, data = mtcars, pooled_sd = TRUE, alternative = "less") ``` #### Common Language Effect Sizes Related effect sizes are the *common language effect sizes* which present information about group differences in terms of probability. ```{r} cles(mpg ~ am, data = mtcars) ``` ### One Sample and Paired Samples In the case of a one-sample test, the effect size represents the standardized distance of the mean of the sample from the null value. For paired-samples, the difference between the paired samples is used: ```{r} t.test(extra ~ group, data = sleep, paired = TRUE) cohens_d(extra ~ group, data = sleep, paired = TRUE) hedges_g(extra ~ group, data = sleep, paired = TRUE) ``` ### For a Bayesian *t*-test ```{r} (BFt <- ttestBF(mtcars$mpg[mtcars$am == 0], mtcars$mpg[mtcars$am == 1])) effectsize(BFt, test = NULL) ``` ## One way ANOVA For more details, see [ANOVA vignette](https://easystats.github.io/effectsize/articles/anovaES.html). ```{r, message=FALSE} onew <- oneway.test(mpg ~ gear, data = mtcars, var.equal = TRUE) eta_squared(onew) ``` ## Contingency Tables and Proportions For contingency tables Cramér's *V*, $\phi$ (Phi, also known as Cohen's *w*) and Pearson's contingency coefficient indicate the strength of association with 0 indicating no association between the variables. While Cramér's *V* and Pearson's *C* are capped at 1 (perfect association), $\phi$ can be larger than 1. ```{r} (Music <- matrix( c( 150, 130, 35, 55, 100, 50, 10, 40, 165, 65, 2, 25 ), byrow = TRUE, nrow = 3, dimnames = list( Study = c("Psych", "Econ", "Law"), Music = c("Pop", "Rock", "Jazz", "Classic") ) )) chisq.test(Music) cramers_v(Music) phi(Music) pearsons_c(Music) ``` Pearson's *C* and $\phi$ are also applicable to tests of goodness-of-fit, where small values indicate no deviation from the hypothetical probabilities and large values indicate... large deviation from the hypothetical probabilities. ```{r} O <- c(89, 37, 130, 28, 2) # observed group sizes E <- c(.40, .20, .20, .15, .05) # expected group freq chisq.test(O, p = E, rescale.p = TRUE) pearsons_c(O, p = E, rescale.p = TRUE) phi(O, p = E, rescale.p = TRUE) ``` These can also be extracted from the equivalent Bayesian test: ```{r} (BFX <- contingencyTableBF(Music, sampleType = "jointMulti")) effectsize(BFX, type = "cramers_v", test = NULL) effectsize(BFX, type = "phi", test = NULL) effectsize(BFX, type = "pearsons_c", test = NULL) ``` ### Comparing Two Proportions (2x2 tables) For $2\times 2$ tables, in addition to Cramér's *V*, $\phi$ and Pearson's *C*, we can also compute the Odds-ratio (OR), where each column represents a different group. Values larger than 1 indicate that the odds are higher in the first group (and vice versa). ```{r} (RCT <- matrix( c( 71, 30, 50, 100 ), nrow = 2, byrow = TRUE, dimnames = list( Diagnosis = c("Sick", "Recovered"), Group = c("Treatment", "Control") ) )) chisq.test(RCT) # or fisher.test(RCT) oddsratio(RCT) ``` We can also compute the Risk-ratio (RR), which is the ratio between the proportions of the two groups - a measure which some claim is more intuitive. ```{r} riskratio(RCT) ``` Additionally, Cohen's *h* can also be computed, which uses the *arcsin* transformation. Negative values indicate smaller proportion in the first group (and vice versa). ```{r} cohens_h(RCT) ``` ### Paired Contingency Tables For dependent (paired) contingency tables, Cohen's *g* represents the symmetry of the table, ranging between 0 (perfect symmetry) and 0.5 (perfect asymmetry). ```{r} (Performance <- matrix( c( 794, 86, 150, 570 ), nrow = 2, byrow = TRUE, dimnames = list( "1st Survey" = c("Approve", "Disapprove"), "2nd Survey" = c("Approve", "Disapprove") ) )) mcnemar.test(Performance) cohens_g(Performance) ``` ## Rank Based tests Rank based tests get rank based effect sizes! ### Difference in Ranks For two independent samples, the rank-biserial correlation ($r_{rb}$) is a measure of relative superiority - i.e., larger values indicate a higher probability of a randomly selected observation from *X* being larger than randomly selected observation from *Y*. A value of $(-1)$ indicates that all observations in the second group are larger than the first, and a value of $(+1)$ indicates that all observations in the first group are larger than the second. ```{r, warning=FALSE} A <- c(48, 48, 77, 86, 85, 85) B <- c(14, 34, 34, 77) wilcox.test(A, B) # aka Mann–Whitney U test rank_biserial(A, B) ``` Here too we have a *common language effect size*: ```{r} cles(A, B, rank = TRUE) ``` For one sample, $r_{rb}$ measures the symmetry around $\mu$ (mu; the null value), with 0 indicating perfect symmetry, $(-1)$ indicates that all observations fall below $\mu$, and $(+1)$ indicates that all observations fall above $\mu$. For paired samples the difference between the paired samples is used: ```{r} x <- c(1.15, 0.88, 0.90, 0.74, 1.21, 1.36, 0.89) wilcox.test(x, mu = 1) # aka Signed-Rank test rank_biserial(x, mu = 1) x <- c(1.83, 0.50, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.30) y <- c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29) wilcox.test(x, y, paired = TRUE) # aka Signed-Rank test rank_biserial(x, y, paired = TRUE) ``` ### Rank One way ANOVA The Rank-Epsilon-Squared ($\varepsilon^2$) is a measure of association for the rank based one-way ANOVA. Values range between 0 (no relative superiority between any of the groups) to 1 (complete separation - with no overlap in ranks between the groups). ```{r} group_data <- list( g1 = c(2.9, 3.0, 2.5, 2.6, 3.2), # normal subjects g2 = c(3.8, 2.7, 4.0, 2.4), # with obstructive airway disease g3 = c(2.8, 3.4, 3.7, 2.2, 2.0) # with asbestosis ) kruskal.test(group_data) rank_epsilon_squared(group_data) ``` ### Rank One way Repeated-Measures ANOVA For a rank based repeated measures one-way ANOVA, Kendall's *W* is a measure of agreement on the effect of condition between various "blocks" (the subjects), or more often conceptualized as a measure of reliability of the rating / scores of observations (or "groups") between "raters" ("blocks"). ```{r} # Subjects are COLUMNS (ReactionTimes <- matrix( c(398, 338, 520, 325, 388, 555, 393, 363, 561, 367, 433, 470, 286, 492, 536, 362, 475, 496, 253, 334, 610), nrow = 7, byrow = TRUE, dimnames = list( paste0("Subject", 1:7), c("Congruent", "Neutral", "Incongruent") ) )) friedman.test(ReactionTimes) kendalls_w(ReactionTimes) ``` # References effectsize/vignettes/effectsize.Rmd0000644000175000017500000002560514170302654017350 0ustar nileshnilesh--- title: "Effect Sizes: Getting Started" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, conversion] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{effectsize} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` # Aims of the Package In both theoretical and applied research, it is often of interest to assess the strength of an observed association. This is typically done to allow the judgment of the magnitude of an effect [especially when units of measurement are not meaningful, e.g., in the use of estimated latent variables; @bollen1989structural], to facilitate comparing between predictors' importance within a given model, or both. Though some indices of effect size, such as the correlation coefficient (itself a standardized covariance coefficient) are readily available, other measures are often harder to obtain. **effectsize** is an R package [@rcore] that fills this important gap, providing utilities for easily estimating a wide variety of standardized effect sizes (i.e., effect sizes that are not tied to the units of measurement of the variables of interest) and their confidence intervals (CIs), from a variety of statistical models. **effectsize** provides easy-to-use functions, with full documentation and explanation of the various effect sizes offered, and is also used by developers of other R packages as the back-end for effect size computation, such as **parameters** [@ludecke2020extracting], **ggstatsplot** [@patil2020ggstatsplot], **gtsummary** [@sjoberg2020gtsummary] and more. # Comparison to Other Packages **effectsize**'s functionality is in part comparable to packages like **lm.beta** [@behrendt2014lmbeta], **MOTE** [@buchanan2019MOTE], and **MBESS** [@kelley2020MBESS]. Yet, there are some notable differences, e.g.: - **lm.beta** provides standardized regression coefficients for linear models, based on post-hoc model matrix standardization. However, the functionality is available only for a limited number of models (models inheriting from the `lm` class), whereas **effectsize** provides support for many types of models, including (generalized) linear mixed models, Bayesian models, and more. Additionally, in additional to post-hoc model matrix standardization, **effectsize** offers other methods of standardization (see below). - Both **MOTE** and **MBESS** provide functions for computing effect sizes such as Cohen's *d* and effect sizes for ANOVAs [@cohen1988statistical], and their confidence intervals. However, both require manual input of *F*- or *t*-statistics, *degrees of freedom*, and *sums of squares* for the computation the effect sizes, whereas **effectsize** can automatically extract this information from the provided models, thus allowing for better ease-of-use as well as reducing any potential for error. # Examples of Features **effectsize** provides various functions for extracting and estimating effect sizes and their confidence intervals [estimated using the noncentrality parameter method; @steiger2004beyond]. In this article, we provide basic usage examples for estimating some of the most common effect size. A comprehensive overview, including in-depth examples and [a full list of features and functions](https://easystats.github.io/effectsize/reference/index.html), are accessible via a dedicated website (https://easystats.github.io/effectsize/). ## Indices of Effect Size ### Standardized Differences **effectsize** provides functions for estimating the common indices of standardized differences such as Cohen's *d* (`cohens_d()`), Hedges' *g* (`hedges_g()`) for both paired and independent samples [@cohen1988statistical; @hedges1985statistical], and Glass' $\Delta$ (`glass_delta()`) for independent samples with different variances [@hedges1985statistical]. ```{r} library(effectsize) cohens_d(mpg ~ am, data = mtcars) ``` ### Contingency Tables Pearson's $\phi$ (`phi()`) and Cramér's *V* (`cramers_v()`) can be used to estimate the strength of association between two categorical variables [@cramer1946mathematical], while Cohen's *g* (`cohens_g()`) estimates the deviance between paired categorical variables [@cohen1988statistical]. ```{r} M <- rbind(c(150, 130, 35, 55), c(100, 50, 10, 40), c(165, 65, 2, 25)) cramers_v(M) ``` ## Parameter and Model Standardization Standardizing parameters (i.e., coefficients) can allow for their comparison within and between models, variables and studies. To this end, two functions are available: `standardize()`, which returns an updated model, re-fit with standardized data, and `standardize_parameters()`, which returns a table of standardized coefficients from a provided model [for a list of supported models, see the *insight* package; @luedecke2019insight]. ```{r} model <- lm(mpg ~ cyl * am, data = mtcars) standardize(model) standardize_parameters(model) ``` Standardized parameters can also be produced for generalized linear models (GLMs; where only the predictors are standardized): ```{r} model <- glm(am ~ cyl + hp, family = "binomial", data = mtcars) standardize_parameters(model, exponentiate = TRUE) ``` `standardize_parameters()` provides several standardization methods, such as robust standardization, or *pseudo*-standardized coefficients for (generalized) linear mixed models [@hoffman2015longitudinal]. A full review of these methods can be found in the [*Parameter and Model Standardization* vignette](https://easystats.github.io/effectsize/articles/standardize_parameters.html). ## Effect Sizes for ANOVAs Unlike standardized parameters, the effect sizes reported in the context of ANOVAs (analysis of variance) or ANOVA-like tables represent the amount of variance explained by each of the model's terms, where each term can be represented by one or more parameters. `eta_squared()` can produce such popular effect sizes as Eta-squared ($\eta^2$), its partial version ($\eta^2_p$), as well as the generalized $\eta^2_G$ [@cohen1988statistical; @olejnik2003generalized]: ```{r} options(contrasts = c('contr.sum', 'contr.poly')) data("ChickWeight") # keep only complete cases and convert `Time` to a factor ChickWeight <- subset(ChickWeight, ave(weight, Chick, FUN = length) == 12) ChickWeight$Time <- factor(ChickWeight$Time) model <- aov(weight ~ Diet * Time + Error(Chick / Time), data = ChickWeight) eta_squared(model, partial = TRUE) eta_squared(model, generalized = "Time") ``` **effectsize** also offers $\epsilon^2_p$ (`epsilon_squared()`) and $\omega^2_p$ (`omega_squared()`), which are less biased estimates of the variance explained in the population [@kelley1935unbiased; @olejnik2003generalized]. For more details about the various effect size measures and their applications, see the [*Effect sizes for ANOVAs* vignette](https://easystats.github.io/effectsize/articles/anovaES.html). ## Effect Size Conversion ### From Test Statistics In many real world applications there are no straightforward ways of obtaining standardized effect sizes. However, it is possible to get approximations of most of the effect size indices (*d*, *r*, $\eta^2_p$...) with the use of test statistics [@friedman1982simplified]. These conversions are based on the idea that test statistics are a function of effect size and sample size (or more often of degrees of freedom). Thus it is possible to reverse-engineer indices of effect size from test statistics (*F*, *t*, $\chi^2$, and *z*). ```{r} F_to_eta2(f = c(40.72, 33.77), df = c(2, 1), df_error = c(18, 9)) t_to_d(t = -5.14, df_error = 22) t_to_r(t = -5.14, df_error = 22) ``` These functions also power the `effectsize()` convenience function for estimating effect sizes from R's `htest`-type objects. For example: ```{r} data(hardlyworking, package = "effectsize") aov1 <- oneway.test(salary ~ n_comps, data = hardlyworking, var.equal = TRUE) effectsize(aov1) xtab <- rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477)) Xsq <- chisq.test(xtab) effectsize(Xsq) ``` These functions also power our *Effect Sizes From Test Statistics* shiny app (https://easystats4u.shinyapps.io/statistic2effectsize/). ### Between Effect Sizes For comparisons between different types of designs and analyses, it is useful to be able to convert between different types of effect sizes [*d*, *r*, Odds ratios and Risk ratios; @borenstein2009converting; @grant2014converting]. ```{r} r_to_d(0.7) d_to_oddsratio(1.96) oddsratio_to_riskratio(34.99, p0 = 0.4) oddsratio_to_r(34.99) ``` ## Effect Size Interpretation Finally, **effectsize** provides convenience functions to apply existing or custom interpretation rules of thumb, such as for instance Cohen's (1988). Although we strongly advocate for the cautious and parsimonious use of such judgment-replacing tools, we provide these functions to allow users and developers to explore and hopefully gain a deeper understanding of the relationship between data values and their interpretation. More information is available in the [*Automated Interpretation of Indices of Effect Size* vignette](https://easystats.github.io/effectsize/articles/interpret.html). ```{r} interpret_cohens_d(c(0.02, 0.52, 0.86), rules = "cohen1988") ``` # Licensing and Availability **effectsize** is licensed under the GNU General Public License (v3.0), with all source code stored at GitHub (https://github.com/easystats/effectsize), and with a corresponding issue tracker for bug reporting and feature enhancements. In the spirit of honest and open science, we encourage requests/tips for fixes, feature updates, as well as general questions and concerns via direct interaction with contributors and developers, by [filing an issue](https://github.com/easystats/effectsize/issues/). See the package's [*Contribution Guidelines*](https://github.com/easystats/effectsize/blob/main/.github/CONTRIBUTING.md/). # Acknowledgments **effectsize** is part of the [*easystats*](https://github.com/easystats/easystats/) ecosystem, a collaborative project created to facilitate the usage of R for statistical analyses. Thus, we would like to thank the [members of easystats](https://github.com/orgs/easystats/people/) as well as the users. # References effectsize/vignettes/apa.csl0000644000175000017500000016075014132466117016025 0ustar nileshnilesh effectsize/vignettes/standardize_parameters.Rmd0000644000175000017500000005220714170302654021752 0ustar nileshnilesh--- title: "Parameter and Model Standardization" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, standardization, effect size, cohen d, standardized coefficients] vignette: > %\VignetteIndexEntry{Parameter and Model Standardization} \usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) knitr::opts_chunk$set(comment = ">", warning = FALSE, message = FALSE) options(digits = 2) options(knitr.kable.NA = '') pkgs <- c("effectsize", "parameters", "correlation") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(333) ``` # Introduction Standardizing parameters (*i.e.*, coefficients) can allow for their comparison within and between models, variables and studies. Moreover, as it returns coefficients expressed in terms of **change of variance** (for instance, coefficients expressed in terms of SD of the response variable), it can allow for the usage of [effect size interpretation guidelines](https://easystats.github.io/effectsize/articles/interpret.html), such as Cohen's (1988) famous rules of thumb. However, standardizing a model's parameters should *not* be automatically and mindlessly done: for some research fields, particular variables or types of studies (*e.g.*, replications), it sometimes makes more sense to keep, use and interpret the original parameters, especially if they are well known or easily understood. Critically, **parameters standardization is not a trivial process**. Different techniques exist, that can lead to drastically different results. Thus, it is critical that the standardization method is explicitly documented and detailed. ## Standardizing Parameters of Simple Models ### Standardized Associations ```{r} library(effectsize) m <- lm(rating ~ complaints, data = attitude) standardize_parameters(m) ``` Standardizing the coefficient of this *simple* linear regression gives a value of `0.87`, but did you know that for a simple regression this is actually the **same as a correlation**? Thus, you can eventually apply some (*in*)famous interpretation guidelines (e.g., Cohen's rules of thumb). ```{r} correlation::correlation(attitude, select = c("rating", "complaints")) ``` ### Standardized Differences How does it work in the case of differences, when **factors** are entered and differences between a given level and a reference level? You might have heard that it is similar to a **Cohen's *d***. Well, let's see. ```{r include=FALSE} mtcars <- datasets::mtcars ``` ```{r} # Select portion of data containing the two levels of interest mtcars$am <- factor(mtcars$am, labels = c("Manual", "Automatic")) m <- lm(mpg ~ am, data = mtcars) standardize_parameters(m) ``` This linear model suggests that the *standardized* difference between *Manual* (the reference level - the model's intercept) and *Automatic* is of 1.20 standard deviation of `mpg` (because the response variable was standardized, right?). Let's compute the **Cohen's *d*** between these two levels: ```{r} cohens_d(mpg ~ am, data = mtcars) ``` ***It is larger!*** Why? How? Both differences should be expressed in units of SD! But which SDs? Different SDs! When looking at the difference between groups as a **slope**, the standardized parameter is the difference between the means in $SD_{mpg}$. That is, the *slope* between `Manual` and `Automatic` is a change of 1.20 $SD_{mpg}$s. However, when looking a the difference as a **distance between two populations**, Cohen's d is the distance between the means in units of [**pooled SDs**](https://easystats.github.io/effectsize/reference/sd_pooled.html). That is, the *distance* between `Manual` and `Automatic` is of 1.48 SDs of *each of the groups* (here assumed to be equal). In this simple model, the pooled SD is the residual SD, so we can also estimate Cohen's *d* as: ```{r} coef(m)[2] / sigma(m) ``` And we can also get an approximation of Cohen's *d* by converting the $t$-statistic from the regression model via `t_to_d()`: ```{r} parameters::model_parameters(m) t_to_d(4.11, df_error = 30) ``` It is also interesting to note that using the `smart` method (explained in detail below) when standardizing parameters will give you indices equivalent to **Glass' *delta***, which is a standardized difference expressed in terms of SD of the reference group. ```{r} m <- lm(mpg ~ am, data = mtcars) standardize_parameters(m, method = "smart") glass_delta(mpg ~ am, data = mtcars) ``` ***... So note that some standardized differences are different than others! :)*** ## Standardizing Parameters of Linear Models As mentioned above, standardization of parameters can also be used to compare among parameters within the same model. Essentially, what prevents us from normally being able to compare among different parameters is that their underlying variables are on different scales.[^But also as noted above, this is not always an issue. For example, when the variables scale is important for the interpretation of results, standardization might in fact hinder interpretation!] For example, in the following example, we use a liner regression model to predict a worker's salary (in Shmekels) from their age (years), seniority (years), overtime (`xtra_hours`) and how many compliments they give their boss (`n_comps`). Let us explore the different parameter standardization methods provided by `effectsize`. ### Standardized Slopes are Not (Always) Correlations We saw that in simple linear models, the standardized slope is equal to the correlation between the outcome and predictor - does this hold for **multiple regression** as well? As in each effect in a regression model is "adjusted" for the other ones, we might expect coefficients to be somewhat alike to **partial correlations**. Let's first start by computing the partial correlation between numeric predictors and the outcome. ```{r} data("hardlyworking", package = "effectsize") head(hardlyworking) correlation::correlation( hardlyworking, select = "salary", select2 = c("xtra_hours", "n_comps", "age", "seniority"), partial = TRUE # get partial correlations ) ``` Let's compare these to the standardized slopes: ```{r} mod <- lm(salary ~ xtra_hours + n_comps + age + seniority, data = hardlyworking) standardize_parameters(mod) ``` They are quite different! It seems then that ***standardized slopes in multiple linear regressions are not the same a correlations or partial correlations*** :( However, not all hope is lost yet - we can still try and recover the partial correlations from our model, in another way: by converting the *t*-statistics (and their degrees of freedom, *df*) into a partial correlation coefficient *r*. ```{r} params <- parameters::model_parameters(mod) t_to_r(params$t[-1], df_error = params$df_error[-1]) ``` Wow, the retrieved correlations coefficients from the regression model are **exactly** the same as the partial correlations we estimated above! So these "*r*" effect sizes can also be used. ### Methods of Standardizing Parameters Let's convert `age` into a 3-level factor: ```{r} hardlyworking$age_g <- cut(hardlyworking$age, breaks = c(25,30,35,45)) mod <- lm(salary ~ xtra_hours + n_comps + age_g + seniority, data = hardlyworking) parameters::model_parameters(mod) ``` It seems like the best or most important predictor is `n_comps` as it has the coefficient. However, it is hard to compare among predictors, as they are on different scales. To address this issue, we must have all the predictors on the same scale - usually in the arbitrary unit of *standard deviations*. #### **`"refit"`**: Re-fitting the model with standardized data **This method is based on a complete model re-fit with a standardized version of data**. Hence, this method is equal to standardizing the variables *before* fitting the model. It is the "purest" and the most accurate [@neter1989applied], but it is also the most computationally costly and long (especially for heavy models such as Bayesian models, or complex mixed models). This method is particularly recommended for models that include interactions or transformations (e.g., exponentiation, log, polynomial or spline terms). ```{r} standardize_parameters(mod, method = "refit") ``` `standardize_parameters` also has a `robust` argument (default to `FALSE`), which enables a **robust standardization of the data**, *i.e.*, based on the **median** and **MAD** instead of the **mean** and **SD**: ```{r} standardize_parameters(mod, method = "refit", robust = TRUE) ``` Note that since `age_g` is a factor, it is not numerically standardized, and so it standardized parameter is still not directly comparable to those of numeric variables. To address this, we can set `two_sd = TRUE`, thereby scaling parameters on 2 SDs (or MADs) of the predictors [@gelman2008scaling]. ```{r} standardize_parameters(mod, method = "refit", two_sd = TRUE) ``` `effectsize` also comes with a helper function that returns the re-fit model, without summarizing it, which can then be used as the original model would: ```{r} mod_z <- standardize(mod, two_sd = FALSE, robust = FALSE) mod_z parameters::model_parameters(mod_z) ``` #### **`"posthoc"`**: Refit without refitting Post-hoc standardization of the parameters aims at emulating the results obtained by `"refit"` without refitting the model. The coefficients are divided by the standard deviation (or MAD if `robust`) of the outcome (which becomes their expression 'unit'). Then, the coefficients related to numeric variables are additionally multiplied by the standard deviation (or MAD if `robust`) of the related terms, so that they correspond to changes of 1 SD of the predictor (e.g., "A change in 1 SD of *x* is related to a change of 0.24 of the SD of *y*). This does not apply to binary variables or factors, so the coefficients are still related to changes in levels. This method is not accurate and tend to give aberrant results when interactions are specified. ```{r} standardize_parameters(mod, method = "posthoc") ``` #### **`"smart"`**: Standardization of Model's parameters with Adjustment, Reconnaissance and Transformation > Experimental Similar to `method = "posthoc"` in that it does not involve model refitting. The difference is that the SD of the response is computed on the relevant section of the data. For instance, if a factor with 3 levels A (the intercept), B and C is entered as a predictor, the effect corresponding to B vs. A will be scaled by the variance of the response at the intercept only. As a results, the coefficients for effects of factors are similar to a Glass' *delta*. ```{r} standardize_parameters(mod, method = "smart") ``` #### **`"basic"`**: Raw scaling of the model frame This method is similar to `method = "posthoc"`, but treats all variables as continuous: it scales the coefficient by the standard deviation of model's matrix' parameter of factors levels (transformed to integers) or binary predictors. Although it can be argued that this might be inappropriate for these cases, this method allows for easier importance judgment across all predictor type (numeric, factor, interactions...). It is also the type of standardization implemented by default in other software packages (also `lm.beta::lm.beta()`), and, such as can be used for reproducibility and replication purposes. ```{r} standardize_parameters(mod, method = "basic") ``` ### Standardizing Parameters In Mixed Models Linear mixed models (LMM/HLM/MLM) offer an additional conundrum to standardization - how does one even calculate the SDs of the various predictors? Or of the response - is it the deviations within each group? Or perhaps between them? The solution: standardize according to level of the predictor [@hoffman2015longitudinal, page 342]! Level 1 parameters are standardized according to variance *within* groups, while level 2 parameters are standardized according to variance *between* groups. The resulting standardized coefficient are also called *pseudo*-standardized coefficients.[^Note that like method `"basic"`, these are based on the model matrix.] ```{r, eval=knitr::opts_chunk$get("eval") && require(lme4) && require(lmerTest), warning=FALSE} m <- lme4::lmer(Reaction ~ Days + (Days|Subject), data = lme4::sleepstudy) standardize_parameters(m, method = "pseudo", ci_method = "satterthwaite") # compare to: standardize_parameters(m, method = "basic", ci_method = "satterthwaite") ``` ### Standardizing Parameters In Generalized Linear Models Unlike linear (/mixed) models, in generalized linear (/mixed) models (GLMs) there is *less* of a need for standardization. Why? Because in many GLMs the estimated coefficients are themselves measures of effect size, such as *odds-ratios* (OR) in logistic regression, or *incidence rate ratios* (IRR) in Poisson regressions. This is because in such model the outcome is **not** on an arbitrary scale - that is, the meaning of rates and probabilities are changed by arbitrary linear transformations. But still, some standardization is sometimes needed, for the predictors. Luckily, `standardize_parameters()` (and `standardize()`) are smart enough to know when GLMs are passed so as to only standardize according to the predictors: ```{r} mod_b <- glm(am ~ mpg + factor(cyl), data = mtcars, family = binomial()) standardize_parameters(mod_b, method = "refit", two_sd = TRUE) # standardize_parameters(mod_b, method = "posthoc", two_sd = TRUE) # standardize_parameters(mod_b, method = "basic") ``` These can then be converted to OR (with `exp()`) and discussed as the "*change in Odds as a function of a change in one SD of x*". ```{r} std <- standardize_parameters(mod_b, method = "refit", two_sd = TRUE) exp(std$Std_Coefficient) ``` Or we can directly ask for the coefficients to be exponentiated: ```{r} standardize_parameters(mod_b, method = "refit", two_sd = TRUE, exponentiate = TRUE) ``` ## Cohen's *f* Cohen's $f$ (of [ANOVA fame](https://easystats.github.io/effectsize/articles/anovaES.html)) can be used as a measure of effect size in the context of sequential multiple regression (i.e., [**nested models**](https://easystats.github.io/performance/reference/test_performance.html)). That is, when comparing two models, we can examine the ratio between the increase in $R^2$ and the unexplained variance: $$ f^{2}={R_{AB}^{2}-R_{A}^{2} \over 1-R_{AB}^{2}} $$ ```{r} m1 <- lm(salary ~ xtra_hours, data = hardlyworking) m2 <- lm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking) cohens_f_squared(m1, model2 = m2) ``` # References effectsize/vignettes/bibliography.bib0000644000175000017500000003543214170302654017705 0ustar nileshnilesh@article{lovakov2021empirically, title={Empirically Derived Guidelines for Effect Size Interpretation in Social Psychology}, author={Lovakov, Andrey and Agadullina, Elena R}, journal={European Journal of Social Psychology}, year={2021} } @article{vehtari2019rank, title={Rank-normalization, folding, and localization: An improved $$\backslash$widehat $\{$R$\}$ $ for assessing convergence of MCMC}, author={Vehtari, Aki and Gelman, Andrew and Simpson, Daniel and Carpenter, Bob and B{\"u}rkner, Paul-Christian}, journal={arXiv preprint arXiv:1903.08008}, year={2019} } @article{funder2019evaluating, title={Evaluating effect size in psychological research: sense and nonsense}, author={Funder, David C and Ozer, Daniel J}, journal={Advances in Methods and Practices in Psychological Science}, pages={2515245919847202}, year={2019}, publisher={SAGE Publications Sage CA: Los Angeles, CA} } @article{burkner2017brms, title={brms: An R package for Bayesian multilevel models using Stan}, author={B{\"u}rkner, Paul-Christian and others}, journal={Journal of Statistical Software}, volume={80}, number={1}, pages={1--28}, year={2017}, publisher={Foundation for Open Access Statistics} } @article{gignac2016effect, title={Effect size guidelines for individual differences researchers}, author={Gignac, Gilles E and Szodorai, Eva T}, journal={Personality and individual differences}, volume={102}, pages={74--78}, year={2016}, publisher={Elsevier} } @article{jarosz2014odds, title={What are the odds? A practical guide to computing and reporting Bayes factors}, author={Jarosz, Andrew F and Wiley, Jennifer}, journal={The Journal of Problem Solving}, volume={7}, number={1}, pages={2}, year={2014}, publisher={Purdue University Press} } @book{field2013discovering, title={Discovering statistics using IBM SPSS statistics}, author={Field, Andy}, year={2013}, publisher={sage} } @article{menard2011standards, title={Standards for standardized logistic regression coefficients}, author={Menard, Scott}, journal={Social Forces}, volume={89}, number={4}, pages={1409--1428}, year={2011}, publisher={The University of North Carolina Press} } @article{schielzeth2010simple, title={Simple means to improve the interpretability of regression coefficients}, author={Schielzeth, Holger}, journal={Methods in Ecology and Evolution}, volume={1}, number={2}, pages={103--113}, year={2010}, publisher={Wiley Online Library} } @article{gelman2008scaling, title={Scaling regression inputs by dividing by two standard deviations}, author={Gelman, Andrew}, journal={Statistics in medicine}, volume={27}, number={15}, pages={2865--2873}, year={2008}, publisher={Wiley Online Library} } @article{menard2004six, title={Six approaches to calculating standardized logistic regression coefficients}, author={Menard, Scott}, journal={The American Statistician}, volume={58}, number={3}, pages={218--223}, year={2004}, publisher={Taylor \& Francis} } @article{bring1994standardize, title={How to standardize regression coefficients}, author={Bring, Johan}, journal={The American Statistician}, volume={48}, number={3}, pages={209--213}, year={1994}, publisher={Taylor \& Francis} } @article{neter1989applied, title={Applied linear regression models}, author={Neter, John and Wasserman, William and Kutner, Michael H}, year={1989}, publisher={Irwin Homewood, IL} } @article{hair2011pls, title={PLS-SEM: Indeed a silver bullet}, author={Hair, Joe F and Ringle, Christian M and Sarstedt, Marko}, journal={Journal of Marketing theory and Practice}, volume={19}, number={2}, pages={139--152}, year={2011}, publisher={Taylor \& Francis} } @article{chen2010big, title={How big is a big odds ratio? Interpreting the magnitudes of odds ratios in epidemiological studies}, author={Chen, Henian and Cohen, Patricia and Chen, Sophie}, journal={Communications in Statistics—Simulation and Computation{\textregistered}}, volume={39}, number={4}, pages={860--864}, year={2010}, publisher={Taylor \& Francis} } @article{sawilowsky2009new, title={New effect size rules of thumb}, author={Sawilowsky, Shlomo S}, year={2009} } @article{sanchez2003effect, title={Effect-size indices for dichotomized outcomes in meta-analysis.}, author={S{\'a}nchez-Meca, Julio and Mar{\'\i}n-Mart{\'\i}nez, Fulgencio and Chac{\'o}n-Moscoso, Salvador}, journal={Psychological methods}, volume={8}, number={4}, pages={448}, year={2003}, publisher={American Psychological Association} } @article{chin1998partial, title={The partial least squares approach to structural equation modeling}, author={Chin, Wynne W and others}, journal={Modern methods for business research}, volume={295}, number={2}, pages={295--336}, year={1998}, publisher={London} } @book{evans1996straightforward, title={Straightforward statistics for the behavioral sciences.}, author={Evans, James D}, year={1996}, publisher={Thomson Brooks/Cole Publishing Co} } @article{raftery1995bayesian, title={Bayesian model selection in social research}, author={Raftery, Adrian E}, journal={Sociological methodology}, volume={25}, pages={111--164}, year={1995}, publisher={Blackwell Publishers} } @article{gelman1992inference, title={Inference from iterative simulation using multiple sequences}, author={Gelman, Andrew and Rubin, Donald B and others}, journal={Statistical science}, volume={7}, number={4}, pages={457--472}, year={1992}, publisher={Institute of Mathematical Statistics} } @book{falk1992primer, title={A primer for soft modeling.}, author={Falk, R Frank and Miller, Nancy B}, year={1992}, publisher={University of Akron Press} } @book{cohen1988statistical, title={Statistical power analysis for the behavioral sciences, 2nd ed.}, author={Cohen, J}, year={1988}, publisher={New York: Routledge} } @misc{jeffreys1961theory, title={Theory of probability, Clarendon}, author={Jeffreys, Harold}, year={1961}, publisher={Oxford} } @article{friedman1982simplified, title={Simplified determinations of statistical power, magnitude of effect and research sample sizes}, author={Friedman, Herbert}, journal={Educational and Psychological Measurement}, volume={42}, number={2}, pages={521--526}, year={1982}, publisher={Sage Publications Sage CA: Thousand Oaks, CA} } @article{albers2018power, title={When power analyses based on pilot data are biased: Inaccurate effect size estimators and follow-up bias}, author={Albers, Casper and Lakens, Dani{\"e}l}, journal={Journal of experimental social psychology}, volume={74}, pages={187--195}, year={2018}, publisher={Elsevier} } @article{mordkoff2019simple, title={A simple method for removing bias from a popular measure of standardized effect size: Adjusted partial eta squared}, author={Mordkoff, J Toby}, journal={Advances in Methods and Practices in Psychological Science}, volume={2}, number={3}, pages={228--232}, year={2019}, publisher={SAGE Publications Sage CA: Los Angeles, CA} } @book{wolf1986meta, title={Meta-analysis: Quantitative methods for research synthesis}, author={Wolf, Fredric M}, volume={59}, year={1986}, publisher={Sage} } @article{rosenthal1991meta, title={Meta-analytic procedures for social sciences}, author={Rosenthal, Robert}, journal={Newbury Park, CA: Sage}, volume={10}, pages={9781412984997}, year={1991} } @article{rosnow2000contrasts, title={Contrasts and correlations in effect-size estimation}, author={Rosnow, Ralph L and Rosenthal, Robert and Rubin, Donald B}, journal={Psychological science}, volume={11}, number={6}, pages={446--453}, year={2000}, publisher={SAGE Publications Sage CA: Los Angeles, CA} } @article{cohen1965some, title={Some statistical issues in psychological research}, author={Cohen, Jacob and others}, journal={Handbook of clinical psychology}, pages={95--121}, year={1965} } @article{cohen1992power, title={A power primer.}, author={Cohen, Jacob}, journal={Psychological bulletin}, volume={112}, number={1}, pages={155}, year={1992}, publisher={American Psychological Association} } @article{borenstein2009converting, title={Converting among effect sizes}, author={Borenstein, Michael and Hedges, Larry V and Higgins, JPT and Rothstein, Hannah R}, journal={Introduction to meta-analysis}, pages={45--49}, year={2009}, publisher={John Wiley \& Sons, Chichester} } @article{grant2014converting, title={Converting an odds ratio to a range of plausible relative risks for better communication of research findings}, author={Grant, Robert L}, journal={Bmj}, volume={348}, pages={f7450}, year={2014}, publisher={British Medical Journal Publishing Group} } @article{marsman2019bayesian, title={Bayesian estimation of explained variance in ANOVA designs}, author={Marsman, Maarten and Waldorp, Lourens and Dablander, Fabian and Wagenmakers, Eric-Jan}, journal={Statistica Neerlandica}, volume={73}, number={3}, pages={351--372}, year={2019}, publisher={Wiley Online Library} } @article{gelman2014bayesian, title={Bayesian data analysis (Vol. 2)}, author={Gelman, Andrew and Carlin, John B and Stern, Hal S and Rubin, Donald B}, journal={Boca Raton, FL: Chapman}, year={2014} } @book{hoffman2015longitudinal, title={Longitudinal analysis: Modeling within-person fluctuation and change}, author={Hoffman, Lesa}, year={2015}, publisher={Routledge} } @book{allen2017statistics, title={Statistics and experimental design for psychologists: A model comparison approach}, author={Allen, Rory}, year={2017}, publisher={World Scientific Publishing Company} } @article{carroll1975sampling, title={Sampling Characteristics of Kelley's Epsilon and Hays' Omega}, author={Carroll, Robert M and Nordholm, Lena A}, journal={Educational and Psychological Measurement}, volume={35}, number={3}, pages={541--554}, year={1975}, publisher={Sage Publications Sage CA: Thousand Oaks, CA} } @article{olejnik2003generalized, title={Generalized eta and omega squared statistics: measures of effect size for some common research designs.}, author={Olejnik, Stephen and Algina, James}, journal={Psychological methods}, volume={8}, number={4}, pages={434}, year={2003}, publisher={American Psychological Association} } @article{landis1977measurement, title={The measurement of observer agreement for categorical data}, author={Landis, J Richard and Koch, Gary G}, journal={biometrics}, pages={159--174}, year={1977}, publisher={JSTOR} } @book{bollen1989structural, title={Structural equations with latent variables}, author={Bollen, Kenneth A}, publisher={John Wiley \& Sons}, address={New York}, year={1989}, ISBN={9780471011712}, DOI = {10.1002/9781118619179} } @article{ludecke2020extracting, title={Extracting, computing and exploring the parameters of statistical models using {R}}, author={L{\"u}decke, Daniel and Ben-Shachar, Mattan S and Patil, Indrajeet and Makowski, Dominique}, journal={Journal of Open Source Software}, volume={5}, number={53}, pages={2445}, year={2020}, doi = {10.21105/joss.02445} } @manual{rcore, title = {{R}: A Language and Environment for Statistical Computing}, author = {{R Core Team}}, organization = {R Foundation for Statistical Computing}, address = {Vienna, Austria}, year = {2020}, url = {https://www.R-project.org/} } @article{patil2020ggstatsplot, title = {{ggstatsplot}: 'ggplot2' Based Plots with Statistical Details}, author = {Indrajeet Patil}, year = {2018}, journal = {CRAN}, url = {https://CRAN.R-project.org/package=ggstatsplot/}, doi = {10.5281/zenodo.2074621}, } @manual{sjoberg2020gtsummary, title = {{g}tsummary: Presentation-Ready Data Summary and Analytic Result Tables}, author = {Daniel D. Sjoberg and Michael Curry and Margie Hannum and Karissa Whiting and Emily C. Zabor}, year = {2020}, note = {R package version 1.3.5}, url = {https://CRAN.R-project.org/package=gtsummary/}, } @manual{behrendt2014lmbeta, title = {{l}m.beta: Add Standardized Regression Coefficients to lm-Objects}, author = {Stefan Behrendt}, year = {2014}, note = {R package version 1.5-1}, url = {https://CRAN.R-project.org/package=lm.beta/}, } @manual{buchanan2019MOTE, title = {{MOTE: Measure of the Effect}: Package to assist in effect size calculations and their confidence intervals}, author = {Erin M. Buchanan and Amber Gillenwaters and John E. Scofield and K.D. Valentine}, year = {2019}, note = {R package version 1.0.2}, url = {https://github.com/doomlab/MOTE/}, } @manual{kelley2020MBESS, title = {MBESS: The MBESS {R} Package}, author = {Ken Kelley}, year = {2020}, note = {R package version 4.8.0}, url = {https://CRAN.R-project.org/package=MBESS/}, } @article{steiger2004beyond, title={Beyond the {F} test: effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis}, author={Steiger, James H}, journal={Psychological Methods}, volume={9}, number={2}, pages={164--182}, year={2004}, publisher={American Psychological Association}, doi = {10.1037/1082-989X.9.2.164} } @book{hedges1985statistical, title={Statistical methods for meta-analysis}, author={Hedges, L and Olkin, I}, publisher={Academic Press}, address={San Diego}, ISBN={9780080570655}, year={1985} } @book{cramer1946mathematical, title={Mathematical methods of statistics}, author={Cram{\'e}r, Harald}, year={1946}, publisher={Princeton University Press}, address={Princeton}, ISBN={9781400883868} } @article{luedecke2019insight, title = {{i}nsight: A unified interface to access information from model objects in {R}}, volume = {4}, doi = {10.21105/joss.01412}, number = {38}, journal = {Journal of Open Source Software}, author = {Daniel Lüdecke and Philip Waggoner and Dominique Makowski}, year = {2019}, pages = {1412}, } @article{kelley1935unbiased, title={An unbiased correlation ratio measure}, author={Kelley, Truman L}, journal={Proceedings of the National Academy of Sciences of the United States of America}, volume={21}, number={9}, pages={554--559}, year={1935}, publisher={National Academy of Sciences}, doi={10.1073/pnas.21.9.554} }effectsize/vignettes/anovaES.Rmd0000644000175000017500000002300514170072537016551 0ustar nileshnilesh--- title: "Effect sizes for ANOVAs" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, ANOVA] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Effect sizes for ANOVAs} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") options(digits = 2) knitr::opts_chunk$set(comment = ">", warning = FALSE) set.seed(1) pkgs <- c("effectsize", "parameters", "car", "afex") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` ## Eta2 In the context of ANOVA-like tests, it is common to report ANOVA-like effect sizes. Unlike [standardized parameters](https://easystats.github.io/effectsize/articles/standardize_parameters.html), these effect sizes represent the amount of variance explained by each of the model's terms, where each term can be represented by 1 *or more* parameters. For example, in the following case, the parameters for the `treatment` term represent specific contrasts between the factor's levels (treatment groups) - the difference between each level and the reference level (`obk.long == 'control'`). ```{r} data(obk.long, package = "afex") # modify the data slightly for the demonstration: obk.long <- obk.long[1:240 %% 3 == 0, ] obk.long$id <- seq_len(nrow(obk.long)) m <- lm(value ~ treatment, data = obk.long) parameters::model_parameters(m) ``` But we can also ask about the overall effect of `treatment` - how much of the variation in our dependent variable `value` can be predicted by (or explained by) the variation between the `treatment` groups. Such a question can be answered with an ANOVA test: ```{r} parameters::model_parameters(anova(m)) ``` As we can see, the variance in `value` (the *sums-of-squares*, or *SS*) has been split into pieces: - The part associated with `treatment`. - The unexplained part (The Residual-*SS*). We can now ask what is the percent of the total variance in `value` that is associated with `treatment`. This measure is called Eta-squared (written as $\eta^2$): $$ \eta^2 = \frac{SS_{effect}}{SS_{total}} = \frac{72.23}{72.23 + 250.96} = 0.22 $$ and can be accessed via the `eta_squared()` function: ```{r} library(effectsize) eta_squared(m, partial = FALSE) ``` ### Adding More Terms When we add more terms to our model, we can ask two different questions about the percent of variance explained by a predictor - how much variance is accounted by the predictor in *total*, and how much is accounted when *controlling* for any other predictors. The latter questions is answered by the *partial*-Eta squared ($\eta^2_p$), which is the percent of the **partial** variance (after accounting for other predictors in the model) associated with a term: $$ \eta^2_p = \frac{SS_{effect}}{SS_{effect} + SS_{error}} $$ which can also be accessed via the `eta_squared()` function: ```{r} m <- lm(value ~ gender + phase + treatment, data = obk.long) eta_squared(m, partial = FALSE) eta_squared(m) # partial = TRUE by default ``` *(`phase` is a repeated-measures variable, but for simplicity it is not modeled as such.)* In the calculation above, the *SS*s were computed sequentially - that is the *SS* for `phase` is computed after controlling for `gender`, and the *SS* for `treatment` is computed after controlling for both `gender` and `phase`. This method of sequential *SS* is called also *type-I* test. If this is what you want, that's great - however in many fields (and other statistical programs) it is common to use "simultaneous" sums of squares (*type-II* or *type-III* tests), where each *SS* is computed controlling for all other predictors, regardless of order. This can be done with `car::Anova(type = ...)`: ```{r} eta_squared(car::Anova(m, type = 2), partial = FALSE) eta_squared(car::Anova(m, type = 3)) # partial = TRUE by default ``` $\eta^2_p$ will always be larger than $\eta^2$. The idea is to simulate the effect size in a design where only the term of interest was manipulated. This terminology assumes some causal relationship between the predictor and the outcome, which reflects the experimental world from which these analyses and measures hail; However, $\eta^2_p$ can also simply be seen as a **signal-to-noise- ratio**, as it only uses the term's *SS* and the error-term's *SS*.[^in repeated-measure designs the term-specific residual-*SS* is used for the computation of the effect size]. (Note that in a one-way fixed-effect designs $\eta^2 = \eta^2_p$.) ### Adding Interactions Type II and type III treat interaction differently. Without going into the weeds here, keep in mind that **when using type III SS, it is important to center all of the predictors**; for numeric variables this can be done by mean-centering the predictors; for factors this can be done by using orthogonal coding (such as `contr.sum` for *effects-coding*) for the dummy variables (and *NOT* treatment coding, which is the default in R). This unfortunately makes parameter interpretation harder, but *only* when this is does do the *SS*s associated with each lower-order term (or lower-order interaction) represent the ***SS*** of the **main effect** (with treatment coding they represent the *SS* of the simple effects). ```{r} # compare m_interaction1 <- lm(value ~ treatment * gender, data = obk.long) # to: m_interaction2 <- lm( value ~ treatment * gender, data = obk.long, contrasts = list( treatment = "contr.sum", gender = "contr.sum" ) ) eta_squared(car::Anova(m_interaction1, type = 3)) eta_squared(car::Anova(m_interaction2, type = 3)) ``` If all of this type-III-effects-coding seems like a hassle, you can use the `afex` package, which takes care of all of this behind the scenes: ```{r} library(afex) m_afex <- aov_car(value ~ treatment * gender + Error(id), data = obk.long) eta_squared(m_afex) ``` ## Other Measures of Effect Size ### Unbiased Effect Sizes These effect sizes are unbiased estimators of the population's $\eta^2$: - **Omega Squared** ($\omega^2$) - **Epsilon Squared** ($\epsilon^2$), also referred to as *Adjusted Eta Squared*. ```{r} omega_squared(m_afex) epsilon_squared(m_afex) ``` Both $\omega^2$ and $\epsilon^2$ (and their partial counterparts, $\omega^2_p$ & $\epsilon^2_p$) are unbiased estimators of the population's $\eta^2$ (or $\eta^2_p$, respectively), which is especially important is small samples. Though $\omega^2$ is the more popular choice [@albers2018power], $\epsilon^2$ is analogous to adjusted-$R^2$ [@allen2017statistics, p. 382], and has been found to be less biased [@carroll1975sampling]. ### Generalized Eta2 *Partial* Eta squared aims at estimating the effect size in a design where only the term of interest was manipulated, assuming all other terms are have also manipulated. However, not all predictors are always manipulated - some can only be observed. For such cases, we can use *generalized* Eta squared ($\eta^2_G$), which like $\eta^2_p$ estimating the effect size in a design where only the term of interest was manipulated, accounting for the fact that some terms cannot be manipulated (and so their variance would be present in such a design). ```{r} eta_squared(m_afex, generalized = "gender") ``` $\eta^2_G$ is useful in repeated-measures designs, as it can estimate what a *within-subject* effect size would have been had that predictor been manipulated *between-subjects* [@olejnik2003generalized]. ### Cohen's *f* Finally, we have the forgotten child - Cohen's $f$. Cohen's $f$ is a transformation of $\eta^2_p$, and is the ratio between the term-*SS* and the error-*SS*. $$\text{Cohen's} f_p = \sqrt{\frac{\eta^2_p}{1-\eta^2_p}} = \sqrt{\frac{SS_{effect}}{SS_{error}}}$$ It can take on values between zero, when the population means are all equal, and an indefinitely large number as the means are further and further apart. It is analogous to Cohen's $d$ when there are only two groups. ```{r} cohens_f(m_afex) ``` ## When Sum-of-Squares are Hard to Come By Until now we've discusses effect sizes in fixed-effect linear model and repeated-measures ANOVA's - cases where the *SS*s are readily available, and so the various effect sized presented can easily be estimated. How ever this is not always the case. For example, in linear mixed models (LMM/HLM/MLM), the estimation of all required *SS*s is not straightforward. However, we can still *approximate* these effect sizes (only their partial versions) based on the **test-statistic approximation method** (learn more in the [*Effect Size from Test Statistics* vignette](https://easystats.github.io/effectsize/articles/from_test_statistics.html)). ```{r, eval=require(lmerTest)} library(lmerTest) fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) anova(fit_lmm) # note the type-3 errors F_to_eta2(45.8, df = 1, df_error = 17) ``` Or directly with `eta_squared() and co.: ```{r, eval=require(lmerTest)} eta_squared(fit_lmm) epsilon_squared(fit_lmm) omega_squared(fit_lmm) ``` Another case where *SS*s are not available is when use Bayesian models. `effectsize` has Bayesian solutions for Bayesian models, about which you can read in the [*Effect Sizes for Bayesian Models* vignette](https://easystats.github.io/effectsize/articles/bayesian_models.html). # References effectsize/vignettes/convert.Rmd0000644000175000017500000001416714170065645016710 0ustar nileshnilesh--- title: "Converting Between Indices of Effect Size" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, conversion] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Converting Between Indices of Effect Size} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 3) pkgs <- c("effectsize", "ggplot2", "correlation", "parameters", "bayestestR") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` The `effectsize` package contains function to convert among indices of effect size. This can be useful for meta-analyses, or any comparison between different types of statistical analyses. ## Converting Between *d*, *r*, and *OR* The most basic conversion is between *r* values, a measure of standardized association between two continuous measures, and *d* values (such as Cohen's *d*), a measure of standardized differences between two groups / conditions. Let's simulate some data: ```{r} set.seed(1) data <- bayestestR::simulate_difference( n = 10, d = 0.2, names = c("Group", "Outcome") ) ``` ```{r, echo=FALSE} print(data, digits = 3) ``` We can compute Cohen's *d* between the two groups: ```{r} cohens_d(Outcome ~ Group, data = data) ``` But we can also treat the 2-level `group` variable as a numeric variable, and compute Pearson's *r*: ```{r, warning=FALSE} correlation::correlation(data, include_factors = TRUE)[2, ] ``` But what if we only have summary statistics? Say, we only have $d=-0.37$ and we want to know what the *r* would have been? We can approximate *r* using the following formula [@borenstein2009converting]: $$ r \approx \frac{d}{\sqrt{d^2 + 4}} $$ And indeed, if we use `d_to_r()`, we get a pretty decent approximation: ```{r} d_to_r(-0.31) ``` (Which also works in the other way, with `r_to_d(0.17)` gives `r round(r_to_d(-0.17),3)`) As we can see, these are rough approximations, but they can be useful when we don't have the raw data on hand. ### In multiple regression Although not exactly a classic Cohen's d, we can also approximate a partial-*d* value (that is, the standardized difference between two groups / conditions, with variance from other predictors partilled out). For example: ```{r} fit <- lm(mpg ~ am + hp, data = mtcars) parameters::model_parameters(fit) # A couple of ways to get partial-d: 5.28 / sigma(fit) t_to_d(4.89, df_error = 29)[[1]] ``` We can convert these semi-*d* values to *r* values, but in this case these represent the *partial* correlation: ```{r} t_to_r(4.89, df_error = 29) correlation::correlation(mtcars[, c("mpg", "am", "hp")], partial = TRUE)[1, ] # all close to: d_to_r(1.81) ``` ## From Odds ratios In binomial regression (more specifically in logistic regression), Odds ratios (OR) are themselves measures of effect size; they indicate the expected change in the odds of a some event. In some fields, it is common to dichotomize outcomes in order to be able to analyze them with logistic models. For example, if the outcome is the count of white blood cells, it can be more useful (medically) to predict the crossing of the threshold rather than the raw count itself. And so, where some scientists would maybe analyze the above data with a *t*-test and present Cohen's *d*, others might analyze it with a logistic regression model on the dichotomized outcome, and present OR. So the question can be asked: given such a OR, what would Cohen's *d* have been? Fortunately, there is a formula to approximate this [@sanchez2003effect]: $$ d = log(OR) \times \frac{\sqrt{3}}{\pi} $$ which is implemented in the `oddsratio_to_d()` function. Let's give it a try: ```{r} # 1. Set a threshold thresh <- 0 # 2. dichotomize the outcome data$Outcome_binom <- data$Outcome < thresh # 3. Fit a logistic regression: fit <- glm(Outcome_binom ~ Group, data = data, family = binomial() ) parameters::model_parameters(fit) # Convert log(OR) (the coefficient) to d oddsratio_to_d(-0.81, log = TRUE) ``` ### Odds ratios to Risk Ratios Odds ratio, although popular, are not very intuitive in their interpretations. We don't often think about the chances of catching a disease in terms of *odds*, instead we instead tend to think in terms of *probability* or some event - or the *risk*. Talking about *risks* we can also talk about the *change in risk*, knows as the *risk ratio* (*RR*). For example, if we find that for individual suffering from a migraine, for every bowl of brussels sprouts they eat, they're odds of reducing the migraine increase by an $OR = 3.5$ over a period of an hour. So, should people eat brussels sprouts to effectively reduce pain? Well, hard to say... Maybe if we look at *RR* we'll get a clue. We can convert between *OR* and *RR* for the following formula [@grant2014converting]: $$ RR = \frac{OR}{(1 - p0 + (p0 \times OR))} $$ Where $p0$ is the base-rate risk - the probability of the event without the intervention (e.g., what is the probability of the migraine subsiding within an hour without eating any brussels sprouts). If it the base-rate risk is, say, 85%, we get a *RR* of: ```{r} OR <- 3.5 baserate <- 0.85 oddsratio_to_riskratio(OR, baserate) ``` That is - for every bowl of brussels sprouts, we increase the chances of reducing the migraine by a mere 12%! Is if worth it? Depends on you affinity to brussels sprouts... Note that the base-rate risk is crucial here. If instead of 85% it was only 4%, then the *RR* would be: ```{r} OR <- 3.5 baserate <- 0.04 oddsratio_to_riskratio(OR, baserate) ``` That is - for every bowl of brussels sprouts, we increase the chances of reducing the migraine by a whopping 318%! Is if worth it? I guess that still depends on your affinity to brussels sprouts... # References effectsize/vignettes/effectsize_API.Rmd0000644000175000017500000002201314170065645020035 0ustar nileshnilesh--- title: "Support functions for model extensions" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, ANOVA, standardization, standardized coefficients] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Support functions for model extensions} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) knitr::opts_chunk$set(comment = ">", warning = FALSE, message = FALSE) options(digits = 2) options(knitr.kable.NA = '') pkgs <- c("effectsize") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(333) ``` ```{r} library(effectsize) ``` ## Supporting ANOVA Effect Sizes To add support for you model, create a new `.anova_es()` method function. This functions should generally do 3 things: 1. Build a data frame with all the required information. 2. Pass the data frame to one of the 3 functions. 3. Set some attributes to the output. ### Simple ANOVA tables The input data frame must have these columns: - `Parameter` (char) - The name of the parameter or, more often, the term. - `Sum_Squares` (num) - The sum of squares. - `df` (num) - The degrees of freedom associated with the `Sum_Squares`. - `Mean_Square_residuals` (num; *optional*) - if *not* present, is calculated as `Sum_Squares / df`. (Any other column is ignored.) And exactly *1* row Where `Parameter` is `Residual`. Optionally, one of the rows can have a `(Intercept)` value for `Parameter`. An example of a minimally valid data frame: ```{r} min_aov <- data.frame( Parameter = c("(Intercept)", "A", "B", "Residuals"), Sum_Squares = c(30, 40, 10, 100), df = c(1, 1, 2, 50) ) ``` Pass the data frame to `.es_aov_simple()`: ```{r} .es_aov_simple( min_aov, type = "eta", partial = TRUE, generalized = FALSE, include_intercept = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE ) ``` The output is a data frame with the columns: `Parameter`, the effect size, and (optionally) `CI` + `CI_low` + `CI_high`, And with the following attributes: `partial`, `generalized`, `ci`, `alternative`, `anova_type` (`NA` or `NULL`), `approximate`. You can then set the `anova_type` attribute to {1, 2, 3, or `NA`} and return the output. ### ANOVA Tables with Multiple Error Strata (e.g., `aovlist` models.) The input data frame must have these columns: - `Group` (char) - The strata - `Parameter` (char) - `Sum_Squares` (num) - `df` (num) - `Mean_Square_residuals` (num; *optional*) And exactly *1* row ***per `Group`*** Where `Parameter` is `Residual`. Optionally, one of the rows can have a `(Intercept)` value for `Parameter`. An example of a minimally valid data frame: ```{r} min_aovlist <- data.frame( Group = c("S", "S", "S:A", "S:A"), Parameter = c("(Intercept)", "Residuals", "A", "Residuals"), Sum_Squares = c(34, 21, 34, 400), df = c(1, 12, 4, 30) ) ``` Pass the data frame to `.es_aov_strata()`, along with a list of predictors (including the stratifying variables) to the `DV_names` argument: ```{r} .es_aov_strata( min_aovlist, DV_names = c("S", "A"), type = "omega", partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = TRUE ) ``` The output is a data frame with the columns: `Group`, `Parameter`, the effect size, and (optionally) `CI` + `CI_low` + `CI_high`, And with the following attributes: `partial`, `generalized`, `ci`, `alternative`, `approximate`. You can then set the `anova_type` attribute to {1, 2, 3, or `NA`} and return the output. ### Approximate Effect sizes When *sums of squares* cannot be extracted, we can still get *approximate* effect sizes based on the `F_to_eta2()` family of functions. The input data frame must have these columns: - `Parameter` (char) - `F` (num) - The *F* test statistic. - `df` (num) - effect degrees of freedom. - (Can also have a `t` col instead, in which case `df` is set to 1, and `F` is `t^2`). - `df_error` (num) - error degrees of freedom. Optionally, one of the rows can have `(Intercept)` as the `Parameter`. An example of a minimally valid data frame: ```{r} min_anova <- data.frame( Parameter = c("(Intercept)", "A", "B"), F = c(4, 7, 0.7), df = c(1, 1, 2), df_error = 34 ) ``` Pass the table to `.es_aov_table()`: ```{r} .es_aov_table( min_anova, type = "eta", partial = TRUE, generalized = FALSE, include_intercept = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE ) ``` The output is a data frame with the columns: `Parameter`, the effect size, and (optionally) `CI` + `CI_low` + `CI_high`, And with the following attributes: `partial`, `generalized`, `ci`, `alternative`, `approximate`. You can then set the `anova_type` attribute to {1, 2, 3, or `NA`} and return the output, and optionally the `approximate` attribute, and return the output. ### *Example* Let's fit a simple linear model and change its class: ```{r} mod <- lm(mpg ~ factor(cyl) + am, mtcars) class(mod) <- "superMODEL" ``` We now need a new `.anova_es.superMODEL` function: ```{r} .anova_es.superMODEL <- function(model, ...) { # Get ANOVA table anov <- suppressWarnings(stats:::anova.lm(model)) anov <- as.data.frame(anov) # Clean up anov[["Parameter"]] <- rownames(anov) colnames(anov)[2:1] <- c("Sum_Squares", "df") # Pass out <- .es_aov_simple(anov, ...) # Set attribute attr(out, "anova_type") <- 1 out } ``` And... that's it! Our new `superMODEL` class of models is fully supported! ```{r} eta_squared(mod) eta_squared(mod, partial = FALSE) omega_squared(mod) # Etc... ``` ## Supporting Model Re-Fitting with Standardized Data `effectsize::standardize.default()` should support your model if you have methods for: 1. `{insight}` functions. 2. An `update()` method that can take the model and a data frame via the `data = ` argument. Or you can make your own `standardize.my_class()` function, DIY-style (possibly using `datawizard::standardize.data.frame()` or `datawizard::standardize.numeric()`). This function should return a fiffed model of the same class as the input model. ## Supporting Standardized Parameters `standardize_parameters.default()` offers a few methods of parameter standardization: - For `method = "refit"` all you need is to have `effectsize::standardize()` support (see above) as well as `parameters::model_parameters()`. - ***API for post-hoc methods coming soon...*** # References effectsize/vignettes/from_test_statistics.Rmd0000644000175000017500000001574514170065645021507 0ustar nileshnilesh--- title: "Effect Size from Test Statistics" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, standardization, cohen d] vignette: > %\VignetteIndexEntry{Effect Size from Test Statistics} \usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) library(effectsize) knitr::opts_chunk$set(comment = ">") options(digits = 2) options(knitr.kable.NA = "") pkgs <- c("effectsize", "afex", "lmerTest", "emmeans", "parameters") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } else { library(afex) library(lmerTest) library(emmeans) library(parameters) } set.seed(747) ``` # Introduction In many real world applications there are no straightforward ways of obtaining standardized effect sizes. However, it is possible to get approximations of most of the effect size indices ($d$, $r$, $\eta^2_p$...) with the use of test statistics. These conversions are based on the idea that **test statistics are a function of effect size and sample size**. Thus information about samples size (or more often of degrees of freedom) is used to reverse-engineer indices of effect size from test statistics. This idea and these functions also power our [***Effect Sizes From Test Statistics*** *shiny app*](https://easystats4u.shinyapps.io/statistic2effectsize/). The measures discussed here are, in one way or another, ***signal to noise ratios***, with the "noise" representing the unaccounted variance in the outcome variable^[Note that for generalized linear models (Poisson, Logistic...), where the outcome is never on an arbitrary scale, estimates themselves **are** indices of effect size! Thus this vignette is relevant only to general linear models.]. The indices are: - Percent variance explained ($\eta^2_p$, $\omega^2_p$, $\epsilon^2_p$). - Measure of association ($r$). - Measure of difference ($d$). ## (Partial) Percent Variance Explained These measures represent the ratio of $Signal^2 / (Signal^2 + Noise^2)$, with the "noise" having all other "signals" partial-ed out (be they of other fixed or random effects). The most popular of these indices is $\eta^2_p$ (Eta; which is equivalent to $R^2$). The conversion of the $F$- or $t$-statistic is based on @friedman1982simplified. Let's look at an example: ```{r} library(afex) data(md_12.1) aov_fit <- aov_car(rt ~ angle * noise + Error(id / (angle * noise)), data = md_12.1, anova_table = list(correction = "none", es = "pes") ) aov_fit ``` Let's compare the $\eta^2_p$ (the `pes` column) obtained here with ones recovered from `F_to_eta2()`: ```{r} library(effectsize) F_to_eta2( f = c(40.72, 33.77, 45.31), df = c(2, 1, 2), df_error = c(18, 9, 18) ) ``` **They are identical!**^[Note that these are *partial* percent variance explained, and so their sum can be larger than 1.] (except for the fact that `F_to_eta2()` also provides confidence intervals^[Confidence intervals for all indices are estimated using the non-centrality parameter method; These methods search for a the best non-central parameter of the non-central $F$/$t$ distribution for the desired tail-probabilities, and then convert these ncps to the corresponding effect sizes.] :) In this case we were able to easily obtain the effect size (thanks to `afex`!), but in other cases it might not be as easy, and using estimates based on test statistic offers a good approximation. For example: ### In Simple Effect and Contrast Analysis ```{r} library(emmeans) joint_tests(aov_fit, by = "noise") F_to_eta2( f = c(5, 79), df = 2, df_error = 29 ) ``` We can also use `t_to_eta2()` for contrast analysis: ```{r} pairs(emmeans(aov_fit, ~angle)) t_to_eta2( t = c(-5.7, -8.9, -3.2), df_error = 18 ) ``` ### In Linear Mixed Models ```{r} library(lmerTest) fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) anova(fit_lmm) F_to_eta2(45.8, 1, 17) ``` We can also use `t_to_eta2()` for the slope of `Days` (which in this case gives the same result). ```{r} parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite") t_to_eta2(6.77, df_error = 17) ``` ### Bias-Corrected Indices Alongside $\eta^2_p$ there are also the less biased $\omega_p^2$ (Omega) and $\epsilon^2_p$ (Epsilon; sometimes called $\text{Adj. }\eta^2_p$, which is equivalent to $R^2_{adj}$; @albers2018power, @mordkoff2019simple). ```{r} F_to_eta2(45.8, 1, 17) F_to_epsilon2(45.8, 1, 17) F_to_omega2(45.8, 1, 17) ``` ## Measure of Association Similar to $\eta^2_p$, $r$ is a signal to noise ratio, and is in fact equal to $\sqrt{\eta^2_p}$ (so it's really a *partial* $r$). It is often used instead of $\eta^2_p$ when discussing the *strength* of association (but I suspect people use it instead of $\eta^2_p$ because it gives a bigger number, which looks better). ### For Slopes ```{r} parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite") t_to_r(6.77, df_error = 17) ``` In a fixed-effect linear model, this returns the **partial** correlation. Compare: ```{r} fit_lm <- lm(rating ~ complaints + critical, data = attitude) parameters::model_parameters(fit_lm) t_to_r( t = c(7.46, 0.01), df_error = 27 ) ``` to: ```{r, eval=require(correlation, quietly = TRUE)} correlation::correlation(attitude[, c(1, 2, 6)], partial = TRUE)[1:2, c(2, 3, 7, 8)] ``` ### In Contrast Analysis This measure is also sometimes used in contrast analysis, where it is called the point bi-serial correlation - $r_{pb}$ [@cohen1965some; @rosnow2000contrasts]: ```{r} pairs(emmeans(aov_fit, ~angle)) t_to_r( t = c(-5.7, -8.9, -3.2), df_error = 18 ) ``` ## Measures of Difference These indices represent $Signal/Noise$ with the "signal" representing the difference between two means. This is akin to Cohen's $d$, and is a close approximation when comparing two groups of equal size [@wolf1986meta; @rosnow2000contrasts]. These can be useful in contrast analyses. ### Between-Subject Contrasts ```{r} m <- lm(breaks ~ tension, data = warpbreaks) em_tension <- emmeans(m, ~tension) pairs(em_tension) t_to_d( t = c(2.53, 3.72, 1.20), df_error = 51 ) ``` However, these are merely approximations of a *true* Cohen's *d*. It is advised to directly estimate Cohen's *d*, whenever possible. For example, here with `emmeans::eff_size()`: ```{r} eff_size(em_tension, sigma = sigma(m), edf = df.residual(m)) ``` ### Within-Subject Contrasts ```{r} pairs(emmeans(aov_fit, ~angle)) t_to_d( t = c(-5.7, -5.9, -3.2), df_error = 18, paired = TRUE ) ``` (Note set `paired = TRUE` to not over estimate the size of the effect; @rosenthal1991meta; @rosnow2000contrasts) # References effectsize/vignettes/interpret.Rmd0000644000175000017500000002554514170072534017241 0ustar nileshnilesh--- title: "Automated Interpretation of Indices of Effect Size" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, interpretation] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Automated Interpretation of Indices of Effect Size} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 2) pkgs <- c("effectsize") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` ## Why? The metrics used in statistics (indices of fit, model performance, or parameter estimates) can be very abstract. A long experience is required to intuitively ***feel*** the meaning of their values. In order to facilitate the understanding of the results they are facing, many scientists use (often implicitly) some set of **rules of thumb**. Some of these rules of thumb have been standardize and validated and subsequently published as guidelines. Understandably then, such rules of thumb are just suggestions and there is nothing universal about them. The interpretation of **any** effect size measures is always going to be relative to the discipline, the specific data, and the aims of the analyst. This is important because what might be considered a small effect in psychology might be large for some other field like public health. One of the most famous interpretation grids was proposed by **Cohen (1988)** for a series of widely used indices, such as the correlation **r** (*r* = .20, small; *r* = .40, moderate and *r* = .60, large) or the **standardized difference** (*Cohen's d*). However, there is now a clear evidence that Cohen's guidelines (which he himself later disavowed; Funder, 2019) are much too stringent and not particularly meaningful taken out of context [@funder2019evaluating]. This led to the emergence of a literature discussing and creating new sets of rules of thumb. Although **everybody** agrees on the fact that effect size interpretation in a study should be justified with a rationale (and depend on the context, the field, the literature, the hypothesis, etc.), these pre-baked rules can nevertheless be useful to give a rough idea or frame of reference to understand scientific results. The package **`effectsize`** catalogs such sets of rules of thumb for a variety of indices in a flexible and explicit fashion, helping you understand and report your results in a scientific yet meaningful way. Again, readers should keep in mind that these thresholds, as ubiquitous as they may be, **remain arbitrary**. Thus, their use should be discussed on a case-by-case basis depending on the field, hypotheses, prior results, and so on, to avoid their crystallization, as for the infamous $p < .05$ criterion of hypothesis testing. Moreover, some authors suggest the counter-intuitive idea that *very large effects*, especially in the context of psychological research, is likely to be a "gross overestimate that will rarely be found in a large sample or in a replication" [@funder2019evaluating]. They suggest that smaller effect size are worth taking seriously (as they can be potentially consequential), as well as more believable. ## Correlation *r* #### @funder2019evaluating ```r interpret_r(x, rules = "funder2019") ``` - **r < 0.05** - Tiny - **0.05 <= r < 0.1** - Very small - **0.1 <= r < 0.2** - Small - **0.2 <= r < 0.3** - Medium - **0.3 <= r < 0.4** - Large - **r >= 0.4** - Very large #### @gignac2016effect Gignac's rules of thumb are actually one of few interpretation grid justified and based on actual data, in this case on the distribution of effect magnitudes in the literature. ```r interpret_r(x, rules = "gignac2016") ``` - **r < 0.1** - Very small - **0.1 <= r < 0.2** - Small - **0.2 <= r < 0.3** - Moderate - **r >= 0.3** - Large #### @cohen1988statistical ```r interpret_r(x, rules = "cohen1988") ``` - **r < 0.1** - Very small - **0.1 <= r < 0.3** - Small - **0.3 <= r < 0.5** - Moderate - **r >= 0.5** - Large #### @evans1996straightforward ```r interpret_r(x, rules = "evans1996") ``` - **r < 0.2** - Very weak - **0.2 <= r < 0.4** - Weak - **0.4 <= r < 0.6** - Moderate - **0.6 <= r < 0.8** - Strong - **r >= 0.8** - Very strong #### @lovakov2021empirically ```r interpret_r(x, rules = "lovakov2021") ``` - **r < 0.12** - Very small - **0.12 <= r < 0.24** - Small - **0.24 <= r < 0.41** - Moderate - **r >= 0.41** - Large ## Standardized Difference *d* (Cohen's *d*) The standardized difference can be obtained through the standardization of linear model's parameters or data, in which they can be used as indices of effect size. #### @cohen1988statistical ```r interpret_cohens_d(x, rules = "cohen1988") ``` - **d < 0.2** - Very small - **0.2 <= d < 0.5** - Small - **0.5 <= d < 0.8** - Medium - **d >= 0.8** - Large #### @sawilowsky2009new ```r interpret_cohens_d(x, rules = "sawilowsky2009") ``` - **d < 0.1** - Tiny - **0.1 <= d < 0.2** - Very small - **0.2 <= d < 0.5** - Small - **0.5 <= d < 0.8** - Medium - **0.8 <= d < 1.2** - Large - **1.2 <= d < 2** - Very large - **d >= 2** - Huge #### @gignac2016effect Gignac's rules of thumb are actually one of few interpretation grid justified and based on actual data, in this case on the distribution of effect magnitudes in the literature. These is in fact the same grid used for *r*, based on the conversion of *r* to *d*: ```r interpret_cohens_d(x, rules = "gignac2016") ``` - **d < 0.2** - Very small - **0.2 <= d < 0.41** - Small - **0.41 <= d < 0.63** - Moderate - **d >= 0.63** - Large #### @lovakov2021empirically ```r interpret_cohens_d(x, rules = "lovakov2021") ``` - **r < 0.15** - Very small - **0.15 <= r < 0.36** - Small - **0.36 <= r < 0.65** - Moderate - **r >= 0.65** - Large ## Odds Ratio (OR) Odds ratio, and *log* odds ratio, are often found in epidemiological studies. However, they are also the parameters of ***logistic*** regressions, where they can be used as indices of effect size. Note that the (log) odds ratio from logistic regression coefficients are *unstandardized*, as they depend on the scale of the predictor. In order to apply the following guidelines, make sure you [*standardize*](https://easystats.github.io/effectsize/articles/standardize_parameters.html) your predictors! Keep in mind that these apply to Odds *ratios*, so Odds ratio of 10 is as extreme as a Odds ratio of 0.1 (1/10). #### @chen2010big ```r interpret_oddsratio(x, rules = "chen2010") ``` - **OR < 1.68** - Very small - **1.68 <= OR < 3.47** - Small - **3.47 <= OR < 6.71** - Medium - **OR >= 6.71 ** - Large #### @cohen1988statistical ```r interpret_oddsratio(x, rules = "cohen1988") ``` - **OR < 1.44** - Very small - **1.44 <= OR < 2.48** - Small - **2.48 <= OR < 4.27** - Medium - **OR >= 4.27 ** - Large This converts (log) odds ratio to standardized difference *d* using the following formula [@cohen1988statistical;@sanchez2003effect]: $$ d = log(OR) \times \frac{\sqrt{3}}{\pi} $$ ## Coefficient of determination (R2) ### For Linear Regression #### @cohen1988statistical ```r interpret_r2(x, rules = "cohen1988") ``` - **R2 < 0.02** - Very weak - **0.02 <= R2 < 0.13** - Weak - **0.13 <= R2 < 0.26** - Moderate - **R2 >= 0.26** - Substantial #### @falk1992primer ```r interpret_r2(x, rules = "falk1992") ``` - **R2 < 0.1** - Negligible - **R2 >= 0.1** - Adequate ### For PLS / SEM R-Squared of *latent* variables #### @chin1998partial ```r interpret_r2(x, rules = "chin1998") ``` - **R2 < 0.19** - Very weak - **0.19 <= R2 < 0.33** - Weak - **0.33 <= R2 < 0.67** - Moderate - **R2 >= 0.67** - Substantial #### @hair2011pls ```r interpret_r2(x, rules = "hair2011") ``` - **R2 < 0.25** - Very weak - **0.25 <= R2 < 0.50** - Weak - **0.50 <= R2 < 0.75** - Moderate - **R2 >= 0.75** - Substantial ## Omega / Eta / Epsilon Squared The Omega squared is a measure of effect size used in ANOVAs. It is an estimate of how much variance in the response variables are accounted for by the explanatory variables. Omega squared is widely viewed as a lesser biased alternative to eta-squared, especially when sample sizes are small. #### @field2013discovering ```r interpret_omega_squared(x, rules = "field2013") ``` - **ES < 0.01** - Very small - **0.01 <= ES < 0.06** - Small - **0.16 <= ES < 0.14** - Medium - **ES >= 0.14 ** - Large #### @cohen1992power These are applicable to one-way ANOVAs, or to *partial* Eta / Omega / Epsilon Squared in a multi-way ANOVA. ```r interpret_omega_squared(x, rules = "cohen1992") ``` - **ES < 0.02** - Very small - **0.02 <= ES < 0.13** - Small - **0.13 <= ES < 0.26** - Medium - **ES >= 0.26** - Large ## Kendall's coefficient of concordance The interpretation of Kendall's coefficient of concordance (*w*) is a measure of effect size used in non-parametric ANOVAs (the Friedman rank sum test). It is an estimate of agreement among multiple raters. #### @landis1977measurement ```r interpret_omega_squared(w, rules = "landis1977") ``` - **0.00 <= w < 0.20** - Slight agreement - **0.20 <= w < 0.40** - Fair agreement - **0.40 <= w < 0.60** - Moderate agreement - **0.60 <= w < 0.80** - Substantial agreement - **w >= 0.80** - Almost perfect agreement ## Cohen's *g* Cohen's *g* is a measure of effect size used for McNemar's test of agreement in selection - when repeating a multiple chose selection, is the percent of matches (first response is equal to the second response) different than 50%? #### @cohen1988statistical ```r interpret_cohens_g(x, rules = "cohen1988") ``` - **d < 0.05** - Very small - **0.05 <= d < 0.15** - Small - **0.15 <= d < 0.25** - Medium - **d >= 0.25** - Large ## Interpretation of other Indices `effectsize` also offers functions for interpreting other statistical indices: - `interpret_gfi()`, `interpret_agfi()`, `interpret_nfi()`, `interpret_nnfi()`, `interpret_cfi()`, `interpret_rmsea()`, `interpret_srmr()`, `interpret_rfi()`, `interpret_ifi()`, and `interpret_pnfi()` for interpretation CFA / SEM goodness of fit. - `interpret_p()` for interpretation of *p*-values. - `interpret_direction()` for interpretation of direction. - `interpret_bf()` for interpretation of Bayes factors. - `interpret_rope()` for interpretation of Bayesian ROPE tests. - `interpret_ess()` and `interpret_rhat()` for interpretation of Bayesian diagnostic indices. # References effectsize/vignettes/bayesian_models.Rmd0000644000175000017500000001460314170302654020353 0ustar nileshnilesh--- title: "Effect Sizes for Bayesian Models" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, bayesian, effect size] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Effect Sizes for Bayesian Models} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") options(digits = 2) knitr::opts_chunk$set(comment = ">") set.seed(1) pkgs <- c("effectsize", "parameters", "rstanarm", "bayestestR", "car") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` ## Standardized Parameters ### Introduction Like in OLS / ML or other frequentists methods of model parameter estimation, standardizing the parameters of Bayesian (generalized) linear regression models can allow for the comparison of so-called "effects" within and between models, variables and studies. As with frequentists methods, standardizing parameters should not be the only method of examining the role different predictors play in a particular Bayesian model, and this vignette generally assumes that the issues of model convergence, goodness of fit and model selection have already been taken care of. (Learn more about how to become a Bayesian master with [the `bayestestR` package](https://easystats.github.io/bayestestR/).) ### Setup We will examine the predictive role of overtime (`xtra_hours`), number of compliments given to the boss (`n_comps`) and seniority in predicting workers salaries. Let's fit the model: ```{r, warning=FALSE} library(rstanarm) data("hardlyworking", package = "effectsize") head(hardlyworking) mod <- stan_glm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking, prior = normal(0, scale = c(1, 0.5, 0.5), autoscale = TRUE), # set some priors refresh = 0 ) parameters::model_parameters(mod, test = NULL) ``` Looking at the un-standardized ("raw") parameters, it looks like all predictors positively predict workers' salaries, but which has the highest predictive power? Unfortunately, the predictors are not on the same scale (hours, compliments, years), so comparing them is hard when looking at the raw data. This is where standardization comes in. Like with [frequentists models](https://easystats.github.io/effectsize/articles/standardize_parameters.html) we can choose from the same standardization methods. Let's use the (slow) `"refit"` method. ```{r} library(effectsize) standardize_parameters(mod, method = "refit", ci = 0.89) ``` Note that the central tendency of the posterior distribution is still the *median* - the median of the standardized posterior distribution. We can easily change this, of the type of credible interval used: ```{r} library(effectsize) standardize_parameters(mod, method = "basic", ci = 0.89, centrality = "MAP", ci_method = "eti" ) ``` As we can see, working harder (or at least for longer hours) has stronger predictive power than complementing or seniority. (Do note, however, that this does not mean that if you wish to have a higher salary you should work overtime - the raw parameters seem to suggest that complementing your boss is the way to go, with one compliment worth almost 3.5 times **more** than a full hours' work!) ## Eta2 ### Introduction In classical frequentists models, the computation of $\eta^2$ or $\eta^2_p$ is straightforward: based on the right combinations of sums-of-squares (*SS*s), we get the correct proportion of variance accounted for by some predictor term. However such a computation is not as straightforward for Bayesian models, for various reasons (e.g., the model-*SS* and the residual-*SS* don't necessarily sum to the total-*SS*). Although some have proposed Bayesian methods of estimating explained variance in ANOVA designs [@marsman2019bayesian], these are not yet easy to implement with `stan`-based models. An alternative route to obtaining effect sizes of explained variance, is via the use of the ***posterior predictive distribution*** (*PPD*). The PPD is the Bayesian expected distribution of possible unobserved values. Thus, after observing some data, we can estimate not just the expected mean values (the conditional marginal means), but also the full *distribution* of data around these values [@gelman2014bayesian, chapter 7]. By sampling from the PPD, we can decompose the sample to the various *SS*s needed for the computation of explained variance measures. By repeatedly sampling from the PPD, we can generate a posterior distribution of explained variance estimates. But note that **these estimates are conditioned not only on the location-parameters of the model, but also on the scale-parameters of the model!** So it is vital to [validate the PPD](https://mc-stan.org/docs/2_23/stan-users-guide/meta-models-part.html#meta-models.part/) before using it to estimate explained variance measures. ### Setup Let's factorize out data from above: ```{r} hardlyworking$age_f <- cut(hardlyworking$age, breaks = c(25, 35, 45), right = FALSE, labels = c("Young", "Less_young") ) hardlyworking$comps_f <- cut(hardlyworking$n_comps, breaks = c(0, 1, 2, 3), include.lowest = TRUE, right = FALSE ) table(hardlyworking$age_f, hardlyworking$comps_f) ``` And fit our model: ```{r} # use (special) effects coding contrasts(hardlyworking$age_f) <- bayestestR::contr.bayes contrasts(hardlyworking$comps_f) <- bayestestR::contr.bayes modAOV <- stan_glm(salary ~ age_f * comps_f, data = hardlyworking, family = gaussian(), refresh = 0 ) ``` We can use `eta_squared_posterior()` to get the posterior distribution of $eta^2$ or $eta^2_p$ for each effect. Like an ANOVA table, we must make sure to use the right effects-coding and *SS*-type: ```{r} pes_posterior <- eta_squared_posterior(modAOV, draws = 500, # how many samples from the PPD? partial = TRUE, # partial eta squared # type 3 SS ss_function = car::Anova, type = 3 ) head(pes_posterior) bayestestR::describe_posterior(pes_posterior, rope_range = c(0, 0.1), test = "rope") ``` Compare to: ```{r} modAOV_f <- lm(salary ~ age_f * comps_f, data = hardlyworking ) eta_squared(car::Anova(modAOV_f, type = 3)) ``` # References effectsize/build/0000755000175000017500000000000014174212105013627 5ustar nileshnilesheffectsize/build/vignette.rds0000644000175000017500000000102414174212105016163 0ustar nileshnileshSK0vex^,!!? Y!nI&RbGr_s;u]N{3|CBH;go.#\EGE͙ B^"5(@eAx2 "3%؀66pmx8€*Ek^93{/~o*b:csgUGԶi&|m]pԄO Sł{wZ3Ozf Hyd. L8M4/K "1\^E6B[pKcrG5ܹ3b?Af_m÷F*e) IUM_Lۏ-f- u`𨥴 bԂõxSlAjR6'4*x:aGm?9v7y^,]y,Xx~~cG`wؿ7͡7=pă3(A^?r!7 \}\߸V?펒iߑ)3lY effectsize/build/partial.rdb0000644000175000017500000007210214174212037015762 0ustar nileshnilesh cIvXfh6nc.P   ݭn`U TvWUVgfDaݒeYe]e,ɲ5׮ծw׻k[x_ hFfT9dU ^\/"^?Ht$vؑIq.$?qGbWbEv,9ƪYc9l9ˣ/F)H~w|n3V$GS׿:Y\4O'5k*\ܕI+;J: <{oY9ZW _]2[gNUB>2gtçgwr`_9haݬk^* VGɱ'鸽Eމr5 bw+U gG^5WV̬Zz<{|;suihKp}ߪMc{jeQ]ޅv|D{$CV$#\4\ӯ\9;[)%,Ĭڥ5Ay d5& O8y$vˑOLW50ZoOj@ZNGjE6Lv%/^)Q [)9R4LuϢQ 7$G,P$ģK+^D&vX+nh"@Z9וk쒪f@' hfe/^3.x14 mtX3Fg{5 mY3e}J6W3{RgifhkfAګ}Fz5s@h* c#uz]"K~c'ecxr8b.W/e/+*95|*95|*HGJ(*RW%ӯ%-aptHz#raa ]:"p:N{ɞ::=Wy1rZge]N1y6J9+k^a_JT}2Aȃ̩l8e"NBfNش0!mȷc f8Qʚ|Z.V̩3|-=HCr%u | K0J#S)=l3X/{DzSU 1cv,]vJw:ƖڳB$ù f[jJM D=?T>Kv.,UWr%rY2vR,kq֗J1Nw'k&k1 AWnH9vHh;Ջ0z%f9ZR3IXȖ8O"ݚZ+]wPvzvͲC,4ns?jJR_E^mjARς6[9"-בim(TS${s Ox 6hPO~Ie:APZIg7c+)e.>$&=2 p?csEL2٦+SBLJT9]Bɿӫ,+3LdxU=lz”:n!Էj7dޡP ; w6klK^E!?lCᦑvz w־Ի^p y_>On0s   n >ca JT~uEFc ݳXqF5yi-L1Tm븟xt%v<@<Pf7 '}סD&*5Foܾ}+\EK"F_a^d<3]*ZDe,w2itJsLU۝V6@(-h-,IIɟOi@ }ڮ&vd-E^7Q#T K+VΤ$3%ͬfsT6A7PWhQW$jol!F)'`ńG!l'ꈍ,7#1s*sfZ Zjy(3r􄟺Y.^#=.Q6WqJfn)#  a_u:JZd\(%w?d\1*,T4|T`n$>5nVV7C ĤqP8˦"ʤa< .oN C/@|N/B[SoT+4ޯۛ:K9O)lw*ED/ &|ryV9ͤ^E^ިFkU٢iૐ#9,vbsk_H v|NB rx(u`rt@6oFRI&y c+Tppl'f-f\ Mm9w  /hԐY,C>8v1'w!G;jH77v-+tO$!2)`> IvP)U#%RB=[Qd6S&y6>vdת1;Rx"8~(xZMJ q'݇އut&"nvב:2/\գȽ<Yuj'%Q8%:Hno_b2r{2*!Ve8y,^"ߌP ׀(m؂K~JBM;EXa=`rMAWs0]ZiӭNxrF|`ڲk@?\o_QSМRp :֝rh1Fy@|N@ַgrf`GQ8 9P]GoB+-4A-#됯kSK'WKZGKOcLk2^lNC3y1m3<9~(Nۣ<$'g ƻMPʼn|8*HgjO)t`t Ws*A&>54(3ݣiȑ= oG B:!G_lR|$jntWc[$w32rT\bn9dyg!ﬤ?8!I5W*B7kcP~ⲅt 2̚iJ8kqmtkU0h+r0yxԆ Gb:Yp&`hoUΙ7ECEhzY:0MY#g:| `ANQV"'`axB1OJTԎ:X%< 9$_(@N^3 sn UW7w\%σzR\\sw!w.D@-ӔˎP~&E`7ȆI}bi a9%!4vE8BX,j=}z67jyrڲ{7R [0^d\6b{ dJ:\\$S?@ͮ{Z-g#mW Ec[/5Lv7𫐿Ax4btUAOP ;U_D} OĚy..#('f=| #&,+69|<_ sL2tG(nMGEmh(]Fhk'1H+l-8FiYE>˭,}R?tG !ҧ*6비z jj/PvпT ga,D;{"=s Zo-!TE6[MTSՑnt7R"-m2d 3ȒI?~7d}#s$zӤ?iM\EW[=WQ%lNrr1ZA󨔄 M~֨zo_9j%or$Uy$A'%5 񴦂Zȷ jR؜ \ˢ}`p'Z>@}#LCNk{WRmZ͈^8y-`&FߗOkهYw@z| 4vMo.@F۴8DJ{?+`>U-LWYl_tR<(>Z ht)|MmVJI]<&' DZ v=\.S,3zuH@TE#M5]r9(F;$ģ,ĕl_("-u;@ew1gzUhZ@"F.`Fwǵro"'nq)XOC[륷pLo*73C^!-5B *xtO[rw_1]ͥP?s/%D9O-N1,!VkSTzi'%hEǵzjK)|9 2.j%灎R!ocU#y$M)Bf+OLJ!,cfh'M'yx{ +V)nd"3hGn/eM'! ?զoC[{ɵ΁XL@L0EB <9ZqLB<6J 8Q'/CveGCm`lC5 B'g!?;MxD+7OӞlRCȑS Y(Ӑӱs,Iu"|MAd傑5^S`'©H" |IZIS#OBhYuCv@KF4J8<9Ru# ǁ@XF GjyZ7Jim.R:5HtWTy w &% EոMbhJD=f>"KB9oq Y"N#yEG GŤv}Qqށ?HuHyvȫ8Vh64,1i,GsSB0J~NB<1i g+{rpĒ3 uKU? Է9ݨ>yH ))< 㯗$BRq9$Hr% F%)Qzf*e "㟅j7R=stz9d}؄vq\ٜv#СX[$bu9adc -;4$7hBb|ͤy)F4Wӟs?OA'o|WYU"nk--KPUjϖsZR'M.g_ũ b[T$-VɷR~K~.DGTs/j<7_~ZVu(Ӥ8e~Tx6D@..kQZo.R>/WgfttX}@SۭRe`ց( ƯY^ @ >Wb5p|o_VXҡj.c|>Qf1W G; Cd1OgBbu{Q6 ( b 7ՉA.g:^~ݰ7x>oOfw\ium;L8 y462z *_pz/}}#,Mxr3eFX #; F641+W\e!Y$ a]N?(gG!Ovz:>>r(FU?)B}@PܺZA֧~1.M^ɕ0"M0=O8zϐvXmp<{b`.1:z2/@jgSw@hzJT#v P"<˺Ӆ?8O.;]`WgBa\ޠMo^+sn#zw(-Ᏻ.*>$ @pR|]N;tF#ųq!o@0 9َEℱ:[v _CrWE\r(l"5E#ڒf/ OD: \x"Ig=vA{ll:nMS{`|ȻfHwCW}u:#'tc[֪%|Q)l9s'DVB^穀=nkBZl:8V`8ݍl.ث)vudFL={"S\U c;r*>;wwlll>D-l+u>)˙@`vWҖݻ|š' SB2ޅ-W\CFֱ˗pN4~ 7̧(YO#Fw81ְY1;_Ljow۞}w7OJ;E.F 7Y,ldO> #CUƩ.ڕFGEb?u+ۻ 7θjm;zy{Jl&%!m5%4vv.Z+fmt֋p̈́a|n*uK%荐1KhY|ZG~sqOYؼAMYҿƒ-獎N z|:Ms$7 sR56L,T5p <V2"':!xi`(9НbCAs.Lq}2?DhJ(;m[L7) ¢VJ9q=rkZ(ᣄ6wlp9 \5 ;oF{V?ZV'9vU${[<;f)Z-~M;++Z~j-jW,:TMy0܊7M?0Eĵ^4VKWəR7 tM+xLG5K *gZl'ɀy?L@0fm3|\؁H om3am=ʔsqQNwD&7$78C9ܓ mϞvw^:5ÝgIo91<{EhW54N|,E70cn8֑Ivc~ خWL}ZcN8TF }r&nl>s*܇JKQ3dJxaf^#/-p.2J=V\G ]Mx\VS 16E.sf)` U X^ CTo#E/j]A Y_)/#=^ oA9&ѧv(3?84޶S"<\Shb(C;ٚ)t})ui>륨cQ3)nI9ՠ]es\+:y~Kg5fkHm"ZLSwԷ^_=m3ӪiAi,M v:懮JB COf/wT4>G%# !$MVBn[@S Y(g>'!GEן좕e˕B8K1,sO>; dI J u/jX5nw@t-sæ=MLxc'%.Ml6]^0,a4_IZ-4Ľ/ZSWV`1U U"nL/Ѩ%8[GkTmlIxjmC#3W!ja u(oAjh{<2Nն A>|70Gǹ`De/74 ̑$䓭o)W$ģmN&"!7'tv1~bAm}|3:E_&!-g%F 4Do-Jx@c4l'jY͸}RޑF=Ibź޽H@6Y_NZ}pWv-b՗v׽tP قh˹ZuߋռӹbVSzD} /zWصƺ-/loo1gYZ_:-> Ssk㊉^[Yޯ]V5HmPahݍ]v-6[=%s=kFIW~5_ -y"^:XŶ۞E\zHΛ"uA[k\vsHailaganfV5CnSl)H ٚf]A,S\3EoPVi5^;]ϱk^ uSq Ufg{ 9ۓKFpV+Z7-fKbj͍uۑL. >S%oWv(/ԷyPP>魗Юw2:or\0W/rz RjRa L,l|ts6`a±}۳Wd֩lmo) dwowkvB 2Vv s?>ڰ}J-K7l3t[6)cU&w3ixm 3 N"yoװs5 %[0ٮmxƶ)Q} V\cU~cwʛ[5cFlv-?|*y6DFC#Ht$vV&۳-wn?qZ5򭳸}W lY}{#܅˄^J:/T?N sfz͒_@X;_ fIgzݼᘽ^nNg'.6(oYEzbyo.[e>4:xuwg~7TO~1 {e&չHvzan>y>v$\3{Bc])g"OĆO?')|駟B<#_cǴqu٫:0t]|r ҭdڎD4l-E##bIvz=hyF?_A8 yVK Gcg\QN#'}kJ6ҡ꽛pZkҲV=-g g[G ^B~xP._mmhjar~f̣z"XH^yгGM9aEpS|oSxcEȋZCԑ'$lpP55}LM:TyJ!."99-e<`(V/&36Ae,{oq"ىgS$s9ɱn1Y<"_5*&N=ldCz į#-SeO˩5C\ҫ&gtBo GB<8і{.io9;+~n99ѹX<+s_gIT`֚fN/!ߏ1d8os\Kap&lCYʖ#NBBH'oC[{ع}b llZ S$tA(Hd1 4H(㐏VKG*6N\t^O+qx؝bE!](kb6o}?[g8%=#Tly`r4/e&__CQkLW]>@1+"'uw:%LP綼uwCޯOsTB<1ȑs XIs9R˫Ժ;!#nԬLr,ˆ?ce"\)H\{MiNing8-bJMjGa6{>a #{ZJxت{U7>OغPm<"g:)wp!gS"zYRwɓbU;"<>#$l䆀!_c6ncJc8 yy,!V(! hcT۝l\B~764oxDbUgL4J9TMHw!Gh1\i Qra=k;"U m?3'SW=vq"6cN vd+䩆z&]!G7.%V~7ÐY&c""o6ߘN" :af4 e+WU$=c鬜d5^WIЂ{TܤE1lI$H(k]=V .%)adw^ 46r9 w>45AEٱ+۶'uwMFtGx m3n܆p:$%w9~g |9ĠaR-)!i %!!V80zil V/X%kʊp)"WA#0O% ZJ}6kA|j%»eֆ_aV F4LI}~#`s4+FR2mN ˤ(-㖚;Tt #T$fj^nuh5 VEñ|mn946Ts:rV%[R=gx]y۳vŭW#mc&+ٝ*C܌1?)\0-K`3e3d0EƟKxʷ_2 -d8Boj"<[/J0U".;o NN^]fGȾ ѕ GiM 41 :nDQ0V&Ty4|9cASj ?j OLwaي\tBݒF.6rfFoGtjKT"7<K!m-QmQڞkJBEc/ASZOs67쒭Flc#moW(5ȑkod"ލPV -\s0Y1U~ϡ!A*ni0^5ȞFy #H,E 7\>'\.[%(;R9-KPUjϖzQ?O\Ϟk6SAx-Ew* B} 1+k&:Bn|բ&"ۮ| f g"q^%HvZ?pxlEƊ.Z|9uqLT:e[UlpL7)yJnU gƲ]O{_ijK5Gr46^.2Ղj-LAd'̏*φhuu2*B-=N=>/WgfttX}W'55ۭzmZ;$j}(tQY SOB>qKhOenjoF;GN%KV[{h/AaMa !Ooi;P%w*kf^Fڔ;-T-N/oQ"!P$Ƽ&l^]hG G?͆)+QȣwP,DLƀSb3jn`Gb/#SUG\qJkp|ǥ|`|FF/ L.)ҹ f W^xm~QGvICT7QbXUWuI{ x KKKwJ;Wc8P5n v~xKUQhp\\aO^M1r:G%6;{zĉȜ9Y*YJ꺲M UйP7EJAx8MhÅ1&_/v|8vxrE= SղgH9<9#e͜^|!f\)Ʊȏcd|λj]'!?UdĎP }r-atIك'J5y~w W (@8囏@gDM(~)߫)U8vag\K"6| 9R̠Ǽ`iY5l>|LLGsZB<6J8 3'BE*uȯ73j8]^|9ƀo@tLM \Ff(sbSiu6?HD5܎,6F`uxWpJ2o]?j#"MN\Z> |9ڦɆMApVN%NS(Dƒ6_S0D-ZW?jX?;fJYͿ+6 {;{ ?oYm.FzoZ+_h`Q>Mؙކ|Iɍ G[i鹷qm붦}L~OcJToZV ]%;5Wf'D5@{-L:1*A׃xgn3E=< rǀ}Nd:κVwNWXQO>=*pr$jvх\r|>uh<9Z3`–/VPm\;%ԳX1#Zby˩t?4n݈DZ[H5R9xɊ3SsvTG"#⼩-G=q`M6Ɠ`Me##)F:e1QW%h$N_607v x؊TbmYj6El%9&+OT1F'u CDwءS? l9Tqi[mSgT3I0~X@excM*(㐏V5V;lj3D$@8ff}7O㷰G,oФȋ0_i|DD.Un͟.E+u01( LaӤm4H>EJ <9ҽj^0`B*,!Xu6LoIUC9ss2TuWG:fOPҁ֌F-Qm c*zOE麞AT5պ#JSbTʎsȷnl>W"P5'}1srVeAD %)~h=hѸ !fw9E㛾cJ2K4i6^Ƚ^f~ ȯ4[Z OAdo]_8W)X@h1,v(7 e=LUR+6d:~!2  y&gT)A0 9J=O={P:-V-TחKgS??SrϧVQjU֪4('5Җywۨqwۨq-wP+1&wQ_qGI$l/E? +b]tSqT쁦rZUA[^ws-nSSHc6^Ewϣ֍)סjBw{v?@`L^̈NHY~ߺHR4 J nՍeP+aA e`{T/Ƞc$|Aݟ&aCb}Uy%v&ʔ?OROQe۰Fyغa#vزa㗡jB-`+g ť~H5@NJ$QW$|F_A#l(+}-E~j%|1F2ȯR~eݠI"4 [e"{A{S~Q@ROQeqGGE) h*.o- 2uב#@<ߏ0zil7lcLCcvIoSS8Ǧ~D:nݑ4G"HIպA/@=}NWVP0˿zZǜn(ҋP*!P_{5Egűq0&ynsL.+Id:I_s~A58Aq|ͫP4[{˶Uo(懑֪e$?(Sd`A/y 1: <y>/ | 89qw! pp熔!Uvy[\r/r+KpgCHNssRtPp9`?&h!NGsPj-sJR>7hLD2;WT8֏Cm;m 9>o͟F?B?\FsDm?$ ;6M ׃.I.*жEˢmxO?\Jxs!(;؞9FYy g05ʎ@j?U&3TJ x+qlE'f[F*\:.4KfFGوϣdRVDlj8VqIJ9DE\R[ @ʪϑ9"bjeټsP ?v ȑ& S #(—!ǹ6Uhr;F{?5:pUYSD$HA|;`>-;Nwɥ\X5Ǘ+V!O<.ccc%zo)g[ɮOSFz{G҃i͐{05?;19AMxY,9q17RySOQ1\5S* )d|2`=WӶvw3].J~ʾ]6GKce#k~D9"X Ud:ҝ \P 7κj;zyq52̋ztwblV}[zҊcoW[kl碵bF 8xK[F_%48eL|Z ~}OY׼b{s1R(Rl!͒}۝ F+6/-EYYOګ%KgWrO=/f-򷌱y\CqoC&F>g`yX/"ׄ M f'٬7]py킽b});o@ u ȑ3бd;&n*+#9C_fd\ճ/ Μ_F2,JRgsQX0ު:s6ƿџ=.ut1jKG9ɝ" D\yЮR5pϕEV0J/=5Oy4u4OEs<7 ڳ5[j'jGw(MEg{߮0m/vF;ݝr&>m&Ui_%'& g4{ <~/BvXv&+X+o۹+;+؂Jl_%9>sZilZw .A^،8  %JE_l=Rxbxm(.;VndOJ֚}sLU6ÿ!| kjZkv^dFfBב/sb荖,ף(nn@kP@k𺪿Rִ+RƨJm&gz$l.|jЮ2khb`\YYTon^/=4[Cj3֪޿]Mu6b'n77VO_! ʂgZfwĿoP} D[M&S$W@P5;#9&!f{ (㐏V#)TgElvMF"Q2 %\369UP6G MO͹<0N&ÙbL#suvC!FCߞIwXfx'3ү1"ÀiȤlNU>>}My6C(o)luYT{iѦϦQ1oiF{mRƨ^m䔦z$l]UMs,En+ovg֋SCoѡÅ8>R~ڛ/NhM|b4~˯UD;eR&F`V)vC3JExr#5ߛl1U훩6hl;1X9aM0\u6D.̔.@o.NLpu ;g?30<BY9&vf2Vuҙm]2)ʡN[!%֧/Ll)ռџuxd1jGF9" t}vhWi+y KvgLu6kZ&zE|}pja3;vOL|4ށEm 1&azB:$\+oKCF>{>cJl@g7ihKcO\q{w\mp|`gBc>lyw^:3t`'s$49㦃=LjT.;ftuṞn1Y<"ora ٩lM2}1Gz['{F/|oͽSMbmc/13O./Y]{0n PmWbhK`TN^V+d = Ƌ6L5}sJ,y;G{yf:,*S >JQ}cIh)@E~)8 <`N!#Ql4 -ɡk?,!jZC#MT,h4`5B9\TB.|E%-Vnaw{UjPJd԰* =U!@*ZEtB -VI'^tB 6RU^iJB {۫PChTU|X%}U>!x*UGgvҟ.&]Rڴa**⤝7K] 5$kMǐ.>׵?YJ*<}@gp dYldNw< &^%sBJx"ZkJgS wxb^\LG}iL:R\BrUVt4$1ʆ96Φ',L{)Qm`ӝb,kqR}!uNGhB,vuyv.usʪVLjsv̩B_'0h2&t:t]( p"Q3I#tJDYR6qiXǜJqb)UH5pԬ^y6QcBzp PgNUx5]3KAYUx>=zBg#d |sHaPOK}wϤm{bH;ș6%/!VӐŬ-N%b jBޮxn8Yj2#+%5P1ٲiVŊZΔ#=l)zYb9lr&D4JME;iPZm%QkhQ6Vvd-E^6Hn|Ig%'gY\3U`r uvCnQW$j}(cA" (䣑-X1ecd9=F xͬ~f(3r4?<[]R\LF٤ 31S2sʧa Al1i:G%<ڹF,J%!f(è7ҲA2%Oi\E ؜e')}XO8"ʆg7cű+wD}8yV-m[-4S OilU()GMVRlPKkQvn$OkSV5k8 9}7iZmOc05@1.1EaՄ luFo=]ByE勽DCBYץN&"9k*FUP])eٽۣ̒%n~,IE=;UrJ%<;+I8j( J>MA\9aS#-kJs7?2kT Λ5\>/_ϛd ů$4]SKXx&(p3 `,LլL즵V>kuewwފN4QJ佶yoUpS]^w 4lZs Rˆ5<Ŭ $H(muGO[}4PbGd&y#gBx%ݠMZ^l98قpK!W mC R /D^)ߓN)fwfL3UmW E8Jn7𫐿Ax4btU8dG ަQ}ԧ㍂ҡ~cxbX(F8mLyX z~\vyIE$ KD3!!m|"% >(B:T7?| [=(0ި,b⻀{ i}sOwJy[AP_,}9ly-9'`XtSŁ#P&MTj>9F}#䕰r_Gg$CQ{Pc[{`r6q|,jCJ^|-dPjQI nvTyTVy\ͩ4g)la?"u6ѻ$<Y駐^( h1QK<؜jnY$ ?9ҒZ}yNJw$Z.JxZco#o:v{0S_N=Ly(|dPgj=QI4E zUR9}x|8 F<^6C C|2j{AED D`7nm9q.(Q^|]B\8ت:(oP73g_3'!V-\5ߊ--q\[).}bGVRKwrL1U>f^rxcPP{UjhΈf>|}fO4NIESrǀ!Δ BDU4R,KvG7sYp3<őoՑ789a-2[PrNBm(DuצYQzlI (1{/FA%Kt(E[]h MDBlJ#-*(]I5{3Bv't'|x"[IXe`y@tꌫP})([DcXB< *@=uIbNPTĎXR鶪lzZFd|9 7p \s~K6m &lБL!%y>l5 ӏKUl2A쟻;Cc>ɮnڶΧ brKv)_gBv&#E5mȷcqoW77TXL@0EBYaε΋@瘄xm$Aqߪ)!Gr6t:IٙTD rW4p[_C=ڴk:v,gQh8U F{E?x$'Vnr 9.ȋj)#wzFm㽖֍hO+qx r..~F_-֍8vA*n)yY#Λ1޺+N{'cl[6uAzMiNi dTnK1k}V5@fC }30g@EW!Ǐ*do|ٟBPm<"%|ZJya)qO p` 5 4!GZ]UYʫǁg۫ir@6FUk9B~764oxDbUgL4J9TMhW4h-& f% ߣ}~GY&BW3g'!Gto}'oC~;6t`M6BjwMyaٮ[=}8 9k2&"2 f)D&7ce퇈LCTψxt-J_ ҂%bib0ս Bئ}/|xZM0zil V/X%kԍQ&jc'|:yȑ|'[|D㒄xZ24~5u!inɽlЅbDc(*hUsrA/ެIm^-\~-6H5KA8 9GFIl)%G67 VEñ|mn947ȅUIV5Tzi6'ټE֫RȱeMN}EC{17t7:M,ǒW09ZB9oq Y"N#yEG ǿ>pEijWexrZ#i X4\ʲY l.>(_e%j4Q@禄xX߄fld7ckaɱT)G~FI'&;lqxOYG,9Pdmѡ\EYmnRmiʼRO0zI"$$lψlψP(Q / jJ|}LSFmX GޮQr=k#-)>l75JAE"Vׁ {&K6ݴ94$7h.>USl0^5ȞFy&GXA'/N|ؽ|Vl-ۗ{WL%k_*gK||Ik#Z>ɥ; _5JQ*ݩHZ 7f#W-"4wy5Cn|T 33@8]^/F@RC#brX%U"27 Ƿ;i '[lǣ3ػ?Zg^|4@ǷlwKjv[!<MtJ^;[6~K~lW fjK5G_E.l|,\-Xmÿ&49Nve*<I Q5v˨-޷LD׌|7@ՙV_ uHT[۴DwH4%`u#J'4d,3_wRfuq*楩'3c`,:G,kOBB?@tD6FKyL\nN :Gnݚ:MZ|FO,p(YR9M73H;@=n4˩ΙO=xߏ5>2OGeCfAT/FV 3Vz -N(8:y+j\uLZނ| Ǒvb g2 /Cv]vwFPL:&/kz7XᓣQHqfxUrxC?WH:˔jqD6?I"? ;gvz:ZDG|[Y g@0^xvz Uk%q_i5nV./^Y"lA|PiFγ:O)A9UoY&n8fAro*9P&78'+KbU2ɟ1s&nlL!40b)6tQӽ7Rjt^9wJQcSe"v@iŜ]h2~Qk-RͿdP(20ry)֨";K(k;AWOD:ĥ+ vx"I*p7ݱmdM;+fHw-XTOiZJ~oGx zJxrfEc$i6- d50>d ksF6oUʙj~ *!Է쬿Rl?ߝbC4S@0a3ߎ..OKqY5Ǘ+V!O<.y|n[V|S3t&sjo__f 3<887ן kK,LNL? 98dqpK߉Z.|ސX߈Z<ʼnrf'Xp/:;m;m|Di>m+oi]:X-YOȅlmFqvјUotϕg*F ϕ]t Sg`ǞpAg^d{oEVk}ћzꃩoWkl碵bF8 8x=kJ֠!c>:3qci Pbi6ύxO7\copXǂOyNբu M_u7J8F|.(DMy3_F[@U<>8/=::Rr:V}'聾ovɿD+]R?T h0 9:Bi_H+} ː#9 BQrOaD): a !cJ Uѧay0܏ByK7a߄iVP~FM(S 7jH@ $| rkY hIW#2鿙/W*{+)&!GB{2@Cݐ[-Jx@c4 ND1FG%lG}+Nrvk5X^{mOzrW,e#b/pfp͠zm?Sr7ZPvձ>mA7'?ZBeffectsize/tests/0000755000175000017500000000000014174212106013673 5ustar nileshnilesheffectsize/tests/testthat/0000755000175000017500000000000014174255623015546 5ustar nileshnilesheffectsize/tests/testthat/test-effectsize.R0000644000175000017500000001745514170065645021010 0ustar nileshnileshif (require("testthat") && require("effectsize")) { # htest ------------------------------------------------------------------- test_that("t-test", { x <<- 1:10 y <<- c(1, 1:9) model <- t.test(x, y) expect_equal(effectsize(model), cohens_d(x, y, pooled_sd = FALSE), ignore_attr = TRUE) expect_equal(effectsize(model, type = "g"), hedges_g(x, y, pooled_sd = FALSE), ignore_attr = TRUE) model <- t.test(x, y, alternative = "less", conf.level = 0.8) expect_equal(effectsize(model), cohens_d(x, y, pooled_sd = FALSE, alternative = "less", ci = 0.8), ignore_attr = TRUE) model <- t.test(x, y, paired = TRUE) expect_equal(effectsize(model), cohens_d(x, y, paired = TRUE), ignore_attr = TRUE) model <- t.test(x, y, var.equal = TRUE) expect_equal(effectsize(model), cohens_d(x, y), ignore_attr = TRUE) model <- t.test(x, y, var.equal = TRUE, mu = 3) expect_equal(effectsize(model), cohens_d(x, y, mu = 3), ignore_attr = TRUE) df <- data.frame(DV = c(x, y), g = rep(1:2, each = 10)) model <- t.test(DV ~ g, data = df, var.equal = TRUE, mu = 3) expect_warning(effectsize(model)) ## Auto convert y to factor Ts <- t.test(mtcars$mpg ~ mtcars$vs) expect_equal(effectsize(Ts, verbose = FALSE), cohens_d(mtcars$mpg, factor(mtcars$vs), pooled_sd = FALSE), ignore_attr = TRUE ) # one sample z <<- mtcars$wt model <- t.test(z, mu = 3, var.equal = TRUE) expect_equal(effectsize(model), cohens_d(z, mu = 3), ignore_attr = TRUE ) }) test_that("Chisq-test", { contingency_table <- as.table(rbind(c(760, 330, 470), c(480, 240, 480), c(480, 240, 480))) Xsq1 <- chisq.test(contingency_table) Xsq2 <- chisq.test(contingency_table / 10) expect_equal(effectsize(Xsq1)$Cramers_v, 0.073, tolerance = 0.01) expect_equal( effectsize(Xsq1)$Cramers_v, effectsize(Xsq2)$Cramers_v ) # types expect_equal( effectsize(Xsq1, type = "phi"), phi(contingency_table) ) expect_equal( effectsize(Xsq1, type = "w"), cohens_w(contingency_table) ) expect_error(effectsize(Xsq1, type = "riskratio")) contingency_table22 <- contingency_table[1:2, 1:2] Xsq4 <- chisq.test(contingency_table22) expect_equal( effectsize(Xsq4, type = "oddsratio"), oddsratio(contingency_table22) ) expect_equal( effectsize(Xsq4, type = "riskratio"), riskratio(contingency_table22) ) # goodness of fit observed.dfc <<- c(119, 61) expected.dfc <<- c(0.165, 0.835) x <- chisq.test(x = observed.dfc, p = expected.dfc) expect_error(effectsize(x, type = "v")) }) test_that("cor.test / other", { r_ <- cor.test(iris$Sepal.Width, iris$Sepal.Length) expect_warning(effectsize(r_)) }) test_that("one way", { onew <- oneway.test(mpg ~ cyl, mtcars) expect_warning(effectsize(onew)) onew <- oneway.test(mpg ~ cyl, mtcars, var.equal = TRUE) m <- aov(mpg ~ cyl, mtcars) expect_equal(eta_squared(m, partial = FALSE)[, -1], effectsize(onew), tolerance = 0.03, ignore_attr = TRUE ) expect_equal(omega_squared(m, partial = FALSE)[, -1], effectsize(onew, type = "omega"), tolerance = 0.03, ignore_attr = TRUE ) expect_equal(cohens_f(m, partial = FALSE)[, -1], effectsize(onew, type = "f"), tolerance = 0.03, ignore_attr = TRUE ) }) test_that("McNemar", { Performance <<- rbind( c(794, 86), c(150, 570) ) model <- mcnemar.test(Performance) expect_equal(effectsize(model), cohens_g(Performance), ignore_attr = TRUE) model <- mcnemar.test(mtcars$cyl, mtcars$gear) expect_equal(effectsize(model), cohens_g(mtcars$cyl, mtcars$gear), ignore_attr = TRUE) }) test_that("htest | wrappers", { x <<- 1:10 y <<- c(1, 1:9) Ts <- t.test(x, y) expect_equal(cohens_d(Ts), cohens_d(x, y, pooled_sd = FALSE), ignore_attr = TRUE, tolerance = 0.01 ) expect_equal(hedges_g(Ts), hedges_g(x, y, pooled_sd = FALSE), ignore_attr = TRUE, tolerance = 0.01 ) M <<- as.table(rbind(c(762, 327, 468), c(484, 239, 477))) Xsq <- chisq.test(M) expect_equal(phi(Xsq), phi(M), tolerance = 0.01) expect_equal(cramers_v(Xsq), cramers_v(M), tolerance = 0.01) M <<- as.table(rbind(c(762, 327), c(484, 239))) Xsq <- chisq.test(M) expect_equal(oddsratio(Xsq), oddsratio(M), tolerance = 0.01) expect_equal(riskratio(Xsq), riskratio(M), tolerance = 0.01) OWA <- oneway.test(mpg ~ factor(cyl), data = mtcars, var.equal = TRUE) m <- lm(mpg ~ factor(cyl), data = mtcars) expect_equal(eta_squared(OWA), eta_squared(m, verbose = FALSE)[, -1], ignore_attr = TRUE, tolerance = 0.01 ) expect_equal(omega_squared(OWA), omega_squared(m, verbose = FALSE)[, -1], ignore_attr = TRUE, tolerance = 0.01 ) expect_equal(epsilon_squared(OWA), epsilon_squared(m, verbose = FALSE)[, -1], ignore_attr = TRUE, tolerance = 0.01 ) expect_equal(cohens_f(OWA), cohens_f(m, verbose = FALSE)[, -1], ignore_attr = TRUE, tolerance = 0.01 ) expect_equal(cohens_f_squared(OWA), cohens_f_squared(m, verbose = FALSE)[, -1], ignore_attr = TRUE, tolerance = 0.01 ) Performance <<- rbind( c(794, 86), c(150, 570) ) Mc <- mcnemar.test(Performance) expect_equal(cohens_g(Mc), cohens_g(Performance), tolerance = 0.01) H <- effectsize(chisq.test(Performance), type = "h") expect_equal(H, cohens_h(Performance), tolerance = 0.01) expect_equal(H[[1]], 1.580585, tolerance = 0.01) expect_equal(H$CI_low, 1.480959, tolerance = 0.01) expect_equal(H$CI_high, 1.68021, tolerance = 0.01) }) test_that("htest | Get args from htest", { tt <- t.test(mtcars$hp, mtcars$mpg, alternative = "l", mu=-3, conf.level = 0.8, var.equal = TRUE) expect_equal(cohens_d(tt), cohens_d(mtcars$hp, mtcars$mpg, alternative = "l", mu = -3, ci = 0.8), ignore_attr = TRUE) suppressWarnings(ww1 <- wilcox.test(mtcars$hp, mtcars$mpg, alternative = "l", mu = -3)) expect_equal(rank_biserial(ww1), rank_biserial(mtcars$hp, mtcars$mpg, alternative = "l", mu = -3), ignore_attr = TRUE) suppressWarnings(ww2 <- wilcox.test(mtcars$hp, mtcars$mpg, alternative = "l", mu = -3, conf.int = TRUE, conf.level = 0.8)) expect_equal(rank_biserial(ww2), rank_biserial(mtcars$hp, mtcars$mpg, alternative = "l", mu = -3, ci = 0.8), ignore_attr = TRUE) }) # aov --------------------------------------------------------------------- test_that("aov", { data <- iris data$Cat1 <- rep(c("A", "B"), length.out = nrow(data)) model <- aov(Sepal.Length ~ Species * Cat1, data = data) expect_equal(effectsize(model), eta_squared(model)) expect_equal(effectsize(model, type = "omega"), omega_squared(model)) }) # BayesFactor ------------------------------------------------------------- test_that("BayesFactor", { skip_if_not_installed("BayesFactor") skip_on_cran() set.seed(6) data(raceDolls, package = "BayesFactor") bf1 <- BayesFactor::contingencyTableBF(raceDolls, sampleType = "poisson", fixedMargin = "cols") expect_equal(effectsize(bf1)[[1]], 0.164, tolerance = 0.01) expect_equal(effectsize(bf1, type = "OR")[[1]], 1 / 0.503, tolerance = 0.03) bf2 <- BayesFactor::ttestBF(mtcars$mpg[mtcars$am == 1], mtcars$mpg[mtcars$am == 0]) expect_equal(effectsize(bf2)[[1]], 1.30, tolerance = 0.03) bf3 <- BayesFactor::correlationBF(iris$Sepal.Length, iris$Sepal.Width) expect_equal(effectsize(bf3)[[1]], -0.116, tolerance = 0.03) bf4 <- BayesFactor::proportionBF(4, 12, 0.5) expect_equal(effectsize(bf4)[[1]], 0.3911, tolerance = 0.03) }) } effectsize/tests/testthat/test-xtab.R0000644000175000017500000001135014170065645017603 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("contingency table", { contingency_table <- as.table(rbind( c(762, 327, 468), c(484, 239, 477), c(484, 239, 477) )) res <- cramers_v(contingency_table) expect_equal(res$Cramers_v, 0.072, tolerance = 0.01) expect_equal(res$CI_low, 0.051, tolerance = 0.01) expect_equal(res$CI_high, 1) ## Size does not affect estimate xtab <- rbind( c(760, 330, 470), c(480, 240, 480), c(480, 240, 480) ) cv1 <- cramers_v(xtab) cv2 <- cramers_v(xtab / 2) expect_equal(cv1$Cramers_v, cv2$Cramers_v) # Upper bound of phi is the ratio between phi / V and sqrt(min(K,L)-1) expect_equal(phi(xtab, alternative = "greater")$CI_high, sqrt(2)) expect_equal(phi(xtab)[[1]] / cramers_v(xtab)[[1]], sqrt(2)) ## 2*2 tables return phi and cramers_v xtab <- rbind( c(760, 330), c(480, 240) ) expect_equal( cramers_v(xtab)[[1]], phi(xtab)[[1]] ) res <- pearsons_c(xtab) expect_equal(res[[1]], 0.032, tolerance = 0.01) ## 2*2 perfect correlation xtab <- rbind( c(100, 0), c(0, 200) ) expect_equal(V <- cramers_v(xtab)[[1]], 1) expect_true(pearsons_c(xtab)[[1]] < V) # C is not perfect ## 2*2 0 correlation xtab <- rbind( c(50, 50), c(100, 100) ) expect_equal(cramers_v(xtab)$Cramers_v, 0) ## Empty rows/columns xtab <- rbind( c(50, 50, 0), c(100, 100, 0) ) expect_error(cramers_v(xtab)) ## 0 xtab <- table(mtcars$am, mtcars$vs) phi3 <- phi(xtab, adjust = TRUE) expect_equal(phi3$phi_adjusted, 0) expect_equal(phi3$CI_low, 0) expect_equal(phi3$CI_high, 1) }) test_that("goodness of fit", { expect_error(cramers_v(table(mtcars$cyl))) phi1 <- phi(table(mtcars$cyl), p = c(0.34375, 0.21875, 0.43750)) phi2 <- phi(table(mtcars$cyl), p = c(0.8, 0.1, 0.1)) expect_equal(phi1$phi, 0) expect_true(phi1$phi < phi2$phi) expect_true(phi1$CI_low < phi2$CI_low) expect_true(phi2$CI_low < phi2$CI_high) expect_equal(phi2$CI_high, Inf) C <- pearsons_c(table(mtcars$cyl), p = c(0.8, 0.1, 0.1)) expect_equal(C[[1]], sqrt(49.289 / (49.289 + sum(table(mtcars$cyl)))), tolerance = 0.001) expect_equal(C$CI_high, 1) # some weird exeptions... df <- subset(mtcars, am == "0") expect_equal(phi(table(df$am, df$cyl))[[1]], 0.64, tolerance = 0.01) expect_equal(phi(table(df$am, df$cyl)), phi(table(df$cyl))) expect_equal(phi(table(df$am, df$cyl)), phi(table(df$cyl, df$am))) }) test_that("oddsratio & riskratio", { ## Risk ratio RCT <- rbind( c(30, 71), c(100, 50) ) OR <- oddsratio(RCT) RR <- riskratio(RCT) p0 <- RCT[1, 2] / sum(RCT[, 2]) expect_equal( oddsratio_to_riskratio(OR$Odds_ratio, p0), RR$Risk_ratio ) expect_equal( riskratio_to_oddsratio(RR$Risk_ratio, p0), OR$Odds_ratio ) ## OR data("mtcars") expect_error(oddsratio(mtcars$am, mtcars$cyl)) m <- glm(am ~ I(cyl > 4), data = mtcars, family = binomial()) log_or <- oddsratio(mtcars$am, mtcars$cyl > 4, log = TRUE) expect_equal(coef(m)[2], log_or$log_Odds_ratio, ignore_attr = TRUE ) expect_equal(log_or, oddsratio(mtcars$cyl > 4, mtcars$am, log = TRUE)) skip_if_not_installed("MASS") expect_equal(confint(m)[2, ], unlist(log_or[c("CI_low", "CI_high")]), tolerance = 0.1, # different methods, give slightly different values ignore_attr = TRUE ) }) test_that("Cohen's g", { # From mcnemar.test Performance <- matrix(c(794, 86, 150, 570), nrow = 2, dimnames = list( "1st Survey" = c("Approve", "Disapprove"), "2nd Survey" = c("Approve", "Disapprove") ) ) g <- cohens_g(Performance) expect_equal(g$Cohens_g, 0.136, tolerance = 0.01) expect_equal(g$CI_low, 0.072, tolerance = 0.01) expect_equal(g$CI_high, 0.194, tolerance = 0.01) AndersonRainBarrel <- matrix(c( 9L, 17L, 5L, 15L ), nrow = 2) g <- cohens_g(AndersonRainBarrel) expect_equal(g$Cohens_g, 0.273, tolerance = 0.01) expect_equal(g$CI_low, 0.066, tolerance = 0.01) expect_equal(g$CI_high, 0.399, tolerance = 0.01) M <- matrix(c( 794, 86, 150, 570, 794, 86, 150, 570, 15 ), nrow = 3 ) g <- cohens_g(M) expect_equal(g$Cohens_g, 0.300, tolerance = 0.01) expect_equal(g$CI_low, 0.280, tolerance = 0.01) expect_equal(g$CI_high, 0.319, tolerance = 0.01) }) } effectsize/tests/testthat/test-eta_squared.R0000644000175000017500000004301414170307011021126 0ustar nileshnileshif (require("testthat") && require("effectsize")) { # anova() ----------------------------------------------------------------- test_that("anova()", { m <- structure( c(3, 1, 1), .Dim = c(1L, 3L), .Dimnames = list("Term", c("F value", "NumDF", "DenDF")), class = "anova" ) expect_error(eta_squared(m), regexp = NA) expect_equal( eta_squared(m)[, -1], F_to_eta2(3, 1, 1), ignore_attr = TRUE ) }) # aov --------------------------------------------------------------------- test_that("aov", { df <- iris df$Sepal.Big <- ifelse(df$Sepal.Width >= 3, "Yes", "No") fit <- aov(Sepal.Length ~ Species * Sepal.Big, df) # eta expect_equal(eta_squared(fit, partial = FALSE)$Eta2, c(0.618, 0.046, 0.000), tolerance = 0.01 ) expect_equal(eta_squared(fit, partial = TRUE)$Eta2_partial, c(0.649, 0.121, 0.001), tolerance = 0.01 ) # omega expect_equal(omega_squared(fit, partial = FALSE)$Omega2, c(0.612, 0.043, -0.004), tolerance = 0.01 ) expect_equal(omega_squared(fit, partial = TRUE)$Omega2_partial, c(0.638, 0.112, -0.012), tolerance = 0.01 ) # epsilon expect_equal(epsilon_squared(fit, partial = FALSE)$Epsilon2, c(0.614, 0.044, -0.004), tolerance = 0.001 ) expect_equal(epsilon_squared(fit, partial = TRUE)$Epsilon2_partial, c(0.644, 0.115, -0.012), tolerance = 0.01 ) # Cohen's f/f2 expect_equal(cohens_f_squared(fit, partial = FALSE)$Cohens_f2, c(1.623, 0.049, 0.000), tolerance = 0.001 ) expect_equal(cohens_f_squared(fit, partial = TRUE)$Cohens_f2_partial, c(1.850, 0.139, 0.001), tolerance = 0.001 ) expect_equal(cohens_f(fit, partial = FALSE)$Cohens_f, c(1.273, 0.220, 0.021), tolerance = 0.01 ) expect_equal(cohens_f(fit, partial = TRUE)$Cohens_f_partial, c(1.360, 0.373, 0.036), tolerance = 0.001 ) expect_equal(cohens_f(fit, squared = TRUE), cohens_f_squared(fit)) expect_equal(cohens_f_squared(fit, squared = FALSE), cohens_f(fit)) #### One way-between expect_message(eta_squared(aov(mpg ~ factor(gear), mtcars))) expect_message(eta_squared(aov(mpg ~ factor(gear) + am, mtcars)), regexp = NA) #### Alternative m <- aov(mpg ~ factor(gear) + am, mtcars) et1 <- eta_squared(m) et2 <- eta_squared(m, ci = 0.9, alternative = "two.sided") expect_equal(et1$CI_low, et2$CI_low) }) # aovlist ----------------------------------------------------------------- test_that("aovlist", { skip_on_cran() df <- iris df$Sepal.Big <- ifelse(df$Sepal.Width >= 3, "Yes", "No") model <- aov(Sepal.Length ~ Sepal.Big + Error(Species), data = df) res <- eta_squared(model, partial = TRUE) expect_true(all(c("Group", "Parameter") %in% colnames(res))) res <- omega_squared(model, partial = TRUE) expect_true(all(c("Group", "Parameter") %in% colnames(res))) res <- epsilon_squared(model, partial = TRUE) expect_true(all(c("Group", "Parameter") %in% colnames(res))) skip_if_not_installed("afex") # non-partial Eta2 should be the same for aov and aovlist data(obk.long, package = "afex") model <- afex::aov_car(value ~ treatment * gender + Error(id / (phase * hour)), data = obk.long, observed = "gender", include_aov = TRUE ) model2 <- aov(value ~ treatment * gender * phase * hour, data = model$data$long, contrasts = list( treatment = contr.sum, gender = contr.sum, phase = contr.sum, hour = contr.sum ) ) a1 <- eta_squared(model2, partial = F) a2 <- eta_squared(model$aov, partial = F) rownames(a1) <- a1$Parameter rownames(a2) <- a2$Parameter expect_equal( a1[a1$Parameter, "Eta2"], a2[a1$Parameter, "Eta2"] ) }) # mlm / anova table ------------------------------------------------------- test_that("mlm / anova table", { data("mtcars") mtcars$am_f <- factor(mtcars$am) mtcars$cyl_f <- factor(mtcars$cyl) mod <- lm(cbind(mpg, qsec) ~ am_f * cyl_f, data = mtcars) m1 <- lm(mpg ~ am_f * cyl_f, data = mtcars) m2 <- lm(qsec ~ am_f * cyl_f, data = mtcars) expect_equal( eta_squared(mod)$Eta2_partial[1:3], eta_squared(m1)$Eta2_partial ) expect_equal( eta_squared(mod)$Eta2_partial[4:6], eta_squared(m2)$Eta2_partial ) expect_equal( eta_squared(mod, partial = FALSE)$Eta2[1:3], eta_squared(m1, partial = FALSE)$Eta2 ) expect_equal( eta_squared(mod, partial = FALSE)$Eta2[4:6], eta_squared(m2, partial = FALSE)$Eta2 ) # MANOVA table mod <- manova(cbind(mpg, qsec) ~ am_f * cyl_f, data = mtcars) expect_equal(nrow(eta_squared(mod)), 3L) # Row order fit <- lm(cbind(mpg, disp, hp) ~ factor(cyl), data = mtcars) out <- eta_squared(fit, partial = FALSE, ci = NULL) expect_equal(as.character(out$Response), c("mpg", "disp", "hp")) }) # Cohen's f - R2 change --------------------------------------------------- test_that("Cohen's f - R2 change", { data(hardlyworking) m1 <- lm(salary ~ xtra_hours, data = hardlyworking) m2 <- lm(salary ~ xtra_hours + n_comps, data = hardlyworking) fsD <- cohens_f_squared(m1, model2 = m2)[, 1:4] fs <- cohens_f_squared(m2)[-1, -1] # this ONLY works because of the default type-I errors!!!! rownames(fsD) <- rownames(fs) <- 1 expect_equal(fsD, fs, tolerance = 0.01, ignore_attr = TRUE) fsD <- cohens_f_squared(m1, model2 = m2) R2_1 <- performance::r2(m1)[[1]] R2_2 <- performance::r2(m2)[[1]] expect_equal( fsD$Cohens_f2_partial, unname((R2_2 - R2_1) / (1 - R2_2)) ) }) # generalized Eta ------------------------------------------------------------- test_that("generalized | between", { skip_if_not_installed("afex") skip_if_not_installed("car") data(obk.long, package = "afex") m <- suppressWarnings( afex::aov_car(value ~ treatment * gender + Error(id), data = obk.long, observed = "gender", include_aov = TRUE ) ) Aov <- car::Anova(m$aov, type = 3) expect_equal( anova(m, es = "ges", observed = NULL)$ges, eta_squared(Aov, generalized = TRUE, verbose = FALSE)$Eta2_generalized ) expect_equal( anova(m, es = "ges", observed = "gender")$ges, eta_squared(Aov, generalized = "gender", verbose = FALSE)$Eta2_generalized ) # in a completely between design, with all measured, # all are equal to total expect_equal( eta_squared(Aov, generalized = c("gender", "treatment"), verbose = FALSE)[[2]], eta_squared(Aov, partial = FALSE, verbose = FALSE)[[2]] ) }) test_that("generalized | within-mixed", { skip_if_not_installed("afex") data(obk.long, package = "afex") # estimate mixed ANOVA on the full design: m <- afex::aov_car(value ~ treatment * gender + Error(id / (phase * hour)), data = obk.long, observed = "gender", include_aov = TRUE ) ef <- eta_squared(m$aov, generalized = "gender") af <- anova(m, es = "ges", observed = "gender") expect_equal(ef$Eta2_generalized, c( 0.211, 0.083, 0.186, 0.193, 0.099, 0.002, 0.015, 0.132, 0.001, 0.004, 0.011, 0.016, 0.008, 0.01, 0.02 ), tolerance = 0.05 ) expect_equal(ef$Eta2_generalized, af$ges, tolerance = 0.1 ) ef <- eta_squared(m$aov, generalized = TRUE) af <- anova(m, es = "ges", observed = NULL) expect_equal(ef$Eta2_generalized, c( 0.286, 0.111, 0.218, 0.264, 0.142, 0.004, 0.021, 0.185, 0.002, 0.005, 0.016, 0.023, 0.013, 0.014, 0.029 ), tolerance = 0.05 ) expect_equal(ef$Eta2_generalized, af$ges, tolerance = 0.1 ) }) # rm-omega ---------------------------------------------------------------- test_that("omega", { skip_if_not_installed("afex") # cross validated with MOTE data(obk.long, package = "afex") m <- suppressWarnings( afex::aov_car(value ~ treatment * gender + Error(id / (phase)), data = obk.long, observed = "gender", include_aov = TRUE ) ) ef <- omega_squared(m, partial = TRUE) expect_equal(ef$Omega2_partial, c(0.3115, 0.1814, 0.2221, 0.2637, 0.1512, -0.0173, -0.0171), tolerance = 0.01 ) expect_equal(ef$CI_low, c(0, 0, 0, 0, 0, 0, 0), tolerance = 0.01 ) # TODO Why are we getting diferent results on different systems and R releases? # expect_equal(ef$CI_high, # c(0.5814, 0.5036, 0.5052, 0.4589, 0.3023, 0, 0), # tolerance = 0.01) }) # afex -------------------------------------------------------------------- test_that("afex | within-mixed", { skip_if_not_installed("afex") data(obk.long, package = "afex") mod <- afex::aov_ez("id", "value", obk.long, between = c("treatment", "gender"), within = c("phase", "hour"), observed = "gender" ) x <- eta_squared(mod, generalized = TRUE) a <- anova(mod, observed = "gender") expect_equal(a$ges, x$Eta2_generalized) x <- eta_squared(mod) a <- anova(mod, es = "pes") expect_equal(a$pes, x$Eta2_partial) x <- eta_squared(mod, include_intercept = TRUE) a <- anova(mod, es = "pes", intercept = TRUE) expect_equal(a$pes, x$Eta2_partial) # see issue #389 data <- data.frame(subject = c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L), y = c(0.0586978983148275, -0.159870038198774, 0.0125690871484012, -0.0152529928817782, 0.092433880558952, 0.0359796249184537, -0.00786545388312909, 0.0340005375703463, 0.165294695432772, 0.0201040753050847, 0.0741924965491503, -0.0345053066539826, 0.0108194665250311, -0.163941830205729, 0.310344189786906, -0.106627229564326), A = c("A1", "A1", "A1", "A1", "A1", "A1", "A1", "A1", "A2", "A2", "A2", "A2", "A2", "A2", "A2", "A2"), B = c("B1", "B1", "B1", "B1", "B2", "B2", "B2", "B2", "B1", "B1", "B1", "B1", "B2", "B2", "B2", "B2"), C = c("C1", "C1", "C2", "C2", "C1", "C1", "C2", "C2", "C1", "C1", "C2", "C2", "C1", "C1", "C2", "C2")) mod <- afex::aov_ez("subject", "y", data, within = c("A", "B", "C")) tab <- as.data.frame(anova(mod, es = "pes")) res <- eta_squared(mod) tab <- tab[order(rownames(tab)),] res <- res[order(res$Parameter),] expect_equal(res$Eta2_partial,tab$pes, tolerance = 0.001) }) # car --------------------------------------------------------------------- test_that("car MVM", { skip_if_not_installed("afex") skip_if_not_installed("car") # Simple --- ds <- data.frame(I = c(116, 96, 120, 110, 116, 126, 86, 80), II = c(76, 93, 112, 113, 75, 120, 90, 105), III = c(85, 63, 89, 60, 115, 101, 129, 67), IV = c(50, 87, 100, 60, 79, 70, 65, 65), id = 1:8) ds_long <- data.frame( id = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L), ind_var = c("I", "I", "I", "I", "I", "I", "I", "I", "II", "II", "II", "II", "II", "II", "II", "II", "III", "III", "III", "III", "III", "III", "III", "III", "IV", "IV", "IV", "IV", "IV", "IV", "IV", "IV"), score = c(116, 96, 120, 110, 116, 126, 86, 80, 76, 93, 112, 113, 75, 120, 90, 105, 85, 63, 89, 60, 115, 101, 129, 67, 50, 87, 100, 60, 79, 70, 65, 65) ) fit <- lm(cbind(I, II, III, IV) ~ 1, data = ds) in_rep <- data.frame(ind_var = gl(4, 1)) A_car <- car::Anova(fit, idata = in_rep, idesign = ~ ind_var) eta_car <- effectsize::eta_squared(A_car, ci = NULL)[[2]] eta_afex <- afex::aov_ez('id', 'score', ds_long, within = 'ind_var', anova_table = list(es = "pes"))$anova_table$pes expect_equal(eta_car, eta_afex) # Complex --- data(obk.long, package = "afex") mod <- afex::aov_ez("id", "value", obk.long, between = c("treatment", "gender"), within = c("phase", "hour"), observed = "gender") expect_equal( sort(eta_squared(mod$Anova, generalized = "gender")[[2]]), sort(mod$anova_table$ges) ) }) # failed CIs -------------------------------------------------------------- test_that("failed CIs", { library(testthat) model <- aov(wt ~ cyl + Error(gear), data = mtcars) expect_warning(eta_squared(model), regexp = "CIs") expect_warning(eta <- eta_squared(model, verbose = FALSE), regexp = NA) expect_equal(nrow(eta), 2L) expect_equal(eta[1, "Eta2_partial"], 1) expect_warning(eta_squared(model, partial = FALSE), regexp = "CIs") expect_warning(eta <- eta_squared(model, partial = FALSE, verbose = FALSE), regexp = NA) expect_equal(nrow(eta), 2L) expect_equal(eta[1, "Eta2"], 0.34, tolerance = 0.01) }) # Include intercept ------------------------------------------------------- test_that("include_intercept | car", { skip_on_cran() skip_if_not_installed("car") m <- lm(mpg ~ factor(cyl) * factor(am), data = mtcars) AOV <- car::Anova(m, type = 3) res0 <- eta_squared(AOV, verbose = FALSE) res1 <- eta_squared(AOV, include_intercept = TRUE, verbose = FALSE) expect_equal(nrow(res0), 3) expect_equal(nrow(res1), nrow(res0) + 1) expect_equal(res1[[1]][1], "(Intercept)") expect_equal(res1[[2]][1], 0.8680899, tolerance = 0.01) res0 <- epsilon_squared(AOV, verbose = FALSE) res1 <- epsilon_squared(AOV, include_intercept = TRUE, verbose = FALSE) expect_equal(nrow(res0), 3) expect_equal(nrow(res1), nrow(res0) + 1) expect_equal(res1[[1]][1], "(Intercept)") res0 <- omega_squared(AOV, verbose = FALSE) res1 <- omega_squared(AOV, include_intercept = TRUE, verbose = FALSE) expect_equal(nrow(res0), 3) expect_equal(nrow(res1), nrow(res0) + 1) expect_equal(res1[[1]][1], "(Intercept)") # generalized res1 <- eta_squared(AOV, generalized = "cyl", include_intercept = TRUE, verbose = FALSE) expect_equal(res1[[1]][1], "(Intercept)") expect_equal(res1[[2]][1], 0.784483, tolerance = 0.01) }) test_that("include_intercept | afex", { skip_if_not_installed("afex") data(obk.long, package = "afex") suppressWarnings(suppressMessages( a <- afex::aov_car(value ~ treatment * gender + Error(id), include_aov = TRUE, data = obk.long ) )) resE0 <- eta_squared(a, verbose = FALSE) resA0 <- anova(a, es = "pes") expect_equal(nrow(resE0), 3) expect_equal(nrow(resE0), nrow(resA0)) resE1 <- eta_squared(a, include_intercept = TRUE, verbose = FALSE) resA1 <- anova(a, es = "pes", intercept = TRUE) expect_equal(nrow(resE1), nrow(resE0) + 1) expect_equal(nrow(resE1), nrow(resA1)) skip_if_not_installed("car") resE1 <- eta_squared(car::Anova(a$aov, type = 3), include_intercept = TRUE, generalized = "gender", verbose = FALSE) resA1 <- anova(a, es = "ges", intercept = TRUE, observed = "gender") expect_equal(resE1[[2]][1], 0.9386555, tolerance = 0.01) expect_equal(resE1[[2]][1], resA1[[5]][1], tolerance = 0.01) }) # Special cases -------------------------------------------------------------- ## tidymodels ------------------- test_that("ets_squared | tidymodels", { skip_on_cran() skip_if_not_installed("tidymodels") require("tidymodels", quietly = TRUE) set.seed(123) mod_lm <- linear_reg() %>% set_engine("lm") %>% set_mode("regression") %>% fit(mpg ~ am + vs, data = mtcars) set.seed(123) tidy_lm <- eta_squared(mod_lm) lm_lm <- eta_squared(lm(mpg ~ am + vs, data = mtcars)) expect_equal(tidy_lm, lm_lm, tolerance = 0.001) }) ## GAMs ------------------- test_that("ets_squared | gam", { skip_on_cran() skip_if_not_installed("mgcv") set.seed(2) ## simulate some data... dat <- mgcv::gamSim(1, n = 400, dist = "normal", scale = 2) b <- mgcv::gam(y ~ x0 + s(x1) + s(x2) + t2(x1, x2) + s(x3), data = dat) expect_error(out <- eta_squared(b), regexp = NA) expect_output(print(out), "Type III") }) ## rms ------------------- test_that("ets_squared | rms", { skip_on_cran() skip_if_not_installed("rms") b <- rms::ols(mpg ~ cyl + am, data = mtcars) expect_error(out <- eta_squared(b), regexp = NA) expect_output(print(out), "Type II") skip_if_not_installed("car") b_lm <- car::Anova(lm(mpg ~ cyl + am, data = mtcars), type = 2) out_lm <- eta_squared(b_lm) expect_equal(out[1:2, ], out_lm, ignore_attr = TRUE) }) } effectsize/tests/testthat/test-format_standardize.R0000644000175000017500000000205014132466117022517 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("format_standardize", { expect_equal( format_standardize(c(-1, 0, 1), digits = 0), structure(3:1, .Label = c("+1 SD", "Mean", "-1 SD"), class = "factor") ) skip_if_not_installed("bayestestR") ref <- bayestestR::distribution_normal(1000) expect_equal( format_standardize(c(-1, 0, 1, 2), reference = ref, digits = 0), structure(4:1, .Label = c("+2 SD", "+1 SD", "Mean", "-1 SD"), class = "factor" ) ) expect_equal( format_standardize(c(-1, 0, 1, 2), reference = ref, robust = TRUE, digits = 0), structure(4:1, .Label = c("+2 MAD", "+1 MAD", "Median", "-1 MAD"), class = "factor" ) ) expect_equal( format_standardize(c(-1, 0, 1, 2), reference = ref, robust = TRUE, digits = 2, protect_integers = FALSE), structure(4:1, .Label = c("+2.00 MAD", "+1.00 MAD", "Median", "-1.00 MAD"), class = "factor" ) ) }) } effectsize/tests/testthat/test-equivalence_test.R0000644000175000017500000000135514132466117022206 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("equivalence_test", { ds <- t_to_d( t = c(0.45, -0.65, 7, -2.2, 2.25), df_error = c(675, 525, 2000, 900, 1875), ci = 0.9 ) # TOST approach expect_equal( equivalence_test(ds, range = 0.2)$ROPE_Equivalence, c("Accepted", "Undecided", "Rejected", "Rejected", "Accepted") ) expect_equal( equivalence_test(ds, range = 0.2, rule = "cet")$ROPE_Equivalence, c("Accepted", "Undecided", "Rejected", "Rejected", "Rejected") ) expect_equal( equivalence_test(ds, range = 0.2, rule = "bayes")$ROPE_Equivalence, c("Accepted", "Undecided", "Rejected", "Undecided", "Accepted") ) }) } effectsize/tests/testthat/test-sd_pooled.R0000644000175000017500000000061414132466117020613 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("sd_pooled", { expect_equal(sd_pooled(1:4, 1:3 * 5), 3.316625, tolerance = 0.001) expect_equal(mad_pooled(1:4, 1:3 * 5), 3.297154, tolerance = 0.001) expect_equal(sd_pooled(c(1:3, 40), 1:3 * 5), 15.06652, tolerance = 0.001) expect_equal(mad_pooled(c(1:3, 40), 1:3 * 5), 3.297154, tolerance = 0.001) }) } effectsize/tests/testthat/test-eta_squared_posterior.R0000644000175000017500000000325114132466117023246 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("eta_squared_posterior", { skip_on_cran() skip_if_not_installed("rstanarm") skip_if_not_installed("bayestestR") skip_if_not_installed("car") fit_bayes <- rstanarm::stan_glm(mpg ~ factor(cyl) * wt + qsec, data = mtcars, family = gaussian(), refresh = 0 ) # PARTIAL, type = 3 ------------------------------------------------------- es_tab <- eta_squared( car::Anova( lm(mpg ~ factor(cyl) * wt + qsec, data = mtcars ), type = 3 ), partial = TRUE ) expect_warning( es_post <- eta_squared_posterior(fit_bayes, ss_function = car::Anova, type = 3 ), regexp = "bogus" ) expect_equal(colnames(es_post), es_tab$Parameter) # this is a very soft test... es_tab_bayes <- bayestestR::describe_posterior(es_post) expect_equal(order(es_tab_bayes$Median), order(es_tab$Eta2)) # non-PARTIAL, type = 3 --------------------------------------------------- es_tab <- eta_squared( car::Anova( lm(mpg ~ factor(cyl) * wt + qsec, data = mtcars ), type = 3 ), partial = FALSE ) expect_warning( es_post <- eta_squared_posterior(fit_bayes, partial = FALSE, ss_function = car::Anova, type = 3 ), regexp = "bogus" ) expect_equal(colnames(es_post), es_tab$Parameter) # this is a very soft test... es_tab_bayes <- bayestestR::describe_posterior(es_post) expect_equal(order(es_tab_bayes$Median), order(es_tab$Eta2)) }) } effectsize/tests/testthat/test-interpret.R0000644000175000017500000002607214174160423020662 0ustar nileshnileshif (require("testthat") && require("effectsize")) { # interpret generic ---- test_that("interpret generic", { rules_grid <- rules(c(0.01, 0.05), c("very significant", "significant", "not significant")) expect_equal(interpret(0.001, rules_grid)[1], "very significant") expect_equal(interpret(0.021, rules_grid)[1], "significant") expect_equal(interpret(0.08, rules_grid)[1], "not significant") expect_equal( interpret(c(0.01, 0.005, 0.08), rules_grid)[1:3], c("very significant", "very significant", "not significant") ) expect_error(interpret_r(0.6, rules(c(0.5), c("A", "B", "C")))) expect_error(interpret_r(0.6, rules(c(0.5, 0.2, 0.7), c("A", "B", "C", "D")))) r1 <- rules(c(0, 1), labels = c("some", "few", "many")) r2 <- rules(c(0, 1), labels = c("some", "few", "many"), right = FALSE) expect_equal(interpret(c(0, 1), r1)[], c("some", "few"), ignore_attr = TRUE) expect_equal(interpret(c(0, 1), r2)[], c("few", "many"), ignore_attr = TRUE) }) # interpret types ---- test_that("interpret_r", { expect_equal(interpret_r(0.21)[1], "medium") expect_equal(interpret_r(0.21, "cohen1988")[1], "small") expect_equal(interpret_r(0.21, "lovakov2021")[1], "small") expect_equal(interpret_r(0.7, "evans1996")[1], "strong") expect_equal(interpret_r(c(0.5, -0.08), "cohen1988")[1:2], c("large", "very small")) expect_equal(interpret_r(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_r(0.6, "DUPA")) }) test_that("interpret_p", { expect_equal(interpret_p(0.021)[1], "significant") expect_equal(interpret_p(0.08)[1], "not significant") expect_equal(interpret_p(c(0.01, 0.08))[1:2], c("significant", "not significant")) expect_equal(interpret_p(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_p(0.6, "DUPA")) }) test_that("interpret_direction", { expect_equal(interpret_direction(c(0.01, -0.08))[1:2], c("positive", "negative")) }) test_that("interpret_cohens_d", { expect_equal(interpret_cohens_d(0.021)[1], "very small") expect_equal(interpret_cohens_d(1.3, "sawilowsky2009")[1], "very large") expect_equal(interpret_cohens_d(c(0.45, 0.85), "cohen1988")[1:2], c("small", "large")) expect_equal(interpret_cohens_d(c(0.45, 0.85), "lovakov2021")[1:2], c("medium", "large")) expect_equal(interpret_cohens_d(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_cohens_d(0.6, "DUPA")) }) test_that("interpret_cohens_g", { expect_equal(interpret_cohens_g(0.021)[1], "very small") expect_equal(interpret_cohens_g(c(0.10, 0.35), "cohen1988")[1:2], c("small", "large")) expect_equal(interpret_cohens_g(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_cohens_g(0.6, "DUPA")) }) test_that("interpret_rope", { expect_equal(interpret_rope(0, ci = 0.9)[1], "significant") expect_equal(interpret_rope(c(0.50, 1), ci = 0.9)[1:2], c("undecided", "negligible")) expect_equal(interpret_rope(c(0.98, 0.991), ci = 1)[1:2], c("probably negligible", "negligible")) expect_equal(interpret_rope(0.6, , rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_rope(0.6, , "DUPA")) }) test_that("interpret_oddsratio", { expect_equal(interpret_oddsratio(2)[1], "small") expect_equal(interpret_oddsratio(c(1, 3))[1:2], c("very small", "small")) expect_equal(interpret_oddsratio(c(1, 3), "cohen1988")[1:2], c("very small", "medium")) expect_equal(interpret_oddsratio(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_oddsratio(0.6, "DUPA")) }) test_that("interpret_r2", { expect_equal(interpret_r2(0.4)[1], "substantial") expect_equal(interpret_r2(c(0, 0.4), "falk1992")[1:2], c("negligible", "adequate")) expect_equal(interpret_r2(c(0.1, 0.4), "chin1998")[1:2], c("very weak", "moderate")) expect_equal(interpret_r2(c(0.1, 0.4), "hair2011")[1:2], c("very weak", "weak")) expect_equal(interpret_r2(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_r2(0.6, "DUPA")) }) test_that("interpret_bf", { expect_warning(interpret_bf(-2)) expect_equal(interpret_bf(1)[1], "no evidence against or in favour of") expect_equal( interpret_bf(c(0.8, 3.5), "jeffreys1961")[1:2], c("anecdotal evidence against", "moderate evidence in favour of") ) expect_equal( interpret_bf(c(0.8, 3.5), "raftery1995")[1:2], c("weak evidence against", "positive evidence in favour of") ) expect_equal(interpret_bf(2, rules(c(0.5), c("A", "B")))[1], "B evidence in favour of") expect_error(interpret_bf(2, "DUPA")) skip_on_cran() # just in case there are changes in insight bf <- c(10^seq(-4, 4), NA) expect_equal(interpret_bf(bf, include_value = TRUE, protect_ratio = TRUE, exact = TRUE), c( "extreme evidence (BF = 1/1.00e+04) against", "extreme evidence (BF = 1/1000.00) against", "very strong evidence (BF = 1/100.00) against", "moderate evidence (BF = 1/10.00) against", "no evidence (BF = 1.00) against or in favour of", "strong evidence (BF = 10.00) in favour of", "extreme evidence (BF = 100.00) in favour of", "extreme evidence (BF = 1000.00) in favour of", "extreme evidence (BF = 1.00e+04) in favour of", "" ), ignore_attr = TRUE ) }) test_that("interpret_omega_squared", { expect_equal(interpret_omega_squared(0.1)[1], "medium") expect_equal(interpret_omega_squared(c(0.1, 0.25))[1:2], c("medium", "large")) expect_equal(interpret_omega_squared(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_omega_squared(0.6, "DUPA")) # these should be same expect_equal(interpret_eta_squared(0.1)[1], interpret_omega_squared(0.1)[1]) expect_equal( interpret_eta_squared(c(0.1, 0.25))[1:2], interpret_omega_squared(c(0.1, 0.25))[1:2] ) }) test_that("interpret_kendalls_w", { expect_equal(interpret_kendalls_w(0.1)[1], "slight agreement") expect_equal( interpret_kendalls_w(c(0.1, 0.25))[1:2], c("slight agreement", "fair agreement") ) expect_equal(interpret_kendalls_w(0.9)[1], "almost perfect agreement") expect_equal(interpret_kendalls_w(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_kendalls_w(0.6, "DUPA")) }) test_that("interpret_rhat", { expect_equal(interpret_rhat(1)[1], "converged") expect_equal(interpret_rhat(c(1, 1.02))[1:2], c("converged", "failed")) expect_equal(interpret_rhat(c(1, 1.02), "gelman1992")[1:2], c("converged", "converged")) expect_equal(interpret_rhat(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_rhat(0.6, "DUPA")) }) test_that("interpret_ess", { expect_equal(interpret_ess(1000)[1], "sufficient") expect_equal(interpret_ess(c(1000, 800))[1:2], c("sufficient", "insufficient")) expect_equal(interpret_ess(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_ess(0.6, "DUPA")) }) test_that("interpret_fit", { expect_equal(interpret_gfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_agfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_nfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_nnfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_cfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_rfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_ifi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_pnfi(c(.5, .99)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_rmsea(c(.1, .05)), c("poor", "satisfactory"), ignore_attr = TRUE) expect_equal(interpret_srmr(c(.1, .05)), c("poor", "satisfactory"), ignore_attr = TRUE) cr <- rules(c(0.5), c("A", "B")) expect_equal(interpret_gfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_agfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_nfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_nnfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_cfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_rfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_ifi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_pnfi(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_rmsea(0.6, cr), "B", ignore_attr = TRUE) expect_equal(interpret_srmr(0.6, cr), "B", ignore_attr = TRUE) expect_error(interpret_gfi(0.6, "DUPA")) expect_error(interpret_agfi(0.6, "DUPA")) expect_error(interpret_nfi(0.6, "DUPA")) expect_error(interpret_nnfi(0.6, "DUPA")) expect_error(interpret_cfi(0.6, "DUPA")) expect_error(interpret_rfi(0.6, "DUPA")) expect_error(interpret_ifi(0.6, "DUPA")) expect_error(interpret_pnfi(0.6, "DUPA")) expect_error(interpret_rmsea(0.6, "DUPA")) expect_error(interpret_srmr(0.6, "DUPA")) skip_on_cran() skip_if_not_installed("lavaan") skip_if_not_installed("performance", minimum_version = "0.8.0.1") structure <- " ind60 =~ x1 + x2 + x3 dem60 =~ y1 + y2 + y3 dem60 ~ ind60 " model <- lavaan::sem(structure, data = lavaan::PoliticalDemocracy) int <- interpret(model) expect_equal(int$Name, c("GFI", "AGFI", "NFI", "NNFI", "CFI", "RMSEA", "SRMR", "RFI", "PNFI", "IFI")) expect_equal(int$Value,c(0.9666, 0.9124, 0.9749, 1.0001, 1, 0, 0.0273, 0.9529, 0.5199, 1.0001), tolerance = 0.001) }) test_that("interpret_icc", { expect_equal(interpret_icc(c(0.45, 0.55, 0.8, 0.95)), c("poor", "moderate", "good", "excellent"), ignore_attr = TRUE) expect_equal(interpret_icc(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_icc(0.6, "DUPA")) }) test_that("interpret_vif", { expect_equal(interpret_vif(c(1, 5.5, 10)), c("low", "moderate", "high"), ignore_attr = TRUE) expect_equal(interpret_icc(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_icc(0.6, "DUPA")) }) test_that("interpret_pd", { expect_equal(interpret_pd(c(0.9, 0.99)), c("not significant", "significant"), ignore_attr = TRUE) expect_equal(interpret_pd(c(0.9, 0.99), "makowski2019"), c("uncertain", "likely existing"), ignore_attr = TRUE) expect_equal(interpret_pd(0.6, rules(c(0.5), c("A", "B")))[1], "B") expect_error(interpret_pd(0.6, "DUPA")) }) # interpret effectsize_table ---- test_that("interpret effectsize_table", { d <- cohens_d(mpg ~ am, data = mtcars) expect_error(interpret(d)) d_ <- interpret(d, rules = "cohen1988") expect_equal(d_[["Interpretation"]], "large", ignore_attr = TRUE) expect_s3_class(d_[["Interpretation"]], "effectsize_interpret") expect_output(print(d_), "large") expect_output(print(d_), "Interpretation rule: cohen1988") }) } effectsize/tests/testthat/test-standardized_differences.R0000644000175000017500000001505714170065645023666 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("cohens_d errors and warnings", { # Direction --------------------------------------------------------------- rez_t <- t.test(iris$Sepal.Length, iris$Sepal.Width) rez_d <- cohens_d(iris$Sepal.Length, iris$Sepal.Width) expect_equal(sign(rez_t$statistic), sign(rez_d$Cohens_d), ignore_attr = TRUE) # Alternative ------------------------------------------------------------- d1 <- cohens_d(iris$Sepal.Length, iris$Sepal.Width, ci = 0.80) d2 <- cohens_d(iris$Sepal.Length, iris$Sepal.Width, ci = 0.90, alternative = "l") d3 <- cohens_d(iris$Sepal.Length, iris$Sepal.Width, ci = 0.90, alternative = "g") expect_equal(d1$CI_high, d2$CI_high) expect_equal(d1$CI_low, d3$CI_low) # Errors and warnings ----------------------------------------------------- df <- data.frame( a = 1:10, b = 2:11, c = rep(letters[1:2], each = 5), d = c("a", "b", "b", "c", "c", "b", "c", "a", "a", "b"), e = rep(0:1, each = 5) ) df$exp_a <- exp(df$a) a2 <- 1:11 expect_error(cohens_d(a ~ c, data = df), regexp = NA) expect_error(cohens_d("a", "c", data = df), regexp = NA) expect_error(cohens_d("a", "b", data = df), regexp = NA) expect_error(cohens_d(a2, df$b), regexp = NA) expect_error(cohens_d(b ~ e, data = df), regexp = NA) expect_error(cohens_d(df$a ~ df$c), regexp = NA) expect_equal(cohens_d("exp_a", "c", data = df), cohens_d(exp(a) ~ c, data = df)) expect_error(cohens_d(a ~ b, data = df)) expect_error(cohens_d(a ~ d, data = df)) expect_error(cohens_d("a", "d", data = df)) expect_error(cohens_d("c", "c", data = df)) expect_error(cohens_d(a2, df$c)) expect_error(cohens_d("a", "aa", data = df)) expect_warning(cohens_d("b", "e", data = df)) }) test_that("cohens d - grouping character vector", { dat <- data.frame( g = rep(c("treatment", "control"), each = 100), y = c(rnorm(n = 200)) ) d <- cohens_d(dat$y, factor(dat$g), ci = NULL)[[1]] expect_equal(cohens_d(dat$y, dat$g, ci = NULL)[[1]], d) expect_equal(cohens_d(y ~ g, data = dat, ci = NULL)[[1]], d) expect_equal(cohens_d(y ~ factor(g), data = dat, ci = NULL)[[1]], d) expect_equal(cohens_d(dat$y ~ dat$g, ci = NULL)[[1]], d) expect_equal(cohens_d(dat$y ~ factor(dat$g), ci = NULL)[[1]], d) expect_equal(cohens_d("y", "g", data = dat, ci = NULL)[[1]], d) expect_equal(cohens_d("y", dat$g, data = dat, ci = NULL)[[1]], d) expect_equal(cohens_d(dat$y, "g", data = dat, ci = NULL)[[1]], d) }) test_that("cohens_d - mu", { expect_equal(cohens_d(mtcars$mpg - 5), cohens_d(mtcars$mpg, mu = 5), ignore_attr = TRUE ) x <- 1:9 y <- c(1, 1:9) expect_equal(cohens_d(x - 3, y), cohens_d(x, y, mu = 3), ignore_attr = TRUE ) # t.test(x, y, mu = 3.125, var.equal = TRUE) d <- cohens_d(x, y, mu = 3.125) expect_equal(d[[1]], -0.969, tolerance = 0.01) expect_equal(d$CI_low, -1.913, tolerance = 0.01) expect_equal(d$CI_high[[1]], 0, tolerance = 0.01) }) test_that("cohens_d - pooled", { x <- cohens_d(wt ~ am, data = mtcars, pooled_sd = TRUE) expect_equal(colnames(x)[1], "Cohens_d") expect_equal(x[[1]], 1.892, tolerance = 0.001) expect_equal(x$CI_low, 1.030, tolerance = 0.001) expect_equal(x$CI_high, 2.732, tolerance = 0.001) }) test_that("cohens_d - non-pooled", { x <- cohens_d(wt ~ am, data = mtcars, pooled_sd = FALSE) expect_equal(colnames(x)[1], "Cohens_d") expect_equal(x[[1]], 1.934, tolerance = 0.001) expect_equal(x$CI_low, 1.075151, tolerance = 0.001) expect_equal(x$CI_high, 2.772516, tolerance = 0.001) }) test_that("hedges_g (and other bias correction things", { expect_warning(x <- hedges_g(wt ~ am, data = mtcars, correction = TRUE)) expect_equal(colnames(x)[1], "Hedges_g") expect_equal(x[[1]], 1.844, tolerance = 0.001) expect_equal(x$CI_low, 1.004, tolerance = 0.001) expect_equal(x$CI_high, 2.664, tolerance = 0.001) }) test_that("glass_delta", { # must be 2 samples expect_error(glass_delta(1:10)) expect_error(glass_delta(wt, data = mtcars)) x <- glass_delta(wt ~ am, data = mtcars) expect_equal(colnames(x)[1], "Glass_delta") expect_equal(x[[1]], 2.200, tolerance = 0.001) expect_equal(x$CI_low, 1.008664, tolerance = 0.001) expect_equal(x$CI_high, 3.352597, tolerance = 0.001) }) test_that("fixed values", { skip_if_not_installed("bayestestR") x1 <- bayestestR::distribution_normal(1e4, mean = 0, sd = 1) x2 <- bayestestR::distribution_normal(1e4, mean = 1, sd = 1) expect_equal(cohens_d(x1, x2)$Cohens_d, -1, tolerance = 1e-3) x1 <- bayestestR::distribution_normal(1e4, mean = 0, sd = 1) x2 <- bayestestR::distribution_normal(1e4, mean = 1.5, sd = 2) expect_equal(cohens_d(x1, x2)[[1]], -sqrt(0.9), tolerance = 1e-2) expect_equal(glass_delta(x2, x1, ci = NULL)[[1]], 1.5, tolerance = 1e-2) }) test_that("Missing values", { x <- c(1, 2, NA, 3) y <- c(1, 1, 2, 3) expect_equal(cohens_d(x, y)[[1]], 0.2564946, tolerance = 0.01) # indep expect_equal(cohens_d(x, y, paired = TRUE)[[1]], 0.5773503, tolerance = 0.01) # paired # no length problems expect_error(cohens_d(mtcars$mpg - 23), regexp = NA) # Missing factor levels: the actual levels in the data are 3rd and 4th f <- factor(letters[1:2], levels = c("d", "e", "a", "b")) f <- rep(f, each = 5) y <- c(2, 4, 3, 5, 1, 7, 9, 8, 6, 1) expect_error(d <- cohens_d(y, f), regexp = NA) expect_true(attr(d, "pooled_sd")) }) test_that("CLES", { set.seed(3) x <<- rnorm(1000) y <<- rnorm(500, mean = 0.2) d <- cohens_d(x, y) tt <- t.test(x,y, var.equal = TRUE) expect_equal(CLES <- d_to_cles(d), cles(x, y)) expect_equal(cles(tt), CLES, ignore_attr = TRUE) rb <- rank_biserial(x, y, ci = NULL) w <- wilcox.test(x, y) CLES <- cles(x, y, parametric = FALSE, ci = NULL) expect_equal(rb_to_cles(rb), CLES[1,], ignore_attr = TRUE) expect_equal(cles(w, parametric = FALSE)[,1:2], CLES, ignore_attr = TRUE) x <- 1:3 y <- c(1,1:3) CLES <- cles(x, y) # CV from https://rpsychologist.com/cohend/ expect_equal(CLES$Coefficient, c(0.5719, 0.6012, 0.8979), tolerance = 0.001) # Should be close~ expect_equal(cles(x, y, rank = TRUE)$Coefficient[1], CLES$Coefficient[1], tolerance = 0.02) }) } effectsize/tests/testthat/test-standardize_models.R0000644000175000017500000002205214170065645022521 0ustar nileshnileshif (require("testthat") && require("effectsize")) { # standardize.lm ---------------------------------------------------------- test_that("standardize.lm", { iris2 <- na.omit(iris) iris_z <- standardize(iris2) m0 <- lm(Sepal.Length ~ Species * Petal.Width, data = iris_z) m1 <- lm(Sepal.Length ~ Species * Petal.Width, data = iris2) model <- standardize(m1) expect_equal(coef(m0), coef(model)) }) test_that("standardize, mlm", { m <- lm(cbind(mpg, hp) ~ cyl + am, data = mtcars) m2 <- lm(scale(cbind(mpg, hp)) ~ scale(cyl) + scale(am), data = mtcars) mz <- standardize(m) expect_equal(coef(mz), coef(m2), ignore_attr = TRUE) }) test_that("standardize | errors", { my_lm_external_formula <- function(.dat, predicted, predictor){ my_formula <- as.formula(paste0(predicted, "~", predictor)) lm(formula = my_formula, data = .dat) } m <- my_lm_external_formula(mtcars, "mpg", "am") expect_error(standardize(m), "Try instead to standardize the data", fixed = TRUE) }) # Transformations --------------------------------------------------------- test_that("transformations", { # deal with log / sqrt terms expect_message(standardize(lm(mpg ~ sqrt(cyl) + log(hp), mtcars))) expect_message(standardize(lm(mpg ~ sqrt(cyl), mtcars))) expect_message(standardize(lm(mpg ~ log(hp), mtcars))) # difference between stand-methods: mt <- mtcars mt$hp_100 <- mt$hp / 100 fit_exp <- lm(mpg ~ exp(hp_100), mt) fit_scale1 <- lm(scale(mpg) ~ exp(scale(hp_100)), mt) fit_scale2 <- lm(scale(mpg) ~ scale(exp(hp_100)), mt) expect_equal( standardize_parameters(fit_exp, method = "refit")[2, 2], unname(coef(fit_scale1)[2]) ) expect_equal( standardize_parameters(fit_exp, method = "basic")[2, 2], unname(coef(fit_scale2)[2]) ) skip_if_not_installed("insight", minimum_version = "0.10.0") d <- data.frame( time = as.factor(c(1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5)), group = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2), sum = c(0, 5, 10, 15, 20, 0, 20, 25, 45, 50, 0, 5, 10, 15, 20, 0, 20, 25, 45, 50, 0, 5, 10, 15, 20, 0, 20, 25, 45, 50) ) m <- lm(log(sum + 1) ~ as.numeric(time) * group, data = d) expect_message(out <- standardize(m)) expect_equal(coef(m), c( `(Intercept)` = -0.4575, `as.numeric(time)` = 0.5492, group = 0.3379, `as.numeric(time):group` = 0.15779 ), tolerance = 0.01) }) # W/ weights -------------------------------------------------------------- test_that("weights", { expect_warning(standardize(mtcars, weights = "xx")) m <- lm(mpg ~ am + hp, weights = cyl, mtcars) sm <- standardize(m, weights = TRUE) sm_data <- insight::get_data(sm) sm_data2 <- standardize(mtcars, select = c("mpg", "am", "hp"), weights = "cyl") expect_equal(sm_data[, c("mpg", "am", "hp")], sm_data2[, c("mpg", "am", "hp")]) # no weights in stding sm_xw <- standardize(m, weights = FALSE) sm_data_xw <- insight::get_data(sm_xw) expect_false(isTRUE(all.equal(coef(sm)[-1], coef(sm_xw)[-1]))) # refit and posthoc should give same results stdREFIT <- standardize_parameters(m, method = "refit") expect_equal( stdREFIT[[2]], standardize_parameters(m, method = "posthoc")[[2]] ) expect_equal( stdREFIT[[2]], standardize_parameters(m, method = "basic")[[2]] ) }) # weights + missing data -------------------------------------------------- test_that("weights + NA", { set.seed(1234) data(iris) # data setup iris$weight_me <- runif(nrow(iris)) iris$Sepal.Length[sample(nrow(iris), size = 10)] <- NA iris$weight_me[sample(nrow(iris), size = 10)] <- NA # standardize 2nd data set iris2 <- standardize(iris, select = c("Sepal.Length", "Petal.Width"), remove_na = "all" ) iris3 <- standardize(iris, select = c("Sepal.Length", "Petal.Width"), weights = "weight_me", remove_na = "selected" ) m1 <- lm(Sepal.Length ~ Species + Petal.Width, data = iris, weights = weight_me) # weights, missing data, but data isn't weight-stdized m2 <- lm(Sepal.Length ~ Species + Petal.Width, data = iris2, weights = weight_me) sm2 <- standardize(m1, weights = FALSE) expect_equal(coef(m2), coef(sm2)) # weights, missing data, and data is weight-stdized m3 <- lm(Sepal.Length ~ Species + Petal.Width, data = iris3, weights = weight_me) sm3 <- standardize(m1, weights = TRUE) expect_equal(coef(m3), coef(sm3)) }) # weights + missing data ´+ na.action = na.exclude -------------------------------------------------- test_that("weights + NA + na.exclude", { set.seed(1234) data(iris) # data setup iris$weight_me <- runif(nrow(iris)) iris$Sepal.Length[sample(nrow(iris), size = 25)] <- NA iris$weight_me[sample(nrow(iris), size = 15)] <- NA m1 <- lm(Sepal.Length ~ Species + Petal.Width, data = iris, weights = weight_me, na.action = na.exclude) m2 <- lm(Sepal.Length ~ Species + Petal.Width, data = iris, weights = weight_me) expect_equal(coef(standardize(m2)), coef(standardize(m1)), tolerance = 1e-3) expect_equal(standardize_parameters(m1, method = "basic")[[2]], standardize_parameters(m2, method = "basic")[[2]], tolerance = 1e-3 ) }) # don't standardize non-Gaussian response ------------------------------------ test_that("standardize non-Gaussian response", { skip_on_cran() skip_if_not_installed("lme4") set.seed(1234) data(sleepstudy, package = "lme4") m1 <- glm(Reaction ~ Days, family = Gamma(), data = sleepstudy) m2 <- glm(Reaction ~ Days, family = Gamma(link = "identity"), data = sleepstudy) m3 <- glm(Reaction ~ Days, family = inverse.gaussian(), data = sleepstudy) expect_equal(coef(standardize(m1)), c(`(Intercept)` = 0.00338, Days = -0.00034), tolerance = 1e-2) expect_equal(coef(standardize(m2)), c(`(Intercept)` = 298.48571, Days = 29.70754), tolerance = 1e-3) expect_equal(coef(standardize(m3)), c(`(Intercept)` = 1e-05, Days = 0), tolerance = 1e-3) }) # variables evaluated in the environment $$$ ------------------------------ test_that("variables evaluated in the environment", { m <- lm(mtcars$mpg ~ mtcars$cyl + am, data = mtcars) w <- capture_warnings(standardize(m)) expect_true(any(grepl("mtcars$mpg", w, fixed = TRUE))) skip_if(packageVersion("base") == package_version(3.4)) ## Note: # No idea why this is suddenly not giving a warning on older R versions. m <- lm(mtcars$mpg ~ mtcars$cyl + mtcars$am, data = mtcars) warns <- capture_warnings(standardize(m)) expect_true(any(grepl("mtcars$mpg", warns, fixed = TRUE))) expect_true(any(grepl("No variables", warns, fixed = TRUE))) }) # mediation models -------------------------------------------------------- test_that("standardize mediation", { skip_on_cran() skip_if_not_installed("mediation") set.seed(444) data(jobs, package = "mediation") jobs$econ_hard <- jobs$econ_hard * 20 b.int <- lm(job_seek ~ treat * age + econ_hard + sex, data = jobs) d.int <- lm(depress2 ~ treat * job_seek * age + econ_hard + sex, data = jobs) med1 <- mediation::mediate(b.int, d.int, sims = 200, treat = "treat", mediator = "job_seek") med2 <- mediation::mediate(b.int, d.int, sims = 200, treat = "treat", mediator = "job_seek", covariates = list(age = mean(jobs$age)) ) out1 <- summary(standardize(med1)) expect_message(out2 <- summary(standardize(med2))) expect_equal(unlist(out1[c("d0", "d1", "z0", "z1", "n0", "n1", "tau.coef")]), unlist(out2[c("d0", "d1", "z0", "z1", "n0", "n1", "tau.coef")]), tolerance = 0.1 ) med0 <- mediation::mediate(standardize(b.int), standardize(d.int), sims = 200, treat = "treat", mediator = "job_seek") out0 <- summary(med0) medz <- standardize(mediation::mediate(b.int, d.int, sims = 200, treat = "treat", mediator = "job_seek")) outz <- summary(medz) expect_equal(unlist(out0[c("d0", "d1", "z0", "z1", "n0", "n1", "tau.coef")]), unlist(outz[c("d0", "d1", "z0", "z1", "n0", "n1", "tau.coef")]), tolerance = 0.1 ) }) # Offsets ----------------------------------------------------------------- test_that("offsets", { m <- lm(mpg ~ hp + offset(wt), data = mtcars) expect_warning(mz1 <- standardize(m)) expect_warning(mz2 <- standardize(m, two_sd = TRUE)) expect_equal(c(1, 2) * coef(mz1), coef(mz2)) m <- glm(cyl ~ hp + offset(wt), family = poisson(), data = mtcars) expect_warning(mz <- standardize(m), regexp = NA) par1 <- parameters::model_parameters(mz) par2 <- standardize_parameters(m, method = "basic") expect_equal(par2[2,2], par1[2,2], tolerance = 0.05) }) } effectsize/tests/testthat/test-convert_statistic.R0000644000175000017500000000742014132466117022414 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("xtab", { xtab <- as.table(rbind( c(762, 327, 468), c(484, 239, 477), c(484, 239, 477) )) chisq <- chisq.test(xtab, correct = FALSE) res <- chisq_to_cramers_v( chisq$statistic, n = sum(xtab), nrow = nrow(xtab), ncol = ncol(xtab) ) expect_equal(res, cramers_v(xtab), ignore_attr = TRUE) res <- chisq_to_phi( chisq$statistic, n = sum(xtab), nrow = nrow(xtab), ncol = ncol(xtab) ) expect_equal(res, phi(xtab), ignore_attr = TRUE) }) test_that("r", { res1 <- cor.test(iris[[1]], iris[[2]]) res2 <- t_to_r(t = res1$statistic, res1$parameter) expect_equal(res2$r, res1$estimate, tolerance = 0.01, ignore_attr = TRUE) expect_equal(res2$CI_low, res1$conf.int[1], tolerance = 0.02, ignore_attr = TRUE) expect_equal(res2$CI_high, res1$conf.int[2], tolerance = 0.01, ignore_attr = TRUE) res3 <- F_to_r(res1$statistic^2, 1, res1$parameter) expect_equal(res3$r, -res1$estimate, tolerance = 0.01, ignore_attr = TRUE) expect_equal(res3$CI_low, -res1$conf.int[2], tolerance = 0.02, ignore_attr = TRUE) expect_equal(res3$CI_high, -res1$conf.int[1], tolerance = 0.02, ignore_attr = TRUE) expect_error(F_to_r(3, 2, 3)) res4 <- z_to_r(res1$statistic, res1$parameter) expect_equal(res4$r, res1$estimate, tolerance = 0.01, ignore_attr = TRUE) expect_equal(res4$CI_low, res1$conf.int[1], tolerance = 0.02, ignore_attr = TRUE) expect_equal(res4$CI_high, res1$conf.int[2], tolerance = 0.02, ignore_attr = TRUE) }) test_that("d", { res <- t_to_d(4, 68) expect_equal(res$d, 0.970, tolerance = 0.01) expect_equal(res$CI_low, 0.464, tolerance = 0.01) expect_equal(res$CI_high, 1.469, tolerance = 0.01) res <- t_to_d(4, 68, paired = TRUE) expect_equal(res$d, 0.970 / 2, tolerance = 0.01) expect_equal(res$CI_low, 0.464 / 2, tolerance = 0.01) expect_equal(res$CI_high, 1.469 / 2, tolerance = 0.01) res <- F_to_d(16, 1, 68) expect_equal(res$d, 0.970, tolerance = 0.01) expect_equal(res$CI_low, 0.464, tolerance = 0.01) expect_equal(res$CI_high, 1.469, tolerance = 0.01) expect_error(F_to_d(16, 2, 68)) res <- z_to_d(4, 68) expect_equal(res$d, 0.970, tolerance = 0.01) expect_equal(res$CI_low, 0.494, tolerance = 0.01) expect_equal(res$CI_high, 1.446, tolerance = 0.01) # depr arg expect_warning(F_to_d(4^2, 1, 68, pooled = TRUE)) expect_warning(t_to_d(4, 68, pooled = TRUE)) expect_warning(z_to_d(4, 68, pooled = TRUE)) }) test_that("eta2", { res <- F_to_eta2(4, 3, 123) expect_equal(res[[1]], 0.089, tolerance = 0.01) expect_equal(res$CI_low, 0.014, tolerance = 0.02) expect_equal(res$CI_high, 1) expect_equal(t_to_eta2(2, 123), F_to_eta2(4, 1, 123)) res <- F_to_epsilon2(4, 3, 123) expect_equal(res[[1]], 0.067, tolerance = 0.01) expect_equal(res$CI_low, 0.002, tolerance = 0.01) expect_equal(res$CI_high, 1) expect_equal(t_to_epsilon2(2, 123), F_to_epsilon2(4, 1, 123)) res <- F_to_omega2(4, 3, 123) expect_equal(res[[1]], 0.066, tolerance = 0.01) expect_equal(res$CI_low, 0.002, tolerance = 0.01) expect_equal(res$CI_high, 1) expect_equal(t_to_epsilon2(2, 123), F_to_epsilon2(4, 1, 123)) res <- F_to_eta2(4, 3, 123) resf2 <- F_to_f2(4, 3, 123) resf <- F_to_f(4, 3, 123) expect_equal(resf2[[1]], res[[1]] / (1 - res[[1]]), ignore_attr = TRUE) expect_equal(resf[[1]]^2, res[[1]] / (1 - res[[1]]), ignore_attr = TRUE) expect_equal(F_to_f(4, 3, 123), F_to_f2(4, 3, 123, squared = FALSE)) expect_equal(F_to_f2(4, 3, 123), F_to_f(4, 3, 123, squared = TRUE)) }) } effectsize/tests/testthat/test-printing.R0000644000175000017500000000452014170065645020500 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("effectsize table", { es <- eta_squared(aov(mpg ~ cyl + gear, mtcars)) expect_output(print(es), regexp = "Eta2") expect_output(print(es), regexp = "One-sided CIs: upper bound fixed at (1)", fixed = TRUE) stdz1 <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars)) stdz2 <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars), method = "basic") expect_output(print(stdz1), regexp = "refit") expect_output(print(stdz2), regexp = "basic") }) test_that("std effectsize table", { es <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars)) expect_output(print(es), regexp = "refit") es <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars), method = "basic") expect_output(print(es), regexp = "basic") es <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars), robust = TRUE) expect_output(print(es), regexp = "median") es <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars), two_sd = TRUE) expect_output(print(es), regexp = "two") es <- standardize_parameters(lm(mpg ~ cyl + gear, mtcars), include_response = FALSE) expect_output(print(es), regexp = "unstandardized") }) test_that("effectsize difference", { d <- cohens_d(1:3, c(1, 1:3)) expect_output(print(d), regexp = "Cohen") expect_output(print(d), regexp = " pooled", fixed = TRUE) expect_output(print(d, append_CLES = TRUE), regexp = "U3") d <- cohens_d(1:3, c(1, 1:3), pooled_sd = FALSE) expect_output(print(d), regexp = "un-pooled") d <- cohens_d(1:5, c(1, 1:4), paired = TRUE) expect_error(expect_output(print(d), regexp = "pooled")) }) test_that("equivalence test effectsize", { d <- cohens_d(1:3, c(1, 1:3)) equtest <- equivalence_test(d) expect_output(print(equtest), regexp = "ROPE") }) test_that("rules", { r1 <- rules(1:3, letters[1:4], name = "XX") expect_output(print(r1), regexp = "Thresholds") expect_output(print(r1), regexp = "<=") expect_output(print(r1), regexp = "XX") r2 <- rules(1:3, letters[1:3], name = "YY") expect_output(print(r2), regexp = "Values") expect_output(print(r2), regexp = "YY") expect_output(print(interpret(0, r1)), '"a"') expect_output(print(interpret(0, r1)), 'XX') }) } effectsize/tests/testthat/test-convert_between.R0000644000175000017500000000720314170065645022040 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("oddsratio_to_d", { expect_equal(oddsratio_to_d(0.2), -0.887, tolerance = 0.01) expect_equal(oddsratio_to_d(-1.45, log = TRUE), -0.7994, tolerance = 0.01) expect_equal(d_to_oddsratio(-0.887), 0.2, tolerance = 0.01) expect_equal(d_to_oddsratio(-0.7994, log = TRUE), -1.45, tolerance = 0.01) }) test_that("d_to_r", { expect_equal(d_to_r(1.1547), 0.5, tolerance = 0.01) expect_equal(r_to_d(0.5), 1.1547, tolerance = 0.01) expect_equal(oddsratio_to_r(d_to_oddsratio(r_to_d(0.5))), 0.5, tolerance = 0.001) expect_equal(oddsratio_to_d(r_to_oddsratio(d_to_r(1), log = TRUE), log = TRUE), 1, tolerance = 0.001) }) test_that("oddsratio_to_RR", { skip_on_cran() p0 <- 0.4 p1 <- 0.7 OR <- probs_to_odds(p1) / probs_to_odds(p0) RR <- p1 / p0 expect_equal(riskratio_to_oddsratio(RR, p0 = p0), OR) expect_equal(oddsratio_to_riskratio(OR, p0 = p0), RR) expect_equal(oddsratio_to_riskratio(1 / OR, p0 = p1), 1 / RR) expect_equal(riskratio_to_oddsratio(log(RR), p0 = p0, log = TRUE), log(OR)) expect_equal(oddsratio_to_riskratio(log(OR), p0 = p0, log = TRUE), log(RR)) # -- GLMs -- data(mtcars) m <<- glm(am ~ factor(cyl), data = mtcars, family = binomial()) w <- capture_warnings(RR <- oddsratio_to_riskratio(m, ci_method = "wald")) expect_match(w[1],"p0") expect_match(w[2],"CIs") expect_true("(Intercept)" %in% RR$Parameter) expect_false("(p0)" %in% RR$Parameter) # these values confirmed from emmeans expect_equal(RR$Coefficient, c(0.7272, 0.5892, 0.1964), tolerance = 0.001) expect_equal(RR$CI_low, c(NA, 0.1267, 0.0303), tolerance = 0.001) expect_equal(RR$CI_high, c(NA, 1.1648, 0.7589), tolerance = 0.001) expect_warning(RR <- oddsratio_to_riskratio(m, p0 = 0.05), "CIs") expect_true("(p0)" %in% RR$Parameter) expect_false("(Intercept)" %in% RR$Parameter) # these values confirmed from emmeans expect_equal(RR$Coefficient, c(0.05, 0.29173, 0.06557), tolerance = 0.001) # -- GLMMs -- skip_if_not_installed("lme4") m <- lme4::glmer(am ~ factor(cyl) + (1|gear), data = mtcars, family = binomial()) w <- capture_warnings(RR <- oddsratio_to_riskratio(m)) expect_match(w[1],"p0") expect_match(w[2],"CIs") expect_true("(Intercept)" %in% RR$Parameter) expect_false("(p0)" %in% RR$Parameter) # these values confirmed from emmeans expect_equal(RR$Coefficient, c(0.7048, 0.6042, 0.4475), tolerance = 0.001) expect_equal(RR$CI_low, c(NA, 0.08556, 0.0102), tolerance = 0.001) expect_equal(RR$CI_high, c(NA, 1.2706, 1.3718), tolerance = 0.001) }) test_that("odds_to_probs", { expect_equal(odds_to_probs(3), 0.75, tolerance = 0.01) expect_equal(probs_to_odds(0.75), 3, tolerance = 0.01) expect_equal(probs_to_odds(0.75, log = TRUE), 1.098, tolerance = 0.01) expect_equal(odds_to_probs(1.098, log = TRUE), 0.75, tolerance = 0.01) expect_equal( ncol(df <- odds_to_probs( iris, select = c("Sepal.Length"), exclude = c("Petal.Length"), log = TRUE )), 5 ) expect_equal( ncol(probs_to_odds( df, select = c("Sepal.Length"), exclude = c("Petal.Length"), log = TRUE )), 5 ) }) test_that("between anova", { expect_equal(eta2_to_f2(0.25), 1/3) expect_equal(eta2_to_f(0.25), sqrt(eta2_to_f2(0.25))) expect_equal(f2_to_eta2(1/3), 0.25) expect_equal(f_to_eta2(1/sqrt(3)), f2_to_eta2(1/3)) }) } effectsize/tests/testthat/test-helpers.R0000644000175000017500000000042314132466117020303 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("is_effectsize_name works", { expect_false(is_effectsize_name("is_effectsize_name")) expect_true(is_effectsize_name("Eta2")) expect_equal(get_effectsize_label("hEDgES_G"), "Hedges' g") }) } effectsize/tests/testthat/test-standardize_parameters.R0000644000175000017500000003746014170065645023412 0ustar nileshnileshif (require("testthat") && require("effectsize")) { data("iris") df <- iris # simple ------------------------------------------------------------------ test_that("standardize_parameters (simple)", { r <- as.numeric(cor.test(df$Sepal.Length, df$Petal.Length)$estimate) model <- lm(Sepal.Length ~ Petal.Length, data = df) es <- standardize_parameters(model) expect_equal(es[2, 2], r, tolerance = 0.01) }) # model_parameters ------------------------------- test_that("standardize_parameters (model_parameters)", { skip_on_cran() model <<- lm(mpg ~ cyl + am, data = mtcars) mp <<- parameters::model_parameters(model, effects = "fixed") s1 <- standardize_parameters(model, method = "basic") s2 <- standardize_parameters(mp, method = "basic") expect_equal(s1$Parameter, s2$Parameter) expect_equal(s1$Std_Coefficient, s2$Std_Coefficient) expect_equal(s1$CI_low, s2$CI_low) expect_equal(s1$CI_high, s2$CI_high) mp_exp <<- parameters::model_parameters(model, exponentiate = TRUE, effects = "fixed") se1 <- standardize_parameters(model, method = "basic", exponentiate = TRUE) se2 <- standardize_parameters(mp_exp, method = "basic", exponentiate = TRUE) expect_equal(se1$Parameter, se2$Parameter) expect_equal(se1$Std_Coefficient, se2$Std_Coefficient) expect_equal(se1$CI_low, se2$CI_low) expect_equal(se1$CI_high, se2$CI_high) }) # bootstrap_model --------------------------------------------------------- test_that("standardize_parameters (bootstrap_model)", { skip_on_cran() skip_if_not_installed("boot") m <- lm(mpg ~ factor(cyl) + hp, mtcars) set.seed(1) bm_draws <- parameters::bootstrap_model(m, iterations = 599) set.seed(1) bm_tab <- parameters::bootstrap_parameters(m, iterations = 599) out_true <- standardize_parameters(m, method = "basic") out_boot1 <- standardize_parameters(bm_draws, method = "basic") out_boot2 <- standardize_parameters(bm_tab, method = "basic") expect_equal(out_boot1$Std_Coefficient, out_true$Std_Coefficient, tolerance = 0.05 ) expect_equal(out_boot1, out_boot2, ignore_attr = TRUE) expect_error(standardize_parameters(bm_draws, method = "refit")) expect_error(standardize_parameters(bm_tab, method = "refit")) }) # lm with ci ----------------------------------- test_that("standardize_parameters (lm with ci)", { data("iris") model <- lm(Sepal.Length ~ Species + Petal.Width, data = iris) expect_equal( standardize_parameters(model, method = "refit")$Std_Coefficient, c(0.044, -0.072, -0.060, 0.844), tolerance = 0.01 ) expect_equal( standardize_parameters(model, method = "posthoc")$Std_Coefficient, c(0, -0.072, -0.060, 0.844), tolerance = 0.01 ) expect_equal( standardize_parameters(model, method = "smart")$Std_Coefficient, c(0, -0.170, -0.142, 0.844), tolerance = 0.01 ) z_basic <- standardize_parameters(model, method = "basic") expect_equal( z_basic$Std_Coefficient, c(0, -0.034, -0.028, 0.844), tolerance = 0.01 ) ## CI expect_equal( z_basic$CI_low, c(0, -0.294, -0.433, 0.491), tolerance = 0.01 ) expect_equal( z_basic$CI_high, c(0, 0.225, 0.375, 1.196), tolerance = 0.01 ) z_basic.0.80 <- standardize_parameters(model, ci = 0.8, method = "basic") expect_equal( z_basic.0.80$CI_low, c(0, -0.203, -0.292, 0.614), tolerance = 0.01 ) expect_equal( z_basic.0.80$CI_high, c(0, 0.135, 0.234, 1.073), tolerance = 0.01 ) data("mtcars") m0 <- lm(mpg ~ cyl + factor(am), mtcars) expect_equal( standardize_parameters(m0, method = "refit")[[2]][-1], standardize_parameters(m0, method = "smart")[[2]][-1], tolerance = 0.01 ) expect_equal( standardize_parameters(m0, method = "refit", two_sd = TRUE)[[2]][-1], standardize_parameters(m0, method = "smart", two_sd = TRUE)[[2]][-1], tolerance = 0.01 ) }) # aov --------------------------------------------------------------------- test_that("standardize_parameters (aov)", { data <- iris data$Cat1 <- rep(c("A", "B"), length.out = nrow(data)) m_aov <- aov(Sepal.Length ~ Species * Cat1, data = data) m_lm <- lm(Sepal.Length ~ Species * Cat1, data = data) expect_equal(standardize_parameters(m_aov), standardize_parameters(m_lm), ignore_attr = TRUE ) }) # with function interactions" ------------------- test_that("standardize_parameters (with functions / interactions)", { skip_on_cran() X <- scale(rnorm(100), T, F) Z <- scale(rnorm(100), T, F) Y <- scale(Z + X * Z + rnorm(100), T, F) m1 <- lm(Y ~ X * Z) m2 <- lm(Y ~ X * scale(Z)) m3 <- lm(Y ~ scale(X) * Z) m4 <- lm(Y ~ scale(X) * scale(Z)) expect_equal( standardize_parameters(m1, method = "basic")$Std_Coefficient, standardize_parameters(m2, method = "basic")$Std_Coefficient ) expect_equal( standardize_parameters(m1, method = "basic")$Std_Coefficient, standardize_parameters(m3, method = "basic")$Std_Coefficient ) # expect_equal( # standardize_parameters(m1, method = "basic")$Std_Coefficient, # standardize_parameters(m4, method = "basic")$Std_Coefficient # ) # transformed resp or pred should not affect mtcars$cyl_exp <- exp(mtcars$cyl) mtcars$mpg_sqrt <- sqrt(mtcars$mpg) m1 <- lm(exp(cyl) ~ am + sqrt(mpg), mtcars) m2 <- lm(cyl_exp ~ am + mpg_sqrt, mtcars) expect_message(stdX <- standardize_parameters(m1, method = "refit")) expect_false(isTRUE(all.equal( stdX[[2]], standardize_parameters(m2, method = "refit")[[2]] ))) expect_equal( standardize_parameters(m1, method = "basic")[[2]], standardize_parameters(m2, method = "basic")[[2]] ) # posthoc / smart don't support data transformation expect_warning(standardize_parameters(m1, method = "smart")) expect_warning(standardize_parameters(m1, method = "posthoc")) }) # exponentiate ------------------------------------------------------------ test_that("standardize_parameters (exponentiate)", { mod_b <- glm(am ~ mpg + cyl + hp, data = mtcars, family = poisson() ) mod_refit <- standardize_parameters(mod_b, method = "refit", exponentiate = TRUE) expect_equal( mod_refit[[2]][-1], standardize_parameters(mod_b, method = "basic", exponentiate = TRUE)[[2]][-1] ) expect_equal( mod_refit[[2]][-1], standardize_parameters(mod_b, method = "posthoc", exponentiate = TRUE)[[2]][-1] ) expect_equal( mod_refit[[2]][-1], exp(standardize_parameters(mod_b, method = "basic")[[2]])[-1] ) mod_b <- glm(am ~ mpg + cyl, data = mtcars, family = binomial() ) mod_refit <- standardize_parameters(mod_b, method = "refit", exponentiate = TRUE) expect_equal( mod_refit[[2]][-1], standardize_parameters(mod_b, method = "basic", exponentiate = TRUE)[[2]][-1] ) expect_equal( mod_refit[[2]][-1], standardize_parameters(mod_b, method = "posthoc", exponentiate = TRUE)[[2]][-1] ) expect_equal( mod_refit[[2]][-1], exp(standardize_parameters(mod_b, method = "basic")[[2]])[-1] ) mod_b <- glm(am ~ mpg + cyl + hp, data = mtcars, family = gaussian() ) mod_refit <- standardize_parameters(mod_b, method = "refit", exponentiate = TRUE) expect_equal( mod_refit[[2]][-1], standardize_parameters(mod_b, method = "basic", exponentiate = TRUE)[[2]][-1] ) expect_equal( mod_refit[[2]][-1], standardize_parameters(mod_b, method = "posthoc", exponentiate = TRUE)[[2]][-1] ) expect_equal( mod_refit[[2]][-1], exp(standardize_parameters(mod_b, method = "basic")[[2]])[-1] ) }) # Bayes ---------------------------------------- test_that("standardize_parameters (Bayes)", { skip_on_cran() skip_on_ci() skip_if_not_installed("rstanarm") set.seed(1234) suppressWarnings( model <- rstanarm::stan_glm(Sepal.Length ~ Species + Petal.Width, data = iris, iter = 500, refresh = 0 ) ) expect_equal( suppressWarnings(standardize_parameters(model, method = "refit")$Std_Median[1:4]), c(0.065, -0.094, -0.100, 0.862), tolerance = 0.01 ) expect_equal( suppressWarnings(standardize_parameters(model, method = "posthoc")$Std_Median[1:4]), c(0, -0.058, -0.053, 0.838), tolerance = 0.01 ) posts <- standardize_posteriors(model, method = "posthoc") expect_equal(dim(posts), c(1000, 4)) expect_s3_class(posts, "data.frame") }) # Pseudo - GLMM -------------------------------- test_that("standardize_parameters (Pseudo - GLMM)", { skip_on_cran() skip_if_not_installed("lme4") set.seed(1) dat <- data.frame( X = rnorm(1000), Z = rnorm(1000), C = sample(letters[1:3], size = 1000, replace = TRUE), ID = sort(rep(letters, length.out = 1000)) ) dat <- transform(dat, Y = X + Z + rnorm(1000)) dat <- cbind(dat, datawizard::demean(dat, c("X", "Z"), "ID")) m <- lme4::lmer(Y ~ scale(X_within) * X_between + C + (scale(X_within) | ID), data = dat ) ## No robust methods... (yet) expect_warning(standardize_parameters(m, method = "pseudo", robust = TRUE, verbose = FALSE), regexp = "robust") ## Correctly identify within and between terms dev_resp <- standardize_info(m, include_pseudo = TRUE)$Deviation_Response_Pseudo expect_equal(length(unique(dev_resp[c(2, 4, 5, 6)])), 1) expect_true(dev_resp[2] != dev_resp[3]) ## Calc b <- lme4::fixef(m)[-1] mm <- model.matrix(m)[, -1] SD_x <- numeric(ncol(mm)) SD_x[c(1, 3, 4, 5)] <- apply(mm[, c(1, 3, 4, 5)], 2, sd) SD_x[2] <- sd(tapply(mm[, 2], dat$ID, mean)) m0 <- lme4::lmer(Y ~ 1 + (1 | ID), data = dat) m0v <- insight::get_variance(m0) SD_y <- c(sqrt(m0v$var.residual), sqrt(m0v$var.intercept)) SD_y <- SD_y[c(1, 2, 1, 1, 1)] expect_equal( data.frame(Deviation_Response_Pseudo = c(SD_y[2], SD_y), Deviation_Pseudo = c(0, SD_x)), standardize_info(m, include_pseudo = TRUE)[, c("Deviation_Response_Pseudo", "Deviation_Pseudo")] ) expect_equal( standardize_parameters(m, method = "pseudo")$Std_Coefficient[-1], unname(b * SD_x / SD_y) ) ## scaling should not affect m1 <- lme4::lmer(Y ~ X_within + X_between + C + (X_within | ID), data = dat ) m2 <- lme4::lmer(scale(Y) ~ X_within + X_between + C + (X_within | ID), data = dat ) m3 <- lme4::lmer(Y ~ scale(X_within) + X_between + C + (scale(X_within) | ID), data = dat ) m4 <- lme4::lmer(Y ~ X_within + scale(X_between) + C + (X_within | ID), data = dat ) std1 <- standardize_parameters(m1, method = "pseudo") expect_equal(std1$Std_Coefficient, standardize_parameters(m2, method = "pseudo")$Std_Coefficient, tolerance = 0.001 ) expect_equal(std1$Std_Coefficient, standardize_parameters(m3, method = "pseudo")$Std_Coefficient, tolerance = 0.001 ) expect_equal(std1$Std_Coefficient, standardize_parameters(m4, method = "pseudo")$Std_Coefficient, tolerance = 0.001 ) ## Give warning for within that is also between mW <- lme4::lmer(Y ~ X_between + Z_within + C + (1 | ID), dat) mM <- lme4::lmer(Y ~ X + Z + C + (1 | ID), dat) expect_warning(standardize_parameters(mW, method = "pseudo"), regexp = NA) expect_warning(standardize_parameters(mM, method = "pseudo"), regexp = "within-group") }) # ZI models --------------------------------------------------------------- test_that("standardize_parameters (pscl)", { skip_on_cran() skip_if_not_installed("pscl") data("bioChemists", package = "pscl") m <- pscl::zeroinfl(art ~ fem + mar + kid5 + ment | kid5 + phd, data = bioChemists) mp <- parameters::model_parameters(m, effects = "fixed") sm1 <- standardize_parameters(m, method = "refit") expect_warning(sm2 <- standardize_parameters(m, method = "posthoc")) suppressWarnings({ sm3 <- standardize_parameters(m, method = "basic") sm4 <- standardize_parameters(m, method = "smart") }) # post hoc does it right (bar intercept) expect_equal(sm1$Std_Coefficient[-c(1, 6)], sm2$Std_Coefficient[-c(1, 6)], tolerance = 0.01 ) # basic / smart miss the ZI expect_equal(mp$Coefficient[6:8], sm3$Std_Coefficient[6:8], tolerance = 0.01 ) expect_equal(mp$Coefficient[7:8], sm4$Std_Coefficient[7:8], tolerance = 0.1 ) # get count numerics al right expect_equal(sm1$Std_Coefficient[4:5], sm3$Std_Coefficient[4:5], tolerance = 0.01 ) expect_equal(sm1$Std_Coefficient[4:5], sm4$Std_Coefficient[4:5], tolerance = 0.01 ) }) } test_that("include_response | (g)lm", { # lm --- data(iris) iris$Sepal.Length <- iris$Sepal.Length * 5 m <- lm(Sepal.Length ~ Petal.Length + Petal.Width, data = iris) m_z <- standardize(m, include_response = FALSE) par_z0 <- standardize_parameters(m, method = "basic") par_z1 <- standardize_parameters(m, include_response = FALSE) par_z2 <- standardize_parameters(m, method = "basic", include_response = FALSE) expect_equal(coef(m_z), par_z1$Std_Coefficient, ignore_attr = TRUE) expect_equal(par_z1$Std_Coefficient[-1], par_z2$Std_Coefficient[-1]) expect_equal(par_z0$Std_Coefficient * sd(iris$Sepal.Length), par_z2$Std_Coefficient) # glm --- m <- glm(am ~ mpg, mtcars, family = binomial()) expect_equal( standardize_parameters(m), standardize_parameters(m, include_response = FALSE), ignore_attr = TRUE ) }) test_that("include_response | parameters", { skip_if_not_installed("parameters") data(iris) iris$Sepal.Length <- iris$Sepal.Length * 5 m <<- lm(Sepal.Length ~ Petal.Length + Petal.Width, data = iris) # parameters --- pars <- parameters::model_parameters(m, effects = "fixed") pars_z0 <- standardize_parameters(pars, method = "basic") pars_z1 <- standardize_parameters(pars, method = "basic", include_response = FALSE) expect_equal(pars_z0$Std_Coefficient[-1] * sd(iris$Sepal.Length), pars_z1$Std_Coefficient[-1]) # boot --- skip_if_not_installed("boot") pars <- parameters::bootstrap_parameters(m) pars_z0 <- standardize_parameters(pars, method = "basic") pars_z1 <- standardize_parameters(pars, method = "basic", include_response = FALSE) expect_equal(pars_z0$Std_Coefficient[-1] * sd(iris$Sepal.Length), pars_z1$Std_Coefficient[-1]) }) test_that("include_response | bayes", { skip_if_not_installed("rstanarm") skip_on_cran() data(iris) iris$Sepal.Length <- iris$Sepal.Length * 5 m <- rstanarm::stan_glm(Sepal.Length ~ Petal.Length + Petal.Width, data = iris, refresh = 0) expect_warning(m_z <- standardize(m, include_response = FALSE)) expect_warning(par_z1 <- standardize_posteriors(m, include_response = FALSE)) par_z0 <- standardize_posteriors(m, method = "basic") par_z2 <- standardize_posteriors(m, method = "basic", include_response = FALSE) expect_equal(sapply(insight::get_parameters(m_z), mean), sapply(par_z1, mean), tolerance = 0.1) expect_equal(sapply(par_z1, mean)[-1], sapply(par_z2, mean)[-1], tolerance = 0.1) expect_equal(sapply(par_z0, mean) * sd(iris$Sepal.Length), sapply(par_z2, mean), tolerance = 0.1) }) effectsize/tests/testthat/test-rankES.R0000644000175000017500000000577014132466117020036 0ustar nileshnileshif (require("testthat") && require("effectsize")) { test_that("rank_biserial", { x <- c(1.83, 0.50, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.30) y <- c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29) rRB1 <- rank_biserial(x, y, paired = TRUE) rRB2 <- rank_biserial(x - y) expect_equal(rRB1, rRB2) expect_equal(rRB1[[1]], 0.777, tolerance = 0.01) expect_equal(rRB1$CI_low, 0.2953631, tolerance = 0.01) expect_equal(rRB1$CI_high, 0.9441559, tolerance = 0.01) A <- c(48, 48, 77, 86, 85, 85, 16) B <- c(14, 34, 34, 77) expect_equal(rank_biserial(A, B)[[1]], 0.6071429, tolerance = 0.01) df <- data.frame( outcome = c(x, y), g = factor(rep(0:1, each = 9)) ) expect_equal( rank_biserial(outcome ~ g, data = df, ci = NULL), rank_biserial(df$outcome ~ df$g, ci = NULL) ) expect_equal( rank_biserial(outcome ~ g, data = df, ci = NULL), rank_biserial("outcome", "g", data = df, ci = NULL) ) }) test_that("rank_epsilon_squared", { skip_if_not_installed("boot") skip_if_not_installed("base", minimum_version = "3.6.0") x1 <- c(2.9, 3.0, 2.5, 2.6, 3.2) # normal subjects x2 <- c(3.8, 2.7, 4.0, 2.4) # with obstructive airway disease x3 <- c(2.8, 3.4, 3.7, 2.2, 2.0) # with asbestosis x <- c(x1, x2, x3) g <- factor(rep(1:3, c(5, 4, 5))) set.seed(1) E <- rank_epsilon_squared(x, g) expect_equal(E[[1]], 0.05934066, tolerance = 0.01) expect_equal(E$CI_low, 0.01726463, tolerance = 0.01) expect_equal(E$CI_high, 1) expect_equal( rank_epsilon_squared(x ~ g, ci = NULL), rank_epsilon_squared(x, g, ci = NULL) ) }) test_that("kendalls_w", { skip_if_not_installed("boot") skip_if_not_installed("base", minimum_version = "3.6.0") M1 <- structure( c(5.4, 5.85, 5.2, 5.5, 5.7, 5.6, 5.55, 5.75, 5.5), .Dim = c(3L, 3L), .Dimnames = list( c("1", "2", "3"), c("Round Out", "Narrow Angle", "Wide Angle") ) ) M2 <- structure( list( id = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L), name = c( "Round Out", "Narrow Angle", "Wide Angle", "Round Out", "Narrow Angle", "Wide Angle", "Round Out", "Narrow Angle", "Wide Angle" ), value = c(5.4, 5.5, 5.55, 5.85, 5.7, 5.75, 5.2, 5.6, 5.5) ), row.names = c(NA, -9L), class = c("tbl_df", "tbl", "data.frame") ) set.seed(1) W1 <- kendalls_w(M1) W2 <- kendalls_w(value ~ name | id, data = M2, ci = NULL) W3 <- kendalls_w(M2$value, M2$name, M2$id, ci = NULL) W4 <- kendalls_w(M2$value ~ M2$name | M2$id, ci = NULL) expect_equal(W1[[1]], W2[[1]]) expect_equal(W1[[1]], W3[[1]]) expect_equal(W1[[1]], W4[[1]]) expect_equal(W1[[1]], 0.11111111, tolerance = 0.01) expect_equal(W1$CI_low, 0.11111111, tolerance = 0.01) expect_equal(W1$CI_high, 1, tolerance = 0.01) }) } effectsize/tests/testthat.R0000644000175000017500000000010414132466117015660 0ustar nileshnileshlibrary(testthat) library(effectsize) test_check("effectsize") effectsize/tests/spelling.R0000644000175000017500000000023314132466117015640 0ustar nileshnileshif (requireNamespace("spelling", quietly = TRUE)) { spelling::spell_check_test( vignettes = TRUE, error = FALSE, skip_on_cran = TRUE ) } effectsize/R/0000755000175000017500000000000014174160423012736 5ustar nileshnilesheffectsize/R/utils_standardize.R0000644000175000017500000000524614132466117016623 0ustar nileshnilesh # For standardize_parameters ---------------------------------------------- #' @keywords internal .get_object <- function(x, attribute_name = "object_name") { obj_name <- attr(x, attribute_name, exact = TRUE) model <- NULL if (!is.null(obj_name)) { model <- tryCatch( { get(obj_name, envir = parent.frame()) }, error = function(e) { NULL } ) if (is.null(model) || # prevent self reference inherits(model, "parameters_model")) { model <- tryCatch( { get(obj_name, envir = globalenv()) }, error = function(e) { NULL } ) } } model } # For standardize_info ---------------------------------------------------- #' @keywords internal #' @importFrom stats weighted.mean .mean <- function(x, weights = NULL) { if (!.are_weights(weights)) { return(mean(x, na.rm = TRUE)) } stopifnot(all(weights > 0, na.rm = TRUE)) stats::weighted.mean(x, weights, na.rm = TRUE) } #' @keywords internal #' @importFrom stats sd .sd <- function(x, weights = NULL) { # from cov.wt if (!.are_weights(weights)) { return(stats::sd(x, na.rm = TRUE)) } stopifnot(all(weights > 0, na.rm = TRUE)) weights1 <- weights / sum(weights) center <- sum(weights1 * x) xc <- sqrt(weights1) * (x - center) var <- (t(xc) %*% xc) / (1 - sum(weights1^2)) sqrt(as.vector(var)) } #' @keywords internal #' @importFrom stats mad .mad <- function(x, weights = NULL, constant = 1.4826) { # From matrixStats if (!.are_weights(weights)) { return(stats::mad(x, na.rm = TRUE)) } stopifnot(all(weights > 0, na.rm = TRUE)) center <- .median(x, weights = weights) x <- abs(x - center) constant * .median(x, weights = weights) } #' @keywords internal #' @importFrom stats median .median <- function(x, weights = NULL) { # From spatstat + wiki if (!.are_weights(weights)) { return(stats::median(x, na.rm = TRUE)) } stopifnot(all(weights > 0, na.rm = TRUE)) oo <- order(x) x <- x[oo] weights <- weights[oo] Fx <- cumsum(weights) / sum(weights) lefties <- which(Fx <= 0.5) left <- max(lefties) if (length(lefties) == 0) { result <- x[1] } else if (left == length(x)) { result <- x[length(x)] } else { result <- x[left] if (!(Fx[left - 1] < 0.5 && 1 - Fx[left] < 0.5)) { right <- left + 1 y <- x[left] * Fx[left] + x[right] * Fx[right] if (is.finite(y)) result <- y } } return(result) } .are_weights <- function(w) { !is.null(w) && length(w) && !all(w == 1) && !all(w == w[1]) } effectsize/R/reexports.R0000644000175000017500000000117114174211510015106 0ustar nileshnilesh#' @importFrom bayestestR equivalence_test #' @export bayestestR::equivalence_test #' @export #' @importFrom datawizard standardize datawizard::standardize ## TODO: removed the following from see / modelbased -------------------- #' @export #' @importFrom datawizard ranktransform datawizard::ranktransform #' @export #' @importFrom datawizard adjust datawizard::adjust #' @export #' @importFrom datawizard change_scale datawizard::change_scale #' @export #' @importFrom datawizard normalize datawizard::normalize #' @export #' @importFrom datawizard unstandardize datawizard::unstandardizeeffectsize/R/utils_interpret.R0000644000175000017500000000065014132466117016321 0ustar nileshnilesh#' @keywords internal .match.rules <- function(rules, choices) { if (is.rules(rules)) { return(rules) } rule <- pmatch(rules, names(choices)) if (!is.character(rules) || length(rules) != 1 || is.na(rule)) { stop("'rules' must be ", paste0("'", names(choices), "'", collapse = ", "), " or an object of type 'rules'.", call. = FALSE ) } return(choices[[rule]]) } effectsize/R/interpret_cohens_g.R0000644000175000017500000000260614170065645016754 0ustar nileshnilesh#' Interpret Cohen's g #' #' #' @param g Value or vector of effect size values. #' @param rules Can be `"cohen1988"` (default) or a custom set of [rules()]. #' @param ... Not directly used. #' #' @section Rules: #' #' Rules apply to equally to positive and negative *g* (i.e., they are given as #' absolute values). #' #' - Cohen (1988) (`"cohen1988"`; default) #' - **d < 0.05** - Very small #' - **0.05 <= d < 0.15** - Small #' - **0.15 <= d < 0.25** - Medium #' - **d >= 0.25** - Large #' #' @note "*Since **g** is so transparently clear a unit, it is expected that #' workers in any given substantive area of the behavioral sciences will very #' frequently be able to set relevant \[effect size\] values without the #' proposed conventions, or set up conventions of their own which are suited #' to their area of inquiry.*" - Cohen, 1988, page 147. #' #' @examples #' interpret_cohens_g(.02) #' interpret_cohens_g(c(.3, .15)) #' #' @references #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences #' (2nd Ed.). New York: Routledge. #' #' @export interpret_cohens_g <- function(g, rules = "cohen1988", ...) { rules <- .match.rules( rules, list( cohen1988 = rules(c(0.05, 0.15, 0.25), c("very small", "small", "medium", "large"), name = "cohen1988", right = FALSE) ) ) interpret(abs(g), rules) }effectsize/R/equivalence_test.R0000644000175000017500000001253714174211472016432 0ustar nileshnilesh#' @title Test for Practical Equivalence #' #' @description Perform a **Test for Practical Equivalence** for indices of #' effect size. #' #' @param x An effect size table, such as returned by [cohens_d()], #' [eta_squared()], [F_to_r()], etc. #' @param range The range of practical equivalence of an effect. For one-sides #' CIs, a single value can be proved for the lower / upper bound to test #' against (but see more details below). For two-sided CIs, a single value is #' duplicated to `c(-range, range)`. If `"default"`, will be set to `[-.1, #' .1]`. #' @param rule How should acceptance and rejection be decided? See details. #' @param ... Arguments passed to or from other methods. #' #' @details #' The CIs used in the equivalence test are the ones in the provided effect size #' table. For results equivalent (ha!) to those that can be obtained using the #' TOST approach (e.g., Lakens, 2017), appropriate CIs should be extracted using #' the function used to make the effect size table (`cohens_d`, `eta_squared`, #' `F_to_r`, etc), with `alternative = "two.sided"`. See examples. #' #' ## The Different Rules #' - `"classic"` - **the classic method**: #' - If the CI is completely within the ROPE - *Accept H0* #' - Else, if the CI does not contain 0 - *Reject H0* #' - Else - *Undecided* #' - `"cet"` - **conditional equivalence testing**: #' - If the CI does not contain 0 - *Reject H0* #' - Else, If the CI is completely within the ROPE - *Accept H0* #' - Else - *Undecided* #' - `"bayes"` - **The Bayesian approach**, as put forth by Kruschke: #' - If the CI does is completely outside the ROPE - *Reject H0* #' - Else, If the CI is completely within the ROPE - *Accept H0* #' - Else - *Undecided* #' #' @seealso For more details, see [bayestestR::equivalence_test()]. #' #' @return A data frame with the results of the equivalence test. #' #' @references #' - Campbell, H., & Gustafson, P. (2018). Conditional equivalence testing: An #' alternative remedy for publication bias. PLOS ONE, 13(4), e0195145. #' https://doi.org/10.1371/journal.pone.0195145 #' #' - Kruschke, J. K. (2014). Doing Bayesian data analysis: A tutorial with R, #' JAGS, and Stan. Academic Press #' #' - Kruschke, J. K. (2018). Rejecting or accepting parameter values in Bayesian #' estimation. Advances in Methods and Practices in Psychological Science, 1(2), #' 270-280. doi: 10.1177/2515245918771304 #' #' - Lakens, D. (2017). Equivalence Tests: A Practical Primer for t Tests, #' Correlations, and Meta-Analyses. Social Psychological and Personality #' Science, 8(4), 355–362. https://doi.org/10.1177/1948550617697177 #' #' @examples #' \donttest{ #' #' model <- aov(mpg ~ hp + am * factor(cyl), data = mtcars) #' es <- eta_squared(model, ci = 0.9, alternative = "two.sided") #' equivalence_test(es, range = 0.30) # TOST #' #' RCT <- matrix(c(71, 101, #' 50, 100), nrow = 2) #' OR <- oddsratio(RCT, alternative = "greater") #' equivalence_test(OR, range = 1) #' #' ds <- t_to_d( #' t = c(0.45, -0.65, 7, -2.2, 2.25), #' df_error = c(675, 525, 2000, 900, 1875), #' ci = 0.9, alternative = "two.sided" # TOST #' ) #' # Can also plot #' if (require(see)) plot(equivalence_test(ds, range = 0.2)) #' if (require(see)) plot(equivalence_test(ds, range = 0.2, rule = "cet")) #' if (require(see)) plot(equivalence_test(ds, range = 0.2, rule = "bayes")) #' } #' #' @export equivalence_test.effectsize_table <- function(x, range = "default", rule = c("classic", "cet", "bayes"), ...) { rule <- match.arg(rule) if (!all(c("CI", "CI_low", "CI_high") %in% colnames(x))) { stop("CI values missing from effect size table.", call. = FALSE) } if (any(range == "default")) { range <- c(-0.1, 0.1) } # Validate range --- x_es_info <- es_info[get_effectsize_name(colnames(x)),] if (length(range) == 1) { alt <- attr(x, "alternative", exact = TRUE) if (alt == "less") { range <- c(range, x_es_info$ub) } else if (alt == "greater") { range <- c(x_es_info$lb, range) } else { range <- c(-range, range) } } range <- sort(range) if (range[1] < x_es_info$lb) { range[1] <- x_es_info$lb warning("Lower bound set to ", range[1], ".", immediate. = FALSE) } if (range[2] > x_es_info$ub) { range[2] <- x_es_info$ub warning("Upper bound set to ", range[2], ".", immediate. = FALSE) } # Test --- signif <- x$CI_high < x_es_info$null | x_es_info$null < x$CI_low in_rope <- range[1] <= x$CI_low & x$CI_high <= range[2] out_rope <- x$CI_high < range[1] | range[2] < x$CI_low # Label --- x$ROPE_Equivalence <- "Undecided" if (rule == "classic") { x$ROPE_Equivalence[in_rope] <- "Accepted" x$ROPE_Equivalence[signif & !in_rope] <- "Rejected" } else if (rule == "cet") { x$ROPE_Equivalence[signif] <- "Rejected" x$ROPE_Equivalence[in_rope & !signif] <- "Accepted" } else { x$ROPE_Equivalence[out_rope] <- "Rejected" x$ROPE_Equivalence[in_rope] <- "Accepted" } # x$ROPE_Equivalence[x$CI_low == x$CI_high] <- NA_character_ class(x) <- c("equivalence_test_effectsize", "see_equivalence_test_effectsize", "data.frame") attr(x, "rope") <- range attr(x, "rule") <- rule return(x) }effectsize/R/interpret_vif.R0000644000175000017500000000127614170065645015755 0ustar nileshnilesh#' Interpret the Variance Inflation Factor (VIF) #' #' Interpret VIF index of multicollinearity. #' #' @param vif Value or vector of VIFs. #' @param rules Can be `"default"` or a custom set of [rules()]. #' #' @section Rules: #' #' - Default #' - **VIF < 5** - Low #' - **5 <= VIF < 10** - Moderate #' - **VIF >= 10** - High #' #' @examples #' #' interpret_vif(c(1.4, 30.4)) #' #' @export interpret_vif <- function(vif, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(5, 10), c("low", "moderate", "high"), name = "default", right = FALSE) ) ) interpret(vif, rules) } effectsize/R/effectsize.BFBayesFactor.R0000644000175000017500000000647614170065645017644 0ustar nileshnilesh#' @export #' @rdname effectsize #' @inheritParams bayestestR::describe_posterior #' @importFrom insight get_data get_parameters check_if_installed #' @importFrom bayestestR describe_posterior effectsize.BFBayesFactor <- function(model, type = NULL, verbose = TRUE, test = NULL, ...) { insight::check_if_installed("BayesFactor") xtra_class <- xtra_footer <- mu <- paired <- NULL if (length(model) > 1) { if (verbose) { warning("Multiple models detected. Using first only.", call. = FALSE) } model <- model[1] } if (inherits(model@numerator[[1]], "BFcontingencyTable")) { # Chisq ---- if (is.null(type)) type <- "cramers_v" f <- switch(tolower(type), v = , cramers_v = cramers_v, w = , cohens_w = , phi = phi, c = , pearsons_c = pearsons_c, h = , cohens_h = cohens_h, or = , oddsratio = oddsratio, rr = , riskratio = riskratio ) data <- insight::get_data(model) posts <- insight::get_parameters(model) ES <- apply(posts, 1, function(a) { f(matrix(a, nrow = nrow(data)), ci = NULL)[[1]] }) res <- data.frame(ES) colnames(res) <- colnames(f(data, ci = NULL)) } else if (inherits(model@numerator[[1]], c("BFoneSample", "BFindepSample"))) { # t-test ---- if (is.null(type)) type <- "d" type <- c("cohens_d" = "d", d = "d", "cles" = "cles")[type] samps <- as.data.frame(BayesFactor::posterior(model, iterations = 4000, progress = FALSE)) paired <- inherits(model@numerator[[1]], "BFoneSample") if (!paired) { mu <- 0 D <- samps$delta } else { mu <- model@numerator[[1]]@prior$mu D <- (samps$mu - mu) / sqrt(samps$sig2) } res <- data.frame(Cohens_d = D) if (type == "d") { xtra_class <- "effectsize_difference" } else if (type == "cles") { res <- data.frame(d_to_cles(res$Cohens_d), check.names = FALSE) xtra_class <- NULL } } else if (inherits(model@numerator[[1]], "BFcorrelation")) { # Corr ---- type <- "r" rho <- insight::get_parameters(model)[["rho"]] res <- data.frame(rho = rho) } else if (inherits(model@numerator[[1]], "BFproportion")) { # Prop ---- type <- "p" res <- insight::get_parameters(model) p0 <- model@denominator@identifier[["p0"]] xtra_footer <- list(c(sprintf("\n- Against the null: p = %s.", p0), "cyan")) } else { stop("No effect size for this type of BayesFactor object.") } # Clean up out <- bayestestR::describe_posterior(res, test = test, ...) if (type == "cles") { colnames(out)[2] <- "Coefficient" } else { colnames(out)[2] <- out$Parameter out$Parameter <- NULL } class(out) <- c(xtra_class, "effectsize_table", "see_effectsize_table", class(out)) attr(out, "paired") <- paired attr(out, "table_footer") <- xtra_footer attr(out, "mu") <- mu attr(out, "ci") <- out$CI attr(out, "ci_method") <- if (!is.null(out$CIs)) list(method = "MCMC") attr(out, "correction") <- NULL attr(out, "pooled_sd") <- NULL attr(out, "approximate") <- FALSE attr(out, "alternative") <- "two.sided" out }effectsize/R/convert_between_odds_to_probs.R0000644000175000017500000000726614132466117021210 0ustar nileshnilesh#' Convert between Odds and Probabilities #' #' @param odds The *Odds* (or `log(odds)` when `log = TRUE`) to convert. #' @param probs Probability values to convert. #' @param log Take in or output log odds (such as in logistic models). #' @param select When a data frame is passed, character or list of of column #' names to be transformed. #' @param exclude When a data frame is passed, character or list of column names #' to be excluded from transformation. #' @param ... Arguments passed to or from other methods. #' #' @return Converted index. #' #' @seealso [stats::plogis()] #' @family convert between effect sizes #' #' @examples #' odds_to_probs(3) #' odds_to_probs(1.09, log = TRUE) #' #' probs_to_odds(0.95) #' probs_to_odds(0.95, log = TRUE) #' @export #' @aliases convert_odds_to_probs odds_to_probs <- function(odds, log = FALSE, ...) { UseMethod("odds_to_probs") } #' @export convert_odds_to_probs <- odds_to_probs #' @export #' @importFrom stats plogis odds_to_probs.numeric <- function(odds, log = FALSE, ...) { if (log) { stats::plogis(odds) } else { stats::plogis(log(odds)) } } #' @rdname odds_to_probs #' @export odds_to_probs.data.frame <- function(odds, log = FALSE, select = NULL, exclude = NULL, ...) { .odds_to_probs_df(odds = odds, log = log, select = select, exclude = exclude, ...) } #' @rdname odds_to_probs #' @aliases convert_probs_to_odds #' @export probs_to_odds <- function(probs, log = FALSE, ...) { UseMethod("probs_to_odds") } #' @export convert_probs_to_odds <- probs_to_odds #' @export #' @importFrom stats qlogis probs_to_odds.numeric <- function(probs, log = FALSE, ...) { if (log) { stats::qlogis(probs) } else { exp(stats::qlogis(probs)) } } #' @rdname odds_to_probs #' @export probs_to_odds.data.frame <- function(probs, log = FALSE, select = NULL, exclude = NULL, ...) { .odds_to_probs_df(probs = probs, log = log, select = select, exclude = exclude, ...) } # Data frame -------------------------------------------------------------- #' @keywords internal .odds_to_probs_df <- function(odds = NULL, probs = NULL, log = FALSE, select = NULL, exclude = NULL, ...) { # If vector if (!is.null(odds)) { df <- odds } else { df <- probs } # check for formula notation, convert to character vector if (inherits(select, "formula")) { select <- all.vars(select) } if (inherits(exclude, "formula")) { exclude <- all.vars(exclude) } # Variable order var_order <- names(df) # Keep subset if (!is.null(select) && select %in% names(df)) { to_keep <- as.data.frame(df[!names(df) %in% c(select)]) df <- df[names(df) %in% c(select)] } else { to_keep <- NULL } # Remove exceptions if (!is.null(exclude) && exclude %in% names(df)) { if (is.null(to_keep)) { to_keep <- as.data.frame(df[exclude]) } else { to_keep <- cbind(to_keep, as.data.frame(df[exclude])) } df <- df[!names(df) %in% c(exclude)] } # Remove non-numerics dfother <- df[!sapply(df, is.numeric, simplify = TRUE)] dfnum <- df[sapply(df, is.numeric, simplify = TRUE)] # Tranform if (!is.null(odds)) { dfnum <- data.frame(lapply(dfnum, odds_to_probs.numeric, log = log)) } else { dfnum <- data.frame(lapply(dfnum, probs_to_odds.numeric, log = log)) } # Add non-numerics if (is.null(ncol(dfother))) { df <- dfnum } else { df <- cbind(dfother, dfnum) } # Add exceptions if (!is.null(select) | !is.null(exclude) && exists("to_keep")) { df <- cbind(df, to_keep) } # Reorder df <- df[var_order] return(df) } effectsize/R/effectsize.R0000644000175000017500000001224114170065645015216 0ustar nileshnilesh#' Effect Size #' #' This function tries to return the best effect-size measure for the provided #' input model. See details. #' #' @param model An object of class `htest`, or a statistical model. See details. #' @param type The effect size of interest. See details. #' @param ... Arguments passed to or from other methods. See details. #' @inheritParams standardize.default #' #' @details #' #' - For an object of class `htest`, data is extracted via [insight::get_data()], and passed to the relevant function according to: #' - A **t-test** depending on `type`: `"cohens_d"` (default), `"hedges_g"`, or `"cles"`. #' - A **Chi-squared tests of independence or goodness-of-fit**, depending on `type`: `"cramers_v"` (default), `"phi"`, `"cohens_w"`, `"pearsons_c"`, `"cohens_h"`, `"oddsratio"`, or `"riskratio"`. #' - A **One-way ANOVA test**, depending on `type`: `"eta"` (default), `"omega"` or `"epsilon"` -squared, `"f"`, or `"f2"`. #' - A **McNemar test** returns *Cohen's g*. #' - A **Wilcoxon test** depending on `type`: returns "`rank_biserial`" correlation (default) or `"cles"`. #' - A **Kruskal-Wallis test** returns *rank Epsilon squared*. #' - A **Friedman test** returns *Kendall's W*. #' (Where applicable, `ci` and `alternative` are taken from the `htest` if not otherwise provided.) #' - For an object of class `BFBayesFactor`, using [bayestestR::describe_posterior()], #' - A **t-test** depending on `type`: "cohens_d"` (default) or `"cles"`. #' - A **correlation test** returns *r*. #' - A **contingency table test**, depending on `type`: `"cramers_v"` (default), `"phi"`, `"cohens_w"`, `"pearsons_c"`, `"cohens_h"`, `"oddsratio"`, or `"riskratio"`. #' - A **proportion test** returns *p*. #' - Objects of class `anova`, `aov`, or `aovlist`, depending on `type`: `"eta"` (default), `"omega"` or `"epsilon"` -squared, `"f"`, or `"f2"`. #' - Other objects are passed to [standardize_parameters()]. #' #' **For statistical models it is recommended to directly use the listed #' functions, for the full range of options they provide.** #' #' @return A data frame with the effect size (depending on input) and and its #' CIs (`CI_low` and `CI_high`). #' #' @family effect size indices #' #' @examples #' #' ## Hypothesis Testing #' ## ------------------ #' contingency_table <- as.table(rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477))) #' Xsq <- chisq.test(contingency_table) #' effectsize(Xsq) #' effectsize(Xsq, type = "phi") #' #' Tt <- t.test(1:10, y = c(7:20), alternative = "less") #' effectsize(Tt) #' #' Aov <- oneway.test(extra ~ group, data = sleep, var.equal = TRUE) #' effectsize(Aov) #' effectsize(Aov, type = "omega") #' #' Wt <- wilcox.test(1:10, 7:20, mu = -3, alternative = "less") #' effectsize(Wt) #' effectsize(Wt, type = "cles") #' #' ## Bayesian Hypothesis Testing #' ## --------------------------- #' \donttest{ #' if (require(BayesFactor)) { #' bf_prop <- proportionBF(3, 7, p = 0.3) #' effectsize(bf_prop) #' #' bf_corr <- correlationBF(attitude$rating, attitude$complaints) #' effectsize(bf_corr) #' #' data(raceDolls) #' bf_xtab <- contingencyTableBF(raceDolls, sampleType = "poisson", fixedMargin = "cols") #' effectsize(bf_xtab) #' effectsize(bf_xtab, type = "oddsratio") #' #' bf_ttest <- ttestBF(sleep$extra[sleep$group==1], #' sleep$extra[sleep$group==2], #' paired = TRUE, mu = -1) #' effectsize(bf_ttest) #' } #' } #' #' ## Models and Anova Tables #' ## ----------------------- #' fit <- lm(mpg ~ factor(cyl) * wt + hp, data = mtcars) #' effectsize(fit) #' #' anova_table <- anova(fit) #' effectsize(anova_table) #' effectsize(anova_table, type = "epsilon") #' @export effectsize <- function(model, ...) { UseMethod("effectsize") } #' @export effectsize.anova <- function(model, type = NULL, ...) { if (is.null(type)) type <- "eta" f <- switch(tolower(type), eta = , eta2 = , eta_squared = eta_squared, epsilon = , epsilon2 = , epsilon_squared = epsilon_squared, omega = , omega2 = , omega_squared = omega_squared, f = , cohens_f = cohens_f, f2 = , f_squared = , cohens_f2 = cohens_f_squared ) f(model, ...) } #' @export effectsize.afex_aov <- effectsize.anova #' @export #' @rdname effectsize effectsize.aov <- effectsize.anova #' @export effectsize.aovlist <- effectsize.anova #' @export effectsize.easycorrelation <- function(model, ...) { if (is.null(r_name <- attr(model, "coefficient_name"))) { r_name <- "r" } r_cols <- 1:which(colnames(model) == r_name) if (!is.null(attr(model, "ci"))) { model$CI <- attr(model, "ci") CI_cols <- c("CI", "CI_low", "CI_high") CI_cols <- sapply(CI_cols, function(ici) which(colnames(model) == ici)) r_cols <- c(r_cols, CI_cols) } out <- model[, r_cols, drop = FALSE] class(out) <- c("effectsize_table", "see_effectsize_table", "data.frame") attr(out, "approximate") <- FALSE out } #' @export effectsize.default <- function(model, ...) { # message("Using standardize_parameters().") standardize_parameters(model, ...) } effectsize/R/interpret_cohens_d.R0000644000175000017500000000621114170065645016745 0ustar nileshnilesh#' Interpret standardized differences #' #' Interpretation of standardized differences using different sets of rules of #' thumb. #' #' #' @param d,g,delta Value or vector of effect size values. #' @param rules Can be `"cohen1988"` (default), `"gignac2016"`, #' `"sawilowsky2009"`, `"lovakov2021"` or a custom set of [rules()]. #' @param ... Not directly used. #' #' @section Rules: #' #' Rules apply to equally to positive and negative *d* (i.e., they are given as #' absolute values). #' #' - Cohen (1988) (`"cohen1988"`; default) #' - **d < 0.2** - Very small #' - **0.2 <= d < 0.5** - Small #' - **0.5 <= d < 0.8** - Medium #' - **d >= 0.8** - Large #' - Sawilowsky (2009) (`"sawilowsky2009"`) #' - **d < 0.1** - Tiny #' - **0.1 <= d < 0.2** - Very small #' - **0.2 <= d < 0.5** - Small #' - **0.5 <= d < 0.8** - Medium #' - **0.8 <= d < 1.2** - Large #' - **1.2 <= d < 2** - Very large #' - **d >= 2** - Huge #' - Lovakov & Agadullina (2021) (`"lovakov2021"`) #' - **d < 0.15** - Very small #' - **0.15 <= d < 0.36** - Small #' - **0.36 <= d < 0.65** - Medium #' - **d >= 0.65** - Large #' - Gignac & Szodorai (2016) (`"gignac2016"`, based on the [d_to_r()] conversion, see [interpret_r()]) #' - **d < 0.2** - Very small #' - **0.2 <= d < 0.41** - Small #' - **0.41 <= d < 0.63** - Moderate #' - **d >= 0.63** - Large #' #' @examples #' interpret_cohens_d(.02) #' interpret_cohens_d(c(.5, .02)) #' interpret_cohens_d(.3, rules = "lovakov2021") #' @references #' - Lovakov, A., & Agadullina, E. R. (2021). Empirically Derived Guidelines for #' Effect Size Interpretation in Social Psychology. European Journal of Social #' Psychology. #' #' - Gignac, G. E., & Szodorai, E. T. (2016). Effect size guidelines for #' individual differences researchers. Personality and individual differences, #' 102, 74-78. #' #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences #' (2nd Ed.). New York: Routledge. #' #' - Sawilowsky, S. S. (2009). New effect size rules of thumb. #' #' @export interpret_cohens_d <- function(d, rules = "cohen1988", ...) { if (is.character(rules) && rules == "gignac2016") { return(interpret_r(d_to_r(d), rules)) } rules <- .match.rules( rules, list( cohen1988 = rules(c(0.2, 0.5, 0.8), c("very small", "small", "medium", "large"), name = "cohen1988", right = FALSE ), sawilowsky2009 = rules(c(0.1, 0.2, 0.5, 0.8, 1.2, 2), c("tiny", "very small", "small", "medium", "large", "very large", "huge"), name = "sawilowsky2009", right = FALSE ), lovakov2021 = rules(c(0.15, 0.36, 0.65), c("very small", "small", "medium", "large"), name = "lovakov2021", right = FALSE ), gignac2016 = NA # added for the correct error msg ) ) interpret(abs(d), rules) } #' @rdname interpret_cohens_d #' @export interpret_hedges_g <- function(g, rules = "cohen1988") { interpret_cohens_d(g, rules) } #' @rdname interpret_cohens_d #' @export interpret_glass_delta <- function(delta, rules = "cohen1988") { interpret_cohens_d(delta, rules) }effectsize/R/datasets.R0000644000175000017500000000107214132466117014674 0ustar nileshnilesh#' Workers' salary and other information #' #' A sample (simulated) dataset, used in tests and some examples. #' #' @docType data #' #' @name hardlyworking #' #' @keywords data #' #' @format A data frame with 500 rows and 5 variables: #' \describe{ #' \item{salary}{Salary, in Shmekels} #' \item{xtra_hours}{Number of overtime hours (on average, per week)} #' \item{n_comps}{Number of compliments given to the boss (observed over the last week)} #' \item{age}{Age in years} #' \item{seniority}{How many years with the company} #' } #' NULL effectsize/R/interpret_oddsratio.R0000644000175000017500000000415314170065645017156 0ustar nileshnilesh#' Interpret Odds ratio #' #' @param OR Value or vector of (log) odds ratio values. #' @param rules Can be "`chen2010"` (default), `"cohen1988"` (through #' transformation to standardized difference, see [oddsratio_to_d()]) or custom set #' of [rules()]. #' @param log Are the provided values log odds ratio. #' @inheritParams interpret #' #' @section Rules: #' #' Rules apply to OR as ratios, so OR of 10 is as extreme as a OR of 0.1 (1/10). #' #' - Chen et al. (2010) (`"chen2010"`; default) #' - **OR < 1.68** - Very small #' - **1.68 <= OR < 3.47** - Small #' - **3.47 <= OR < 6.71** - Medium #' - **OR >= 6.71 ** - Large #' - Cohen (1988) (`"cohen1988"`, based on the [oddsratio_to_d()] conversion, see [interpret_cohens_d()]) #' - **OR < 1.44** - Very small #' - **1.44 <= OR < 2.48** - Small #' - **2.48 <= OR < 4.27** - Medium #' - **OR >= 4.27 ** - Large #' #' @examples #' interpret_oddsratio(1) #' interpret_oddsratio(c(5, 2)) #' #' @references #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences #' (2nd Ed.). New York: Routledge. #' #' - Chen, H., Cohen, P., & Chen, S. (2010). How big is a big odds ratio? #' Interpreting the magnitudes of odds ratios in epidemiological studies. #' Communications in Statistics-Simulation and Computation, 39(4), 860-864. #' #' - Sánchez-Meca, J., Marín-Martínez, F., & Chacón-Moscoso, S. (2003). #' Effect-size indices for dichotomized outcomes in meta-analysis. Psychological #' methods, 8(4), 448. #' #' @export interpret_oddsratio <- function(OR, rules = "chen2010", log = FALSE, ...) { if (log) { OR <- exp(abs(OR)) } else { OR <- exp(abs(log(OR))) } if (is.character(rules) && rules == "cohen1988") { d <- oddsratio_to_d(OR, log = FALSE) return(interpret_cohens_d(abs(d), rules = rules)) } rules <- .match.rules( rules, list( chen2010 = rules(c(1.68, 3.47, 6.71), c("very small", "small", "medium", "large"), name = "chen2010", right = FALSE ), cohen1988 = NA # for correct error msg ) ) interpret(OR, rules) } effectsize/R/convert_stat_to_d.R0000644000175000017500000000617414132466117016614 0ustar nileshnilesh#' @rdname t_to_r #' @export t_to_d <- function(t, df_error, paired = FALSE, ci = 0.95, alternative = "two.sided", pooled, ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (!missing(pooled)) { paired <- pooled warning( "Argument 'pooled' is deprecated, use 'paired' instead. Setting paired <- pooled.", call. = FALSE ) } # Will be 1 if TRUE, and 2 if FALSE paired <- 2 - paired res <- data.frame(d = paired * t / sqrt(df_error)) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 ts <- t(mapply( .get_ncp_t, t, df_error, ci.level )) res$CI_low <- paired * ts[, 1] / sqrt(df_error) res$CI_high <- paired * ts[, 2] / sqrt(df_error) ci_method <- list(method = "ncp", distribution = "t") if (alternative == "less") { res$CI_low <- -Inf } else if (alternative == "greater") { res$CI_high <- Inf } } else { alternative <- NULL } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "alternative") <- alternative return(res) } # z ----------------------------------------------------------------------- #' @rdname t_to_r #' @importFrom stats qnorm #' @export z_to_d <- function(z, n, paired = FALSE, ci = 0.95, alternative = "two.sided", pooled, ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (!missing(pooled)) { paired <- pooled warning( "Argument 'pooled' is deprecated, use 'paired' instead. Setting paired <- pooled.", call. = FALSE ) } # Will be 1 if TRUE, and 2 if FALSE paired <- 2 - paired res <- data.frame(d = paired * z / sqrt(n)) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 alpha <- 1 - ci.level probs <- c(alpha / 2, 1 - alpha / 2) qs <- stats::qnorm(probs) zs <- cbind(qs[1] + z, qs[2] + z) res$CI_low <- paired * zs[, 1] / sqrt(n) res$CI_high <- paired * zs[, 2] / sqrt(n) ci_method <- list(method = "normal") if (alternative == "less") { res$CI_low <- -Inf } else if (alternative == "greater") { res$CI_high <- Inf } } else { alternative <- NULL } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "alternative") <- alternative return(res) } # F ----------------------------------------------------------------------- #' @rdname t_to_r #' @export F_to_d <- function(f, df, df_error, paired = FALSE, ci = 0.95, alternative = "two.sided", ...) { if (df > 1) { stop("Cannot convert F with more than 1 df to (partial) r.") } t_to_d(sqrt(f), df_error, paired = paired, ci = ci, alternative = alternative, ...) } effectsize/R/interpret_rope.R0000644000175000017500000000322414170072540016121 0ustar nileshnilesh#' Interpret Bayesian diagnostic indices #' #' Interpretation of Bayesian indices of percentage in ROPE. #' #' @param rope Value or vector of percentages in ROPE. #' @param ci The Credible Interval (CI) probability, corresponding to the proportion of HDI, that was used. Can be `1` in the case of "full ROPE". #' @param rules A character string (see details) or a custom set of [rules()]. #' #' @section Rules: #' #' - Default #' - For CI < 1 #' - **Rope = 0** - Significant #' - **0 < Rope < 1** - Undecided #' - **Rope = 1** - Negligible #' - For CI = 1 #' - **Rope < 0.01** - Significant #' - **0.01 < Rope < 0.025** - Probably significant #' - **0.025 < Rope < 0.975** - Undecided #' - **0.975 < Rope < 0.99** - Probably negligible #' - **Rope > 0.99** - Negligible #' #' #' @examples #' interpret_rope(0, ci = 0.9) #' interpret_rope(c(0.005, 0.99), ci = 1) #' @references #' [BayestestR's reporting guidelines](https://easystats.github.io/bayestestR/articles/guidelines.html) #' #' @export interpret_rope <- function(rope, ci = 0.9, rules = "default") { if (ci < 1) { e <- .Machine$double.eps default_rule <- rules(c(0, 0 + e, 1 - e, 1), c("significant", "undecided", "undecided", "negligible"), name = "default" ) } else { default_rule <- rules(c(0.01, 0.025, 0.975, 0.99), c( "significant", "probably significant", "undecided", "probably negligible", "negligible" ), name = "default" ) } rules <- .match.rules( rules, list( default = default_rule ) ) interpret(rope, rules) } effectsize/R/interpret_p.R0000644000175000017500000000225114132466117015417 0ustar nileshnilesh#' Interpret p-values #' #' @param p Value or vector of p-values. #' @param rules Can be `"default"`, `"rss"` (for *Redefine statistical #' significance* rules) or custom set of [rules()]. #' #' @section Rules: #' #' - Default #' - **p >= 0.05** - Not significant #' - **p < 0.05** - Significant #' - Benjamin et al. (2018) (`"rss"`) #' - **p >= 0.05** - Not significant #' - **0.005 <= p < 0.05** - Suggestive #' - **p < 0.005** - Significant #' #' @references #' - Benjamin, D. J., Berger, J. O., Johannesson, M., Nosek, B. A., Wagenmakers, E. J., Berk, R., ... & Cesarini, D. (2018). Redefine statistical significance. Nature Human Behaviour, 2(1), 6-10. #' #' @examples #' interpret_p(c(.5, .02, 0.001)) #' interpret_p(c(.5, .02, 0.001), rules = "rss") #' @export interpret_p <- function(p, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.05), c("significant", "not significant"), name = "default", right = FALSE ), rss = rules(c(0.005, 0.05), c("significant", "suggestive", "not significant"), name = "rss", right = FALSE ) ) ) interpret(p, rules) } effectsize/R/plot.R0000644000175000017500000000067714132466117014054 0ustar nileshnilesh#' @rdname print.effectsize_table #' @export #' @importFrom insight check_if_installed plot.effectsize_table <- function(x, ...) { insight::check_if_installed("see", reason = "for plotting") NextMethod() } #' @rdname print.effectsize_table #' @export #' @importFrom insight check_if_installed plot.equivalence_test_effectsize <- function(x, ...) { insight::check_if_installed("see", reason = "for plotting") NextMethod() } effectsize/R/zzz_deprecated.R0000644000175000017500000000200714170065645016103 0ustar nileshnilesh#' Deprecated functions #' #' @param ... Arguments to the deprecated function. #' #' @details #' - `interpret_d` is now [`interpret_cohens_d`]. #' - `interpret_g` is now [`interpret_hedges_g`]. #' - `interpret_delta` is now [`interpret_glass_delta`]. #' - `interpret_parameters` for *standardized parameters* was incorrect. Use [`interpret_r`] instead. #' #' @rdname effectsize_deprecated #' @name effectsize_deprecated NULL #' @rdname effectsize_deprecated #' @export interpret_d <- function(...) { .Deprecated("interpret_cohens_d") interpret_cohens_d(...) } #' @rdname effectsize_deprecated #' @export interpret_g <- function(...) { .Deprecated("interpret_hedges_g") interpret_hedges_g(...) } #' @rdname effectsize_deprecated #' @export interpret_delta <- function(...) { .Deprecated("interpret_glass_delta") interpret_glass_delta(...) } #' @rdname effectsize_deprecated #' @export interpret_parameters <- function(...) { .Deprecated("interpret_r") interpret_r(...) }effectsize/R/interpret_r.R0000644000175000017500000000705714132466117015432 0ustar nileshnilesh#' Interpret correlation coefficient #' #' @param r Value or vector of correlation coefficient. #' @param rules Can be `"funder2019"` (default), `"gignac2016"`, `"cohen1988"`, #' `"evans1996"`, `"lovakov2021"` or a custom set of [rules()]. #' #' @note As \eqn{\phi}{\phi} can be larger than 1 - it is recommended to compute #' and interpret Cramer's *V* instead. #' #' @section Rules: #' #' Rules apply positive and negative *r* alike. #' #' - Funder & Ozer (2019) (`"funder2019"`; default) #' - **r < 0.05** - Tiny #' - **0.05 <= r < 0.1** - Very small #' - **0.1 <= r < 0.2** - Small #' - **0.2 <= r < 0.3** - Medium #' - **0.3 <= r < 0.4** - Large #' - **r >= 0.4** - Very large #' - Gignac & Szodorai (2016) (`"gignac2016"`) #' - **r < 0.1** - Very small #' - **0.1 <= r < 0.2** - Small #' - **0.2 <= r < 0.3** - Moderate #' - **r >= 0.3** - Large #' - Cohen (1988) (`"cohen1988"`) #' - **r < 0.1** - Very small #' - **0.1 <= r < 0.3** - Small #' - **0.3 <= r < 0.5** - Moderate #' - **r >= 0.5** - Large #' - Lovakov & Agadullina (2021) (`"lovakov2021"`) #' - **r < 0.12** - Very small #' - **0.12 <= r < 0.24** - Small #' - **0.24 <= r < 0.41** - Moderate #' - **r >= 0.41** - Large #' - Evans (1996) (`"evans1996"`) #' - **r < 0.2** - Very weak #' - **0.2 <= r < 0.4** - Weak #' - **0.4 <= r < 0.6** - Moderate #' - **0.6 <= r < 0.8** - Strong #' - **r >= 0.8** - Very strong #' #' @examples #' interpret_r(.015) #' interpret_r(c(.5, -.02)) #' interpret_r(.3, rules = "lovakov2021") #' @seealso Page 88 of APA's 6th Edition. #' #' @references #' - Lovakov, A., & Agadullina, E. R. (2021). Empirically Derived Guidelines for #' Effect Size Interpretation in Social Psychology. European Journal of Social #' Psychology. #' #' - Funder, D. C., & Ozer, D. J. (2019). Evaluating effect size in #' psychological research: sense and nonsense. Advances in Methods and Practices #' in Psychological Science. #' #' - Gignac, G. E., & Szodorai, E. T. (2016). Effect size guidelines for #' individual differences researchers. Personality and individual differences, #' 102, 74-78. #' #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences #' (2nd Ed.). New York: Routledge. #' #' - Evans, J. D. (1996). Straightforward statistics for the behavioral #' sciences. Thomson Brooks/Cole Publishing Co. #' #' @export interpret_r <- function(r, rules = "funder2019") { rules <- .match.rules( rules, list( funder2019 = rules(c(0.05, 0.1, 0.2, 0.3, 0.4), c("tiny", "very small", "small", "medium", "large", "very large"), name = "funder2019", right = FALSE ), gignac2016 = rules(c(0.1, 0.2, 0.3), c("very small", "small", "moderate", "large"), name = "gignac2016", right = FALSE ), cohen1988 = rules(c(0.1, 0.3, 0.5), c("very small", "small", "moderate", "large"), name = "cohen1988", right = FALSE ), evans1996 = rules(c(0.2, 0.4, 0.6, 0.8), c("very weak", "weak", "moderate", "strong", "very strong"), name = "evans1996", right = FALSE ), lovakov2021 = rules(c(0.12, 0.24, 0.41), c("very small", "small", "medium", "large"), name = "lovakov2021", right = FALSE ) ) ) interpret(abs(r), rules) } #' @export #' @rdname interpret_r interpret_phi <- interpret_r #' @export #' @rdname interpret_r interpret_cramers_v <- interpret_r #' @export #' @rdname interpret_r interpret_rank_biserial <- interpret_r effectsize/R/interpret_icc.R0000644000175000017500000000231614132466117015720 0ustar nileshnilesh#' Interpret Intraclass Correlation Coefficient (ICC) #' #' The value of an ICC lies between 0 to 1, with 0 indicating no reliability among raters and 1 indicating perfect reliability. #' #' @param icc Value or vector of Intraclass Correlation Coefficient (ICC) values. #' @param rules Can be `"koo2016"` (default) or custom set of [rules()]. #' @param ... Not used for now. #' #' @section Rules: #' #' - Koo (2016) (`"koo2016"`; default) #' - **ICC < 0.50** - Poor reliability #' - **0.5 <= ICC < 0.75** - Moderate reliability #' - **0.75 <= ICC < 0.9** - Good reliability #' - **ICC >= 0.9 ** - Excellent reliability #' #' @examples #' interpret_icc(0.6) #' interpret_icc(c(0.4, 0.8)) #' @references #' - Koo, T. K., \& Li, M. Y. (2016). A guideline of selecting and reporting intraclass correlation coefficients for reliability research. Journal of chiropractic medicine, 15(2), 155-163. #' #' @export interpret_icc <- function(icc, rules = "koo2016", ...) { rules <- .match.rules( rules, list( koo2016 = rules(c(0.5, 0.75, 0.9), c("poor", "moderate", "good", "excellent"), name = "koo2016", right = FALSE ) ) ) interpret(icc, rules) } effectsize/R/rank_effectsizes.R0000644000175000017500000004630514170302654016416 0ustar nileshnilesh#' Effect size for non-parametric (rank sum) tests #' #' Compute the rank-biserial correlation (\eqn{r_{rb}}{r_rb}), Cliff's *delta* #' (\eqn{\delta}), rank epsilon squared (\eqn{\varepsilon^2}{\epsilon^2}), and #' Kendall's *W* effect sizes for non-parametric (rank sum) tests. #' #' @inheritParams cohens_d #' @param x Can be one of: #' - A numeric vector, or a character name of one in `data`. #' - A formula in to form of `DV ~ groups` (for `rank_biserial()` and #' `rank_epsilon_squared()`) or `DV ~ groups | blocks` (for `kendalls_w()`; #' See details for the `blocks` and `groups` terminology used here). #' - A list of vectors (for `rank_epsilon_squared()`). #' - A matrix of `blocks x groups` (for `kendalls_w()`). See details for the #' `blocks` and `groups` terminology used here. #' @param y An optional numeric vector of data values to compare to `x`, or a #' character name of one in `data`. Ignored if `x` is not a vector. #' @param groups,blocks A factor vector giving the group / block for the #' corresponding elements of `x`, or a character name of one in `data`. #' Ignored if `x` is not a vector. #' @param mu a number indicating the value around which (a-)symmetry (for #' one-sample or paired samples) or shift (for independent samples) is to be #' estimated. See [stats::wilcox.test]. #' @param iterations The number of bootstrap replicates for computing confidence #' intervals. Only applies when `ci` is not `NULL`. (Deprecated for #' `rank_biserial()`). #' @param alternative a character string specifying the alternative hypothesis; #' Controls the type of CI returned: `"two.sided"` (two-sided CI; default for #' rank-biserial correlation and Cliff's *delta*), `"greater"` (default for #' rank epsilon squared and Kendall's *W*) or `"less"` (one-sided CI). Partial #' matching is allowed (e.g., `"g"`, `"l"`, `"two"`...). See *One-Sided CIs* #' in [effectsize_CIs]. #' #' @details #' The rank-biserial correlation is appropriate for non-parametric tests of #' differences - both for the one sample or paired samples case, that would #' normally be tested with Wilcoxon's Signed Rank Test (giving the #' **matched-pairs** rank-biserial correlation) and for two independent samples #' case, that would normally be tested with Mann-Whitney's *U* Test (giving #' **Glass'** rank-biserial correlation). See [stats::wilcox.test]. In both #' cases, the correlation represents the difference between the proportion of #' favorable and unfavorable pairs / signed ranks (Kerby, 2014). Values range #' from `-1` (*all* values of the second sample are larger than *all* the values #' of the first sample) to `+1` (*all* values of the second sample are smaller #' than *all* the values of the first sample). Cliff's *delta* is an alias to #' the rank-biserial correlation in the two sample case. #' \cr\cr #' The rank epsilon squared is appropriate for non-parametric tests of #' differences between 2 or more samples (a rank based ANOVA). See #' [stats::kruskal.test]. Values range from 0 to 1, with larger values #' indicating larger differences between groups. #' \cr\cr #' Kendall's *W* is appropriate for non-parametric tests of differences between #' 2 or more dependent samples (a rank based rmANOVA), where each `group` (e.g., #' experimental condition) was measured for each `block` (e.g., subject). This #' measure is also common as a measure of reliability of the rankings of the #' `groups` between raters (`blocks`). See [stats::friedman.test]. Values range #' from 0 to 1, with larger values indicating larger differences between groups #' / higher agreement between raters. #' #' ## Ties #' When tied values occur, they are each given the average of the ranks that #' would have been given had no ties occurred. No other corrections have been #' implemented yet. #' #' # Confidence Intervals #' Confidence intervals for the rank-biserial correlation (and Cliff's *delta*) #' are estimated using the normal approximation (via Fisher's transformation). #' Confidence intervals for rank Epsilon squared, and Kendall's *W* are #' estimated using the bootstrap method (using the `{boot}` package). #' #' @return A data frame with the effect size (`r_rank_biserial`, #' `rank_epsilon_squared` or `Kendalls_W`) and its CI (`CI_low` and #' `CI_high`). #' #' @family effect size indices #' #' @examples #' \donttest{ #' data(mtcars) #' mtcars$am <- factor(mtcars$am) #' mtcars$cyl <- factor(mtcars$cyl) #' #' # Rank Biserial Correlation #' # ========================= #' #' # Two Independent Samples ---------- #' (rb <- rank_biserial(mpg ~ am, data = mtcars)) #' # Same as: #' # rank_biserial("mpg", "am", data = mtcars) #' # rank_biserial(mtcars$mpg[mtcars$am=="0"], mtcars$mpg[mtcars$am=="1"]) #' #' # More options: #' rank_biserial(mpg ~ am, data = mtcars, mu = -5) #' print(rb, append_CLES = TRUE) #' #' #' # One Sample ---------- #' rank_biserial(wt ~ 1, data = mtcars, mu = 3) #' # same as: #' # rank_biserial("wt", data = mtcars, mu = 3) #' # rank_biserial(mtcars$wt, mu = 3) #' #' #' # Paired Samples ---------- #' dat <- data.frame(Cond1 = c(1.83, 0.5, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.3), #' Cond2 = c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29)) #' (rb <- rank_biserial(Pair(Cond1, Cond2) ~ 1, data = dat, paired = TRUE)) #' #' # same as: #' # rank_biserial(dat$Cond1, dat$Cond2, paired = TRUE) #' #' interpret_rank_biserial(0.78) #' interpret(rb, rules = "funder2019") #' #' #' # Rank Epsilon Squared #' # ==================== #' #' rank_epsilon_squared(mpg ~ cyl, data = mtcars) #' #' #' #' # Kendall's W #' # =========== #' dat <- data.frame(cond = c("A", "B", "A", "B", "A", "B"), #' ID = c("L", "L", "M", "M", "H", "H"), #' y = c(44.56, 28.22, 24, 28.78, 24.56, 18.78)) #' (W <- kendalls_w(y ~ cond | ID, data = dat, verbose = FALSE)) #' #' interpret_kendalls_w(0.11) #' interpret(W, rules = "landis1977") #' } #' #' @references #' - Cureton, E. E. (1956). Rank-biserial correlation. Psychometrika, 21(3), #' 287-290. #' #' - Glass, G. V. (1965). A ranking variable analogue of biserial correlation: #' Implications for short-cut item analysis. Journal of Educational Measurement, #' 2(1), 91-95. #' #' - Kendall, M.G. (1948) Rank correlation methods. London: Griffin. #' #' - Kerby, D. S. (2014). The simple difference formula: An approach to teaching #' nonparametric correlation. Comprehensive Psychology, 3, 11-IT. #' #' - King, B. M., & Minium, E. W. (2008). Statistical reasoning in the #' behavioral sciences. John Wiley & Sons Inc. #' #' - Cliff, N. (1993). Dominance statistics: Ordinal analyses to answer ordinal #' questions. Psychological bulletin, 114(3), 494. #' #' - Tomczak, M., & Tomczak, E. (2014). The need to report effect size estimates #' revisited. An overview of some recommended measures of effect size. #' #' @export #' @importFrom stats na.omit complete.cases rank_biserial <- function(x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", paired = FALSE, verbose = TRUE, ..., iterations) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (inherits(x, "htest")) { if (!grepl("Wilcoxon", x$method)) { stop("'x' is not a Wilcoxon-test!", call. = FALSE) } return(effectsize(x, verbose = verbose, type = "rb")) } if (!missing(iterations) && verbose) { warning( "'iterations' argument is deprecated. CIs are estimated using a parametric normal approximation.", immediate. = TRUE ) } ## Prep data out <- .get_data_2_samples(x, y, data) x <- out$x y <- out$y if (is.null(y)) { y <- rep(0, length.out = length(x)) paired <- TRUE } if (paired) { oo <- stats::complete.cases(x, y) x <- x[oo] y <- y[oo] } else { x <- stats::na.omit(x) y <- stats::na.omit(y) } ## Compute r_rbs <- .r_rbs(x, y, mu = mu, paired = paired, verbose = verbose) out <- data.frame(r_rank_biserial = r_rbs) ## CI ci_method <- NULL if (is.numeric(ci)) { # if (requireNamespace("boot", quietly = TRUE)) { # out <- cbind(out, .rbs_ci_boot( # y, # mu = mu, # paired = paired, # ci = ci, # iterations = iterations # )) # # ci_method <- list(method = "bootstrap", iterations = iterations) # } else { # ci <- NULL # warning("For CIs, the 'boot' package must be installed.") # } # Parametric method stopifnot(length(ci) == 1, ci < 1, ci > 0) out$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 alpha <- 1 - ci.level rf <- atanh(r_rbs) if (paired) { nd <- sum((x - mu) != 0) maxw <- (nd^2 + nd) / 2 # From: https://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test#Historical_T_statistic/ # wSE <- sqrt((n * (n + 1) * (2 * n + 1)) / 24) # Delta method for f(x) = w * 2 / (maxw) - 1 # r_rbsSE <- wSE * sqrt(4 / (maxw)^2) # Delta method for z: z_rbsSE <- r_rbsSE / (1 - r_rbs^2) # But simulations suggest that z_rbsSE is positively biased # more than r_rbsSE is negatively biased, especially when r_rbs is large, # so we use r_rbsSE instead rfSE <- sqrt((2 * nd^3 + 3 * nd^2 + nd) / 6) / maxw } else { n1 <- length(x) n2 <- length(y) # From: https://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U_test#Normal_approximation_and_tie_correction/ # wSE <- sqrt((n1 * n2 * (n1 + n2 + 1)) / 12) # Delta method for f(x) = 1 - 2 * w / (n1 * n2) * sign(diff) # r_rbsSE <- wSE * sqrt(4 / (n1 * n2)^2) # Delta method for z: z_rbsSE <- r_rbsSE / (1 - r_rbs^2) # But simulations suggest that z_rbsSE is positively biased # more than r_rbsSE is negatively biased, especially when r_rbs is large, # so we use r_rbsSE instead rfSE <- sqrt((n1 + n2 + 1) / (3 * n1 * n2)) } confint <- tanh(rf + c(-1, 1) * qnorm(1 - alpha / 2) * rfSE) out$CI_low <- confint[1] out$CI_high <- confint[2] ci_method <- list(method = "normal") if (alternative == "less") { out$CI_low <- -1 } else if (alternative == "greater") { out$CI_high <- 1 } } else { alternative <- NULL } class(out) <- c("effectsize_difference", "effectsize_table", "see_effectsize_table", class(out)) attr(out, "paired") <- paired attr(out, "mu") <- mu attr(out, "ci") <- ci attr(out, "ci_method") <- ci_method attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative return(out) } #' @export #' @rdname rank_biserial cliffs_delta <- function(x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", verbose = TRUE, ...) { rank_biserial( x, y, data = data, mu = mu, paired = FALSE, ci = ci, alternative = alternative, verbose = verbose, ... ) } #' @rdname rank_biserial #' @export #' @importFrom stats na.omit #' @importFrom insight check_if_installed rank_epsilon_squared <- function(x, groups, data = NULL, ci = 0.95, alternative = "greater", iterations = 200, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (inherits(x, "htest")) { if (!grepl("Kruskal-Wallis", x$method)) { stop("'x' is not a Kruskal-Wallis-test!", call. = FALSE) } return(effectsize(x, ci = ci, iterations = iterations, alternative = alternative)) } ## pep data data <- .get_data_multi_group(x, groups, data) data <- stats::na.omit(data) ## compute out <- data.frame(rank_epsilon_squared = .repsilon(data)) ## CI ci_method <- NULL if (is.numeric(ci)) { if (insight::check_if_installed("boot", "for estimating CIs", stop = FALSE)) { out <- cbind(out, .repsilon_ci(data, ci, alternative, iterations)) ci_method <- list(method = "percentile bootstrap", iterations = iterations) } else { ci <- NULL } } if (is.null(ci)) alternative <- NULL class(out) <- c("effectsize_table", "see_effectsize_table", class(out)) attr(out, "ci") <- ci attr(out, "ci_method") <- ci_method attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative return(out) } #' @rdname rank_biserial #' @export #' @importFrom stats na.omit #' @importFrom insight check_if_installed kendalls_w <- function(x, groups, blocks, data = NULL, ci = 0.95, alternative = "greater", iterations = 200, verbose = TRUE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (inherits(x, "htest")) { if (!grepl("Friedman", x$method)) { stop("'x' is not a Friedman-test!", call. = FALSE) } return(effectsize(x, ci = ci, iterations = iterations, verbose = verbose, alternative = alternative)) } ## prep data data <- .get_data_nested_groups(x, groups, blocks, data) data <- stats::na.omit(data) ## compute W <- .kendalls_w(data, verbose = verbose) out <- data.frame(Kendalls_W = W) ## CI ci_method <- NULL if (is.numeric(ci)) { if (insight::check_if_installed("boot", "for estimating CIs", stop = FALSE)) { out <- cbind(out, .kendalls_w_ci(data, ci, alternative, iterations)) ci_method <- list(method = "percentile bootstrap", iterations = iterations) } else { ci <- NULL } } if (is.null(ci)) alternative <- NULL class(out) <- c("effectsize_table", "see_effectsize_table", class(out)) attr(out, "ci") <- ci attr(out, "ci_method") <- ci_method attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative return(out) } # rank_eta_squared <- function(x, g, data = NULL, ci = 0.95, iterations = 200) { # # data <- .get_data_multi_group(x, g, data) # data <- stats::na.omit(data) # x <- data$x # g <- data$g # # model <- stats::kruskal.test(x, g) # # H <- unname(model$statistic) # k <- length(unique(g)) # model$parameter + 1 # n <- length(g) # # E <- (H - k + 1) / (n - k) # # out <- data.frame(rank_eta_squared = E) # # if (is.numeric(ci)) { # warning("Nope. Not yet.") # out$CI <- ci # out$CI_low <- 0 # out$CI_high <- 1 # } # # class(out) <- c("effectsize_table", class(out)) # return(out) # } # Utils ------------------------------------------------------------------- ## Get ---- #' @keywords internal #' @importFrom stats na.omit .r_rbs <- function(x, y, mu, paired, verbose = FALSE) { if (paired) { Ry <- .safe_ranktransform((x - y) - mu, sign = TRUE, verbose = verbose) Ry <- stats::na.omit(Ry) n <- length(Ry) S <- (n * (n + 1) / 2) U1 <- sum(Ry[Ry > 0], na.rm = TRUE) U2 <- -sum(Ry[Ry < 0], na.rm = TRUE) } else { Ry <- .safe_ranktransform(c(x - mu, y), verbose = verbose) n1 <- length(x) n2 <- length(y) S <- (n1 * n2) U1 <- sum(Ry[seq_along(x)]) - n1 * (n1 + 1) / 2 U2 <- sum(Ry[-seq_along(x)]) - n2 * (n2 + 1) / 2 } u_ <- U1 / S f_ <- U2 / S return(u_ - f_) } #' @keywords internal #' @importFrom stats kruskal.test .repsilon <- function(data) { model <- stats::kruskal.test(data$x, data$groups) H <- unname(model$statistic) n <- nrow(data) E <- H / ((n^2 - 1) / (n + 1)) } #' @keywords internal .kendalls_w <- function(data, verbose) { rankings <- apply(data, 1, .safe_ranktransform, verbose = verbose) rankings <- t(rankings) # keep dims # TODO add ties correction? n <- ncol(rankings) # items m <- nrow(rankings) # judges R <- colSums(rankings) S <- var(R) * (n - 1) W <- (12 * S) / (m^2 * (n^3 - n)) } ## CI ---- # #' @keywords internal # #' @importFrom bayestestR ci # .rbs_ci_boot <- function(x, # y, # mu = 0, # paired = FALSE, # ci = 0.95, # iterations = 200) { # stopifnot(length(ci) == 1, ci < 1, ci > 0) # # if (paired) { # data <- data.frame(x, y) # boot_rbs <- function(.data, .i) { # .data <- .data[.i, ] # .x <- .data$x # .y <- .data$y # .r_rbs(.x, .y, mu = mu, paired = TRUE, verbose = FALSE) # } # } else { # data <- data.frame( # i = seq_along(c(x, y)) # ) # # boot_rbs <- function(.data, .i) { # .x <- sample(x, replace = TRUE) # .y <- sample(y, replace = TRUE) # # .r_rbs(.x, .y, mu = mu, paired = FALSE, verbose = FALSE) # } # } # # R <- boot::boot( # data = data, # statistic = boot_rbs, # R = iterations # ) # # out <- as.data.frame( # bayestestR::ci(na.omit(R$t), ci = ci, verbose = FALSE) # ) # out$CI <- ci # out # } #' @keywords internal .repsilon_ci <- function(data, ci, alternative, iterations) { stopifnot(length(ci) == 1, ci < 1, ci > 0) ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 boot_r_epsilon <- function(.data, .i) { split(.data$x, .data$groups) <- lapply(split(.data$x, .data$groups), sample, replace = TRUE ) .repsilon(.data) } R <- boot::boot( data = data, statistic = boot_r_epsilon, R = iterations ) bCI <- boot::boot.ci(R, conf = ci.level, type = "perc")$percent bCI <- tail(as.vector(bCI), 2) data.frame( CI = ci, CI_low = if (alternative == "less") 0 else bCI[1], CI_high = if (alternative == "greater") 1 else bCI[2] ) } #' @keywords internal .kendalls_w_ci <- function(data, ci, alternative, iterations) { stopifnot(length(ci) == 1, ci < 1, ci > 0) ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 boot_w <- function(.data, .i) { .kendalls_w(.data[.i, ], verbose = FALSE) # sample rows } R <- boot::boot( data = data, statistic = boot_w, R = iterations ) bCI <- boot::boot.ci(R, conf = ci.level, type = "perc")$percent bCI <- tail(as.vector(bCI), 2) data.frame( CI = ci, CI_low = if (alternative == "less") 0 else bCI[1], CI_high = if (alternative == "greater") 1 else bCI[2] ) } # Utils ------------------------------------------------------------------- .safe_ranktransform <- function(x, verbose = TRUE, ...) { if (length(unique(x)) == 1) { if (verbose) warning("Only one unique value - rank fixed at 1") return(rep(1, length(x))) } datawizard::ranktransform(x, verbose = verbose, ...) } effectsize/R/standardize_info.R0000644000175000017500000003733414170302654016416 0ustar nileshnilesh#' Get Standardization Information #' #' This function extracts information, such as the deviations (SD or MAD) from #' parent variables, that are necessary for post-hoc standardization of #' parameters. This function gives a window on how standardized are obtained, #' i.e., by what they are divided. The "basic" method of standardization uses. #' #' @inheritParams standardize_parameters #' @param include_pseudo (For (G)LMMs) Should Pseudo-standardized information be #' included? #' @param ... Arguments passed to or from other methods. #' #' @return A data frame with information on each parameter (see #' [parameters::parameters_type]), and various standardization coefficients #' for the post-hoc methods (see [standardize_parameters()]) for the predictor #' and the response. #' #' @family standardize #' #' @examples #' model <- lm(mpg ~ ., data = mtcars) #' standardize_info(model) #' standardize_info(model, robust = TRUE) #' standardize_info(model, two_sd = TRUE) #' @importFrom parameters parameters_type #' @export standardize_info <- function(model, robust = FALSE, two_sd = FALSE, include_pseudo = FALSE, ...) { UseMethod("standardize_info") } #' @export standardize_info.default <- function(model, robust = FALSE, two_sd = FALSE, include_pseudo = FALSE, ...) { params <- if (inherits(model, c("glmmTMB", "MixMod"))) { insight::find_parameters(model, effects = "fixed", component = "conditional", flatten = TRUE, ...) } else { insight::find_parameters(model, effects = "fixed", flatten = TRUE, ...) } types <- parameters::parameters_type(model) model_matrix <- as.data.frame(stats::model.matrix(model)) data <- insight::get_data(model) # Sanity Check for ZI if (insight::model_info(model)$is_zero_inflated) { warning("Non-refit parameter standardization is ignoring the zero-inflation component.", call. = FALSE) # would need to also get the binomial model matrix... } out <- data.frame( Parameter = params, Type = types$Type, Link = types$Link, Secondary_Parameter = types$Secondary_Parameter, stringsAsFactors = FALSE ) # Type of effect size out$EffectSize_Type <- ifelse(types$Type == "interaction", "interaction", ifelse(types$Link == "Association", "r", ifelse(types$Link == "Difference", "d", NA) ) ) # Response - Basic out <- merge( out, .std_info_response_basic(model, params, robust = robust), by = "Parameter", all = TRUE ) # Response - Smart out <- merge( out, .std_info_response_smart(model, data, model_matrix, types, robust = robust), by = "Parameter", all = TRUE ) # Basic out <- merge( out, .std_info_predictors_basic(model, model_matrix, types, robust = robust, two_sd = two_sd), by = "Parameter", all = TRUE ) # Smart out <- merge( out, .std_info_predictors_smart(model, data, params, types, robust = robust, two_sd = two_sd ), by = "Parameter", all = TRUE ) # Pseudo (for LMM) if (include_pseudo && insight::model_info(model)$is_mixed && length(insight::find_random(model)$random) == 1) { out <- merge( out, .std_info_pseudo( model, params, model_matrix, types = types$Type, robust = robust, two_sd = two_sd ) ) } # Reorder out <- out[match(params, out$Parameter), ] out$Parameter <- params row.names(out) <- NULL # Remove all means for now (because it's not used) out <- out[!grepl("Mean_", names(out))] # Select only desired columns # if(method == "all") method <- c("smart", "basic") # if(!any(method == "smart")){ # out <- out[!grepl("_Smart", names(out))] # } # if(!any(method == "basic")){ # out <- out[!grepl("_Basic", names(out))] # } out } # Predictors - Smart ------------------------------------------------------------ #' @keywords internal .std_info_predictors_smart <- function(model, data, params, types, robust = FALSE, two_sd = FALSE, ...) { w <- insight::get_weights(model, na_rm = TRUE) # Get deviations for all parameters means <- deviations <- rep(NA_real_, times = length(params)) for (i in seq_along(params)) { var <- params[i] info <- .std_info_predictor_smart( data = data, variable = types[types$Parameter == var, "Variable"], type = types[types$Parameter == var, "Type"], robust = robust, two_sd = two_sd, weights = w ) deviations[i] <- info$sd means[i] <- info$mean } # Out data.frame( Parameter = params, Deviation_Smart = deviations, Mean_Smart = means ) } #' @keywords internal .std_info_predictor_smart <- function(data, variable, type, robust = FALSE, two_sd = FALSE, weights = NULL, ...) { if (type == "intercept") { info <- list(sd = 0, mean = 0) } else if (type == "numeric") { info <- .compute_std_info( data = data, variable = variable, robust = robust, two_sd = two_sd, weights = weights ) } else if (type == "factor") { info <- list(sd = 1, mean = 0) # TO BE IMPROVED: Adjust if involved in interactions # interactions <- types[types$Type %in% c("interaction"), ] # if(variable %in% interactions$Secondary_Variable){ # interac_var <- unique(interactions[interactions$Secondary_Variable == variable, "Variable"]) # for(i in interac_var){ # if(types[types$Parameter == i, "Type"] == "numeric"){ # sd_x <- sd_x * .get_deviation(data, i, robust) # } # } # } } else if (type %in% c("interaction", "nested")) { if (is.numeric(data[, variable])) { info <- .compute_std_info( data = data, variable = variable, robust = robust, two_sd = two_sd, weights = weights ) } else if (is.factor(data[, variable])) { info <- list(sd = 1, mean = 0) } else { info <- list(sd = 1, mean = 0) } } else { info <- list(sd = 1, mean = 0) } list(sd = info$sd, mean = info$mean) } # Predictors - Basic ------------------------------------------------------------ #' @keywords internal .std_info_predictors_basic <- function(model, model_matrix, types, robust = FALSE, two_sd = FALSE, ...) { w <- insight::get_weights(model, na_rm = TRUE) # Get deviations for all parameters means <- deviations <- rep(NA_real_, length = length(names(model_matrix))) for (i in seq_along(names(model_matrix))) { var <- names(model_matrix)[i] if (types[i, "Type"] == "intercept") { means[i] <- deviations[i] <- 0 } else { std_info <- .compute_std_info( data = model_matrix, variable = var, robust = robust, two_sd = two_sd, weights = w ) deviations[i] <- std_info$sd means[i] <- std_info$mean } } # Out data.frame( Parameter = types$Parameter[seq_along(names(model_matrix))], Deviation_Basic = deviations, Mean_Basic = means ) } # Response ------------------------------------------------------------ #' @keywords internal .std_info_response_smart <- function(model, data, model_matrix, types, robust = FALSE, ...) { info <- insight::model_info(model) w <- insight::get_weights(model, na_rm = TRUE) if (info$is_linear) { # response <- insight::get_response(model) response <- model.frame(model)[[1]] means <- deviations <- rep(NA_real_, length = length(names(model_matrix))) for (i in seq_along(names(model_matrix))) { var <- names(model_matrix)[i] if (any(types$Parameter == var) && types$Link[types$Parameter == var] == "Difference") { parent_var <- types$Variable[types$Parameter == var] intercept <- unique(data[[parent_var]])[1] response_at_intercept <- response[data[[parent_var]] == intercept] weights_at_intercept <- if (length(w)) w[data[[parent_var]] == intercept] else NULL std_info <- .compute_std_info( response = response_at_intercept, robust = robust, weights = weights_at_intercept ) } else { std_info <- .compute_std_info( response = response, robust = robust, weights = w ) } deviations[i] <- std_info$sd means[i] <- std_info$mean } } else { deviations <- 1 means <- 0 } # Out data.frame( Parameter = types$Parameter[seq_along(names(model_matrix))], Deviation_Response_Smart = deviations, Mean_Response_Smart = means ) } #' @importFrom stats model.frame #' @keywords internal .std_info_response_basic <- function(model, params, robust = FALSE, ...) { info <- insight::model_info(model) w <- insight::get_weights(model, na_rm = TRUE) # response <- insight::get_response(model) response <- stats::model.frame(model)[[1]] if (info$is_linear) { if (robust == FALSE) { sd_y <- .sd(response, w) mean_y <- .mean(response, w) } else { sd_y <- .mad(response, w) mean_y <- .median(response, w) } } else { sd_y <- 1 mean_y <- 0 } # Out data.frame( Parameter = params, Deviation_Response_Basic = sd_y, Mean_Response_Basic = mean_y ) } # Pseudo (GLMM) ----------------------------------------------------------- #' @importFrom insight clean_names get_random model_info find_formula get_variance get_data check_if_installed #' @importFrom performance check_heterogeneity_bias #' @importFrom datawizard demean #' @importFrom stats as.formula sd .std_info_pseudo <- function(model, params, model_matrix, types, robust = FALSE, two_sd = FALSE) { if (robust) { warning("'robust' standardization not available for 'pseudo' method.", call. = FALSE ) } f <- if (two_sd) 2 else 1 within_vars <- unclass(performance::check_heterogeneity_bias(model)) id <- insight::get_random(model)[[1]] w <- insight::get_weights(model, na_rm = TRUE) ## Find which parameters vary on level 1 ("within") is_within <- logical(length = length(params)) is_within[] <- NA for (i in seq_along(params)) { if (types[i] == "intercept") { is_within[i] <- FALSE } else if (types[i] == "numeric") { is_within[i] <- insight::clean_names(params[i]) %in% within_vars } else if (types[i] == "factor") { is_within[i] <- any(sapply(paste0("^", within_vars), grepl, insight::clean_names(params[i]))) } else if (types[i] == "interaction") { ints <- unlist(strsplit(params[i], ":", fixed = TRUE)) is_within[i] <- any(sapply(ints, function(int) { int <- insight::clean_names(int) int %in% within_vars | # numeric any(sapply(paste0("^", within_vars), grepl, int)) # factor })) } } ## test "within"s are fully "within" # only relevant to numeric predictors that can have variance if (any(check_within <- is_within & types == "numeric")) { p_check_within <- params[check_within] temp_d <- data.frame(model_matrix[, p_check_within, drop = FALSE]) colnames(temp_d) <- paste0("W", seq_len(ncol(temp_d))) # overwrite because can't deal with ":" dm <- datawizard::demean(cbind(id, temp_d), select = colnames(temp_d), group = "id" ) dm <- dm[, paste0(colnames(temp_d), "_between"), drop = FALSE] has_lvl2_var <- sapply(seq_along(colnames(temp_d)), function(i) { # If more than 1% of the variance in the within-var is between: var(dm[, i]) / var(temp_d[, i]) }) > 0.01 also_between <- p_check_within[has_lvl2_var] if (length(also_between)) { warning( "The following within-group terms have between-group variance:\n\t", paste0(also_between, collapse = ", "), "\nThis can inflate standardized within-group parameters associated with", "\nthese terms. See help(\"demean\", package = \"datawizard\") for modeling", "\nbetween- and within-subject effects.", call. = FALSE ) } } ## Get 2 types of Deviation_Response_Pseudo sd_y_within <- sd_y_between <- 1 if (insight::model_info(model)$is_linear) { insight::check_if_installed("lme4") rand_name <- insight::find_random(model)$random # maintain any y-transformations frm <- insight::find_formula(model) frm <- paste0(frm$conditional[2], " ~ (1|", rand_name, ")") m0 <- suppressWarnings(suppressMessages( lme4::lmer(stats::as.formula(frm), weights = w, data = insight::get_data(model) ) )) m0v <- insight::get_variance(m0) sd_y_between <- unname(sqrt(m0v$var.intercept)) sd_y_within <- unname(sqrt(m0v$var.residual)) } ## Get scaling factors for each parameter Deviation_Response_Pseudo <- Deviation_Pseudo <- numeric(ncol(model_matrix)) for (i in seq_along(params)) { if (types[i] == "intercept") { Deviation_Response_Pseudo[i] <- sd_y_between # doesn't matter Deviation_Pseudo[i] <- 0 } else { ## dumb way if (is_within[i]) { ## is within X <- model_matrix[[i]] Deviation_Response_Pseudo[i] <- sd_y_within } else { ## is between X <- tapply(model_matrix[[i]], id, mean) Deviation_Response_Pseudo[i] <- sd_y_between } Deviation_Pseudo[i] <- f * .sd(X, w) ## smart way? ## DONT USE: see correspondence with between Mattan and Eran BC # m <- suppressWarnings(suppressMessages(lme4::lmer(model_matrix[[i]] ~ (1|id)))) # if (is_within[i]) { # ## is within # Deviation_Pseudo[i] <- sqrt(unname(unlist(suppressWarnings( # insight::get_variance(m, component = "residual") # )))) # Deviation_Response_Pseudo[i] <- sd_y_within # } else { # ## is between # Deviation_Pseudo[i] <- sqrt(unname(unlist(suppressWarnings( # insight::get_variance(m, component = "intercept") # )))) # Deviation_Response_Pseudo[i] <- sd_y_between # } } } data.frame( Parameter = params, Deviation_Response_Pseudo, Deviation_Pseudo ) } # Utils ------------------------------------------------------------------- #' @keywords internal .compute_std_info <- function(data = NULL, variable = NULL, response = NULL, robust = FALSE, two_sd = FALSE, weights = NULL) { f <- if (two_sd) 2 else 1 if (is.null(response)) { response <- as.numeric(data[, variable]) } if (robust == FALSE) { sd_x <- .sd(response, weights) mean_x <- .mean(response, weights) } else { sd_x <- .mad(response, weights) mean_x <- .median(response, weights) } list(sd = f * sd_x, mean = mean_x) } effectsize/R/standardize_parameters.R0000644000175000017500000005527514170065645017640 0ustar nileshnilesh#' Parameters standardization #' #' Compute standardized model parameters (coefficients). #' #' @param model A statistical model. #' @param method The method used for standardizing the parameters. Can be #' `"refit"` (default), `"posthoc"`, `"smart"`, `"basic"` or `"pseudo"`. See #' 'Details'. #' @param include_response If `TRUE` (default), the response value will also be #' standardized. If `FALSE`, only the predictors will be standardized. For #' GLMs the response value will never be standardized (see *Generalized Linear #' Models* section). #' @inheritParams standardize.default #' @inheritParams chisq_to_phi #' @param ... For `standardize_parameters()`, arguments passed to #' [parameters::model_parameters], such as: #' - `ci_method`, `centrality` for Mixed models and Bayesian models... #' - `exponentiate`, ... #' - etc. #' @param parameters Deprecated. #' #' @details #' #' # Standardization Methods: #' - **refit**: This method is based on a complete model re-fit with a #' standardized version of the data. Hence, this method is equal to #' standardizing the variables before fitting the model. It is the "purest" and #' the most accurate (Neter et al., 1989), but it is also the most #' computationally costly and long (especially for heavy models such as Bayesian #' models). This method is particularly recommended for complex models that #' include interactions or transformations (e.g., polynomial or spline terms). #' The `robust` (default to `FALSE`) argument enables a robust standardization #' of data, i.e., based on the `median` and `MAD` instead of the `mean` and #' `SD`. **See [standardize()] for more details.** #' - **Note** that `standardize_parameters(method = "refit")` may not return #' the same results as fitting a model on data that has been standardized with #' `standardize()`; `standardize_parameters()` used the data used by the model #' fitting function, which might not be same data if there are missing values. #' see the `remove_na` argument in `standardize()`. #' - **posthoc**: Post-hoc standardization of the parameters, aiming at #' emulating the results obtained by "refit" without refitting the model. The #' coefficients are divided by the standard deviation (or MAD if `robust`) of #' the outcome (which becomes their expression 'unit'). Then, the coefficients #' related to numeric variables are additionally multiplied by the standard #' deviation (or MAD if `robust`) of the related terms, so that they correspond #' to changes of 1 SD of the predictor (e.g., "A change in 1 SD of `x` is #' related to a change of 0.24 of the SD of `y`). This does not apply to binary #' variables or factors, so the coefficients are still related to changes in #' levels. This method is not accurate and tend to give aberrant results when #' interactions are specified. #' - **basic**: This method is similar to `method = "posthoc"`, but treats all #' variables as continuous: it also scales the coefficient by the standard #' deviation of model's matrix' parameter of factors levels (transformed to #' integers) or binary predictors. Although being inappropriate for these cases, #' this method is the one implemented by default in other software packages, #' such as [lm.beta::lm.beta()]. #' - **smart** (Standardization of Model's parameters with Adjustment, #' Reconnaissance and Transformation - *experimental*): Similar to `method = #' "posthoc"` in that it does not involve model refitting. The difference is #' that the SD (or MAD if `robust`) of the response is computed on the relevant #' section of the data. For instance, if a factor with 3 levels A (the #' intercept), B and C is entered as a predictor, the effect corresponding to B #' vs. A will be scaled by the variance of the response at the intercept only. #' As a results, the coefficients for effects of factors are similar to a Glass' #' delta. #' - **pseudo** (*for 2-level (G)LMMs only*): In this (post-hoc) method, the #' response and the predictor are standardized based on the level of prediction #' (levels are detected with [performance::check_heterogeneity_bias()]): Predictors #' are standardized based on their SD at level of prediction (see also #' [datawizard::demean()]); The outcome (in linear LMMs) is standardized based #' on a fitted random-intercept-model, where `sqrt(random-intercept-variance)` #' is used for level 2 predictors, and `sqrt(residual-variance)` is used for #' level 1 predictors (Hoffman 2015, page 342). A warning is given when a #' within-group varialbe is found to have access between-group variance. #' #' # Transformed Variables #' When the model's formula contains transformations (e.g. `y ~ exp(X)`) `method #' = "refit"` will give different results compared to `method = "basic"` #' (`"posthoc"` and `"smart"` do not support such transformations): While #' `"refit"` standardizes the data *prior* to the transformation (e.g. #' equivalent to `exp(scale(X))`), the `"basic"` method standardizes the #' transformed data (e.g. equivalent to `scale(exp(X))`). #' \cr\cr #' See the *Transformed Variables* section in [standardize.default()] for more #' details on how different transformations are dealt with when `method = #' "refit"`. #' #' # Confidence Intervals #' The returned confidence intervals are re-scaled versions of the #' unstandardized confidence intervals, and not "true" confidence intervals of #' the standardized coefficients (cf. Jones & Waller, 2015). #' #' @inheritSection standardize.default Generalized Linear Models #' @inheritSection standardize.default Dealing with Factors #' #' @return A data frame with the standardized parameters (`Std_*`, depending on #' the model type) and their CIs (`CI_low` and `CI_high`). Where applicable, #' standard errors (SEs) are returned as an attribute (`attr(x, #' "standard_error")`). #' #' @family standardize #' @family effect size indices #' #' @examples #' library(effectsize) #' #' model <- lm(len ~ supp * dose, data = ToothGrowth) #' standardize_parameters(model, method = "refit") #' \donttest{ #' standardize_parameters(model, method = "posthoc") #' standardize_parameters(model, method = "smart") #' standardize_parameters(model, method = "basic") #' #' # Robust and 2 SD #' standardize_parameters(model, robust = TRUE) #' standardize_parameters(model, two_sd = TRUE) #' #' #' model <- glm(am ~ cyl * mpg, data = mtcars, family = "binomial") #' standardize_parameters(model, method = "refit") #' standardize_parameters(model, method = "posthoc") #' standardize_parameters(model, method = "basic", exponentiate = TRUE) #' } #' #' \donttest{ #' if (require("lme4")) { #' m <- lmer(mpg ~ cyl + am + vs + (1 | cyl), mtcars) #' standardize_parameters(m, method = "pseudo", ci_method = "satterthwaite") #' } #' #' #' \dontrun{ #' if (require("rstanarm")) { #' model <- stan_glm(rating ~ critical + privileges, data = attitude, refresh = 0) #' standardize_posteriors(model, method = "refit") #' standardize_posteriors(model, method = "posthoc") #' standardize_posteriors(model, method = "smart") #' head(standardize_posteriors(model, method = "basic")) #' } #' } #' } #' #' @references #' - Hoffman, L. (2015). Longitudinal analysis: Modeling within-person fluctuation and change. Routledge. #' - Jones, J. A., & Waller, N. G. (2015). The normal-theory and asymptotic distribution-free (ADF) covariance matrix of standardized regression coefficients: theoretical extensions and finite sample behavior. Psychometrika, 80(2), 365-378. #' - Neter, J., Wasserman, W., & Kutner, M. H. (1989). Applied linear regression models. #' - Gelman, A. (2008). Scaling regression inputs by dividing by two standard deviations. Statistics in medicine, 27(15), 2865-2873. #' #' @export standardize_parameters <- function(model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, parameters, ...) { if (!missing(parameters)) { warning( "'parameters' argument is deprecated, and will not be used.", immediate. = TRUE ) } UseMethod("standardize_parameters") } #' @importFrom parameters model_parameters #' @importFrom insight model_info #' @export standardize_parameters.default <- function(model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { object_name <- deparse(substitute(model), width.cutoff = 500) method <- match.arg(method, c("refit", "posthoc", "smart", "basic", "classic", "pseudo")) m_info <- insight::model_info(model) include_response <- include_response && .safe_to_standardize_response(m_info, verbose = verbose) if (method == "refit") { model <- standardize(model, robust = robust, two_sd = two_sd, include_response = include_response, verbose = verbose, m_info = m_info) } # need model_parameters to return the parameters, not the terms if (inherits(model, "aov")) class(model) <- class(model)[class(model) != "aov"] pars <- parameters::model_parameters(model, ci = ci, standardize = NULL, effects = "fixed", ...) # should post hoc exponentiate? dots <- list(...) exponentiate <- "exponentiate" %in% names(dots) && dots$exponentiate coefficient_name <- attr(pars, "coefficient_name") if (method %in% c("posthoc", "smart", "basic", "classic", "pseudo")) { if (m_info$is_multivariate) { # TODO FIX stop('Cannot post-hoc standardize multivariate models. Try using method "refit" instead.') } pars <- .standardize_parameters_posthoc(pars, method, model, robust, two_sd, exponentiate, include_response, verbose) method <- attr(pars, "std_method") robust <- attr(pars, "robust") } ## clean cols if (!is.null(ci)) pars$CI <- attr(pars, "ci") colnm <- c("Component", "Response", "Group", "Parameter", head(.col_2_scale, -2), "CI", "CI_low", "CI_high") pars <- pars[, colnm[colnm %in% colnames(pars)]] if (!is.null(coefficient_name) && coefficient_name %in% c("Odds Ratio", "Risk Ratio", "IRR")) { colnames(pars)[colnames(pars) == "Coefficient"] <- gsub(" ", "_", coefficient_name) } i <- colnames(pars) %in% c("Coefficient", "Median", "Mean", "MAP", c("Odds_Ratio", "Risk_Ratio", "IRR")) colnames(pars)[i] <- paste0("Std_", colnames(pars)[i]) ## SE attribute? if ("SE" %in% colnames(pars)) { attr(pars, "standard_error") <- pars$SE pars$SE <- NULL } ## attributes attr(pars, "std_method") <- method attr(pars, "two_sd") <- two_sd attr(pars, "robust") <- robust attr(pars, "object_name") <- object_name attr(pars, "ci") <- ci attr(pars, "include_response") <- include_response class(pars) <- c("effectsize_std_params", "effectsize_table", "see_effectsize_table", "data.frame") return(pars) } #' @export standardize_parameters.mediate <- function(model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { if (method != "refit") warning("Only method = 'refit' is supported for mediation models.", immediate. = TRUE) NextMethod("standardize_parameters", method = "refit", ci = ci, robust = robust, two_sd = two_sd, include_response = include_response, verbose = verbose) } #' @export standardize_parameters.parameters_model <- function(model, method = "refit", ci = NULL, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { if (method == "refit") { stop("Method 'refit' not supported for 'model_parameters()", call. = TRUE) } if (!is.null(ci)) { warnings("Argument 'ci' argument not supported for 'model_parameters(). It is ignored.", call. = TRUE) } pars <- model ci <- attr(pars, "ci") model <- .get_object(pars) if (is.null(model)) model <- attr(pars, "object") m_info <- insight::model_info(model) include_response <- include_response && .safe_to_standardize_response(m_info, verbose = verbose) if (is.null(exponentiate <- attr(pars, "exponentiate"))) exponentiate <- FALSE pars <- .standardize_parameters_posthoc(pars, method, model, robust, two_sd, exponentiate, include_response, verbose) method <- attr(pars, "std_method") robust <- attr(pars, "robust") ## clean cols if (!is.null(ci)) pars$CI <- attr(pars, "ci") colnm <- c("Component", "Response", "Group", "Parameter", head(.col_2_scale, -2), "CI", "CI_low", "CI_high") pars <- pars[, colnm[colnm %in% colnames(pars)]] i <- colnames(pars) %in% c("Coefficient", "Median", "Mean", "MAP") colnames(pars)[i] <- paste0("Std_", colnames(pars)[i]) ## SE attribute? if ("SE" %in% colnames(pars)) { attr(pars, "standard_error") <- pars$SE pars$SE <- NULL } ## attributes attr(pars, "std_method") <- method attr(pars, "two_sd") <- two_sd attr(pars, "robust") <- robust attr(pars, "ci") <- ci attr(pars, "include_response") <- include_response class(pars) <- c("effectsize_std_params", "effectsize_table", "see_effectsize_table", "data.frame") return(pars) } #' @export standardize_parameters.bootstrap_model <- function(model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { object_name <- deparse(substitute(model), width.cutoff = 500) method <- match.arg(method, c("refit", "posthoc", "smart", "basic", "classic", "pseudo")) pars <- model model <- attr(pars, "original_model") m_info <- insight::model_info(model) include_response <- include_response && .safe_to_standardize_response(m_info, verbose = verbose) if (method == "refit") { stop("The 'refit' method is not supported for bootstrapped models.") ## But it would look something like this: # model <- standardize(model, robust = robust, two_sd = two_sd, verbose = verbose, m_info = m_info) # model <- parameters::bootstrap_model(model, iterations = 1000, verbose = verbose) # return(model) } # need model_parameters to return the parameters, not the terms if (inherits(model, "aov")) class(model) <- class(model)[class(model) != "aov"] if (method %in% c("posthoc", "smart", "basic", "classic", "pseudo")) { pars <- .standardize_posteriors_posthoc(pars, method, model, robust, two_sd, include_response, verbose) method <- attr(pars, "std_method") robust <- attr(pars, "robust") } pars <- bayestestR::describe_posterior(pars, centrality = "median", ci = ci, ci_method = "quantile", test = NULL) names(pars)[names(pars) == "Median"] <- "Std_Coefficient" attr(pars, "std_method") <- method attr(pars, "two_sd") <- two_sd attr(pars, "robust") <- robust attr(pars, "object_name") <- object_name attr(pars, "ci") <- ci attr(pars, "include_response") <- include_response class(pars) <- c("effectsize_std_params", "effectsize_table", "see_effectsize_table", "data.frame") return(pars) } #' @export standardize_parameters.bootstrap_parameters <- function(model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { standardize_parameters(attr(model, "boot_samples"), method = method, ci = ci, robust = robust, two_sd = two_sd, include_response = include_response, verbose = verbose, ...) } #' @export standardize_parameters.model_fit <- function(model, method = "refit", ci = 0.95, robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { standardize_parameters( model$fit, method = method, ci = ci, robust = robust, two_sd = two_sd, include_response = include_response, verbose = verbose, ... ) } #' @keywords internal #' @importFrom insight model_info find_random .standardize_parameters_posthoc <- function(pars, method, model, robust, two_sd, exponentiate, include_response, verbose) { # Sanity Check for "pseudo" method <- .should_pseudo(method, model) method <- .cant_smart_or_posthoc(method, model, pars$Parameter) if (robust && method == "pseudo") { warning("'robust' standardization not available for 'pseudo' method.", call. = FALSE ) robust <- FALSE } ## Get scaling factors deviations <- standardize_info(model, robust = robust, include_pseudo = method == "pseudo", two_sd = two_sd) i_missing <- setdiff(seq_len(nrow(pars)), seq_len(nrow(deviations))) unstd <- pars if (length(i_missing)) { deviations[i_missing, ] <- NA } if (method == "basic") { col_dev_resp <- "Deviation_Response_Basic" col_dev_pred <- "Deviation_Basic" } else if (method == "posthoc") { col_dev_resp <- "Deviation_Response_Basic" col_dev_pred <- "Deviation_Smart" } else if (method == "smart") { col_dev_resp <- "Deviation_Response_Smart" col_dev_pred <- "Deviation_Smart" } else if (method == "pseudo") { col_dev_resp <- "Deviation_Response_Pseudo" col_dev_pred <- "Deviation_Pseudo" } else { stop("'method' must be one of 'basic', 'posthoc', 'smart' or 'pseudo'.") } .dev_pred <- deviations[[col_dev_pred]] .dev_resp <- deviations[[col_dev_resp]] if (!include_response) .dev_resp <- 1 .dev_factor <- .dev_pred / .dev_resp # Sapply standardization pars[, colnames(pars) %in% .col_2_scale] <- lapply( pars[, colnames(pars) %in% .col_2_scale, drop = FALSE], function(x) { if (exponentiate) { x ^ .dev_factor } else { x * .dev_factor } } ) to_complete <- apply(pars[, colnames(pars) %in% .col_2_scale], 1, anyNA) if (length(i_missing) || any(to_complete)) { i_missing <- union(i_missing, which(to_complete)) pars[i_missing, colnames(pars) %in% .col_2_scale] <- unstd[i_missing, colnames(pars) %in% .col_2_scale] } attr(pars, "std_method") <- method attr(pars, "two_sd") <- two_sd attr(pars, "robust") <- robust return(pars) } #' @keywords internal .col_2_scale <- c("Coefficient", "Median", "Mean", "MAP", "SE", "CI_low", "CI_high") # standardize_posteriors -------------------------------------------------- #' @rdname standardize_parameters #' @export standardize_posteriors <- function(model, method = "refit", robust = FALSE, two_sd = FALSE, include_response = TRUE, verbose = TRUE, ...) { object_name <- deparse(substitute(model), width.cutoff = 500) m_info <- insight::model_info(model) include_response <- include_response && .safe_to_standardize_response(m_info, verbose = verbose) if (method == "refit") { model <- standardize(model, robust = robust, two_sd = two_sd, include_response = include_response, verbose = verbose, m_info = m_info) } pars <- insight::get_parameters(model) if (method %in% c("posthoc", "smart", "basic", "classic", "pseudo")) { pars <- .standardize_posteriors_posthoc(pars, method, model, robust, two_sd, include_response, verbose) method <- attr(pars, "std_method") robust <- attr(pars, "robust") } ## attributes attr(pars, "std_method") <- method attr(pars, "two_sd") <- two_sd attr(pars, "robust") <- robust attr(pars, "include_response") <- include_response attr(pars, "object_name") <- object_name class(pars) <- c("effectsize_std_params", class(pars)) return(pars) } #' @keywords internal #' @importFrom insight model_info find_random .standardize_posteriors_posthoc <- function(pars, method, model, robust, two_sd, include_response, verbose) { # Sanity Check for "pseudo" method <- .should_pseudo(method, model) method <- .cant_smart_or_posthoc(method, model, pars$Parameter) if (robust && method == "pseudo") { warning("'robust' standardization not available for 'pseudo' method.", call. = FALSE ) robust <- FALSE } ## Get scaling factors deviations <- standardize_info(model, robust = robust, include_pseudo = method == "pseudo", two_sd = two_sd) i <- match(deviations$Parameter, colnames(pars)) pars <- pars[, i] if (method == "basic") { col_dev_resp <- "Deviation_Response_Basic" col_dev_pred <- "Deviation_Basic" } else if (method == "posthoc") { col_dev_resp <- "Deviation_Response_Basic" col_dev_pred <- "Deviation_Smart" } else if (method == "smart") { col_dev_resp <- "Deviation_Response_Smart" col_dev_pred <- "Deviation_Smart" } else if (method == "pseudo") { col_dev_resp <- "Deviation_Response_Pseudo" col_dev_pred <- "Deviation_Pseudo" } else { stop("'method' must be one of 'basic', 'posthoc', 'smart' or 'pseudo'.") } .dev_pred <- deviations[[col_dev_pred]] .dev_resp <- deviations[[col_dev_resp]] if (!include_response) .dev_resp <- 1 .dev_factor <- .dev_pred / .dev_resp # Sapply standardization pars <- t(t(pars) * .dev_factor) pars <- as.data.frame(pars) attr(pars, "std_method") <- method attr(pars, "two_sd") <- two_sd attr(pars, "robust") <- robust return(pars) } # util -------------------------------------------------------------------- #' @keywords internal .cant_smart_or_posthoc <- function(method, model, params) { if (method %in% c("smart", "posthoc")) { cant_posthocsmart <- FALSE if (insight::model_info(model)$is_linear) { if (!colnames(model.frame(model))[1] == insight::find_response(model)) { can_posthocsmart <- TRUE } } # factors are allowed if (!cant_posthocsmart && !all(params == insight::clean_names(params) | grepl("(as.factor|factor)\\(", params))) { cant_posthocsmart <- TRUE } if (cant_posthocsmart) { warning("Method '", method, "' does not currently support models with transformed parameters.", "\nReverting to 'basic' method. Concider using the 'refit' method directly.", call. = FALSE ) method <- "basic" } } method } #' @keywords internal .should_pseudo <- function(method, model) { if (method == "pseudo" && !(insight::model_info(model)$is_mixed && length(insight::find_random(model)$random) == 1)) { warning( "'pseudo' method only available for 2-level (G)LMMs.\n", "Setting method to 'basic'.", call. = FALSE ) method <- "basic" } method } effectsize/R/xtab.R0000644000175000017500000004133314170065645014031 0ustar nileshnilesh#' Effect size for contingency tables #' #' Compute Cramer's *V*, phi (\eqn{\phi}), Cohen's *w* (an alias of phi), #' Pearson's contingency coefficient, Odds ratios, Risk ratios, Cohen's *h* and #' Cohen's *g* for contingency tables or goodness-of-fit. See details. #' #' @inheritParams stats::chisq.test #' @param ci Confidence Interval (CI) level #' @param alternative a character string specifying the alternative hypothesis; #' Controls the type of CI returned: `"greater"` (two-sided CI; default for #' Cramer's *V*, phi (\eqn{\phi}), and Cohen's *w*), `"two.sided"` (default #' for OR, RR, Cohen's *h* and Cohen's *g*) or `"less"` (one-sided CI). #' Partial matching is allowed (e.g., `"g"`, `"l"`, `"two"`...). See #' *One-Sided CIs* in [effectsize_CIs]. #' @param adjust Should the effect size be bias-corrected? Defaults to `FALSE`. #' @param ... Arguments passed to [stats::chisq.test()], such as `p`. Ignored #' for `cohens_g()`. #' #' @details #' Cramer's *V*, phi (\eqn{\phi}) and Pearson's *C* are effect sizes for tests #' of independence in 2D contingency tables. For 2-by-k tables, Cramer's *V* and #' phi are identical, and are equal to the simple correlation between two #' dichotomous variables, ranging between 0 (no dependence) and 1 (perfect #' dependence). For larger tables, Cramer's *V* or Pearson's *C* should be used, #' as they are bounded between 0-1, whereas phi can be larger than 1 (upper #' bound is `sqrt(min(nrow, ncol) - 1))`). #' \cr\cr #' For goodness-of-fit in 1D tables Pearson's *C* or phi can be used. Phi has no #' upper bound (can be arbitrarily large, depending on the expected #' distribution), while Pearson's *C* is bounded between 0-1. #' \cr\cr #' For 2-by-2 contingency tables, Odds ratios, Risk ratios and Cohen's *h* can #' also be estimated. Note that these are computed with each **column** #' representing the different groups, and the first column representing the #' treatment group and the second column baseline (or control). Effects are #' given as `treatment / control`. If you wish you use rows as groups you must #' pass a transposed table, or switch the `x` and `y` arguments. #' \cr\cr #' Cohen's *g* is an effect size for dependent (paired) contingency tables #' ranging between 0 (perfect symmetry) and 0.5 (perfect asymmetry) (see #' [stats::mcnemar.test()]). #' #' # Confidence Intervals for Cohen's g, OR, RR and Cohen's h #' For Cohen's *g*, confidence intervals are based on the proportion (\eqn{P = g #' + 0.5}) confidence intervals returned by [stats::prop.test()] (minus 0.5), #' which give a good close approximation. #' \cr\cr #' For Odds ratios, Risk ratios and Cohen's *h*, confidence intervals are #' estimated using the standard normal parametric method (see Katz et al., 1978; #' Szumilas, 2010). #' \cr\cr #' See *Confidence (Compatibility) Intervals (CIs)*, *CIs and Significance #' Tests*, and *One-Sided CIs* sections for *phi*, Cohen's *w*, Cramer's *V* and #' Pearson's *C*. #' #' @inheritSection effectsize_CIs Confidence (Compatibility) Intervals (CIs) #' @inheritSection effectsize_CIs CIs and Significance Tests #' #' @return A data frame with the effect size (`Cramers_v`, `phi` (possibly with #' the suffix `_adjusted`), `Odds_ratio`, `Risk_ratio` (possibly with the #' prefix `log_`), `Cohens_h`, or `Cohens_g`) and its CIs (`CI_low` and #' `CI_high`). #' #' @seealso [chisq_to_phi()] for details regarding estimation and CIs. #' @family effect size indices #' #' @examples #' M <- #' matrix(c(150, 100, 165, #' 130, 50, 65, #' 35, 10, 2, #' 55, 40, 25), nrow = 4, #' dimnames = list( #' Music = c("Pop", "Rock", "Jazz", "Classic"), #' Study = c("Psych", "Econ", "Law"))) #' M #' #' # Note that Phi is not bound to [0-1], but instead #' # the upper bound for phi is sqrt(min(nrow, ncol) - 1) #' phi(M) #' #' cramers_v(M) #' #' pearsons_c(M) #' #' #' ## 2-by-2 tables #' ## ------------- #' RCT <- #' matrix(c(71, 30, #' 50, 100), nrow = 2, byrow = TRUE, #' dimnames = list( #' Diagnosis = c("Sick", "Recovered"), #' Group = c("Treatment", "Control"))) #' RCT # note groups are COLUMNS #' #' oddsratio(RCT) #' oddsratio(RCT, alternative = "greater") #' #' riskratio(RCT) #' #' cohens_h(RCT) #' #' #' #' ## Dependent (Paired) Contingency Tables #' ## ------------------------------------- # #' Performance <- #' matrix(c(794, 150, #' 86, 570), nrow = 2, #' dimnames = list( #' "1st Survey" = c("Approve", "Disapprove"), #' "2nd Survey" = c("Approve", "Disapprove"))) #' Performance #' #' cohens_g(Performance) #' #' @references #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd Ed.). New York: Routledge. #' - Katz, D. J. S. M., Baptista, J., Azen, S. P., & Pike, M. C. (1978). Obtaining confidence intervals for the risk ratio in cohort studies. Biometrics, 469-474. #' - Szumilas, M. (2010). Explaining odds ratios. Journal of the Canadian academy of child and adolescent psychiatry, 19(3), 227. #' #' @importFrom stats chisq.test #' @export phi <- function(x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], "BFcontingencyTable")) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "phi", adjust = adjust, ci = ci, ...)) } if (inherits(x, "htest")) { if (!(grepl("Pearson's Chi-squared", x$method) || grepl("Chi-squared test for given probabilities", x$method))) { stop("'x' is not a Chi-squared test!", call. = FALSE) } } else { x <- suppressWarnings(stats::chisq.test(x, y, ...)) x$data.name <- NULL } effectsize(x, type = "phi", adjust = adjust, ci = ci, alternative = alternative) } #' @rdname phi #' @export cohens_w <- phi #' @rdname phi #' @importFrom stats chisq.test #' @export cramers_v <- function(x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], "BFcontingencyTable")) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "cramers_v", adjust = adjust, ci = ci, ...)) } if (inherits(x, "htest")) { if (!(grepl("Pearson's Chi-squared", x$method) || grepl("Chi-squared test for given probabilities", x$method))) { stop("'x' is not a Chi-squared test!", call. = FALSE) } } else { x <- suppressWarnings(stats::chisq.test(x, y, ...)) x$data.name <- NULL } effectsize(x, type = "cramers_v", adjust = adjust, ci = ci, alternative = alternative) } #' @rdname phi #' @importFrom stats chisq.test #' @export pearsons_c <- function(x, y = NULL, ci = 0.95, alternative = "greater", adjust = FALSE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], "BFcontingencyTable")) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "pearsons_c", adjust = adjust, ci = ci, ...)) } if (inherits(x, "htest")) { if (!(grepl("Pearson's Chi-squared", x$method) || grepl("Chi-squared test for given probabilities", x$method))) { stop("'x' is not a Chi-squared test!", call. = FALSE) } } else { x <- suppressWarnings(stats::chisq.test(x, y, ...)) x$data.name <- NULL } effectsize(x, type = "pearsons_c", adjust = adjust, ci = ci, alternative = alternative) } #' @rdname phi #' @inheritParams oddsratio_to_d #' @export #' @importFrom stats chisq.test qnorm oddsratio <- function(x, y = NULL, ci = 0.95, alternative = "two.sided", log = FALSE, ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (inherits(x, "htest")) { if (grepl("Pearson's Chi-squared", x$method) || grepl("Chi-squared test for given probabilities", x$method)) { return(effectsize(x, type = "or", log = log, ci = ci, alternative = alternative)) } else if (grepl("Fisher's Exact", x$method)) { return(effectsize(x, alternative = alternative, ...)) } else { stop("'x' is not a Chi-squared / Fisher's Exact test!", call. = FALSE) } } else if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], "BFcontingencyTable")) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "or", log = log, ci = ci, ...)) } res <- suppressWarnings(stats::chisq.test(x, y, ...)) Obs <- res$observed if (any(c(colSums(Obs), rowSums(Obs)) == 0L)) { stop("Cannot have empty rows/columns in the contingency tables.", call. = FALSE) } if (nrow(Obs) != 2 || ncol(Obs) != 2) { stop("Odds ratio only available for 2-by-2 contingency tables", call. = FALSE) } OR <- (Obs[1, 1] / Obs[2, 1]) / (Obs[1, 2] / Obs[2, 2]) res <- data.frame(Odds_ratio = OR) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 alpha <- 1 - ci.level SE_logodds <- sqrt(sum(1 / Obs)) Z_logodds <- stats::qnorm(alpha / 2, lower.tail = FALSE) confs <- exp(log(OR) + c(-1, 1) * SE_logodds * Z_logodds) res$CI_low <- confs[1] res$CI_high <- confs[2] ci_method <- list(method = "normal") if (alternative == "less") { res$CI_low <- 0 } else if (alternative == "greater") { res$CI_high <- Inf } } else { alternative <- NULL } if (log) { res[colnames(res) %in% c("Odds_ratio", "CI_low", "CI_high")] <- log(res[colnames(res) %in% c("Odds_ratio", "CI_low", "CI_high")]) colnames(res)[1] <- "log_Odds_ratio" } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "log") <- log attr(res, "approximate") <- FALSE attr(res, "alternative") <- alternative return(res) } #' @rdname phi #' @inheritParams oddsratio_to_d #' @export #' @importFrom stats chisq.test qnorm riskratio <- function(x, y = NULL, ci = 0.95, alternative = "two.sided", log = FALSE, ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (inherits(x, "htest")) { if (!(grepl("Pearson's Chi-squared", x$method) || grepl("Chi-squared test for given probabilities", x$method))) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "rr", log = log, ci = ci, alternative = alternative)) } else if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], "BFcontingencyTable")) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "rr", log = log, ci = ci, ...)) } res <- suppressWarnings(stats::chisq.test(x, y, ...)) Obs <- res$observed if (any(c(colSums(Obs), rowSums(Obs)) == 0L)) { stop("Cannot have empty rows/columns in the contingency tables.", call. = FALSE) } if (nrow(Obs) != 2 || ncol(Obs) != 2) { stop("Risk ratio only available for 2-by-2 contingency tables", call. = FALSE) } n1 <- sum(Obs[, 1]) n2 <- sum(Obs[, 2]) p1 <- Obs[1, 1] / n1 p2 <- Obs[1, 2] / n2 RR <- p1 / p2 res <- data.frame(Risk_ratio = RR) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 alpha <- 1 - ci.level SE_logRR <- sqrt(p1 / ((1 - p1) * n1)) + sqrt(p2 / ((1 - p2) * n2)) Z_logRR <- stats::qnorm(alpha / 2, lower.tail = FALSE) confs <- exp(log(RR) + c(-1, 1) * SE_logRR * Z_logRR) res$CI_low <- confs[1] res$CI_high <- confs[2] ci_method <- list(method = "normal") if (alternative == "less") { res$CI_low <- 0 } else if (alternative == "greater") { res$CI_high <- Inf } } else { alternative <- NULL } if (log) { res[colnames(res) %in% c("Risk_ratio", "CI_low", "CI_high")] <- log(res[colnames(res) %in% c("Risk_ratio", "CI_low", "CI_high")]) colnames(res)[1] <- "log_Risk_ratio" } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "log") <- log attr(res, "approximate") <- FALSE attr(res, "alternative") <- alternative return(res) } #' @rdname phi #' @export #' @importFrom stats qnorm cohens_h <- function(x, y = NULL, ci = 0.95, alternative = "two.sided", ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (inherits(x, "htest")) { if (!(grepl("Pearson's Chi-squared", x$method) || grepl("Chi-squared test for given probabilities", x$method))) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "cohens_h", ci = ci, alternative = alternative)) } else if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], "BFcontingencyTable")) { stop("'x' is not a Chi-squared test!", call. = FALSE) } return(effectsize(x, type = "cohens_h", ci = ci, ...)) } res <- suppressWarnings(stats::chisq.test(x, y, ...)) Obs <- res$observed if (any(c(colSums(Obs), rowSums(Obs)) == 0L)) { stop("Cannot have empty rows/columns in the contingency tables.", call. = FALSE) } if (nrow(Obs) != 2 || ncol(Obs) != 2) { stop("Cohen's h only available for 2-by-2 contingency tables", call. = FALSE) } n1 <- sum(Obs[, 1]) n2 <- sum(Obs[, 2]) p1 <- Obs[1, 1] / n1 p2 <- Obs[1, 2] / n2 H <- 2 * asin(sqrt(p1)) - 2 * asin(sqrt(p2)) out <- data.frame(Cohens_h = H) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) out$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 alpha <- 1 - ci.level se_arcsin <- sqrt(0.25 * (1 / n1 + 1 / n2)) Zc <- stats::qnorm(alpha / 2, lower.tail = FALSE) out$CI_low <- H - Zc * (2 * se_arcsin) out$CI_high <- H + Zc * (2 * se_arcsin) ci_method <- list(method = "normal") if (alternative == "less") { out$CI_low <- -pi } else if (alternative == "greater") { out$CI_high <- pi } } else { alternative <- NULL } class(out) <- c("effectsize_table", "see_effectsize_table", class(out)) attr(out, "ci") <- ci attr(out, "ci_method") <- ci_method attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative return(out) } #' @rdname phi #' @export #' @importFrom stats complete.cases prop.test cohens_g <- function(x, y = NULL, ci = 0.95, alternative = "two.sided", ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) if (inherits(x, "htest")) { if (!grepl("McNemar", x$method)) { stop("'x' is not a McNemar test!", call. = FALSE) } return(effectsize(x, ci = ci, alternative = alternative)) } if (!is.matrix(x)) { if (is.null(y)) { stop("if 'x' is not a matrix, 'y' must be given") } if (length(x) != length(y)) { stop("'x' and 'y' must have the same length") } OK <- stats::complete.cases(x, y) x <- as.factor(x[OK]) y <- as.factor(y[OK]) if ((nlevels(x) < 2) || (nlevels(y) != nlevels(x))) { stop("'x' and 'y' must have the same number of levels (minimum 2)") } x <- table(x, y) } else { if ((nrow(x) < 2) || (ncol(x) != nrow(x))) { stop("'x' must be square with at least two rows and columns") } } b <- x[upper.tri(x)] c <- t(x)[upper.tri(x)] P <- sum(pmax(b, c)) / (sum(b) + sum(c)) g <- P - 0.5 out <- data.frame(Cohens_g = g) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) out$CI <- ci n <- sum(b) + sum(c) k <- P * n res <- stats::prop.test(k, n, p = 0.5, alternative = alternative, conf.level = ci, correct = FALSE ) out$CI <- ci out$CI_low <- res$conf.int[1] - 0.5 out$CI_high <- res$conf.int[2] - 0.5 ci_method <- list(method = "binomial") } class(out) <- c("effectsize_table", "see_effectsize_table", class(out)) attr(out, "ci") <- ci attr(out, "ci_method") <- ci_method attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative return(out) } effectsize/R/interpret_direction.R0000644000175000017500000000043014132466117017135 0ustar nileshnilesh#' Interpret direction #' #' @param x Numeric value. #' #' #' @examples #' interpret_direction(.02) #' interpret_direction(c(.5, -.02)) #' @export interpret_direction <- function(x) { interpret(x, rules(0, c("negative", "positive"), name = "math", right = FALSE)) } effectsize/R/utils_ncp_ci.R0000644000175000017500000000436414170065645015551 0ustar nileshnilesh #' @keywords internal #' @importFrom stats pf qf optim .get_ncp_F <- function(f, df, df_error, conf.level = 0.9) { if (!is.finite(f) || !is.finite(df) || !is.finite(df_error)) { return(c(NA, NA)) } alpha <- 1 - conf.level probs <- c(alpha / 2, 1 - alpha / 2) lambda <- f * df ncp <- suppressWarnings(stats::optim( par = 1.1 * rep(lambda, 2), fn = function(x) { p <- stats::pf(q = f, df, df_error, ncp = x) abs(max(p) - probs[2]) + abs(min(p) - probs[1]) }, control = list(abstol = 1e-09) )) f_ncp <- sort(ncp$par) / df if (f <= stats::qf(probs[1], df, df_error)) { f_ncp[2] <- 0 } if (f <= stats::qf(probs[2], df, df_error)) { f_ncp[1] <- 0 } return(f_ncp) } #' @keywords internal #' @importFrom stats pt #' @importFrom stats qt #' @importFrom stats optim .get_ncp_t <- function(t, df_error, conf.level = 0.95) { # # Note: these aren't actually needed - all t related functions would fail earlier # if (!is.finite(t) || !is.finite(df_error)) { # return(c(NA, NA)) # } alpha <- 1 - conf.level probs <- c(alpha / 2, 1 - alpha / 2) ncp <- suppressWarnings(optim( par = 1.1 * rep(t, 2), fn = function(x) { p <- pt(q = t, df = df_error, ncp = x) abs(max(p) - probs[2]) + abs(min(p) - probs[1]) }, control = list(abstol = 1e-09) )) t_ncp <- unname(sort(ncp$par)) return(t_ncp) } #' @keywords internals #' @importFrom stats pchisq qchisq optim .get_ncp_chi <- function(chi, df, conf.level = 0.95) { # # Note: these aren't actually needed - all chisq related functions would fail earlier # if (!is.finite(chi) || !is.finite(df)) { # return(c(NA, NA)) # } alpha <- 1 - conf.level probs <- c(alpha / 2, 1 - alpha / 2) ncp <- suppressWarnings(stats::optim( par = 1.1 * rep(chi, 2), fn = function(x) { p <- stats::pchisq(q = chi, df, ncp = x) abs(max(p) - probs[2]) + abs(min(p) - probs[1]) }, control = list(abstol = 1e-09) )) chi_ncp <- sort(ncp$par) if (chi <= stats::qchisq(probs[1], df)) { chi_ncp[2] <- 0 } if (chi <= stats::qchisq(probs[2], df)) { chi_ncp[1] <- 0 } chi_ncp } effectsize/R/interpret_ess_rhat.R0000644000175000017500000000405514132466117016774 0ustar nileshnilesh#' Interpret Bayesian diagnostic indices #' #' Interpretation of Bayesian diagnostic indices, such as Effective Sample Size (ESS) and Rhat. #' #' @param ess Value or vector of Effective Sample Size (ESS) values. #' @param rhat Value or vector of Rhat values. #' @param rules A character string (see *Rules*) or a custom set of [rules()]. #' #' @section Rules: #' #' ## ESS #' - Bürkner, P. C. (2017) (`"burkner2017"`; default) #' - **ESS < 1000** - Insufficient #' - **ESS >= 1000** - Sufficient #' #' ## Rhat #' - Vehtari et al. (2019) (`"vehtari2019"`; default) #' - **Rhat < 1.01** - Converged #' - **Rhat >= 1.01** - Failed #' - Gelman & Rubin (1992) (`"gelman1992"`) #' - **Rhat < 1.1** - Converged #' - **Rhat >= 1.1** - Failed #' #' #' @examples #' interpret_ess(1001) #' interpret_ess(c(852, 1200)) #' #' interpret_rhat(1.00) #' interpret_rhat(c(1.5, 0.9)) #' @references #' - Bürkner, P. C. (2017). brms: An R package for Bayesian multilevel models #' using Stan. Journal of Statistical Software, 80(1), 1-28. #' #' - Gelman, A., & Rubin, D. B. (1992). Inference from iterative simulation #' using multiple sequences. Statistical science, 7(4), 457-472. #' #' - Vehtari, A., Gelman, A., Simpson, D., Carpenter, B., & Bürkner, P. C. #' (2019). Rank-normalization, folding, and localization: An improved Rhat for #' assessing convergence of MCMC. arXiv preprint arXiv:1903.08008. #' @export interpret_ess <- function(ess, rules = "burkner2017") { rules <- .match.rules( rules, list( burkner2017 = rules(c(1000), c("insufficient", "sufficient"), name = "burkner2017", right = FALSE) ) ) interpret(abs(ess), rules) } #' @rdname interpret_ess #' @export interpret_rhat <- function(rhat, rules = "vehtari2019") { rules <- .match.rules( rules, list( vehtari2019 = rules(c(1.01), c("converged", "failed"), name = "vehtari2019"), gelman1992 = rules(c(1.1), c("converged", "failed"), name = "gelman1992") ) ) interpret(abs(rhat), rules) } effectsize/R/convert_stat_to_anova.R0000644000175000017500000002446714170072542017477 0ustar nileshnilesh#' Convert test statistics (F, t) to indices of **partial** variance explained #' (**partial** Eta / Omega / Epsilon squared and Cohen's f) #' #' These functions are convenience functions to convert F and t test statistics #' to **partial** Eta- (\eqn{\eta}), Omega- (\eqn{\omega}) Epsilon- #' (\eqn{\epsilon}) squared (an alias for the adjusted Eta squared) and Cohen's #' f. These are useful in cases where the various Sum of Squares and Mean #' Squares are not easily available or their computation is not straightforward #' (e.g., in liner mixed models, contrasts, etc.). For test statistics derived #' from `lm` and `aov` models, these functions give exact results. For all other #' cases, they return close approximations. #' \cr #' See [Effect Size from Test Statistics vignette.](https://easystats.github.io/effectsize/articles/from_test_statistics.html) #' #' @param t,f The t or the F statistics. #' @param df,df_error Degrees of freedom of numerator or of the error estimate #' (i.e., the residuals). #' @inheritParams chisq_to_phi #' @param ... Arguments passed to or from other methods. #' #' @return A data frame with the effect size(s) between 0-1 (`Eta2_partial`, #' `Epsilon2_partial`, `Omega2_partial`, `Cohens_f_partial` or #' `Cohens_f2_partial`), and their CIs (`CI_low` and `CI_high`). (Note that #' for \eqn{\omega_p^2}{Omega2p} and \eqn{\epsilon_p^2}{Epsilon2p} it is possible to compute a #' negative number; even though this doesn't make any practical sense, it is #' recommended to report the negative number and not a 0). #' #' @details These functions use the following formulae: #' \cr #' \deqn{\eta_p^2 = \frac{F \times df_{num}}{F \times df_{num} + df_{den}}}{\eta^2_p = F * df1 / (F * df1 + df2)} #' \cr #' \deqn{\epsilon_p^2 = \frac{(F - 1) \times df_{num}}{F \times df_{num} + df_{den}}}{\epsilon^2_p = (F - 1) * df1 / (F * df1 + df2)} #' \cr #' \deqn{\omega_p^2 = \frac{(F - 1) \times df_{num}}{F \times df_{num} + df_{den} + 1}}{\omega^2_p=(F - 1) * df1 / (F * df1 + df2 + 1)} #' \cr #' \deqn{f_p = \sqrt{\frac{\eta_p^2}{1-\eta_p^2}}}{f = \eta^2 / (1 - \eta^2)} #' \cr\cr #' For *t*, the conversion is based on the equality of \eqn{t^2 = F} when \eqn{df_{num}=1}{df1 = 1}. #' #' ## Choosing an Un-Biased Estimate #' Both Omega and Epsilon are unbiased estimators of the population Eta. But #' which to choose? Though Omega is the more popular choice, it should be noted #' that: #' 1. The formula given above for Omega is only an approximation for complex #' designs. #' 2. Epsilon has been found to be less biased (Carroll & Nordholm, 1975). #' #' @inheritSection effectsize_CIs Confidence (Compatibility) Intervals (CIs) #' @inheritSection effectsize_CIs CIs and Significance Tests #' #' @note Adjusted (partial) Eta-squared is an alias for (partial) Epsilon-squared. #' #' @seealso [eta_squared()] for more details. #' @family effect size from test statistic #' #' @examples #' \donttest{ #' if (require("afex")) { #' data(md_12.1) #' aov_ez("id", "rt", md_12.1, #' within = c("angle", "noise"), #' anova_table = list(correction = "none", es = "pes") #' ) #' } #' # compare to: #' (etas <- F_to_eta2( #' f = c(40.72, 33.77, 45.31), #' df = c(2, 1, 2), #' df_error = c(18, 9, 18) #' )) #' #' if (require(see)) plot(etas) #' #' #' if (require("lmerTest")) { # for the df_error #' fit <- lmer(extra ~ group + (1 | ID), sleep) #' # anova(fit) #' # #> Type III Analysis of Variance Table with Satterthwaite's method #' # #> Sum Sq Mean Sq NumDF DenDF F value Pr(>F) #' # #> group 12.482 12.482 1 9 16.501 0.002833 ** #' # #> --- #' # #> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 #' #' F_to_eta2(16.501, 1, 9) #' F_to_omega2(16.501, 1, 9) #' F_to_epsilon2(16.501, 1, 9) #' F_to_f(16.501, 1, 9) #' } #' #' #' ## Use with emmeans based contrasts #' ## -------------------------------- #' if (require(emmeans)) { #' warp.lm <- lm(breaks ~ wool * tension, data = warpbreaks) #' #' jt <- joint_tests(warp.lm, by = "wool") #' F_to_eta2(jt$F.ratio, jt$df1, jt$df2) #' } #' } #' @references #' - Albers, C., & Lakens, D. (2018). When power analyses based on pilot data #' are biased: Inaccurate effect size estimators and follow-up bias. Journal of #' experimental social psychology, 74, 187-195. \doi{10.31234/osf.io/b7z4q} #' #' - Carroll, R. M., & Nordholm, L. A. (1975). Sampling Characteristics of #' Kelley's epsilon and Hays' omega. Educational and Psychological Measurement, #' 35(3), 541-554. #' #' - Cumming, G., & Finch, S. (2001). A primer on the understanding, use, and #' calculation of confidence intervals that are based on central and noncentral #' distributions. Educational and Psychological Measurement, 61(4), 532-574. #' #' - Friedman, H. (1982). Simplified determinations of statistical power, #' magnitude of effect and research sample sizes. Educational and Psychological #' Measurement, 42(2), 521-526. \doi{10.1177/001316448204200214} #' #' - Mordkoff, J. T. (2019). A Simple Method for Removing Bias From a Popular #' Measure of Standardized Effect Size: Adjusted Partial Eta Squared. Advances #' in Methods and Practices in Psychological Science, 2(3), 228-232. #' \doi{10.1177/2515245919855053} #' #' - Morey, R. D., Hoekstra, R., Rouder, J. N., Lee, M. D., & Wagenmakers, E. J. #' (2016). The fallacy of placing confidence in confidence intervals. #' Psychonomic bulletin & review, 23(1), 103-123. #' #' - Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals #' and tests of close fit in the analysis of variance and contrast analysis. #' Psychological Methods, 9, 164-182. #' #' @export F_to_eta2 <- function(f, df, df_error, ci = 0.95, alternative = "greater", ...) { .F_to_pve(f, df, df_error, ci = ci, alternative = alternative, es = "eta2", ...) } #' @rdname F_to_eta2 #' @export t_to_eta2 <- function(t, df_error, ci = 0.95, alternative = "greater", ...) { F_to_eta2(t^2, 1, df_error, ci = ci, alternative = alternative, ...) } #' @rdname F_to_eta2 #' @export F_to_epsilon2 <- function(f, df, df_error, ci = 0.95, alternative = "greater", ...) { .F_to_pve(f, df, df_error, ci = ci, alternative = alternative, es = "epsilon2", ...) } #' @rdname F_to_eta2 #' @export t_to_epsilon2 <- function(t, df_error, ci = 0.95, alternative = "greater", ...) { F_to_epsilon2(t^2, 1, df_error, ci = ci, alternative = alternative, ...) } #' @rdname F_to_eta2 #' @export F_to_eta2_adj <- F_to_epsilon2 #' @rdname F_to_eta2 #' @export t_to_eta2_adj <- t_to_epsilon2 #' @rdname F_to_eta2 #' @export F_to_omega2 <- function(f, df, df_error, ci = 0.95, alternative = "greater", ...) { .F_to_pve(f, df, df_error, ci = ci, alternative = alternative, es = "omega2", ...) } #' @rdname F_to_eta2 #' @export t_to_omega2 <- function(t, df_error, ci = 0.95, alternative = "greater", ...) { F_to_omega2(t^2, 1, df_error, ci = ci, alternative = alternative,...) } #' @rdname F_to_eta2 #' @param squared Return Cohen's *f* or Cohen's *f*-squared? #' @export F_to_f <- function(f, df, df_error, ci = 0.95, alternative = "greater", squared = FALSE, ...) { res_eta <- F_to_eta2(f, df, df_error, ci = ci, alternative = alternative, ...) res <- data.frame( Cohens_f2_partial = res_eta$Eta2_partial / (1 - res_eta$Eta2_partial) ) ci_method <- NULL if (is.numeric(ci)) { res$CI <- res_eta$CI res$CI_low <- res_eta$CI_low / (1 - res_eta$CI_low) res$CI_high <- res_eta$CI_high / (1 - res_eta$CI_high) ci_method <- list(method = "ncp", distribution = "F") } if (!squared) { i <- colnames(res) %in% c("Cohens_f2_partial", "CI_low", "CI_high") res[i] <- sqrt(res[i]) colnames(res)[colnames(res) == "Cohens_f2_partial"] <- "Cohens_f_partial" } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "alternative") <- if (is.numeric(ci)) alternative return(res) } #' @rdname F_to_eta2 #' @export t_to_f <- function(t, df_error, ci = 0.95, alternative = "greater", squared = FALSE, ...) { F_to_f(t^2, 1, df_error, ci = ci, alternative = alternative, squared = squared, ...) } #' @rdname F_to_eta2 #' @export F_to_f2 <- function(f, df, df_error, ci = 0.95, alternative = "greater", squared = TRUE, ...) { F_to_f(f, df = df, df_error = df_error, ci = ci, alternative = alternative, squared = squared, ...) } #' @rdname F_to_eta2 #' @export t_to_f2 <- function(t, df_error, ci = 0.95, alternative = "greater", squared = TRUE, ...) { F_to_f(t^2, 1, df_error, ci = ci, alternative = alternative, squared = squared, ...) } #' @keywords internal .F_to_pve <- function(f, df, df_error, ci = 0.95, alternative = "greater", es = "eta2", verbose = TRUE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) res <- switch(tolower(es), eta2 = data.frame(Eta2_partial = (f * df) / (f * df + df_error)), epsilon2 = data.frame(Epsilon2_partial = ((f - 1) * df) / (f * df + df_error)), omega2 = data.frame(Omega2_partial = ((f - 1) * df) / (f * df + df_error + 1)), stop("'es' must be 'eta2', 'epsilon2', or 'omega2'.") ) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 # based on MBESS::ci.R2 f <- pmax(0, (res[[1]] / df) / ((1 - res[[1]]) / df_error)) fs <- t(mapply(.get_ncp_F, f, df, df_error, ci.level)) if (isTRUE(verbose) && anyNA(fs)){ warning("Some CIs could not be estimated due to non-finite F, df, or df_error values.", call. = FALSE) } # This really is a generic F_to_R2 res$CI_low <- F_to_eta2(fs[, 1], df, df_error, ci = NULL)[[1]] res$CI_high <- F_to_eta2(fs[, 2], df, df_error, ci = NULL)[[1]] ci_method <- list(method = "ncp", distribution = "F") if (alternative == "less") { res$CI_low <- 0 } else if (alternative == "greater") { res$CI_high <- 1 } } else { alternative <- NULL } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "alternative") <- alternative return(res) } effectsize/R/eta_squared.R0000644000175000017500000012744114170306574015374 0ustar nileshnilesh#' Effect size for ANOVA #' #' Functions to compute effect size measures for ANOVAs, such as Eta- #' (\eqn{\eta}), Omega- (\eqn{\omega}) and Epsilon- (\eqn{\epsilon}) squared, #' and Cohen's f (or their partialled versions) for ANOVA tables. These indices #' represent an estimate of how much variance in the response variables is #' accounted for by the explanatory variable(s). #' \cr\cr #' When passing models, effect sizes are computed using the sums of squares #' obtained from `anova(model)` which might not always be appropriate. See #' details. #' #' @param model A model, ANOVA object, or the result of `parameters::model_parameters`. #' @param partial If `TRUE`, return partial indices. #' @param generalized If TRUE, returns generalized Eta Squared, assuming all #' variables are manipulated. Can also be a character vector of observed #' (non-manipulated) variables, in which case generalized Eta Squared is #' calculated taking these observed variables into account. For `afex_aov` #' model, when `generalized = TRUE`, the observed variables are extracted #' automatically from the fitted model, if they were provided then. #' @param verbose Toggle warnings and messages on or off. #' @inheritParams chisq_to_phi #' @param ... Arguments passed to or from other methods. #' - Can be `include_intercept = TRUE` to include the effect size for the intercept. #' - For Bayesian models, arguments passed to `ss_function`. #' #' @return #' A data frame with the effect size(s) between 0-1 (`Eta2`, `Epsilon2`, #' `Omega2`, `Cohens_f` or `Cohens_f2`, possibly with the `partial` or #' `generalized` suffix), and their CIs (`CI_low` and `CI_high`). #' \cr\cr #' For `eta_squared_posterior()`, a data frame containing the ppd of the Eta #' squared for each fixed effect, which can then be passed to #' [bayestestR::describe_posterior()] for summary stats. #' #' @details #' #' For `aov`, `aovlist` and `afex_aov` models, and for `anova` objects that #' provide Sums-of-Squares, the effect sizes are computed directly using #' Sums-of-Squares (for `mlm` / `maov` models, effect sizes are computed for #' each response separately). For all other model, effect sizes are approximated #' via test statistic conversion of the omnibus *F* statistic provided by the #' appropriate `anova()` method (see [`F_to_eta2()`] for more details.) #' #' ## Type of Sums of Squares #' The sums of squares (or *F* statistics) used for the computation of the #' effect sizes is based on those returned by `anova(model)` (whatever those may #' be - for `aov` and `aovlist` these are *type-1* sums of squares; for #' `lmerMod` (and `lmerModLmerTest`) these are *type-3* sums of squares). Make #' sure these are the sums of squares you are interested in; You might want to #' pass the result of `car::Anova(mode, type = 2)` or `type = 3` instead of the #' model itself, or use the `afex` package to fit ANOVA models. #' \cr\cr #' For type 3 sum of squares, it is generally recommended to fit models with #' *`contr.sum` factor weights* and *centered covariates*, for sensible results. #' See examples and the `afex` package. #' #' ## Un-Biased Estimate of Eta #' Both ***Omega*** and ***Epsilon*** are unbiased estimators of the #' population's ***Eta***, which is especially important is small samples. But #' which to choose? #' \cr\cr #' Though Omega is the more popular choice (Albers \& Lakens, 2018), Epsilon is #' analogous to adjusted R2 (Allen, 2017, p. 382), and has been found to be less #' biased (Carroll & Nordholm, 1975). #' \cr\cr #' (Note that for Omega- and Epsilon-squared it is possible to compute a #' negative number; even though this doesn't make any practical sense, it is #' recommended to report the negative number and not a 0.) #' #' ## Cohen's f #' Cohen's f can take on values between zero, when the population means are all #' equal, and an indefinitely large number as standard deviation of means #' increases relative to the average standard deviation within each group. #' \cr\cr #' When comparing two models in a sequential regression analysis, Cohen's f for #' R-square change is the ratio between the increase in R-square #' and the percent of unexplained variance. #' \cr\cr #' Cohen has suggested that the values of 0.10, 0.25, and 0.40 represent small, #' medium, and large effect sizes, respectively. #' #' ## Eta Squared from Posterior Predictive Distribution #' For Bayesian models (fit with `brms` or `rstanarm`), #' `eta_squared_posterior()` simulates data from the posterior predictive #' distribution (ppd) and for each simulation the Eta Squared is computed for #' the model's fixed effects. This means that the returned values are the #' population level effect size as implied by the posterior model (and not the #' effect size in the sample data). See [rstantools::posterior_predict()] for #' more info. #' #' @inheritSection effectsize_CIs Confidence (Compatibility) Intervals (CIs) #' @inheritSection effectsize_CIs CIs and Significance Tests #' #' @seealso [F_to_eta2()] #' @family effect size indices #' #' @examples #' \donttest{ #' data(mtcars) #' mtcars$am_f <- factor(mtcars$am) #' mtcars$cyl_f <- factor(mtcars$cyl) #' #' model <- aov(mpg ~ am_f * cyl_f, data = mtcars) #' #' (eta2 <- eta_squared(model)) #' #' # More types: #' eta_squared(model, partial = FALSE) #' eta_squared(model, generalized = "cyl_f") #' omega_squared(model) #' epsilon_squared(model) #' cohens_f(model) #' #' if (require(see)) plot(eta2) #' #' model0 <- aov(mpg ~ am_f + cyl_f, data = mtcars) # no interaction #' cohens_f_squared(model0, model2 = model) #' #' ## Interpretation of effect sizes #' ## ------------------------------------- #' #' interpret_omega_squared(0.10, rules = "field2013") #' interpret_eta_squared(0.10, rules = "cohen1992") #' interpret_epsilon_squared(0.10, rules = "cohen1992") #' #' interpret(eta2, rules = "cohen1992") #' #' # Recommended: Type-3 effect sizes + effects coding #' # ------------------------------------------------- #' if (require(car, quietly = TRUE)) { #' contrasts(mtcars$am_f) <- contr.sum #' contrasts(mtcars$cyl_f) <- contr.sum #' #' model <- aov(mpg ~ am_f * cyl_f, data = mtcars) #' model_anova <- car::Anova(model, type = 3) #' #' eta_squared(model_anova) #' } #' #' # afex takes care of both type-3 effects and effects coding: #' if (require(afex)) { #' data(obk.long, package = "afex") #' model <- aov_car(value ~ treatment * gender + Error(id / (phase)), #' data = obk.long, observed = "gender" #' ) #' eta_squared(model) #' epsilon_squared(model) #' omega_squared(model) #' eta_squared(model, partial = FALSE) #' epsilon_squared(model, partial = FALSE) #' omega_squared(model, partial = FALSE) #' eta_squared(model, generalized = TRUE) # observed vars are pulled from the afex model. #' } #' #' #' #' ## Approx. effect sizes for mixed models #' ## ------------------------------------- #' if (require(lmerTest, quietly = TRUE)) { #' model <- lmer(mpg ~ am_f * cyl_f + (1 | vs), data = mtcars) #' omega_squared(model) #' } #' #' #' #' #' ## Bayesian Models (PPD) #' ## --------------------- #' \dontrun{ #' if (require(rstanarm) && require(bayestestR) && require(car)) { #' fit_bayes <- stan_glm(mpg ~ factor(cyl) * wt + qsec, #' data = mtcars, #' family = gaussian(), #' refresh = 0 #' ) #' #' es <- eta_squared_posterior(fit_bayes, #' ss_function = car::Anova, type = 3 #' ) #' bayestestR::describe_posterior(es) #' #' #' # compare to: #' fit_freq <- lm(mpg ~ factor(cyl) * wt + qsec, #' data = mtcars #' ) #' aov_table <- car::Anova(fit_freq, type = 3) #' eta_squared(aov_table) #' } #' } #' } #' #' @return A data frame containing the effect size values and their confidence #' intervals. #' #' @references #' - Albers, C., \& Lakens, D. (2018). When power analyses based on pilot data #' are biased: Inaccurate effect size estimators and follow-up bias. Journal of #' experimental social psychology, 74, 187-195. #' #' - Allen, R. (2017). Statistics and Experimental Design for Psychologists: A #' Model Comparison Approach. World Scientific Publishing Company. #' #' - Carroll, R. M., & Nordholm, L. A. (1975). Sampling Characteristics of #' Kelley's epsilon and Hays' omega. Educational and Psychological Measurement, #' 35(3), 541-554. #' #' - Kelley, T. (1935) An unbiased correlation ratio measure. Proceedings of the #' National Academy of Sciences. 21(9). 554-559. #' #' - Olejnik, S., & Algina, J. (2003). Generalized eta and omega squared #' statistics: measures of effect size for some common research designs. #' Psychological methods, 8(4), 434. #' #' - Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals #' and tests of close fit in the analysis of variance and contrast analysis. #' Psychological Methods, 9, 164-182. #' #' @export eta_squared <- function(model, partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) out <- .anova_es( model, type = "eta", partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) class(out) <- unique(c("effectsize_anova","effectsize_table", "see_effectsize_table", class(out))) if ("CI" %in% colnames(out)) attr(out, "ci_method") <- list(method = "ncp", distribution = "F") attr(out, "approximate") <- isTRUE(attr(out, "approximate", exact = TRUE)) return(out) } #' @rdname eta_squared #' @export omega_squared <- function(model, partial = TRUE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) out <- .anova_es(model, type = "omega", partial = partial, ci = ci, alternative = alternative, verbose = verbose, ...) class(out) <- unique(c("effectsize_anova","effectsize_table", "see_effectsize_table", class(out))) if ("CI" %in% colnames(out)) attr(out, "ci_method") <- list(method = "ncp", distribution = "F") attr(out, "approximate") <- isTRUE(attr(out, "approximate", exact = TRUE)) return(out) } #' @rdname eta_squared #' @export epsilon_squared <- function(model, partial = TRUE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) out <- .anova_es(model, type = "epsilon", partial = partial, ci = ci, alternative = alternative, verbose = verbose, ...) class(out) <- unique(c("effectsize_anova","effectsize_table", "see_effectsize_table", class(out))) if ("CI" %in% colnames(out)) attr(out, "ci_method") <- list(method = "ncp", distribution = "F") attr(out, "approximate") <- isTRUE(attr(out, "approximate", exact = TRUE)) return(out) } #' @rdname eta_squared #' @inheritParams F_to_f #' @param model2 Optional second model for Cohen's f (/squared). If specified, #' returns the effect size for R-squared-change between the two models. #' @export cohens_f <- function(model, partial = TRUE, ci = 0.95, alternative = "greater", squared = FALSE, verbose = TRUE, model2 = NULL, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (!is.null(model2)) { return(.cohens_f_delta(model, model2, ci = ci, alternative = alternative, squared = squared, verbose = verbose)) } res <- eta_squared(model, partial = partial, ci = ci, alternative = alternative, verbose = verbose, ... ) if ("Eta2_partial" %in% colnames(res)) { res$Eta2_partial <- res$Eta2_partial / (1 - res$Eta2_partial) colnames(res)[colnames(res) == "Eta2_partial"] <- "Cohens_f2_partial" } else { res$Eta2 <- res$Eta2 / (1 - res$Eta2) colnames(res)[colnames(res) == "Eta2"] <- "Cohens_f2" } if (is.numeric(ci)) { res$CI_low <- res$CI_low / (1 - res$CI_low) res$CI_high <- res$CI_high / (1 - res$CI_high) } if (!squared) { i <- colnames(res) %in% c("Cohens_f2", "Cohens_f2_partial", "CI_low", "CI_high") res[i] <- sqrt(res[i]) colnames(res)[colnames(res) %in% c("Cohens_f2", "Cohens_f2_partial")] <- if ("Cohens_f2" %in% colnames(res)) "Cohens_f" else "Cohens_f_partial" } if ("CI" %in% colnames(res)) attr(res, "ci_method") <- list(method = "ncp", distribution = "F") class(res) <- unique(c("effectsize_anova","effectsize_table", "see_effectsize_table", class(res))) attr(res, "approximate") <- isTRUE(attr(res, "approximate", exact = TRUE)) res } #' @rdname eta_squared #' @export cohens_f_squared <- function(model, partial = TRUE, ci = 0.95, alternative = "greater", squared = TRUE, verbose = TRUE, model2 = NULL, ...) { cohens_f( model, partial = partial, ci = ci, alternative = alternative, squared = squared, verbose = verbose, model2 = model2, ... ) } #' @keywords internal #' @importFrom insight model_info .cohens_f_delta <- function(model, model2, ci = 0.95, alternative = "greater", squared = FALSE, verbose = TRUE) { # check if (!inherits(model, "lm") || !inherits(model2, "lm") || !insight::model_info(model)$is_linear || !insight::model_info(model2)$is_linear) { stop("Cohen's f for R2-change only supported for fixed effect linear models.", call. = FALSE ) } # Anova ANOVA <- anova(model, model2) out <- F_to_f(ANOVA[2, "F"], abs(ANOVA[2, "Df"]), min(ANOVA["Res.Df"]), ci = ci, alternative = alternative, squared = squared) R2d <- performance::r2(model)[[1]] - performance::r2(model2)[[1]] out$R2_delta <- abs(R2d) return(out) } # Get ES ------------------------------------------------------------------ #' @param aov_table Input data frame #' @param type Which effect size to compute? #' @param include_intercept Should the intercept (`(Intercept)`) be included? #' @param partial,generalized,ci,alternative,verbose See [eta_squared()]. #' #' @rdname effectsize_API #' @export .es_aov_simple <- function(aov_table, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE) { type <- match.arg(type) aov_table <- as.data.frame(aov_table) # Clean up data --- if (!"Mean_Square" %in% colnames(aov_table)) { aov_table[["Mean_Square"]] <- aov_table[["Sum_Squares"]] / aov_table[["df"]] } if (!"Residuals" %in% aov_table$Parameter) { stop(insight::format_message("No residuals data found - cannot compute effect size.")) } # Include intercept? --- if (include_intercept) { values <- .values_aov(aov_table[aov_table$Parameter != "(Intercept)", ]) } else { aov_table <- aov_table[aov_table$Parameter != "(Intercept)", ] values <- .values_aov(aov_table) } # Get error df --- df_error <- aov_table$df[aov_table$Parameter == "Residuals"] aov_table <- aov_table[aov_table$Parameter != "Residuals", , drop = FALSE] # Validate anova type (1,2,3) and partial --- anova_type <- NULL if (nrow(aov_table) == 1L && (partial || isTRUE(generalized) || is.character(generalized))) { if (verbose) { txt_type <- ifelse(isTRUE(generalized) || is.character(generalized), "generalized", "partial") message( "For one-way between subjects designs, ", txt_type, " ", type, " squared is equivalent to ", type, " squared.\n", "Returning ", type, " squared." ) } partial <- FALSE anova_type <- NA } # Estimate effect size --- if (type == "eta") { if (isTRUE(generalized) || is.character(generalized)) { ## copied from afex obs <- logical(nrow(aov_table)) if (is.character(generalized)) { for (o in generalized) { oi <- grepl(paste0("\\b", o, "\\b"), aov_table$Parameter) if (!any(oi)) stop("Observed variable not in data: ", o, call. = FALSE) obs <- obs | oi } } obs_SSn1 <- sum(aov_table$Sum_Squares * obs) obs_SSn2 <- aov_table$Sum_Squares * obs aov_table$Eta2_generalized <- aov_table$Sum_Squares / (aov_table$Sum_Squares + values$Sum_Squares_residuals + obs_SSn1 - obs_SSn2) } else if (!isTRUE(partial)) { aov_table$Eta2 <- aov_table$Sum_Squares / values$Sum_Squares_total } else { aov_table$Eta2_partial <- aov_table$Sum_Squares / (aov_table$Sum_Squares + values$Sum_Squares_residuals) } } else if (type == "omega") { if (!isTRUE(partial)) { aov_table$Omega2 <- (aov_table$Sum_Squares - aov_table$df * values$Mean_Square_residuals) / (values$Sum_Squares_total + values$Mean_Square_residuals) } else { aov_table$Omega2_partial <- (aov_table$Sum_Squares - aov_table$df * values$Mean_Square_residuals) / (aov_table$Sum_Squares + (values$n - aov_table$df) * values$Mean_Square_residuals) } } else if (type == "epsilon") { if (!isTRUE(partial)) { aov_table$Epsilon2 <- (aov_table$Sum_Squares - aov_table$df * values$Mean_Square_residuals) / values$Sum_Squares_total } else { aov_table$Epsilon2_partial <- (aov_table$Sum_Squares - aov_table$df * values$Mean_Square_residuals) / (aov_table$Sum_Squares + values$Sum_Squares_residuals) } } out <- aov_table # Add CIs --- if (is.numeric(ci)) { # based on MBESS::ci.R2 ES <- pmax(0, out[[ncol(out)]]) f <- (ES / out$df) / ((1 - ES) / df_error) CI_tab <- # This really is a generic F_to_R2 F_to_eta2(f, out$df, df_error, ci = ci, alternative = alternative, verbose = verbose )[-1] out[c("CI", "CI_low", "CI_high")] <- CI_tab[c("CI", "CI_low", "CI_high")] } else { alternative <- NULL } # Clean up output --- out <- out[, colnames(out) %in% c( "Parameter", "Eta2", "Eta2_partial", "Eta2_generalized", "Omega2", "Omega2_partial", "Epsilon2", "Epsilon2_partial", if (!is.null(ci)) c("CI", "CI_low", "CI_high") ), drop = FALSE] rownames(out) <- NULL # Set attributes --- attr(out, "partial") <- partial attr(out, "generalized") <- generalized attr(out, "ci") <- ci attr(out, "anova_type") <- anova_type attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative out } #' @param DV_names A character vector with the names of all the predictors, #' including the grouping variable (e.g., `"Subject"`). #' #' @rdname effectsize_API #' @export .es_aov_strata <- function(aov_table, DV_names, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE) { type <- match.arg(type) aov_table <- as.data.frame(aov_table) # Clean up data --- if (!"Mean_Square" %in% colnames(aov_table)) { aov_table[["Mean_Square"]] <- aov_table[["Sum_Squares"]] / aov_table[["df"]] } if (!"Residuals" %in% aov_table$Parameter) { stop(insight::format_message("No residuals data found - cannot compute effect size.")) } # Include intercept? --- if (include_intercept) { values <- .values_aov(aov_table[aov_table$Parameter != "(Intercept)", ], group = TRUE) } else { aov_table <- aov_table[aov_table$Parameter != "(Intercept)", ] values <- .values_aov(aov_table, group = TRUE) } # Get all the correct SSs... --- aov_table <- aov_table[aov_table$Parameter != "Residuals", , drop = FALSE] Sum_Squares_total <- sum(sapply(values, "[[", "Sum_Squares_total")) Sum_Squares_residuals <- sapply(values[aov_table$Group], "[[", "Sum_Squares_residuals") Mean_Square_residuals <- sapply(values[aov_table$Group], "[[", "Mean_Square_residuals") df_residuals <- sapply(values[aov_table$Group], "[[", "df_residuals") ns <- sapply(values[aov_table$Group], "[[", "n") # Estimate effect size --- if (type == "eta") { if (isTRUE(generalized) || is.character(generalized)) { ## copied from afex obs <- logical(nrow(aov_table)) if (is.character(generalized)) { for (o in generalized) { oi <- grepl(paste0("\\b", o, "\\b"), aov_table$Parameter) if (!any(oi)) stop("Observed variable not in data: ", o, call. = FALSE) obs <- obs | oi } } obs_SSn1 <- sum(aov_table$Sum_Squares * obs) obs_SSn2 <- aov_table$Sum_Squares * obs aov_table$Eta2_generalized <- aov_table$Sum_Squares / (aov_table$Sum_Squares + sum(sapply(values, "[[", "Sum_Squares_residuals")) + obs_SSn1 - obs_SSn2) } else if (!isTRUE(partial)) { aov_table$Eta2 <- aov_table$Sum_Squares / Sum_Squares_total } else { aov_table$Eta2_partial <- aov_table$Sum_Squares / (aov_table$Sum_Squares + Sum_Squares_residuals) } } else if (type == "omega") { SSS_values <- values[[which(names(values) %in% DV_names)]] is_within <- !aov_table$Group %in% DV_names Sum_Squares_Subjects <- SSS_values$Sum_Squares_residuals Mean_Squares_Subjects <- SSS_values$Mean_Square_residuals # implemented from https://www.jasonfinley.com/tools/OmegaSquaredQuickRef_JRF_3-31-13.pdf/ if (!isTRUE(partial)) { aov_table$Omega2 <- (aov_table$Sum_Squares - aov_table$df * Mean_Square_residuals) / (Sum_Squares_total + Mean_Squares_Subjects) } else { aov_table$Omega2_partial <- (aov_table$Sum_Squares - aov_table$df * Mean_Square_residuals) / (aov_table$Sum_Squares + is_within * Sum_Squares_residuals + Sum_Squares_Subjects + Mean_Squares_Subjects) } } else if (type == "epsilon") { if (!isTRUE(partial)) { aov_table$Epsilon2 <- (aov_table$Sum_Squares - aov_table$df * Mean_Square_residuals) / Sum_Squares_total } else { aov_table$Epsilon2_partial <- (aov_table$Sum_Squares - aov_table$df * Mean_Square_residuals) / (aov_table$Sum_Squares + Sum_Squares_residuals) } } out <- aov_table # Add CIs --- if (!is.null(ci)) { # based on MBESS::ci.R2 ES <- pmax(0, out[[ncol(out)]]) f <- (ES / out$df) / ((1 - ES) / df_residuals) CI_tab <- # This really is a generic F_to_R2 F_to_eta2(f, out$df, df_residuals, ci = ci, alternative = alternative, verbose = verbose )[-1] out[c("CI", "CI_low", "CI_high")] <- CI_tab[c("CI", "CI_low", "CI_high")] } else { alternative <- NULL } # Clean up output --- out <- out[, colnames(out) %in% c( "Group", "Parameter", "Eta2", "Eta2_generalized", "Eta2_partial", "Omega2", "Omega2_partial", "Epsilon2", "Epsilon2_partial", if (!is.null(ci)) c("CI", "CI_low", "CI_high") ), drop = FALSE] rownames(out) <- NULL attr(out, "partial") <- partial attr(out, "generalized") <- generalized attr(out, "ci") <- ci attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative out } #' @rdname effectsize_API #' @export .es_aov_table <- function(aov_table, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE) { aov_table <- as.data.frame(aov_table) # Get correct function --- type <- match.arg(type) es_fun <- switch(type, eta = F_to_eta2, omega = F_to_omega2, epsilon = F_to_epsilon2) # Non-Partial / Generalized -> BAD --- if (verbose) { if (!isTRUE(partial)) warning( "Currently only supports partial ", type, " squared for this class of objects.", call. = FALSE ) if (isTRUE(generalized) || is.character(generalized)) warning( "generalized ", type, " squared ", "is not supported for this class of object.", call. = FALSE ) } # Turn ts to Fs (if needed) --- if (!"F" %in% colnames(aov_table)) if ("t" %in% colnames(aov_table)) { aov_table[["F"]] <- aov_table[["t"]]^2 aov_table[["df"]] <- 1 } else { stop(insight::format_message("ANOVA table does not have F values - cannot compute effect size.")) } # include_intercept? --- if (!include_intercept) aov_table <- aov_table[aov_table$Parameter != "(Intercept)", , drop = FALSE] ES_tab <- es_fun(aov_table[["F"]], aov_table[["df"]], aov_table[["df_error"]], ci = ci, alternative = alternative, verbose = verbose) out <- cbind(Parameter = aov_table[["Parameter"]], ES_tab) rownames(out) <- NULL # Set attributes --- attr(out, "partial") <- TRUE attr(out, "generalized") <- FALSE attr(out, "ci") <- if ("CI" %in% colnames(out)) ci attr(out, "alternative") <- if (!is.null(attr(out, "ci"))) alternative attr(out, "anova_type") <- NULL attr(out, "approximate") <- NULL out } # Clean up wrappers ------------------------------------------------------- #' @keywords internal .anova_es <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { UseMethod(".anova_es") } #' @keywords internal #' @importFrom stats anova .anova_es.default <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { .anova_es.anova( stats::anova(model), type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose ) } #' @keywords internal #' @importFrom parameters model_parameters #' @importFrom stats anova .anova_es.aov <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { if (!inherits(model, c("Gam", "anova"))) { # Pass to ANOVA table method res <- .anova_es.anova( stats::anova(model), type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) return(res) } params <- parameters::model_parameters(model, verbose = verbose, effects = "fixed") out <- .es_aov_simple(as.data.frame(params), type, partial, generalized, ci, alternative, verbose = verbose, ...) if (is.null(attr(out, "anova_type"))) attr(out, "anova_type") <- attr(params, "anova_type") out } .anova_es.lm <- .anova_es.aov .anova_es.glm <- .anova_es.aov .anova_es.manova <- .anova_es.aov #' @keywords internal #' @importFrom parameters model_parameters #' @importFrom insight find_predictors .anova_es.aovlist <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE, ...) { params <- parameters::model_parameters(model, verbose = verbose, effects = "fixed") anova_type <- attr(params, "anova_type") params <- as.data.frame(params) DV_names <- insight::find_predictors(model)[[1]] out <- .es_aov_strata( params, DV_names = DV_names, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, include_intercept = include_intercept ) attr(out, "anova_type") <- anova_type out } #' @keywords internal #' @importFrom stats aov #' @importFrom utils packageVersion .anova_es.mlm <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { model <- stats::aov(model) params <- parameters::model_parameters(model, verbose = verbose, effects = "fixed") anova_type <- attr(params, "anova_type") params <- split(params, factor(params$Response, levels = unique(params$Response))) # make sure row order is not changed params <- lapply(params, .es_aov_simple, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) params <- lapply(names(params), function(nm) { cbind(Response = nm, params[[nm]]) }) out <- do.call("rbind", params) rownames(out) <- NULL attr(out, "partial") <- attr(params[[1]], "partial") attr(out, "generalized") <- attr(params[[1]], "generalized") attr(out, "ci") <- attr(params[[1]], "ci", exact = TRUE) attr(out, "anova_type") <- anova_type attr(out, "approximate") <- FALSE attr(out, "alternative") <- if (is.numeric(attr(out, "ci"))) alternative out } .anova_es.maov <- .anova_es.mlm #' @keywords internal .anova_es.anova <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE, ...) { F.nm <- c("F value", "approx F", "F-value") df.nm <- c("NumDF", "num Df", "numDF", "npar") df_error.nm <- c("DenDF", "den Df", "denDF", "df_error") # If there is no df_error *or* is there IS a residuals row... if (!any(df_error.nm %in% colnames(model))) { # Pass to AOV method res <- .anova_es.aov(model, partial = partial, type = type, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, include_intercept = include_intercept, ... ) return(res) } # Clean up table --- par_table <- data.frame( Parameter = rownames(model), F = model[,F.nm[F.nm %in% colnames(model)]], df = model[,df.nm[df.nm %in% colnames(model)]], df_error = model[,df_error.nm[df_error.nm %in% colnames(model)]] ) par_table <- par_table[!par_table[["Parameter"]] %in% "Residuals",] out <- .es_aov_table( par_table, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, include_intercept = include_intercept ) attr(out, "anova_type") <- tryCatch(attr(parameters::model_parameters(model, verbose = FALSE, effects = "fixed"), "anova_type"), error = function(...) 1) attr(out, "approximate") <- TRUE out } #' @keywords internal .anova_es.anova.lme <- .anova_es.anova #' @importFrom stats na.omit #' @keywords internal .anova_es.parameters_model <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, by_response = TRUE, ...) { if (by_response && "Response" %in% colnames(model)) { out <- split(model, model[["Response"]]) out <- lapply(out, .anova_es.parameters_model, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, by_response = FALSE, ...) saved_attr <- attributes(out[[1]]) out <- mapply(out, names(out), FUN = function(x, nm) cbind(Response = nm, x), SIMPLIFY = FALSE) out <- do.call(rbind, out) # Set attributes --- attr(out, "partial") <- saved_attr$partial attr(out, "generalized") <- saved_attr$generalized attr(out, "ci") <- saved_attr$ci attr(out, "alternative") <- saved_attr$alternative attr(out, "anova_type") <- attr(model, "anova_type") attr(out, "approximate") <- saved_attr$approximate return(out) } approximate <- FALSE if ("Sum_Squares" %in% colnames(model) && "Residuals" %in% model[["Parameter"]]) { if ("Group" %in% colnames(model)) { DVs <- unlist(insight::find_predictors(.get_object(model))) out <- .es_aov_strata( model, DV_names = DVs, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) } else { out <- .es_aov_simple( model, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) } } else { out <- .es_aov_table( model, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) approximate <- TRUE } attr(out, "anova_type") <- attr(model, "anova_type") attr(out, "approximate") <- approximate out } # Specific models --------------------------------------------------------- #' @keywords internal .anova_es.htest <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...){ if (!grepl("One-way", model$method)) stop("'model' is not a one-way test!", call. = FALSE) if (verbose && (partial || isTRUE(generalized) || is.character(generalized))) { txt_type <- ifelse(isTRUE(generalized) || is.character(generalized), "generalized", "partial") message( "For one-way between subjects designs, ", txt_type, " ", type, " squared is equivalent to ", type, " squared.\n", "Returning ", type, " squared." ) } effectsize(model, type = type, ci = ci, alternative = alternative, verbose = verbose, ...) } #' @keywords internal .anova_es.Anova.mlm <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE, ...) { # Faking the model_parameters.aovlist output: suppressWarnings(aov_tab <- summary(model)$univariate.tests) aov_tab <- as.data.frame(unclass(aov_tab)) aov_tab$Parameter <- rownames(aov_tab) colnames(aov_tab)[colnames(aov_tab)== "Sum Sq"] <- "Sum_Squares" colnames(aov_tab)[colnames(aov_tab)== "num Df"] <- "df" aov_tab <- aov_tab[c("Parameter", "Sum_Squares","Error SS", "df", "den Df")] id <- "Subject" within <- names(model$idata) within <- lapply(within, function(x) c(NA, x)) within <- do.call(expand.grid, within) within <- apply(within, 1, na.omit) ns <- sapply(within, length) within <- sapply(within, paste, collapse = ":") within <- within[order(ns)] within <- Filter(function(x) nchar(x) > 0, within) l <- sapply(within, grepl, x = aov_tab$Parameter, simplify = TRUE) l <- apply(l, 1, function(x) if (!any(x)) 0 else max(which(x))) l <- c(NA, within)[l+1] l <- sapply(l, function(x) paste0(na.omit(c(id, x)), collapse = ":")) aov_tab$Group <- l aov_tab <- split(aov_tab, aov_tab$Group) aov_tab <- lapply(aov_tab, function (x) { x <- x[c(seq_len(nrow(x)), 1), ] x$Sum_Squares[nrow(x)] <- x[["Error SS"]][1] x$df[nrow(x)] <- x[["den Df"]][1] x$Parameter[nrow(x)] <- "Residuals" x }) aov_tab <- do.call(rbind, aov_tab) aov_tab[["Error SS"]] <- NULL aov_tab[["den Df"]] <- NULL aov_tab$`F` <- ifelse(aov_tab$Parameter == "Residuals", NA, 1) aov_tab$Mean_Square <- aov_tab$Sum_Squares/aov_tab$df DV_names <- c(id, setdiff(unlist(strsplit(model$terms, ":")), "(Intercept)")) out <- .es_aov_strata( aov_tab, DV_names = DV_names, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, include_intercept = include_intercept ) out$Group <- NULL # Reorder rows orig_terms <- model$terms if (include_intercept && !"(Intercept)" %in% orig_terms) { orig_terms <- c("(Intercept)", orig_terms) } else if (!include_intercept && "(Intercept)" %in% orig_terms) { orig_terms <- setdiff(orig_terms, "(Intercept)") } out <- out[match(out$Parameter, orig_terms),] attr(out, "anova_type") <- as.numeric(as.roman(model$type)) attr(out, "approximate") <- FALSE out } #' @keywords internal #' @importFrom stats anova #' @importFrom insight check_if_installed .anova_es.merMod <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { insight::check_if_installed("lmerTest") model <- lmerTest::as_lmerModLmerTest(model) model <- stats::anova(model) out <- .anova_es.anova( model, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, ... ) attr(out, "approximate") <- TRUE out } #' @keywords internal #' @importFrom stats anova .anova_es.gam <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { model <- stats::anova(model) p.table <- as.data.frame(model$pTerms.table) s.table <- as.data.frame(model$s.table) colnames(s.table)[colnames(s.table)=="Ref.df"] <- "df" s.table[setdiff(colnames(p.table), colnames(s.table))] <- NA p.table[setdiff(colnames(s.table), colnames(p.table))] <- NA tab <- rbind(p.table, s.table) colnames(tab)[colnames(tab)=="F"] <- "F-value" colnames(tab)[colnames(tab)=="df"] <- "npar" tab$df_error <- model$residual.df out <- .anova_es.anova( tab, type = type, generalized = generalized, partial = partial, ci = ci, alternative = alternative, verbose = verbose ) attr(out, "anova_type") <- 3 attr(out, "approximate") <- TRUE out } #' @keywords internal .anova_es.afex_aov <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = FALSE, ...) { type <- match.arg(type) if (type == "eta" && isTRUE(generalized) && length(attr(model$anova_table, "observed"))) { generalized <- attr(model$anova_table, "observed") } out <- .anova_es( model$Anova, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = FALSE, include_intercept = include_intercept, ... ) attr(out, "anova_type") <- attr(model, "type", exact = TRUE) attr(out, "approximate") <- FALSE out } #' @keywords internal .anova_es.mixed <- function(model, verbose = TRUE, include_intercept = FALSE, ...) { aov_tab <- as.data.frame(model[["anova_table"]]) if (!"F" %in% colnames(aov_tab)) { stop("Cannot estimate approx effect size for `mixed` type model - no F-statistic found.", call. = FALSE) } if (verbose && include_intercept) { warning("Cannot estimate (Intercept) effect size for `mixed` model.", call. = FALSE) } aov_tab$Parameter <- rownames(aov_tab) aov_tab$df <- aov_tab[["num Df"]] aov_tab$df_error <- aov_tab[["den Df"]] aov_tab <- aov_tab[, c("Parameter", "df", "df_error", "F")] out <- .es_aov_table(aov_tab, verbose = verbose, ...) attr(out, "anova_type") <- attr(model, "type") attr(out, "approximate") <- TRUE out } #' @keywords internal #' @importFrom stats anova .anova_es.rms <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { if (!inherits(model, "anova.rms")) { model <- stats::anova(model, test = "F") } i <- rownames(model) model <- as.data.frame(model) model$Parameter <- i colnames(model) <- gsub("d.f.", "df", colnames(model), fixed = TRUE) model$df_error <- model$df[rownames(model) == "ERROR"] model <- model[rownames(model) != "ERROR", ] out <- .es_aov_table( model, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, ... ) attr(out, "anova_type") <- 2 attr(out, "approximate") <- FALSE out } .anova_es.anova.rms <- .anova_es.rms #' @export .anova_es.model_fit <- function(model, type = c("eta", "omega", "epsilon"), partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, ...) { .anova_es( model$fit, type = type, partial = partial, generalized = generalized, ci = ci, alternative = alternative, verbose = verbose, ... ) }effectsize/R/interpret_bf.R0000644000175000017500000000555614132466117015562 0ustar nileshnilesh#' Interpret Bayes Factor (BF) #' #' @param bf Value or vector of Bayes factor (BF) values. #' @param rules Can be `"jeffreys1961"` (default), `"raftery1995"` or custom set #' of [rules()] (for the *absolute magnitude* of evidence). #' @param log Is the `bf` value `log(bf)`? #' @param include_value Include the value in the output. #' @inheritParams insight::format_bf #' #' @details Argument names can be partially matched. #' #' @section Rules: #' #' Rules apply to BF as ratios, so BF of 10 is as extreme as a BF of 0.1 (1/10). #' #' - Jeffreys (1961) (`"jeffreys1961"`; default) #' - **BF = 1** - No evidence #' - **1 < BF <= 3** - Anecdotal #' - **3 < BF <= 10** - Moderate #' - **10 < BF <= 30** - Strong #' - **30 < BF <= 100** - Very strong #' - **BF > 100** - Extreme. #' - Raftery (1995) (`"raftery1995"`) #' - **BF = 1** - No evidence #' - **1 < BF <= 3** - Weak #' - **3 < BF <= 20** - Positive #' - **20 < BF <= 150** - Strong #' - **BF > 150** - Very strong #' #' #' @examples #' interpret_bf(1) #' interpret_bf(c(5, 2)) #' @references #' - Jeffreys, H. (1961), Theory of Probability, 3rd ed., Oxford University #' Press, Oxford. #' #' - Raftery, A. E. (1995). Bayesian model selection in social research. #' Sociological methodology, 25, 111-164. #' #' - Jarosz, A. F., & Wiley, J. (2014). What are the odds? A practical guide to #' computing and reporting Bayes factors. The Journal of Problem Solving, 7(1), #' 2. #' #' @export interpret_bf <- function(bf, rules = "jeffreys1961", log = FALSE, include_value = FALSE, protect_ratio = TRUE, exact = TRUE) { if (log) bf <- exp(bf) if (any(bf < 0, na.rm = TRUE)) { warning("Negative BFs detected. These are not possible. Ignoring.") bf[bf < 0] <- NA } orig_bf <- bf dir <- ifelse(bf < 1, "against", ifelse(bf > 1, "in favour of", "against or in favour of")) bf <- exp(abs(log(bf))) rules <- .match.rules( rules, list( jeffreys1961 = rules(c(3, 10, 30, 100), c("anecdotal", "moderate", "strong", "very strong", "extreme"), name = "jeffreys1961" ), raftery1995 = rules(c(3, 20, 150), c("weak", "positive", "strong", "very strong"), name = "raftery1995" ) ) ) interpretation <- interpret(bf, rules) # Format text interpretation[] <- paste0(interpretation, " evidence") interpretation[orig_bf == 1] <- "no evidence" # Add value if asked for if (include_value) { interpretation[] <- paste0(interpretation, " (", insight::format_bf(orig_bf, protect_ratio = protect_ratio, exact = exact), ")") } # Add direction interpretation[] <- paste(interpretation[], dir) interpretation[is.na(orig_bf)] <- "" interpretation } effectsize/R/eta_squared_posterior.R0000644000175000017500000001154314132466117017473 0ustar nileshnilesh#' @param ss_function For Bayesian models, the function used to extract #' sum-of-squares. Uses [`anova()`] by default, but can also be `car::Anova()` #' for simple linear models. #' @param draws For Bayesian models, an integer indicating the number of draws #' from the posterior predictive distribution to return. Larger numbers take #' longer to run, but provide estimates that are more stable. #' #' @export #' @rdname eta_squared eta_squared_posterior <- function(model, partial = TRUE, generalized = FALSE, ss_function = stats::anova, draws = 500, verbose = TRUE, ...) { UseMethod("eta_squared_posterior") } #' @export #' @importFrom stats lm setNames #' @importFrom insight model_info find_formula get_predictors find_response check_if_installed eta_squared_posterior.stanreg <- function(model, partial = TRUE, generalized = FALSE, ss_function = stats::anova, draws = 500, verbose = TRUE, ...) { insight::check_if_installed("rstantools") mo_inf <- insight::model_info(model) if ((!mo_inf$is_linear) || mo_inf$is_multivariate) { stop("Computation of Eta Squared is only applicable to univariate linear models.") } if (partial && mo_inf$is_mixed) { if (verbose) { warning( "Bayesian Partial Eta Squared not supported for mixed models.\n", "Returning Eta Squared instead." ) } partial <- FALSE # would need to account for random effects if present. # Too hard right now. } if ((isTRUE(generalized) || is.character(generalized)) && mo_inf$is_mixed) { if (verbose) { warning( "Bayesian Generalized Eta Squared not supported for mixed models.\n", "Returning Eta Squared instead." ) } generalized <- FALSE } ## 1. get model data f <- insight::find_formula(model)$conditional X <- insight::get_predictors(model) resp_name <- insight::find_response(model) # test centered predictors if (verbose) .all_centered(X) ## 2. get ppd ppd <- rstantools::posterior_predict(model, draws = draws, # for rstanreg nsamples = draws ) # for brms ## 3. Compute effect size... if (verbose) { message("Simulating effect size... This can take a while...") } res <- apply(ppd, 1, function(r) { # sampled outcome + predictors temp_dat <- X temp_dat[[resp_name]] <- r # fit a simple linear model temp_fit <- stats::lm(f, temp_dat) # compute effect size ANOVA <- ss_function(temp_fit, ...) es <- eta_squared(ANOVA, ci = NULL, partial = partial, generalized = generalized) es <- stats::setNames( es[[if (partial) "Eta2_partial" else "Eta2"]], es$Parameter ) data.frame(t(es), check.names = FALSE) }) res <- do.call("rbind", res) attr(res, "partial") <- partial attr(res, "generalized") <- generalized return(res) } #' @export eta_squared_posterior.brmsfit <- eta_squared_posterior.stanreg #' @keywords internal #' @importFrom stats contrasts .all_centered <- function(X) { numeric <- sapply(X, inherits, what = c("numeric", "integer")) numerics <- colnames(X)[numeric] factors <- colnames(X)[!numeric] numerics_centered <- factors_centered <- logical(0) if (length(numerics)) { numerics_centered <- sapply( X[, numerics, drop = FALSE], function(xi) isTRUE(all.equal(mean(xi), 0)) ) } of <- options()$contrasts if (length(factors)) { factors_centered <- sapply(X[, factors, drop = FALSE], function(xi) { # if a contrast has negative and positive values, it is assumed to be one of: # "contr.sum", "contr.helmert", "contr.poly", "contr.bayes" (is.factor(xi) && (any(contrasts(xi) < 0) & any(contrasts(xi) > 0))) || # Or if it is not a factor, is the default method one of these? (!is.factor(xi) && all(of %in% c("contr.sum", "contr.poly", "contr.bayes", "contr.helmert"))) }) } if ((length(numerics_centered) && !all(numerics_centered)) || length(factors_centered) && !all(factors_centered)) { non_centered <- !c(numerics_centered, factors_centered) non_centered <- names(non_centered)[non_centered] warning( "Not all variables are centered:\n ", paste(non_centered, collapse = ", "), "\n Results might be bogus if involved in interactions...", call. = FALSE, immediate. = TRUE ) } return(invisible(NULL)) } effectsize/R/utils_values_aov.R0000644000175000017500000000211714170065645016454 0ustar nileshnilesh#' @keywords internal .values_aov <- function(params, group = FALSE) { # number of observations if (isTRUE(group)) { lapply(split(params, params$Group), function(.i) { N <- sum(.i$df) + 1 .prepare_values_aov(.i, N) }) } else { N <- sum(params$df) + 1 .prepare_values_aov(params, N) } } #' @keywords internal .prepare_values_aov <- function(params, N) { iResid <- params$Parameter == "Residuals" # get mean squared of residuals Mean_Square_residuals <- sum(params[iResid, "Mean_Square"]) # get sum of squares of residuals Sum_Squares_residuals <- sum(params[iResid, "Sum_Squares"]) # get total sum of squares Sum_Squares_total <- sum(params$Sum_Squares) # number of terms in model N_terms <- nrow(params) - 1 # df residuals df_residuals <- sum(params[iResid, "df"]) list( "Mean_Square_residuals" = Mean_Square_residuals, "Sum_Squares_residuals" = Sum_Squares_residuals, "Sum_Squares_total" = Sum_Squares_total, "n_terms" = N_terms, "n" = N, "df_residuals" = df_residuals ) } effectsize/R/interpret_cfa_fit.R0000644000175000017500000001771614173457233016573 0ustar nileshnilesh#' Interpret of indices of CFA / SEM goodness of fit #' #' Interpretation of indices of fit found in confirmatory analysis or structural #' equation modelling, such as RMSEA, CFI, NFI, IFI, etc. #' #' @param x vector of values, or an object of class `lavaan`. #' @param rules Can be `"default"` or custom set of [rules()]. #' @inheritParams interpret #' #' @inherit performance::model_performance.lavaan details #' @inherit performance::model_performance.lavaan references #' #' @details #' ## Indices of fit #' - **Chisq**: The model Chi-squared assesses overall fit and the discrepancy #' between the sample and fitted covariance matrices. Its p-value should be > #' .05 (i.e., the hypothesis of a perfect fit cannot be rejected). However, it #' is quite sensitive to sample size. #' #' - **GFI/AGFI**: The (Adjusted) Goodness of Fit is the proportion of variance #' accounted for by the estimated population covariance. Analogous to R2. The #' GFI and the AGFI should be > .95 and > .90, respectively. #' #' - **NFI/NNFI/TLI**: The (Non) Normed Fit Index. An NFI of 0.95, indicates the #' model of interest improves the fit by 95\% relative to the null model. The #' NNFI (also called the Tucker Lewis index; TLI) is preferable for smaller #' samples. They should be > .90 (Byrne, 1994) or > .95 (Schumacker & Lomax, #' 2004). #' #' - **CFI**: The Comparative Fit Index is a revised form of NFI. Not very #' sensitive to sample size (Fan, Thompson, & Wang, 1999). Compares the fit of a #' target model to the fit of an independent, or null, model. It should be > #' .90. #' #' - **RMSEA**: The Root Mean Square Error of Approximation is a #' parsimony-adjusted index. Values closer to 0 represent a good fit. It should #' be < .08 or < .05. The p-value printed with it tests the hypothesis that #' RMSEA is less than or equal to .05 (a cutoff sometimes used for good fit), #' and thus should be not significant. #' #' - **RMR/SRMR**: the (Standardized) Root Mean Square Residual represents the #' square-root of the difference between the residuals of the sample covariance #' matrix and the hypothesized model. As the RMR can be sometimes hard to #' interpret, better to use SRMR. Should be < .08. #' #' - **RFI**: the Relative Fit Index, also known as RHO1, is not guaranteed to #' vary from 0 to 1. However, RFI close to 1 indicates a good fit. #' #' - **IFI**: the Incremental Fit Index (IFI) adjusts the Normed Fit Index (NFI) #' for sample size and degrees of freedom (Bollen's, 1989). Over 0.90 is a good #' fit, but the index can exceed 1. #' #' - **PNFI**: the Parsimony-Adjusted Measures Index. There is no commonly #' agreed-upon cutoff value for an acceptable model for this index. Should be > #' 0.50. #' #' See the documentation for \code{\link[lavaan:fitmeasures]{fitmeasures()}}. #' #' #' ## What to report #' For structural equation models (SEM), Kline (2015) suggests that at a minimum #' the following indices should be reported: The model **chi-square**, the #' **RMSEA**, the **CFI** and the **SRMR**. #' #' @note When possible, it is recommended to report dynamic cutoffs of fit #' indices. See https://dynamicfit.app/cfa/. #' #' #' @examples #' interpret_gfi(c(.5, .99)) #' interpret_agfi(c(.5, .99)) #' interpret_nfi(c(.5, .99)) #' interpret_nnfi(c(.5, .99)) #' interpret_cfi(c(.5, .99)) #' interpret_rmsea(c(.07, .04)) #' interpret_srmr(c(.5, .99)) #' interpret_rfi(c(.5, .99)) #' interpret_ifi(c(.5, .99)) #' interpret_pnfi(c(.5, .99)) #' #' # Structural Equation Models (SEM) #' if (require("lavaan")) { #' structure <- " ind60 =~ x1 + x2 + x3 #' dem60 =~ y1 + y2 + y3 #' dem60 ~ ind60 " #' model <- lavaan::sem(structure, data = PoliticalDemocracy) #' interpret(model) #' } #' #' @references #' - Awang, Z. (2012). A handbook on SEM. Structural equation modeling. #' #' - Byrne, B. M. (1994). Structural equation modeling with EQS and EQS/Windows. #' Thousand Oaks, CA: Sage Publications. #' #' - Tucker, L. R., \& Lewis, C. (1973). The reliability coefficient for maximum #' likelihood factor analysis. Psychometrika, 38, 1-10. #' #' - Schumacker, R. E., \& Lomax, R. G. (2004). A beginner's guide to structural #' equation modeling, Second edition. Mahwah, NJ: Lawrence Erlbaum Associates. #' #' - Fan, X., B. Thompson, \& L. Wang (1999). Effects of sample size, estimation #' method, and model specification on structural equation modeling fit indexes. #' Structural Equation Modeling, 6, 56-83. #' #' - Kline, R. B. (2015). Principles and practice of structural equation #' modeling. Guilford publications. #' #' @export interpret_gfi <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.95), c("poor", "satisfactory"), name = "default", right = FALSE) ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_agfi <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.90), c("poor", "satisfactory"), name = "default", right = FALSE) ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_nfi <- function(x, rules = "byrne1994") { rules <- .match.rules( rules, list( byrne1994 = rules(c(0.90), c("poor", "satisfactory"), name = "byrne1994", right = FALSE), schumacker2004 = rules(c(0.95), c("poor", "satisfactory"), name = "schumacker2004", right = FALSE) ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_nnfi <- interpret_nfi #' @rdname interpret_gfi #' @export interpret_cfi <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.90), c("poor", "satisfactory"), name = "default", right = FALSE) ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_rmsea <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.05), c("satisfactory", "poor"), name = "default"), awang2012 = rules(c(0.05, 0.08), c("good", "satisfactory", "poor"), name = "awang2012") ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_srmr <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.08), c("satisfactory", "poor"), name = "default") ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_rfi <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.90), c("poor", "satisfactory"), name = "default", right = FALSE) ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_ifi <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.90), c("poor", "satisfactory"), name = "default", right = FALSE) ) ) interpret(x, rules) } #' @rdname interpret_gfi #' @export interpret_pnfi <- function(x, rules = "default") { rules <- .match.rules( rules, list( default = rules(c(0.50), c("poor", "satisfactory"), name = "default") ) ) interpret(x, rules) } # lavaan ------------------------------------------------------------------ #' @rdname interpret_gfi #' @export interpret.lavaan <- function(x, ...) { interpret(performance::model_performance(x, ...), ...) } #' @rdname interpret_gfi #' @export interpret.performance_lavaan <- function(x, ...) { mfits <- c("GFI", "AGFI", "NFI", "NNFI", "CFI", "RMSEA", "SRMR", "RFI", "IFI", "PNFI") mfits <- intersect(names(x), mfits) table <- lapply(mfits, function(ind_name) { .interpret_ind <- eval(parse(text = paste0("interpret_", tolower(ind_name)))) data.frame( Name = ind_name, Value = x[[ind_name]], Interpretation = .interpret_ind(x[[ind_name]]) ) }) do.call(rbind, table) } effectsize/R/interpret_pd.R0000644000175000017500000000262114132466117015564 0ustar nileshnilesh#' Interpret Probability of Direction (pd) #' #' @param pd Value or vector of probabilities of direction. #' @param rules Can be `"default"`, `"makowski2019"` or a custom set of #' [rules()]. #' @param ... Not directly used. #' #' @section Rules: #' #' - Default (i.e., equivalent to p-values) #' - **pd <= 0.975** - not significant #' - **pd > 0.975** - significant #' #' - Makowski et al. (2019) (`"makowski2019"`) #' - **pd <= 0.95** - uncertain #' - **pd > 0.95** - possibly existing #' - **pd > 0.97** - likely existing #' - **pd > 0.99** - probably existing #' - **pd > 0.999** - certainly existing #' #' @examples #' interpret_pd(.98) #' interpret_pd(c(.96, .99), rules = "makowski2019") #' @references #' - Makowski, D., Ben-Shachar, M. S., Chen, S. H., \& Lüdecke, D. (2019). Indices of effect existence and significance in the Bayesian framework. Frontiers in psychology, 10, 2767. #' #' @export interpret_pd <- function(pd, rules = "default", ...) { rules <- .match.rules( rules, list( default = rules(c(0.975), c("not significant", "significant"), name = "default", right = TRUE ), makowski2019 = rules(c(0.95, 0.97, 0.99, 0.999), c("uncertain", "possibly existing", "likely existing", "probably existing", "certainly existing"), name = "makowski2019", right = TRUE ) ) ) interpret(pd, rules) } effectsize/R/standardize.models.R0000644000175000017500000003626214174160416016666 0ustar nileshnilesh#' Re-fit a model with standardized data #' #' Performs a standardization of data (z-scoring) using #' [`datawizard::standardize()`] and then re-fits the model to the standardized #' data. #' \cr\cr #' Standardization is done by completely refitting the model on the standardized #' data. Hence, this approach is equal to standardizing the variables *before* #' fitting the model and will return a new model object. This method is #' particularly recommended for complex models that include interactions or #' transformations (e.g., polynomial or spline terms). The `robust` (default to #' `FALSE`) argument enables a robust standardization of data, based on the #' `median` and the `MAD` instead of the `mean` and the `SD`. #' #' @param x A statistical model. #' @param weights If `TRUE` (default), a weighted-standardization is carried out. #' @param include_response If `TRUE` (default), the response value will also be #' standardized. If `FALSE`, only the predictors will be standardized. #' - Note that for GLMs and models with non-linear link functions, the #' response value will not be standardized, to make re-fitting the model work. #' - If the model contains an [stats::offset()], the offset variable(s) will #' be standardized only if the response is standardized. If `two_sd = TRUE`, #' offsets are standardized by one-sd (similar to the response). #' - (For `mediate` models, the `include_response` refers to the outcome in #' the y model; m model's response will always be standardized when possible). #' @inheritParams datawizard::standardize #' #' @return A statistical model fitted on standardized data #' #' @details #' #' # Generalized Linear Models #' Standardization for generalized linear models (GLM, GLMM, etc) is done only #' with respect to the predictors (while the outcome remains as-is, #' unstandardized) - maintaining the interpretability of the coefficients (e.g., #' in a binomial model: the exponent of the standardized parameter is the OR of #' a change of 1 SD in the predictor, etc.) #' #' # Dealing with Factors #' `standardize(model)` or `standardize_parameters(model, method = "refit")` do #' *not* standardized categorical predictors (i.e. factors) / their #' dummy-variables, which may be a different behaviour compared to other R #' packages (such as \pkg{lm.beta}) or other software packages (like SPSS). To #' mimic such behaviours, either use `standardize_parameters(model, method = #' "basic")` to obtain post-hoc standardized parameters, or standardize the data #' with `datawizard::standardize(data, force = TRUE)` *before* fitting the #' model. #' #' # Transformed Variables #' When the model's formula contains transformations (e.g. `y ~ exp(X)`) the #' transformation effectively takes place after standardization (e.g., #' `exp(scale(X))`). Since some transformations are undefined for none positive #' values, such as `log()` and `sqrt()`, the releven variables are shifted (post #' standardization) by `Z - min(Z) + 1` or `Z - min(Z)` (respectively). #' #' #' @family standardize #' @examples #' model <- lm(Infant.Mortality ~ Education * Fertility, data = swiss) #' coef(standardize(model)) #' #' @importFrom stats update #' @importFrom insight get_data model_info find_response get_response find_weights get_weights #' @importFrom datawizard standardize #' @importFrom utils capture.output #' @export #' @aliases standardize_models #' @aliases standardize.models standardize.default <- function(x, robust = FALSE, two_sd = FALSE, weights = TRUE, verbose = TRUE, include_response = TRUE, ...) { if (is.null(m_info <- list(...)[["m_info"]])){ m_info <- insight::model_info(x) } data <- insight::get_data(x) if (insight::is_multivariate(x) && inherits(x, "brmsfit")) { stop("multivariate brmsfit models not supported.", "\nAs an alternative: you may standardize your data (and adjust your priors), and re-fit the model.", call. = FALSE ) } if (m_info$is_bayesian) { warning("Standardizing variables without adjusting priors may lead to bogus results unless priors are auto-scaled.", call. = FALSE, immediate. = TRUE ) } # =-=-=-= Z the RESPONSE? =-=-=-= # Some models have special responses that should not be standardized. This # includes: # - generalized linear models (counts, binomial, etc...) # - Survival models include_response <- include_response && .safe_to_standardize_response(m_info) resp <- NULL if (!include_response) { resp <- unique(c(insight::find_response(x), insight::find_response(x, combine = FALSE))) } else if (include_response && two_sd) { resp <- unique(c(insight::find_response(x), insight::find_response(x, combine = FALSE))) } # If there's an offset, don't standardize offset OR response offsets <- insight::find_offset(x) if (length(offsets)) { if (include_response) { if (verbose) { warning("Offset detected and will be standardized.", call. = FALSE) } if (two_sd) { # Treat offsets like responses - only standardize by 1 SD resp <- c(resp, offsets) offsets <- NULL } } else if (!include_response) { # Don't standardize offsets if not standardizing the response offsets <- NULL } } # =-=-=-= DO NOT Z WEIGHTING-VARIABLE =-=-=-= # because negative weights will cause errors in "update()" weight_variable <- insight::find_weights(x) if (!is.null(weight_variable) && !weight_variable %in% colnames(data) && "(weights)" %in% colnames(data)) { data$.missing_weight <- data[["(weights)"]] colnames(data)[ncol(data)] <- weight_variable weight_variable <- c(weight_variable, "(weights)") } # =-=-=-= DO NOT Z RANDOM-EFFECTS =-=-=-= random_group_factor <- insight::find_random(x, flatten = TRUE, split_nested = TRUE) # =-=-=-= WHICH YES, WHICH NO? SUMMARY =-=-=-= dont_standardize <- c(resp, weight_variable, random_group_factor) do_standardize <- setdiff(colnames(data), dont_standardize) # can't std data$var variables # TODO what about "with"? if (any(doller_vars <- grepl("(.*)\\$(.*)", do_standardize))) { doller_vars <- colnames(data)[doller_vars] warning("Unable to standardize variables evaluated in the environment (i.e., not in `data`).\n", "The following variables will not be standardizd:\n\t", paste0(doller_vars, collapse = ", "), call. = FALSE ) do_standardize <- setdiff(do_standardize, doller_vars) dont_standardize <- c(dont_standardize, doller_vars) } if (!length(do_standardize)) { warning("No variables could be standardized.", call. = FALSE) return(x) } # =-=-=-= STANDARDIZE! =-=-=-= w <- insight::get_weights(x, na_rm = TRUE) data_std <- datawizard::standardize(data[do_standardize], robust = robust, two_sd = two_sd, weights = if (weights) w, verbose = verbose) # if two_sd, it must not affect the response! if (include_response && two_sd) { data_std[resp] <- datawizard::standardize(data[resp], robust = robust, two_sd = FALSE, weights = if (weights) w, verbose = verbose) dont_standardize <- setdiff(dont_standardize, resp) } # =-=-=-= FIX LOG-SQRT VARIABLES! =-=-=-= # if we standardize log-terms, standardization will fail (because log of # negative value is NaN). Do some back-transformation here log_terms <- .log_terms(x, data_std) if (length(log_terms) > 0) { data_std[log_terms] <- lapply(data_std[log_terms], function(i) i - min(i, na.rm = TRUE) + 1) } # same for sqrt sqrt_terms <- .sqrt_terms(x, data_std) if (length(sqrt_terms) > 0) { data_std[sqrt_terms] <- lapply(data_std[sqrt_terms], function(i) i - min(i, na.rm = TRUE)) } if (verbose && length(c(log_terms, sqrt_terms))) { message("Formula contains log- or sqrt-terms. See help(\"standardize\") for how such terms are standardized.") } # =-=-=-= ADD BACK VARIABLES THAT WHERE NOT Z =-=-=-= if (length(dont_standardize)) { remaining_columns <- intersect(colnames(data), dont_standardize) data_std <- cbind(data[, remaining_columns, drop = FALSE], data_std) } # =-=-=-= UPDATE MODEL WITH STANDARDIZED DATA =-=-=-= tryCatch({ if (inherits(x, "brmsfit")) { text <- utils::capture.output(model_std <- stats::update(x, newdata = data_std)) } else if (inherits(x, "biglm")) { text <- utils::capture.output(model_std <- stats::update(x, moredata = data_std)) } else if (inherits(x, "mixor")) { data_std <- data_std[order(data_std[, random_group_factor, drop = FALSE]), ] text <- utils::capture.output(model_std <- stats::update(x, data = data_std)) } else { # DEFAULT METHOD text <- utils::capture.output(model_std <- stats::update(x, data = data_std)) } }, error = function(er) { stop("Unable to refit the model with standardized data.\n", "Failed with the following error:\n\"", er, "\b\"\n\n", "Try instead to standardize the data (standardize(data)) and refit the model manually.", call. = FALSE) }) model_std } # exceptions, models that cannot use the default-method -------------------- #' @export #' @importFrom utils capture.output #' @importFrom insight get_data #' @importFrom stats update standardize.mediate <- function(x, robust = FALSE, two_sd = FALSE, weights = TRUE, verbose = TRUE, include_response = TRUE, ...) { # models and data y <- x$model.y m <- x$model.m y_data <- insight::get_data(y) m_data <- insight::get_data(m) # std models and data y_std <- datawizard::standardize(y, robust = robust, two_sd = two_sd, weights = weights, verbose = verbose, include_response = include_response, ... ) m_std <- datawizard::standardize(m, robust = robust, two_sd = two_sd, weights = weights, verbose = verbose, include_response = TRUE, ... ) y_data_std <- insight::get_data(y_std) m_data_std <- insight::get_data(m_std) # fixed values covs <- x$covariates control.value <- x$control.value treat.value <- x$treat.value if (!is.null(covs)) { covs <- mapply(.rescale_fixed_values, covs, names(covs), SIMPLIFY = FALSE, MoreArgs = list( y_data = y_data, m_data = m_data, y_data_std = y_data_std, m_data_std = m_data_std ) ) if (verbose) message("covariates' values have been rescaled to their standardized scales.") } # if (is.numeric(y_data[[x$treat]]) || is.numeric(m_data[[x$treat]])) { # if (!(is.numeric(y_data[[x$treat]]) && is.numeric(m_data[[x$treat]]))) { # stop("'treat' variable is not of same type across both y and m models.", # "\nCannot consistently standardize.", call. = FALSE) # } # # temp_vals <- .rescale_fixed_values(c(control.value, treat.value), x$treat, # y_data = y_data, m_data = m_data, # y_data_std = y_data_std, m_data_std = m_data_std) # # control.value <- temp_vals[1] # treat.value <- temp_vals[2] # if (verbose) message("control and treatment values have been rescaled to their standardized scales.") # } if (verbose && !all(c(control.value, treat.value) %in% c(0, 1))) { warning("control and treat values are not 0 and 1, and have not been re-scaled.", "\nInterpret results with caution.", call. = FALSE ) } text <- utils::capture.output( model_std <- stats::update(x, model.y = y_std, model.m = m_std, # control.value = control.value, treat.value = treat.value covariates = covs ) ) model_std } # Cannot ------------------------------------------------------------------ #' @export standardize.wbm <- function(x, robust = FALSE, two_sd = FALSE, weights = TRUE, verbose = TRUE, ...) { stop(paste0("Standardization of parameters not possible for models of class '", class(x)[1], "'."), call. = FALSE) } #' @export standardize.Surv <- standardize.wbm #' @export standardize.clm2 <- standardize.wbm #' @export standardize.bcplm <- standardize.wbm #' @export standardize.wbgee <- standardize.wbm # helper ---------------------------- # Find log-terms inside model formula, and return "clean" term names #' @importFrom insight find_terms .log_terms <- function(model, data) { x <- insight::find_terms(model, flatten = TRUE) # log_pattern <- "^log\\((.*)\\)" log_pattern <- "(log\\(log|log|log1|log10|log1p|log2)\\(([^,\\+)]*).*" out <- trimws(gsub(log_pattern, "\\2", x[grepl(log_pattern, x)])) intersect(colnames(data), out) } # Find log-terms inside model formula, and return "clean" term names #' @importFrom insight find_terms .sqrt_terms <- function(model, data) { x <- insight::find_terms(model, flatten = TRUE) pattern <- "sqrt\\(([^,\\+)]*).*" out <- trimws(gsub(pattern, "\\1", x[grepl(pattern, x)])) intersect(colnames(data), out) } #' @keywords internal .safe_to_standardize_response <- function(info, verbose = TRUE) { if (is.null(info)) { if (verbose) { warning("Unable to varify if response should not be standardized.", "\nResponse will be standardized.", immediate. = TRUE, call. = FALSE) } return(TRUE) } # check if model has a response variable that should not be standardized. info$is_linear && !info$family == "inverse.gaussian" && !info$is_survival && !info$is_censored # # alternative would be to keep something like: # !info$is_count && # !info$is_ordinal && # !info$is_multinomial && # !info$is_beta && # !info$is_censored && # !info$is_binomial && # !info$is_survival # # And then treating response for "Gamma()" or "inverse.gaussian" similar to # # log-terms... } #' @keywords internal .rescale_fixed_values <- function(val, cov_nm, y_data, m_data, y_data_std, m_data_std) { if (cov_nm %in% colnames(y_data)) { temp_data <- y_data temp_data_std <- y_data_std } else { temp_data <- m_data temp_data_std <- m_data_std } datawizard::data_rescale(val, to = range(temp_data_std[[cov_nm]]), range = range(temp_data[[cov_nm]]) ) } effectsize/R/sd_pooled.R0000644000175000017500000000716014170065645015043 0ustar nileshnilesh#' Pooled Standard Deviation #' #' The Pooled Standard Deviation is a weighted average of standard deviations #' for two or more groups, *assumed to have equal variance*. It represents the #' common deviation among the groups, around each of their respective means. #' #' @inheritParams cohens_d #' @inheritParams stats::mad #' #' @details #' The standard version is calculated as: #' \deqn{\sqrt{\frac{\sum (x_i - \bar{x})^2}{n_1 + n_2 - 2}}}{sqrt(sum(c(x - mean(x), y - mean(y))^2) / (n1 + n2 - 2))} #' The robust version is calculated as: #' \deqn{1.4826 \times Median(|\left\{x - Median_x,\,y - Median_y\right\}|)}{mad(c(x - median(x), y - median(y)), constant = 1.4826)} #' #' @return Numeric, the pooled standard deviation. #' #' @examples #' sd_pooled(mpg ~ am, data = mtcars) #' mad_pooled(mtcars$mpg, factor(mtcars$am)) #' @seealso [cohens_d()] #' #' @export sd_pooled <- function(x, y = NULL, data = NULL, verbose = TRUE) { # This actually works, you must see if you want to keep this code. If you do, # following will work: # sd_pooled(mpg, hp, data = mtcars) # sd_pooled(x, y) # called from a different function, like cohens_d() # needs modification in in ".sd_pooled()" as well... # x1 <- try(expr = eval(x), silent = TRUE) # y1 <- try(expr = eval(y), silent = TRUE) # # if (inherits(x1, "try-error")) # x <- deparse(substitute(x), width.cutoff = 500) # else # x <- x1 # # if (inherits(y1, "try-error")) # y <- deparse(substitute(y), width.cutoff = 500) # else # y <- y1 .sd_pooled(x, y, data, robust = FALSE, verbose = verbose) } #' @rdname sd_pooled #' @export mad_pooled <- function(x, y = NULL, data = NULL, constant = 1.4826, verbose = TRUE) { .sd_pooled(x, y, data, robust = TRUE, verbose = verbose, constant = constant) } #' @importFrom stats mad sd as.formula ave .sd_pooled <- function(x, y = NULL, data = NULL, robust = FALSE, verbose = TRUE, constant = 1) { # Activate here for evaluation of arguments... # eval_args <- .evaluate_arguments(x, y, data) # out <- .get_data_2_samples(eval_args$x, eval_args$y, eval_args$data) out <- .get_data_2_samples(x, y, data, verbose) x <- na.omit(out$x) y <- na.omit(out$y) if (robust) { f <- constant center <- stats::median div <- stats::mad } else { n1 <- length(x) n2 <- length(y) f <- sqrt(c(n1 + n2 - 1) / c(n1 + n2 - 2)) center <- mean div <- stats::sd } div(c( x - stats::ave(x, FUN = center), y - stats::ave(y, FUN = center) )) * f } # .evaluate_arguments <- function(x, y, data) { # eval_x <- .evaluate_argument(x) # if (!is.null(eval_x$variable)) x <- eval_x$variable # if (!is.null(eval_x$data) && is.null(data)) data <- get(eval_x$data) # # eval_y <- .evaluate_argument(y) # if (!is.null(eval_y$variable)) y <- eval_y$variable # if (!is.null(eval_y$data) && is.null(data)) data <- get(eval_y$data) # # list(x = x, y = y, data = data) # } # .evaluate_argument <- function(arg) { # data_frame <- NULL # if (!is.null(arg)) { # if (is.numeric(arg) && length(arg) > 1) { # # do nothiung # } else if (arg == "NULL") { # arg <- NULL # } else if (grepl("~", arg, fixed = TRUE)) { # arg <- stats::as.formula(arg) # } else if (grepl("\"", arg, fixed = TRUE)) { # arg <- gsub("\"", "", arg, fixed = TRUE) # } else if (grepl("$", arg, fixed = TRUE)) { # data_frame <- gsub("(.*)\\$(.*)", "\\1", arg) # arg <- gsub("(.*)\\$(.*)", "\\2", arg) # } # } # list(variable = arg, data = data_frame) # } effectsize/R/docs_extra.R0000644000175000017500000001761714170072541015227 0ustar nileshnilesh#' Confidence (Compatibility) Intervals #' #' More information regarding Confidence (Compatibiity) Intervals and how #' they are computed in *effectsize*. #' #' @section Confidence (Compatibility) Intervals (CIs): #' Unless stated otherwise, confidence (compatibility) intervals (CIs) are #' estimated using the noncentrality parameter method (also called the "pivot #' method"). This method finds the noncentrality parameter ("*ncp*") of a #' noncentral *t*, *F*, or \eqn{\chi^2} distribution that places the observed #' *t*, *F*, or \eqn{\chi^2} test statistic at the desired probability point of #' the distribution. For example, if the observed *t* statistic is 2.0, with 50 #' degrees of freedom, for which cumulative noncentral *t* distribution is *t* = #' 2.0 the .025 quantile (answer: the noncentral *t* distribution with *ncp* = #' .04)? After estimating these confidence bounds on the *ncp*, they are #' converted into the effect size metric to obtain a confidence interval for the #' effect size (Steiger, 2004). #' \cr\cr #' For additional details on estimation and troubleshooting, see [effectsize_CIs]. #' #' @section CIs and Significance Tests: #' "Confidence intervals on measures of effect size convey all the information #' in a hypothesis test, and more." (Steiger, 2004). Confidence (compatibility) #' intervals and p values are complementary summaries of parameter uncertainty #' given the observed data. A dichotomous hypothesis test could be performed #' with either a CI or a p value. The 100 (1 - \eqn{\alpha})% confidence #' interval contains all of the parameter values for which *p* > \eqn{\alpha} #' for the current data and model. For example, a 95% confidence interval #' contains all of the values for which p > .05. #' \cr\cr #' Note that a confidence interval including 0 *does not* indicate that the null #' (no effect) is true. Rather, it suggests that the observed data together with #' the model and its assumptions combined do not provided clear evidence against #' a parameter value of 0 (same as with any other value in the interval), with #' the level of this evidence defined by the chosen \eqn{\alpha} level (Rafi & #' Greenland, 2020; Schweder & Hjort, 2016; Xie & Singh, 2013). To infer no #' effect, additional judgments about what parameter values are "close enough" #' to 0 to be negligible are needed ("equivalence testing"; Bauer & Kiesser, #' 1996). #' #' @section One-Sided CIs: #' Typically, CIs are constructed as two-tailed intervals, with an equal #' proportion of the cumulative probability distribution above and below the #' interval. CIs can also be constructed as *one-sided* intervals, #' giving only a lower bound or upper bound. This is analogous to computing a #' 1-tailed *p* value or conducting a 1-tailed hypothesis test. #' \cr\cr #' Significance tests conducted using CIs (whether a value is inside the interval) #' and using *p* values (whether p < alpha for that value) are only guaranteed #' to agree when both are constructed using the same number of sides/tails. #' \cr\cr #' Most effect sizes are not bounded by zero (e.g., *r*, *d*, *g*), and as such #' are generally tested using 2-tailed tests and 2-sided CIs. #' \cr\cr #' Some effect sizes are strictly positive--they do have a minimum value, of 0. #' For example, \eqn{R^2}, \eqn{\eta^2}, and other variance-accounted-for effect #' sizes, as well as Cramer's *V* and multiple *R*, range from 0 to 1. These #' typically involve *F*- or \eqn{\chi^2}-statistics and are generally tested #' using *1-tailed* tests which test whether the estimated effect size is #' *larger* than the hypothesized null value (e.g., 0). In order for a CI to #' yield the same significance decision it must then by a *1-sided* CI, #' estimating only a lower bound. This is the default CI computed by #' *effectsize* for these effect sizes, where `alternative = "greater"` is set. #' \cr\cr #' This lower bound interval indicates the smallest effect size that is not #' significantly different from the observed effect size. That is, it is the #' minimum effect size compatible with the observed data, background model #' assumptions, and \eqn{\alpha} level. This type of interval does not indicate #' a maximum effect size value; anything up to the maximum possible value of the #' effect size (e.g., 1) is in the interval. #' \cr\cr #' One-sided CIs can also be used to test against a maximum effect size value #' (e.g., is \eqn{R^2} significantly smaller than a perfect correlation of 1.0?) #' can by setting `alternative = "less"`. This estimates a CI with only an #' *upper* bound; anything from the minimum possible value of the effect size #' (e.g., 0) up to this upper bound is in the interval. #' \cr\cr #' We can also obtain a 2-sided interval by setting `alternative = "two-sided"`. #' These intervals can be interpreted in the same way as other 2-sided #' intervals, such as those for *r*, *d*, or *g*. #' \cr\cr #' An alternative approach to aligning significance tests using CIs and 1-tailed #' *p* values that can often be found in the literature is to construct a #' 2-sided CI at a lower confidence level (e.g., 100(1-2\eqn{\alpha})% = 100 - #' 2*5% = 90%. This estimates the lower bound and upper bound for the above #' 1-sided intervals simultaneously. These intervals are commonly reported when #' conducting **equivalence tests**. For example, a 90% 2-sided interval gives #' the bounds for an equivalence test with \eqn{\alpha} = .05. However, be aware #' that this interval does not give 95% coverage for the underlying effect size #' parameter value. For that, construct a 95% 2-sided CI. #' #' ```{r} #' data("hardlyworking") #' fit <- lm(salary ~ n_comps + age, data = hardlyworking) #' eta_squared(fit) # default, ci = 0.95, alternative = "greater" #' eta_squared(fit, alternative = "less") # Test is eta is smaller than some value #' eta_squared(fit, alternative = "two.sided") # 2-sided bounds for alpha = .05 #' eta_squared(fit, ci = 0.9, alternative = "two.sided") # both 1-sided bounds for alpha = .05 #' ``` #' #' @section CI Does Not Contain the Estimate: #' For very large sample sizes or effect sizes, the width of the CI can be #' smaller than the tolerance of the optimizer, resulting in CIs of width 0. #' This can also result in the estimated CIs excluding the point estimate. #' #' For example: #' ```{r} #' t_to_d(80, df_error = 4555555) #' ``` #' #' In these cases, consider an alternative optimizer, or an alternative method #' for computing CIs, such as the bootstrap. #' #' #' @references #' Bauer, P., & Kieser, M. (1996). #' A unifying approach for confidence intervals and testing of equivalence and difference. #' _Biometrika, 83_(4), 934-–937. #' \doi{10.1093/biomet/83.4.934} #' #' Rafi, Z., & Greenland, S. (2020). #' Semantic and cognitive tools to aid statistical science: Replace confidence and significance by compatibility and surprise. #' _BMC Medical Research Methodology, 20_(1), Article 244. #' \doi{10.1186/s12874-020-01105-9} #' #' Schweder, T., & Hjort, N. L. (2016). #' _Confidence, likelihood, probability: Statistical inference with confidence distributions._ #' Cambridge University Press. #' \doi{10.1017/CBO9781139046671} #' #' Steiger, J. H. (2004). #' Beyond the _F_ test: Effect size confidence intervals and tests of close fit in the analysis of variance and contrast analysis. #' _Psychological Methods, 9_(2), 164--182. #' \doi{10.1037/1082-989x.9.2.164} #' #' Xie, M., & Singh, K. (2013). #' Confidence distribution, the frequentist distribution estimator of a parameter: A review. #' _International Statistical Review, 81_(1), 3–-39. #' \doi{10.1111/insr.12000} #' #' @rdname effectsize_CIs #' @name effectsize_CIs NULL #' `effectsize` API #' #' Read the [*Support functions for model extensions*](https://easystats.github.io/effectsize/articles/effectsize_API.html) vignette. #' #' @rdname effectsize_API #' @name effectsize_API NULLeffectsize/R/convert_between_OR_to_RR.R0000644000175000017500000000553614170065645017776 0ustar nileshnilesh#' Convert between Odds ratios and Risk ratios #' #' @param OR,RR Risk ratio of `p1/p0` or Odds ratio of `odds(p1)/odds(p0)`, #' possibly log-ed. `OR` can also be a logistic regression model. #' @param p0 Baseline risk #' @param ... Arguments passed to and from other methods. #' @inheritParams oddsratio_to_d #' #' @return Converted index, or if `OR` is a logistic regression model, a #' parameter table with the converted indices. #' #' @family convert between effect sizes #' #' @examples #' p0 <- 0.4 #' p1 <- 0.7 #' #' (OR <- probs_to_odds(p1) / probs_to_odds(p0)) #' (RR <- p1 / p0) #' #' riskratio_to_oddsratio(RR, p0 = p0) #' oddsratio_to_riskratio(OR, p0 = p0) #' #' m <- glm(am ~ factor(cyl), data = mtcars, #' family = binomial()) #' oddsratio_to_riskratio(m) #' @references #' #' Grant, R. L. (2014). Converting an odds ratio to a range of plausible #' relative risks for better communication of research findings. Bmj, 348, #' f7450. #' #' @export oddsratio_to_riskratio <- function(OR, p0, log = FALSE, ...) { UseMethod("oddsratio_to_riskratio") } #' @export oddsratio_to_riskratio.numeric <- function(OR, p0, log = FALSE, ...) { if (log) OR <- exp(OR) RR <- OR / (1 - p0 + (p0 * OR)) if (log) RR <- log(RR) return(RR) } #' @export oddsratio_to_riskratio.default <- function(OR, p0, log = FALSE, ...) { mi <- insight::model_info(OR) if (!mi$is_binomial || !mi$is_logit) stop("Model must a binomial model with logit-link (logistic regression)") RR <- parameters::model_parameters(OR, exponentiate = !log, effects = "fixed", ...) RR$SE <- NULL RR$z <- NULL RR$df_error <- NULL RR$p <- NULL if (used_intercept <- missing(p0)) { p0 <- RR[["Coefficient"]][RR$Parameter == "(Intercept)"] if (!log) p0 <- log(p0) p0 <- plogis(p0) warning("'p0' not provided.", "RR is relative to the intercept (p0 = ", insight::format_value(p0), ") - make sure your intercept is meaningful.") } RR[,colnames(RR) %in% c("Coefficient", "CI_low", "CI_high")] <- lapply(RR[,colnames(RR) %in% c("Coefficient", "CI_low", "CI_high")], oddsratio_to_riskratio, p0 = p0, log = log) if (any(c("CI_low", "CI_high") %in% colnames(RR))) { warning("CIs are back-transformed from the logit scale.") } RR[RR$Parameter=="(Intercept)", "Coefficient"] <- p0 RR[RR$Parameter=="(Intercept)", c("CI_low", "CI_high")] <- NA if (!used_intercept) { RR[RR$Parameter=="(Intercept)", "Parameter"] <- "(p0)" } attr(RR, "coefficient_name") <- if (log) "Log-RR" else "Risk Ratio" return(RR) } #' @rdname oddsratio_to_riskratio #' @export riskratio_to_oddsratio <- function(RR, p0, log = FALSE) { if (log) RR <- exp(RR) OR <- RR * (1 - p0) / (1 - RR * p0) if (log) OR <- log(OR) return(OR) } effectsize/R/effectsize.htest.R0000644000175000017500000001506114174160423016341 0ustar nileshnilesh#' @export #' @rdname effectsize effectsize.htest <- function(model, type = NULL, verbose = TRUE, ...) { # Get data? data <- insight::get_data(model) approx <- is.null(data) dots <- list(...) if (grepl("t-test", model$method)) { # t-test ---- if (is.null(type)) type <- "d" dots$alternative <- model$alternative dots$ci <- attr(model$conf.int,"conf.level") dots$mu <- model$null.value if (approx) { if (type == "cles") { stop("Unable to retrieve data from htest object. Cannot compute CLES.") } else if (verbose) { warning("Unable to retrieve data from htest object. Using t_to_d() approximation.") } f <- t_to_d args <- list( t = unname(model$statistic), df_error = unname(model$parameter), paired = !grepl("Two", model$method) ) } else { if (grepl(" by ", model$data.name, fixed = TRUE)) { data[[2]] <- factor(data[[2]]) } f <- switch(tolower(type), d = , cohens_d = cohens_d, g = , hedges_g = hedges_g, cles = cles ) args <- list( x = data[[1]], y = if (ncol(data) == 2) data[[2]], paired = !grepl("Two", model$method), pooled_sd = !grepl("Welch", model$method), verbose = verbose ) if (type == "cles") { if (args$paired || !args$pooled_sd) { stop("Common language effect size only applicable to 2-sample Cohen's d with pooled SD.") } args$pooled_sd <- args$paired <- NULL } } out <- do.call(f, c(args, dots)) attr(out, "approximate") <- approx return(out) } else if (grepl("Pearson's Chi-squared", model$method) || grepl("Chi-squared test for given probabilities", model$method)) { # Chisq ---- if (is.null(type)) type <- "cramers_v" if (grepl("(c|v|w|phi)$", tolower(type))) { f <- switch(tolower(type), v = , cramers_v = chisq_to_cramers_v, w = , cohens_w = , phi = chisq_to_phi, c = , pearsons_c = chisq_to_pearsons_c ) Obs <- model$observed Exp <- model$expected if (!is.null(dim(Exp))) { if (any(c(colSums(Obs), rowSums(Obs)) == 0L)) { stop("Cannot have empty rows/columns in the contingency tables.", call. = FALSE) } nr <- nrow(Obs) nc <- ncol(Obs) } else { nr <- length(Obs) nc <- 1 } out <- f( chisq = .chisq(Obs, Exp), n = sum(Obs), nrow = nr, ncol = nc, ... ) attr(out, "approximate") <- FALSE return(out) } else { f <- switch(tolower(type), or = , oddsratio = oddsratio, rr = , riskratio = riskratio, h = , cohens_h = cohens_h ) } out <- f(x = model$observed, ...) return(out) } else if (grepl("One-way", model$method)) { # one way anove ---- if ((approx <- grepl("not assuming", model$method, fixed = TRUE)) && verbose) { warning("`var.equal = FALSE` - effect size is an approximation.", call. = FALSE) } if (is.null(type)) type <- "eta" f <- switch(tolower(type), eta = , eta2 = , eta_squared = F_to_eta2, epsilon = , epsilon2 = , epsilon_squared = F_to_epsilon2, omega = , omega2 = , omega_squared = F_to_omega2, f = , cohens_f = F_to_f, f2 = , f_squared = , cohens_f2 = F_to_f2 ) out <- f( f = model$statistic, df = model$parameter[1], df_error = model$parameter[2], ... ) colnames(out)[1] <- sub("_partial", "", colnames(out)[1]) attr(out, "approximate") <- approx return(out) } else if (grepl("McNemar", model$method)) { # McNemar ---- if (approx) { stop("Unable to retrieve data from htest object.", "\nTry using 'cohens_g()' directly.", call. = FALSE ) } if (inherits(data, "table")) { out <- cohens_g(data, ...) } else { out <- cohens_g(data[[1]], data[[2]], ...) } return(out) } else if (grepl("Wilcoxon", model$method)) { # Wilcoxon ---- if (is.null(type)) type <- "rb" dots$alternative <- model$alternative dots$ci <- attr(model$conf.int,"conf.level") dots$mu <- model$null.value if (approx) { stop("Unable to retrieve data from htest object.", "\nTry using",ifelse(type=="cles", "'cles()'", "'rank_biserial()'")," directly.", call. = FALSE ) } f <- switch(tolower(type), r = , rb = , rbs = , r_rank_biserial = , rank_biserial = rank_biserial, cles = cles ) args <- list( x = data[[1]], y = if (ncol(data) == 2) data[[2]], paired = grepl("signed rank", model$method, fixed = TRUE) ) if (type == "cles") { if (args$paired) { stop("Common language effect size only applicable to 2-sample rank-biserial correlation.") } args$paired <- NULL args$parametric <- FALSE } out <- do.call(f, c(args, dots)) return(out) } else if (grepl("Kruskal-Wallis", model$method)) { # Kruskal-Wallis ---- if (approx) { stop("Unable to retrieve data from htest object.", "\nTry using 'rank_epsilon_squared()' directly.", call. = FALSE ) } if (inherits(data, "list")) { out <- rank_epsilon_squared(data, ...) } else { # data frame out <- rank_epsilon_squared(data[[1]], data[[2]], ...) } return(out) } else if (grepl("Friedman", model$method)) { # Friedman ---- if (approx) { stop("Unable to retrieve data from htest object.", "\nTry using 'kendalls_w()' directly.", call. = FALSE ) } if (inherits(data, "table")) { data <- as.data.frame(data)[c("Freq", "Var2", "Var1")] } out <- kendalls_w(data[[1]], data[[2]], data[[3]], ...) return(out) } else { # Other ---- if (verbose) warning("This 'htest' method is not (yet?) supported.\n", "Returning 'parameters::model_parameters(model)'.", call. = FALSE) parameters::model_parameters(model, verbose = verbose, ...) } } #' @keywords internal .chisq <- function(Obs, Exp) { sum(((Obs - Exp)^2) / Exp) } effectsize/R/print.effectsize_table.R0000644000175000017500000001423714170065645017527 0ustar nileshnilesh#' Methods for `effectsize` tables #' #' Printing, formatting and plotting methods for `effectsize` tables. #' #' @param x Object to print. #' @inheritParams insight::format_value #' @param ... Arguments passed to or from other functions. #' #' @export print.effectsize_table <- function(x, digits = 2, ...) { x_orig <- x footer <- attr(x, "table_footer") if (!is.null(alt <- attr(x, "alternative")) && alt != "two.sided") { ci_footer <- sprintf( "\n- One-sided CIs: %s bound fixed at (%s).", if (alt == "less") "lower" else "upper", as.character(if (alt == "less") x$CI_low[1] else x$CI_high[1]) ) footer <- c(footer, list(c(ci_footer, "cyan"))) } # if (isTRUE(attr(x, "approximate"))) { # footer <- c(footer, list(c("\n- Effect size is approximated.", "cyan"))) # } attr(x, "table_footer") <- footer x <- format(x, digits = digits) cat(insight::export_table(x, digits = digits, ...)) invisible(x_orig) } #' @rdname print.effectsize_table #' @export format.effectsize_table <- function(x, digits = 2, ...) { i <- is_effectsize_name(colnames(x)) labs <- get_effectsize_label(colnames(x)) colnames(x)[i] <- labs[i] attr(x, "ci") <- NULL attr(x, "ci_method") <- NULL out <- insight::format_table(x, digits = digits, ci_digits = digits, preserve_attributes = TRUE, ...) if (!is.null(rule_name <- attr(x, "rule_name", exact = TRUE))) { attr(out, "table_footer") <- c( attr(out, "table_footer"), list(c(paste0("\n(Interpretation rule: ", rule_name, ")"), "blue")) ) } out } # Print Methods -------------------------------------------------------- #' @export print.effectsize_std_params <- function(x, digits = 2, ...) { x_orig <- x footer <- caption <- subtitle <- NULL caption <- c(sprintf("# Standardization method: %s", attr(x, "std_method")), "blue") # robust / two_sd if (attr(x, "two_sd") || attr(x, "robust")) { footer <- sprintf( "\n- Scaled by %s %s.\n", ifelse(attr(x, "two_sd"), "two", "one"), ifelse(attr(x, "robust"), "MAD(s) from the median", "SD(s) from the mean") ) footer <- c(footer, "cyan") } # include_response if (!attr(x, "include_response")) { msg <- "(Response is unstandardized)\n" if (length(footer)) { footer[1] <- paste0(footer[1], msg) } else { footer <- paste0("\n", msg) footer <- c(footer, "cyan") } } attr(x, "table_footer") <- footer attr(x, "table_caption") <- caption attr(x, "table_subtitle") <- subtitle print.effectsize_table(x, digits = digits, ...) invisible(x_orig) } #' @export print.equivalence_test_effectsize <- function(x, digits = 2, ...) { x_orig <- x caption <- footer <- subtitle <- NULL ## Title (caption) if (attr(x, "rule", exact = TRUE) == "cet") { caption <- "# Conditional Test for Practical Equivalence\n" } else { caption <- "# Test for Practical Equivalence\n" } caption <- c(caption, "blue") ## Rope range .rope <- attr(x, "rope", exact = TRUE) subtitle <- sprintf("\tROPE: [%.*f %.*f]", digits, .rope[1], digits, .rope[2]) ## ROPE_Equivalence if (attr(x, "rule", exact = TRUE) == "bayes") { footer <- c("\n(Using Bayesian guidlines)", "green") } attr(x, "table_footer") <- footer attr(x, "table_caption") <- caption attr(x, "table_subtitle") <- subtitle print.effectsize_table(x, digits = digits, ...) invisible(x_orig) } #' @export #' @rdname print.effectsize_table #' @param append_CLES Should the Common Language Effect Sizes be printed as well? #' Only applicable to Cohen's *d*, Hedges' *g* for independent samples of #' equal variance (pooled sd) or for the rank-biserial correlation for #' independent samples (See [d_to_cles()]) print.effectsize_difference <- function(x, digits = 2, append_CLES = FALSE, ...) { x_orig <- x footer <- caption <- subtitle <- NULL ## Add footer mu <- attr(x, "mu") if (mu != 0) { mu <- sprintf("\n- Deviation from a difference of %s.", mu) footer <- c(footer, list(c(mu, "cyan"))) } if (!is.null(sd_type <- attr(x, "pooled_sd", exact = TRUE))) { sd_type <- sprintf( "\n- Estimated using %s.", ifelse(sd_type, "pooled SD", "un-pooled SD") ) footer <- c(footer, list(c(sd_type, "cyan"))) } attr(x, "table_footer") <- footer attr(x, "table_caption") <- caption attr(x, "table_subtitle") <- subtitle print.effectsize_table(x, digits = digits, ...) if (append_CLES) { if ("r_rank_biserial" %in% colnames(x_orig)) { to_cl_coverter <- rb_to_cles } else { to_cl_coverter <- d_to_cles } tryCatch({ CL <- to_cl_coverter(x_orig) attr(CL, "table_caption") <- c("\n\n# Common Language Effect Sizes", "blue") print(CL, digits = digits) }, error = function(...) invisible(NULL)) } invisible(x_orig) } #' @export #' @importFrom utils as.roman print.effectsize_anova <- function(x, digits = 2, ...) { x_orig <- x footer <- caption <- subtitle <- NULL ## Title (caption) anova_type <- attr(x, "anova_type", exact = TRUE) if (is.null(anova_type) || is.na(anova_type)) { caption <- "# Effect Size for ANOVA" } else { caption <- paste0("# Effect Size for ANOVA (Type ", utils::as.roman(anova_type), ")") } caption <- c(caption, "blue") ## Footer obs <- attr(x, "generalized") if (is.character(obs) || isTRUE(obs)) { if (isTRUE(obs)) { footer <- "\n- Observed variables: All" } else { footer <- paste0("\n- Observed variables: ", paste0(obs, collapse = ", ")) } footer <- list(c(footer, "cyan")) } attr(x, "table_footer") <- footer attr(x, "table_caption") <- caption attr(x, "table_subtitle") <- subtitle print.effectsize_table(x, digits = digits, ...) invisible(x_orig) } # Format Methods -------------------------------------------------------- #' @export format.equivalence_test_effectsize <- function(x, digits = 2, ...) { colnames(x)[colnames(x) == "ROPE_Equivalence"] <- "H0" format.effectsize_table(x, digits = digits, ...) } effectsize/R/convert_between_anova.R0000644000175000017500000000346014132466117017444 0ustar nileshnilesh#' Convert between ANOVA effect sizes #' #' @param es Any measure of variance explained such as Eta-, Epsilon-, Omega-, #' or R-Squared, partial or otherwise. See details. #' @param f,f2 Cohen's *f* or *f*-squared. #' #' @details #' Any measure of variance explained can be converted to a corresponding Cohen's #' *f* via: #' \cr\cr #' \deqn{f^2 = \frac{\eta^2}{1 - \eta^2}} #' \cr\cr #' \deqn{\eta^2 = \frac{f^2}{1 + f^2}} #' \cr\cr #' If a partial Eta-Squared is used, the resulting Cohen's *f* is a #' partial-Cohen's *f*; If a less biased estimate of variance explained is used #' (such as Epsilon- or Omega-Squared), the resulting Cohen's *f* is likewise a #' less biased estimate of Cohen's *f*. #' #' @seealso [eta_squared()] for more details. #' @family convert between effect sizes #' #' @references #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences #' (2nd Ed.). New York: Routledge. #' #' - Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals #' and tests of close fit in the analysis of variance and contrast analysis. #' Psychological Methods, 9, 164-182. #' #' @export eta2_to_f2 <- function(es) { es / (1 - es) } #' @export #' @rdname eta2_to_f2 eta2_to_f <- function(es) { sqrt(eta2_to_f2(es)) } #' @export #' @rdname eta2_to_f2 f2_to_eta2 <- function(f2) { f2 / (1 + f2) } #' @export #' @rdname eta2_to_f2 f_to_eta2 <- function(f) { f2_to_eta2(f^2) } # eta2_to_F <- function(eta2, df, df_error, ...) { # eta2 * df_error / ((1 - eta2) * df) # } # # # f_to_omega2 <- function(f, df, df_error, ci = 0.9, ...) { # eta2 <- f_to_eta2(f) # fvalue <- eta2_to_F(eta2, df, df_error) # F_to_omega2( # f = fvalue, # df = df, # df_error = df_error, # ci = ci # ) # } effectsize/R/interpret_omega_squared.R0000644000175000017500000000341114170302654017770 0ustar nileshnilesh#' Interpret ANOVA effect size #' #' @param es Value or vector of eta / omega / epsilon squared values. #' @param rules Can be `"field2013"` (default), `"cohen1992"` or custom set of [rules()]. #' @param ... Not used for now. #' #' @section Rules: #' #' - Field (2013) (`"field2013"`; default) #' - **ES < 0.01** - Very small #' - **0.01 <= ES < 0.06** - Small #' - **0.16 <= ES < 0.14** - Medium #' - **ES >= 0.14 ** - Large #' - Cohen (1992) (`"cohen1992"`) applicable to one-way anova, or to *partial* #' eta / omega / epsilon squared in multi-way anova. #' - **ES < 0.02** - Very small #' - **0.02 <= ES < 0.13** - Small #' - **0.13 <= ES < 0.26** - Medium #' - **ES >= 0.26** - Large #' #' @examples #' interpret_eta_squared(.02) #' interpret_eta_squared(c(.5, .02), rules = "cohen1992") #' @seealso https://imaging.mrc-cbu.cam.ac.uk/statswiki/FAQ/effectSize/ #' #' #' @references #' - Field, A (2013) Discovering statistics using IBM SPSS Statistics. Fourth #' Edition. Sage:London. #' #' - Cohen, J. (1992). A power primer. Psychological bulletin, 112(1), 155. #' #' @export interpret_omega_squared <- function(es, rules = "field2013", ...) { rules <- .match.rules( rules, list( field2013 = rules(c(0.01, 0.06, 0.14), c("very small", "small", "medium", "large"), name = "field2013", right = FALSE ), cohen1992 = rules(c(0.02, 0.13, 0.26), c("very small", "small", "medium", "large"), name = "cohen1992", right = FALSE ) ) ) interpret(es, rules) } #' @export #' @rdname interpret_omega_squared interpret_eta_squared <- interpret_omega_squared #' @export #' @rdname interpret_omega_squared interpret_epsilon_squared <- interpret_omega_squared effectsize/R/convert_stat_to_r.R0000644000175000017500000001533614170072542016627 0ustar nileshnilesh# t ----------------------------------------------------------------------- #' Convert test statistics (t, z, F) to effect sizes of differences (Cohen's d) #' or association (**partial** r) #' #' These functions are convenience functions to convert t, z and F test #' statistics to Cohen's d and **partial** r. These are useful in cases where #' the data required to compute these are not easily available or their #' computation is not straightforward (e.g., in liner mixed models, contrasts, #' etc.). #' \cr #' See [Effect Size from Test Statistics vignette.](https://easystats.github.io/effectsize/articles/from_test_statistics.html) #' #' @param t,f,z The t, the F or the z statistics. #' @param df,df_error Degrees of freedom of numerator or of the error estimate #' (i.e., the residuals). #' @param n The number of observations (the sample size). #' @param paired Should the estimate account for the t-value being testing the #' difference between dependent means? #' @param alternative a character string specifying the alternative hypothesis; #' Controls the type of CI returned: `"two.sided"` (default, two-sided CI), #' `"greater"` or `"less"` (one-sided CI). Partial matching is allowed (e.g., #' `"g"`, `"l"`, `"two"`...). See *One-Sided CIs* in [effectsize_CIs]. #' @param pooled Deprecated. Use `paired`. #' @inheritParams chisq_to_phi #' @param ... Arguments passed to or from other methods. #' #' #' @return A data frame with the effect size(s)(`r` or `d`), and their CIs #' (`CI_low` and `CI_high`). #' #' #' @details These functions use the following formulae to approximate *r* and *d*: #' \cr\cr #' \deqn{r_{partial} = t / \sqrt{t^2 + df_{error}}} #' \cr\cr #' \deqn{r_{partial} = z / \sqrt{z^2 + N}} #' \cr\cr #' \deqn{d = 2 * t / \sqrt{df_{error}}} #' \cr\cr #' \deqn{d_z = t / \sqrt{df_{error}}} #' \cr\cr #' \deqn{d = 2 * z / \sqrt{N}} #' #' The resulting `d` effect size is an *approximation* to Cohen's *d*, and #' assumes two equal group sizes. When possible, it is advised to directly #' estimate Cohen's *d*, with [cohens_d()], `emmeans::eff_size()`, or similar #' functions. #' #' @inheritSection effectsize_CIs Confidence (Compatibility) Intervals (CIs) #' @inheritSection effectsize_CIs CIs and Significance Tests #' #' @family effect size from test statistic #' #' @examples #' ## t Tests #' res <- t.test(1:10, y = c(7:20), var.equal = TRUE) #' t_to_d(t = res$statistic, res$parameter) #' t_to_r(t = res$statistic, res$parameter) #' t_to_r(t = res$statistic, res$parameter, alternative = "less") #' #' res <- with(sleep, t.test(extra[group == 1], extra[group == 2], paired = TRUE)) #' t_to_d(t = res$statistic, res$parameter, paired = TRUE) #' t_to_r(t = res$statistic, res$parameter) #' t_to_r(t = res$statistic, res$parameter, alternative = "greater") #' #' \donttest{ #' ## Linear Regression #' model <- lm(rating ~ complaints + critical, data = attitude) #' (param_tab <- parameters::model_parameters(model)) #' #' (rs <- t_to_r(param_tab$t[2:3], param_tab$df_error[2:3])) #' #' if (require(see)) plot(rs) #' #' # How does this compare to actual partial correlations? #' if (require("correlation")) { #' correlation::correlation(attitude[, c(1, 2, 6)], partial = TRUE)[1:2, c(2, 3, 7, 8)] #' } #' } #' #' @references #' - Friedman, H. (1982). Simplified determinations of statistical power, #' magnitude of effect and research sample sizes. Educational and Psychological #' Measurement, 42(2), 521-526. \doi{10.1177/001316448204200214} #' #' - Wolf, F. M. (1986). Meta-analysis: Quantitative methods for research #' synthesis (Vol. 59). Sage. #' #' - Rosenthal, R. (1994) Parametric measures of effect size. In H. Cooper and #' L.V. Hedges (Eds.). The handbook of research synthesis. New York: Russell #' Sage Foundation. #' #' - Steiger, J. H. (2004). Beyond the F test: Effect size confidence intervals #' and tests of close fit in the analysis of variance and contrast analysis. #' Psychological Methods, 9, 164-182. #' #' - Cumming, G., & Finch, S. (2001). A primer on the understanding, use, and #' calculation of confidence intervals that are based on central and noncentral #' distributions. Educational and Psychological Measurement, 61(4), 532-574. #' #' @export t_to_r <- function(t, df_error, ci = 0.95, alternative = "two.sided", ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) res <- data.frame(r = t / sqrt(t^2 + df_error)) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 ts <- t(mapply( .get_ncp_t, t, df_error, ci.level )) res$CI_low <- ts[, 1] / sqrt(ts[, 1]^2 + df_error) res$CI_high <- ts[, 2] / sqrt(ts[, 2]^2 + df_error) ci_method <- list(method = "ncp", distribution = "t") if (alternative == "less") { res$CI_low <- -1 } else if (alternative == "greater") { res$CI_high <- 1 } } else { alternative <- NULL } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "alternative") <- alternative return(res) } # z ----------------------------------------------------------------------- #' @rdname t_to_r #' @importFrom stats qnorm #' @export z_to_r <- function(z, n, ci = 0.95, alternative = "two.sided", ...) { alternative <- match.arg(alternative, c("two.sided", "less", "greater")) res <- data.frame(r = z / sqrt(z^2 + n)) ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 alpha <- 1 - ci.level probs <- c(alpha / 2, 1 - alpha / 2) qs <- stats::qnorm(probs) zs <- cbind(qs[1] + z, qs[2] + z) res$CI_low <- zs[, 1] / sqrt(zs[, 1]^2 + n) res$CI_high <- zs[, 2] / sqrt(zs[, 2]^2 + n) ci_method <- list(method = "normal") if (alternative == "less") { res$CI_low <- -1 } else if (alternative == "greater") { res$CI_high <- 1 } } else { alternative <- NULL } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "alternative") <- alternative return(res) } # F ----------------------------------------------------------------------- #' @rdname t_to_r #' @export F_to_r <- function(f, df, df_error, ci = 0.95, alternative = "two.sided", ...) { if (df > 1) { stop("Cannot convert F with more than 1 df to r.") } t_to_r(sqrt(f), df_error, ci = ci, alternative = alternative, ...) } effectsize/R/convert_stat_chisq.R0000644000175000017500000001420314170065645016771 0ustar nileshnilesh#' Conversion Chi-Squared to Phi or Cramer's V #' #' Convert between Chi square (\eqn{\chi^2}), Cramer's V, phi (\eqn{\phi}), #' Cohen's *w* and Pearson's *C* for contingency tables or goodness of fit. #' #' @param chisq The Chi-squared statistic. #' @param n Total sample size. #' @param nrow,ncol The number of rows/columns in the contingency table (ignored #' for Phi when `adjust=FALSE` and `CI=NULL`). #' @param ci Confidence Interval (CI) level #' @param alternative a character string specifying the alternative hypothesis; #' Controls the type of CI returned: `"greater"` (default) or `"less"` #' (one-sided CI), or `"two.sided"` (default, two-sided CI). Partial matching #' is allowed (e.g., `"g"`, `"l"`, `"two"`...). See *One-Sided CIs* in #' [effectsize_CIs]. #' @param adjust Should the effect size be bias-corrected? Defaults to `FALSE`. #' @param ... Arguments passed to or from other methods. #' #' @return A data frame with the effect size(s), and confidence interval(s). See #' [cramers_v()]. #' #' @details These functions use the following formulae: #' \cr #' \deqn{\phi = \sqrt{\chi^2 / n}}{phi = sqrt(\chi^2 / n)} #' \cr #' \deqn{Cramer's V = \phi / \sqrt{min(nrow,ncol)-1}}{Cramer's V = \phi / sqrt(min(nrow,ncol)-1)} #' \cr #' \deqn{Pearson's C = \sqrt{\chi^2 / (\chi^2 + n)}}{Pearson's C = sqrt(\chi^2 / (\chi^2 + n))} #' \cr\cr #' For adjusted versions, see Bergsma, 2013. #' #' @inheritSection effectsize_CIs Confidence (Compatibility) Intervals (CIs) #' @inheritSection effectsize_CIs CIs and Significance Tests #' #' @family effect size from test statistic #' #' @note Cohen's *w* is equivalent to *Phi*. #' #' @examples #' contingency_table <- as.table(rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477))) #' #' # chisq.test(contingency_table) #' #> #' #> Pearson's Chi-squared test #' #> #' #> data: contingency_table #' #> X-squared = 41.234, df = 4, p-value = 2.405e-08 #' #' chisq_to_phi(41.234, #' n = sum(contingency_table), #' nrow = nrow(contingency_table), #' ncol = ncol(contingency_table) #' ) #' chisq_to_cramers_v(41.234, #' n = sum(contingency_table), #' nrow = nrow(contingency_table), #' ncol = ncol(contingency_table) #' ) #' chisq_to_pearsons_c(41.234, #' n = sum(contingency_table), #' nrow = nrow(contingency_table), #' ncol = ncol(contingency_table) #' ) #' @references #' - Cumming, G., & Finch, S. (2001). A primer on the understanding, use, and #' calculation of confidence intervals that are based on central and noncentral #' distributions. Educational and Psychological Measurement, 61(4), 532-574. #' #' - Bergsma, W. (2013). A bias-correction for Cramer's V and Tschuprow's T. #' Journal of the Korean Statistical Society, 42(3), 323-328. #' #' @export chisq_to_phi <- function(chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", adjust = FALSE, ...) { alternative <- match.arg(alternative, c("greater", "two.sided", "less")) if (adjust || is.numeric(ci)) { is_goodness <- ncol == 1 || nrow == 1 if (is_goodness) { df <- pmax(nrow - 1, ncol - 1) max_upper <- Inf } else { df <- (nrow - 1) * (ncol - 1) max_upper <- sqrt((pmin(nrow, ncol) - 1)) } } if (adjust) { res <- data.frame( phi_adjusted = sqrt(pmax(0, (chisq / n) - (df / (n - 1)))) ) } else { res <- data.frame(phi = sqrt(chisq / n)) } ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) res$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 chisqs <- t(mapply( .get_ncp_chi, chisq, df, ci.level )) res$CI_low <- chisq_to_phi(chisqs[, 1], n, nrow, ncol, ci = NULL, adjust = adjust)[[1]] res$CI_high <- chisq_to_phi(chisqs[, 2], n, nrow, ncol, ci = NULL, adjust = adjust)[[1]] ci_method <- list(method = "ncp", distribution = "chisq") if (alternative == "less") { res$CI_low <- 0 } else if (alternative == "greater") { res$CI_high <- max_upper } } else { alternative <- NULL } class(res) <- c("effectsize_table", "see_effectsize_table", class(res)) attr(res, "ci") <- ci attr(res, "ci_method") <- ci_method attr(res, "adjust") <- adjust attr(res, "alternative") <- alternative return(res) } #' @rdname chisq_to_phi #' @export chisq_to_cohens_w <- chisq_to_phi #' @rdname chisq_to_phi #' @export chisq_to_cramers_v <- function(chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", adjust = FALSE, ...) { if (ncol == 1 || nrow == 1) { stop("Cramer's V not applicable to goodness-of-fit tests.", call. = FALSE) } if (adjust) { k <- nrow - ((nrow - 1)^2) / (n - 1) l <- ncol - ((ncol - 1)^2) / (n - 1) } else { k <- nrow l <- ncol } phi_2_V <- sqrt((pmin(k, l) - 1)) res <- chisq_to_phi(chisq, n, nrow, ncol, ci = ci, alternative = alternative, adjust = adjust) res[grepl("^(phi|CI_)", colnames(res))] <- res[grepl("^(phi|CI_)", colnames(res))] / phi_2_V colnames(res)[1] <- gsub("phi", "Cramers_v", colnames(res)[1]) if ("CI" %in% colnames(res)) if ((alternative <- attr(res, "alternative")) == "less") { res$CI_low <- 0 } else if (alternative == "greater") { res$CI_high <- 1 } return(res) } #' @rdname chisq_to_phi #' @export chisq_to_pearsons_c <- function(chisq, n, nrow, ncol, ci = 0.95, alternative = "greater", ...) { res <- chisq_to_phi(chisq, n, nrow, ncol, ci = ci, alternative = alternative, adjust = FALSE) res[grepl("^(phi|CI_)", colnames(res))] <- lapply(res[grepl("^(phi|CI_)", colnames(res))], function(phi) sqrt(1/(1/phi^2 + 1))) colnames(res)[1] <- "Pearsons_c" if ("CI" %in% colnames(res)) if ((alternative <- attr(res, "alternative")) == "less") { res$CI_low <- 0 } else if (alternative == "greater") { res$CI_high <- 1 } return(res) } # Reverse ----------------------------------------------------------------- #' @rdname chisq_to_phi #' @param phi The Phi statistic. #' @export phi_to_chisq <- function(phi, n, ...) { n * (phi^2) } effectsize/R/interpret.R0000644000175000017500000001430414170065645015105 0ustar nileshnilesh# Rules --------------------------------------------------------------- #' Interpretation Grid #' #' Create a container for interpretation rules of thumb. Usually used in conjunction with [interpret]. #' #' @param values Vector of reference values (edges defining categories or #' critical values). #' @param labels Labels associated with each category. If `NULL`, will try to #' infer it from `values` (if it is a named vector or a list), otherwise, will #' return the breakpoints. #' @param name Name of the set of rules (stored as a 'rule_name' attribute). #' @param right logical, for threshold-type rules, indicating if the thresholds #' themselves should be included in the interval to the right (lower values) #' or in the interval to the left (higher values). #' #' #' #' @seealso interpret #' #' @examples #' rules(c(0.05), c("significant", "not significant"), right = FALSE) #' rules(c(0.2, 0.5, 0.8), c("small", "medium", "large")) #' rules(c("small" = 0.2, "medium" = 0.5), name = "Cohen's Rules") #' @export rules <- function(values, labels = NULL, name = NULL, right = TRUE) { if (is.null(labels)) { if (is.list(values)) { values <- unlist(values) } if (is.null(names(values))) { labels <- values } else { labels <- names(values) } } # Sanity checks if (length(labels) < length(values)) { stop("There cannot be less labels than reference values!") } else if (length(labels) > length(values) + 1) { stop("Too many labels for the number of reference values!") } if (length(values) == length(labels) - 1) { if (is.unsorted(values)) { stop("Reference values must be sorted.") } } else { right <- NULL } # Store and return out <- list( values = values, labels = labels ) if (is.null(name)) { attr(out, "rule_name") <- "Custom rules" } else { attr(out, "rule_name") <- name } attr(out, "right") <- right class(out) <- c("rules", "list") out } #' @rdname rules #' @param x An arbitrary R object. #' @export is.rules <- function(x) inherits(x, "rules") # Interpret --------------------------------------------------------------- #' Generic function for interpretation #' #' Interpret a value based on a set of rules. See [rules()]. #' #' @param x Vector of value break points (edges defining categories), or a data #' frame of class `effectsize_table`. #' @param rules Set of [rules()]. When `x` is a data frame, can be a name of an #' established set of rules. #' @param ... Currently not used. #' @inheritParams rules #' #' @return #' - For numeric input: A character vector of interpertations. #' - For data frames: the `x` input with an additional `Interpretation` column. #' #' @seealso rules #' @examples #' rules_grid <- rules(c(0.01, 0.05), c("very significant", "significant", "not significant")) #' interpret(0.001, rules_grid) #' interpret(0.021, rules_grid) #' interpret(0.08, rules_grid) #' interpret(c(0.01, 0.005, 0.08), rules_grid) #' #' interpret(c(0.35, 0.15), c("small" = 0.2, "large" = 0.4), name = "Cohen's Rules") #' interpret(c(0.35, 0.15), rules(c(0.2, 0.4), c("small", "medium", "large"))) #' #' # ---------- #' d <- cohens_d(mpg ~ am, data = mtcars) #' interpret(d, rules = "cohen1988") #' #' d <- glass_delta(mpg ~ am, data = mtcars) #' interpret(d, rules = "gignac2016") #' #' interpret(d, rules = rules(1, c("tiny", "yeah okay"))) #' #' m <- lm(formula = wt ~ am * cyl, data = mtcars) #' eta2 <- eta_squared(m) #' interpret(eta2, rules = "field2013") #' #' X <- chisq.test(mtcars$am, mtcars$cyl == 8) #' interpret(oddsratio(X), rules = "chen2010") #' interpret(cramers_v(X), "lovakov2021") #' @export interpret <- function(x, ...) { UseMethod("interpret") } #' @rdname interpret #' @export interpret.numeric <- function(x, rules, name = attr(rules, "rule_name"), ...) { if (!inherits(rules, "rules")) { rules <- rules(rules) } nm <- names(x) if (length(x) > 1) { out <- sapply(x, .interpret, rules) } else { out <- .interpret(x, rules) } names(out) <- nm if (is.null(name)) { attr(out, "rule_name") <- "Custom rules" } else { attr(out, "rule_name") <- name } class(out) <- c("effectsize_interpret", class(out)) out } #' @rdname interpret #' @export interpret.effectsize_table <- function(x, rules, ...) { if (missing(rules)) stop("You MUST specify the rules of interpretation!") es_name <- colnames(x)[is_effectsize_name(colnames(x))] value <- x[[es_name]] x$Interpretation <- switch(es_name, # std diff Cohens_d = , Hedges_g = , Glass_delta = , d = interpret_cohens_d(value, rules = rules), # xtab Cramers_v = , Cramers_v_adjusted = , phi = , phi_adjusted = interpret_cramers_v(value, rules = rules), Cohens_g = interpret_cohens_g(value, rules = rules), Odds_ratio = interpret_oddsratio(value, rules = rules, log = FALSE), log_Odds_ratio = interpret_oddsratio(value, rules = rules, log = TRUE), # anova Eta2 = , Eta2_partial = , Eta2_generalized = , Epsilon2 = , Epsilon2_partial = , Omega2 = , Omega2_partial = interpret_omega_squared(value, rules = rules), Cohens_f = , Cohens_f_partial = interpret_omega_squared(f_to_eta2(value), rules = rules), Cohens_f2 = , Cohens_f2_partial = interpret_omega_squared(f2_to_eta2(value), rules = rules), # rank Kendalls_W = interpret_kendalls_w(value, rules = rules), r_rank_biserial = , rank_epsilon_squared = , # corr r = interpret_r(value, rules = rules) ) attr(x, "rule_name") <- attr(x$Interpretation, "rule_name") x } #' @keywords internal .interpret <- function(x, rules) { if (is.na(x)) { return(NA) } if (length(rules$values) == length(rules$labels)) { index <- which.min(abs(x - rules$values)) } else { if (isTRUE(attr(rules, "right"))) { check <- x <= rules$values } else { check <- x < rules$values } if (any(check)) { index <- min(which(check)) } else { index <- length(rules$labels) } } rules$labels[index] } effectsize/R/convert_between_d_to_r.R0000644000175000017500000000733014132466117017606 0ustar nileshnilesh#' Convert between *d*, *r* and *Odds ratio* #' #' Enables a conversion between different indices of effect size, such as #' standardized difference (Cohen's d), correlation r or (log) odds ratios. #' #' @param d Standardized difference value (Cohen's d). #' @param r Correlation coefficient r. #' @param OR *Odds ratio* values in vector or data frame. #' @param log Take in or output the log of the ratio (such as in logistic models). #' @param ... Arguments passed to or from other methods. #' #' @family convert between effect sizes #' #' @examples #' r_to_d(0.5) #' d_to_oddsratio(1.154701) #' oddsratio_to_r(8.120534) #' #' d_to_r(1) #' r_to_oddsratio(0.4472136, log = TRUE) #' oddsratio_to_d(1.813799, log = TRUE) #' #' @return Converted index. #' #' @details #' Conversions between *d* and *OR* or *r* is done through these formulae. #' - \eqn{d = \frac{2 * r}{\sqrt{1 - r^2}}}{d = 2 * r / sqrt(1 - r^2)} #' - \eqn{r = \frac{d}{\sqrt{d^2 + 4}}}{r = d / sqrt(d^2 + 4)} #' - \eqn{d = \frac{\log(OR)\times\sqrt{3}}{\pi}}{d = log(OR) * sqrt(3) / pi} #' - \eqn{log(OR) = d * \frac{\pi}{\sqrt(3)}}{log(OR) = d * pi / sqrt(3)} #' #' The conversion from *d* to *r* assumes equally sized groups. The resulting #' *r* is also called the binomial effect size display (BESD; Rosenthal et al., #' 1982). #' #' @references #' - Sánchez-Meca, J., Marín-Martínez, F., & Chacón-Moscoso, S. (2003). #' Effect-size indices for dichotomized outcomes in meta-analysis. Psychological #' methods, 8(4), 448. #' #' - Borenstein, M., Hedges, L. V., Higgins, J. P. T., & Rothstein, H. R. #' (2009). Converting among effect sizes. Introduction to meta-analysis, 45-49. #' #' - Rosenthal, R., & Rubin, D. B. (1982). A simple, general purpose display of #' magnitude of experimental effect. Journal of educational psychology, 74(2), 166. #' #' @export #' @aliases convert_d_to_r d_to_r <- function(d, ...) { d / (sqrt(d^2 + 4)) } #' @export convert_d_to_r <- d_to_r #' @rdname d_to_r #' @aliases convert_r_to_d #' @export r_to_d <- function(r, ...) { 2 * r / sqrt(1 - r^2) } #' @export convert_r_to_d <- r_to_d # OR - d ---------------------------------------------------------------- #' @rdname d_to_r #' @aliases convert_oddsratio_to_d #' @export oddsratio_to_d <- function(OR, log = FALSE, ...) { if (log) { log_OR <- OR } else { log_OR <- log(OR) } log_OR * (sqrt(3) / pi) } #' @export convert_oddsratio_to_d <- oddsratio_to_d #' @rdname d_to_r #' @aliases convert_logoddsratio_to_d #' @export logoddsratio_to_d <- function(OR, log = TRUE, ...) { oddsratio_to_d(OR, log = log, ...) } #' @export convert_logoddsratio_to_d <- logoddsratio_to_d #' @rdname d_to_r #' @aliases convert_d_to_oddsratio #' @export d_to_oddsratio <- function(d, log = FALSE, ...) { log_OR <- d * pi / sqrt(3) if (log) { log_OR } else { exp(log_OR) } } #' @export convert_d_to_oddsratio <- d_to_oddsratio # OR - r ---------------------------------------------------------------- #' @rdname d_to_r #' @aliases convert_oddsratio_to_r #' @export oddsratio_to_r <- function(OR, log = FALSE, ...) { d_to_r(oddsratio_to_d(OR, log = log)) } #' @export convert_oddsratio_to_r <- oddsratio_to_r #' @rdname d_to_r #' @aliases convert_logoddsratio_to_r #' @export logoddsratio_to_r <- function(OR, log = TRUE, ...) { oddsratio_to_r(OR, log = log, ...) } #' @export convert_logoddsratio_to_r <- logoddsratio_to_r #' @rdname d_to_r #' @aliases convert_r_to_oddsratio #' @export r_to_oddsratio <- function(r, log = FALSE, ...) { d_to_oddsratio(r_to_d(r), log = log) } #' @export convert_r_to_oddsratio <- r_to_oddsratio effectsize/R/interpret_r2.R0000644000175000017500000000447114132466117015511 0ustar nileshnilesh#' Interpret coefficient of determination (R2) #' #' @param r2 Value or vector of R2 values. #' @param rules Can be `"cohen1988"` (default), `"falk1992"`, `"chin1998"`, #' `"hair2011"`, or custom set of [rules()]]. #' #' @section Rules: #' #' ## For Linear Regression #' #' - Cohen (1988) (`"cohen1988"`; default) #' - **R2 < 0.02** - Very weak #' - **0.02 <= R2 < 0.13** - Weak #' - **0.13 <= R2 < 0.26** - Moderate #' - **R2 >= 0.26** - Substantial #' - Falk & Miller (1992) (`"falk1992"`) #' - **R2 < 0.1** - Negligible #' - **R2 >= 0.1** - Adequate #' #' ## For PLS / SEM R-Squared of *latent* variables #' #' - Chin, W. W. (1998) (`"chin1998"`) #' - **R2 < 0.19** - Very weak #' - **0.19 <= R2 < 0.33** - Weak #' - **0.33 <= R2 < 0.67** - Moderate #' - **R2 >= 0.67** - Substantial #' - Hair et al. (2011) (`"hair2011"`) #' - **R2 < 0.25** - Very weak #' - **0.25 <= R2 < 0.50** - Weak #' - **0.50 <= R2 < 0.75** - Moderate #' - **R2 >= 0.75** - Substantial #' #' @examples #' interpret_r2(.02) #' interpret_r2(c(.5, .02)) #' @references #' - Cohen, J. (1988). Statistical power analysis for the behavioral sciences #' (2nd Ed.). New York: Routledge. #' #' - Falk, R. F., & Miller, N. B. (1992). A primer for soft modeling. University #' of Akron Press. #' #' - Chin, W. W. (1998). The partial least squares approach to structural #' equation modeling. Modern methods for business research, 295(2), 295-336. #' #' - Hair, J. F., Ringle, C. M., & Sarstedt, M. (2011). PLS-SEM: Indeed a silver #' bullet. Journal of Marketing theory and Practice, 19(2), 139-152. #' #' @export interpret_r2 <- function(r2, rules = "cohen1988") { rules <- .match.rules( rules, list( cohen1988 = rules(c(0.02, 0.13, 0.26), c("very weak", "weak", "moderate", "substantial"), name = "cohen1988", right = FALSE ), falk1992 = rules(c(0.10), c("negligible", "adequate"), name = "falk1992", right = FALSE ), chin1998 = rules(c(0.19, 0.33, 0.67), c("very weak", "weak", "moderate", "substantial"), name = "chin1998", right = FALSE ), hair2011 = rules(c(0.25, 0.50, 0.75), c("very weak", "weak", "moderate", "substantial"), name = "hair2011", right = FALSE ) ) ) interpret(r2, rules) } effectsize/R/is_effectsize_name.R0000644000175000017500000000702314170065645016713 0ustar nileshnilesh#' Checks if character is of a supported effect size #' #' For use by other functions and packages. #' #' @param x A character, or a vector. #' @param ignore_case Should case of input be ignored? #' @export is_effectsize_name <- function(x, ignore_case = TRUE) { x_comp <- es_info$name if (ignore_case) { x <- tolower(x) x_comp <- tolower(x_comp) } x %in% x_comp } #' @export #' @rdname is_effectsize_name get_effectsize_name <- function(x, ignore_case = TRUE) { x[is_effectsize_name(x, ignore_case = ignore_case)] } #' @export #' @rdname is_effectsize_name get_effectsize_label <- function(x, ignore_case = TRUE) { x_comp <- es_info$name if (ignore_case) { x <- tolower(x) x_comp <- tolower(x_comp) } idx <- match(x, x_comp) es_info$label[idx] } #' List of effect size names #' #' Can always add more info here if need be... #' #' @keywords internal es_info <- matrix(c( ## Diffs "Cohens_d", "Cohen's d", "twotail", -Inf, Inf, 0, "Hedges_g", "Hedges' g", "twotail", -Inf, Inf, 0, "Glass_delta", "Glass' delta", "twotail", -Inf, Inf, 0, ## xtab "Cramers_v", "Cramer's V", "onetail", 0, 1, 0, "Cramers_v_adjusted", "Cramer's V (adj.)", "onetail", 0, 1, 0, "phi", "Phi", "onetail", 0, NA, 0, "phi_adjusted", "Phi (adj.)", "onetail", 0, NA, 0, "Pearsons_c", "Pearson's C", "onetail", 0, 1, 0, "Cohens_g", "Cohen's g", "onetail", -0.5, 0.5, 0, "Cohens_h", "Cohen's h", "twotail", -pi, pi, 0, "Odds_ratio", "Odds ratio", "twotail", 0, Inf, 1, "log_Odds_ratio", "log(Odds ratio)", "twotail", -Inf, Inf, 0, "Risk_ratio", "Risk ratio", "twotail", 0, Inf, 1, "log_Risk_ratio", "log(Risk ratio)", "twotail", -Inf, Inf, 0, ## ANOVA "Eta2", "Eta2", "onetail", 0, 1, 0, "Eta2_partial", "Eta2 (partial)", "onetail", 0, 1, 0, "Eta2_generalized", "Eta2 (generalized)", "onetail", 0, 1, 0, "Epsilon2", "Epsilon2", "onetail", 0, 1, 0, "Epsilon2_partial", "Epsilon2 (partial)", "onetail", 0, 1, 0, "Omega2", "Omega2", "onetail", 0, 1, 0, "Omega2_partial", "Omega2 (partial)", "onetail", 0, 1, 0, "Cohens_f", "Cohen's f", "onetail", 0, Inf, 0, "Cohens_f_partial", "Cohen's f (partial)", "onetail", 0, Inf, 0, "Cohens_f2", "Cohen's f2", "onetail", 0, Inf, 0, "Cohens_f2_partial", "Cohen's f2 (partial)", "onetail", 0, Inf, 0, ## Rank "r_rank_biserial", "r (rank biserial)", "twotail", -1, 1, 0, "Kendalls_W", "Kendall's W", "onetail", 0, 1, 0, "rank_epsilon_squared", "Epsilon2 (rank)", "onetail", 0, 1, 0, ## Std Coefficient "Std_Coefficient", "Coefficient (std.)", "twotail", -Inf, Inf, 0, "Std_Odds_ratio", "Odds Ratio (std.)", "twotail", 0, Inf, 1, "Std_Risk_ratio", "Risk Ratio (std.)", "twotail", 0, Inf, 1, "Std_IRR", "IRR (std.)", "twotail", 0, Inf, 1, "Std_Median", "Median (std.)", "twotail", -Inf, Inf, 0, "Std_Mean", "Mean (std.)", "twotail", -Inf, Inf, 0, "Std_MAP", "MAP (std.)", "twotail", -Inf, Inf, 0, ## CLES "p_superiority", "Pr(superiority)", "onetail", 0, 1, 0.5, "Cohens_U3", "Cohen's U3", "onetail", 0, 1, 0.5, "overlap", "Overlap", "onetail", 0, 1, 1, ## Other "r", "r", "twotail", -1, 1, 0, "d", "d", "twotail", -Inf, Inf, 0 ), ncol = 6, byrow = TRUE, dimnames = list(NULL, c("name", "label", "direction", "lb", "ub", "null")) ) es_info <- as.data.frame(es_info, stringsAsFactors = FALSE) es_info$lb <- as.numeric(es_info$lb) es_info$ub <- as.numeric(es_info$ub) es_info$null <- as.numeric(es_info$null) rownames(es_info) <- es_info$name effectsize/R/common_language.R0000644000175000017500000001573214170065645016232 0ustar nileshnilesh#' Estimate Common Language Effect Sizes (CLES) #' #' `cohens_u3()`, `p_superiority()`, and `p_overlap()` give only one of the #' CLESs. #' #' @inheritParams cohens_d #' @param parametric Use parametric estimation (see [cohens_d()]) or #' non-parametric estimation (see [rank_biserial()]). #' @param iterations The number of bootstrap replicates for computing confidence #' intervals. Only applies when `ci` is not `NULL` and `parametric = FALSE`. #' #' @details #' These measures of effect size present group differences in probabilistic #' terms: #' - **Probability of superiority** is the probability that, when sampling an #' observation from each of the groups at random, that the observation from #' the second group will be larger than the sample from the first group. #' - **Cohen's U3** is the proportion of the second group that is smaller than #' the median of the first group. #' - **Overlap** (OVL) is the proportional overlap between the distributions. #' (When `parametric = FALSE`, [bayestestR::overlap()] is used.) #' #' For unequal group sizes, it is recommended to use the non-parametric based #' CLES (`parametric = FALSE`). #' #' @section Confidence Intervals (CIs): #' For parametric CLES, the CIs are transformed CIs for Cohen's *d* #' ([`d_to_cles()`]). For non-parametric (`parametric = FALSE`) CLES, the CI of #' *Pr(superiority)* is a transformed CI of the rank-biserial correlation #' ([`rb_to_cles()`]), while for Cohen's *U3* and the Overlap coefficient the #' confidence intervals are bootstrapped (requires the `boot` package). #' #' @return A data frame containing the common language effect sizes (and #' optionally their CIs). #' #' @references #' - Cohen, J. (1977). Statistical power analysis for the behavioral sciences. #' New York: Routledge. #' #' - Reiser, B., & Faraggi, D. (1999). Confidence intervals for the overlapping #' coefficient: the normal equal variance case. Journal of the Royal Statistical #' Society, 48(3), 413-418. #' #' - Ruscio, J. (2008). A probability-based measure of effect size: robustness #' to base rates and other factors. Psychological methods, 13(1), 19–30. #' #' @seealso [d_to_cles()] [sd_pooled()] #' @family effect size indices #' #' @examples #' cles(mpg ~ am, data = mtcars) #' #' set.seed(4) #' cles(mpg ~ am, data = mtcars, parametric = FALSE) #' #' \dontrun{ #' ## Individual CLES #' p_superiority(extra ~ group, data = sleep) #' #' cohens_u3(extra ~ group, data = sleep, parametric = FALSE) #' #' p_overlap(extra ~ group, data = sleep) #' } #' #' @export cles <- function(x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", parametric = TRUE, verbose = TRUE, iterations = 200, ...) { if (inherits(x, "htest")) { if (!grepl("(t-test|Wilcoxon)", x$method)) { stop("'x' is not a t-test or a Wilcoxon-test!", call. = FALSE) } return(effectsize(x, type = "cles", verbose = verbose, ...)) } else if (inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], c("BFindepSample"))) { stop("'x' is not a t-test!", call. = FALSE) } return(effectsize(x, type = "cles", ci = ci, verbose = verbose, ...)) } data <- .get_data_2_samples(x, y = y, data = data, verbose = verbose) x <- na.omit(data[["x"]]) y <- na.omit(data[["y"]]) if (parametric) { d <- cohens_d( x = x, y = y, paired = FALSE, pooled_sd = TRUE, mu = mu, ci = ci, alternative = alternative, verbose = verbose, ... ) d_to_cles(d) } else { rb <- rank_biserial( x = x, y = y, paired = FALSE, mu = mu, ci = ci, alternative = alternative, verbose = verbose, ... ) out <- rbind( rb_to_cles(rb), .np_U3_OVL(x, y, ci = ci, alternative = alternative, iterations = iterations) ) attr(out, "table_footer") <- c("\n- Non-parametric CLES", "cyan") out } } #' @export #' @rdname cles common_language <- cles #' @export #' @rdname cles cohens_u3 <- function(...) { .cles_which("u3", ...) } #' @export #' @rdname cles p_superiority <- function(...) { .cles_which("p", ...) } #' @export #' @rdname cles p_overlap <- function(...) { .cles_which("ovl", ...) } # Utils ------------------------------------------------------------------- #' @keywords internal .np_U3_OVL <- function(x, y, ci = 0.95, alternative = "two.sided", iterations) { .get_np_U3_OVL <- function(data, i = seq_len(nrow(data))) { data <- data[i, ] c(U3 = sum(data[data$g == "y", "r"] < median(data[data$g == "x", "r"])) / nrow(data[data$g == "y", ]), OVL = bayestestR::overlap( data[data$g == "x", "r"], data[data$g == "y", "r"] )) } d <- data.frame( r = c(x, y), g = rep(c("x", "y"), c(length(x), length(y))) ) out <- data.frame( Parameter = c("Cohen's U3", "Overlap"), Coefficient = .get_np_U3_OVL(d) ) if (is.numeric(ci)) { if (insight::check_if_installed("boot", "for estimating CIs", stop = FALSE)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 R <- boot::boot( data = d, statistic = .get_np_U3_OVL, R = iterations ) bCI <- list(boot::boot.ci(R, conf = ci.level, type = "perc", 1)$percent, boot::boot.ci(R, conf = ci.level, type = "perc", 2)$percent) bCI <- lapply(bCI, function(.bci) tail(as.vector(.bci), 2)) bCI <- matrix(unlist(bCI), 2, byrow = TRUE, dimnames = list(NULL, c("CI_low", "CI_high"))) bCI <- cbind(CI = ci, as.data.frame(bCI)) if (alternative == "less") bCI$CI_low <- 0 if (alternative == "greater") bCI$CI_high <- 1 } else { bCI <- data.frame( CI = NA, CI_low = NA, CI_high = NA )[c(1,1),] } out <- cbind(out, bCI) } out } #' @keywords internal .cles_which <- function(type, x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", verbose = TRUE, parametric = TRUE, ...) { CLES <- cles( x, y = y, data = data, mu = mu, ci = ci, alternative = alternative, verbose = verbose, parametric = parametric, ... ) if (type == "p") { out <- CLES[1, ] colnames(out)[2] <- "p_superiority" } else if (type == "u3") { out <- CLES[2, ] colnames(out)[2] <- "Cohens_U3" } else if (type == "ovl") { out <- CLES[3, ] colnames(out)[2] <- "overlap" } out[[1]] <- NULL out }effectsize/R/cohens_d.R0000644000175000017500000002600314170261321014637 0ustar nileshnilesh#' Effect size for differences #' #' Compute effect size indices for standardized differences: Cohen's *d*, #' Hedges' *g* and Glass’s *delta* (\eqn{\Delta}). (This function returns the #' **population** estimate.) #' \cr\cr #' Both Cohen's *d* and Hedges' *g* are the estimated the standardized #' difference between the means of two populations. Hedges' *g* provides a bias #' correction (using the exact method) to Cohen's *d* for small sample sizes. #' For sample sizes > 20, the results for both statistics are roughly #' equivalent. Glass’s *delta* is appropriate when the standard deviations are #' significantly different between the populations, as it uses only the *second* #' group's standard deviation. #' #' @param x A formula, a numeric vector, or a character name of one in `data`. #' @param y A numeric vector, a grouping (character / factor) vector, a or a #' character name of one in `data`. Ignored if `x` is a formula. #' @param alternative a character string specifying the alternative hypothesis; #' Controls the type of CI returned: `"two.sided"` (default, two-sided CI), #' `"greater"` or `"less"` (one-sided CI). Partial matching is allowed (e.g., #' `"g"`, `"l"`, `"two"`...). See *One-Sided CIs* in [effectsize_CIs]. #' @param data An optional data frame containing the variables. #' @param pooled_sd If `TRUE` (default), a [sd_pooled()] is used (assuming equal #' variance). Else the mean SD from both groups is used instead. #' @param paired If `TRUE`, the values of `x` and `y` are considered as paired. #' This produces an effect size that is equivalent to the one-sample effect #' size on `x - y`. #' @param iterations,correction deprecated. #' @inheritParams chisq_to_phi #' @inheritParams eta_squared #' @inheritParams stats::t.test #' #' @note The indices here give the population estimated standardized difference. #' Some statistical packages give the sample estimate instead (without #' applying Bessel's correction). #' #' @details #' Set `pooled_sd = FALSE` for effect sizes that are to accompany a Welch's #' *t*-test (Delacre et al, 2021). #' #' @inheritSection effectsize_CIs Confidence (Compatibility) Intervals (CIs) #' @inheritSection effectsize_CIs CIs and Significance Tests #' #' @return A data frame with the effect size ( `Cohens_d`, `Hedges_g`, #' `Glass_delta`) and their CIs (`CI_low` and `CI_high`). #' #' @seealso [d_to_cles()] [sd_pooled()] #' @family effect size indices #' #' @examples #' \donttest{ #' data(mtcars) #' mtcars$am <- factor(mtcars$am) #' #' # Two Independent Samples ---------- #' #' (d <- cohens_d(mpg ~ am, data = mtcars)) #' # Same as: #' # cohens_d("mpg", "am", data = mtcars) #' # cohens_d(mtcars$mpg[mtcars$am=="0"], mtcars$mpg[mtcars$am=="1"]) #' #' # More options: #' cohens_d(mpg ~ am, data = mtcars, pooled_sd = FALSE) #' cohens_d(mpg ~ am, data = mtcars, mu = -5) #' cohens_d(mpg ~ am, data = mtcars, alternative = "less") #' hedges_g(mpg ~ am, data = mtcars) #' glass_delta(mpg ~ am, data = mtcars) #' #' #' # One Sample ---------- #' #' cohens_d(wt ~ 1, data = mtcars) #' #' # same as: #' # cohens_d("wt", data = mtcars) #' # cohens_d(mtcars$wt) #' #' # More options: #' cohens_d(wt ~ 1, data = mtcars, mu = 3) #' hedges_g(wt ~ 1, data = mtcars, mu = 3) #' #' #' # Paired Samples ---------- #' #' data(sleep) #' #' cohens_d(Pair(extra[group == 1], extra[group == 2]) ~ 1, data = sleep) #' #' # same as: #' # cohens_d(sleep$extra[sleep$group == 1], sleep$extra[sleep$group == 2], paired = TRUE) #' #' # More options: #' cohens_d(Pair(extra[group == 1], extra[group == 2]) ~ 1, data = sleep, mu = -1) #' hedges_g(Pair(extra[group == 1], extra[group == 2]) ~ 1, data = sleep) #' #' #' # Interpretation ----------------------- #' interpret_cohens_d(-1.48, rules = "cohen1988") #' interpret_hedges_g(-1.48, rules = "sawilowsky2009") #' interpret_glass_delta(-1.48, rules = "gignac2016") #' # Or: #' interpret(d, rules = "sawilowsky2009") #' #' # Common Language Effect Sizes #' d_to_cles(1.48) #' # Or: #' print(d, append_CLES = TRUE) #' } #' #' @references #' - Algina, J., Keselman, H. J., & Penfield, R. D. (2006). Confidence intervals #' for an effect size when variances are not equal. Journal of Modern Applied #' Statistical Methods, 5(1), 2. #' #' - Cohen, J. (1988). Statistical power analysis for the behavioral #' sciences (2nd Ed.). New York: Routledge. #' #' - Delacre, M., Lakens, D., Ley, C., Liu, L., & Leys, C. (2021, May 7). Why #' Hedges’ g*s based on the non-pooled standard deviation should be reported #' with Welch's t-test. https://doi.org/10.31234/osf.io/tu6mp #' #' - Hedges, L. V. & Olkin, I. (1985). Statistical methods for #' meta-analysis. Orlando, FL: Academic Press. #' #' - Hunter, J. E., & Schmidt, F. L. (2004). Methods of meta-analysis: #' Correcting error and bias in research findings. Sage. #' #' @importFrom stats var model.frame #' @export cohens_d <- function(x, y = NULL, data = NULL, pooled_sd = TRUE, mu = 0, paired = FALSE, ci = 0.95, alternative = "two.sided", verbose = TRUE, ...) { .effect_size_difference( x, y = y, data = data, type = "d", pooled_sd = pooled_sd, alternative = alternative, mu = mu, paired = paired, ci = ci, verbose = verbose, ... ) } #' @rdname cohens_d #' @export hedges_g <- function(x, y = NULL, data = NULL, pooled_sd = TRUE, mu = 0, paired = FALSE, ci = 0.95, alternative = "two.sided", verbose = TRUE, ..., correction) { if (!missing(correction)) { warning("`correction` argument is deprecated. *Exact* bias correction method is used.", call. = FALSE, immediate. = TRUE ) } .effect_size_difference( x, y = y, data = data, type = "g", pooled_sd = pooled_sd, alternative = alternative, mu = mu, paired = paired, ci = ci, verbose = verbose, ... ) } #' @rdname cohens_d #' @export glass_delta <- function(x, y = NULL, data = NULL, mu = 0, ci = 0.95, alternative = "two.sided", verbose = TRUE, ..., iterations) { if (!missing(iterations)) { warning("`iterations` argument is deprecated. Parametric CIs are estimated.", call. = FALSE, immediate. = TRUE ) } .effect_size_difference( x, y = y, data = data, alternative = alternative, mu = mu, type = "delta", ci = ci, verbose = verbose, pooled_sd = NULL, paired = FALSE, ... ) } #' @importFrom stats sd na.omit complete.cases #' @keywords internal .effect_size_difference <- function(x, y = NULL, data = NULL, type = "d", mu = 0, alternative = "two.sided", pooled_sd = TRUE, paired = FALSE, ci = 0.95, verbose = TRUE, ...) { if (type != "delta" && inherits(x, "htest")) { if (!grepl("t-test", x$method)) { stop("'x' is not a t-test!", call. = FALSE) } return(effectsize(x, type = type, verbose = verbose, ...)) } else if (type != "delta" && inherits(x, "BFBayesFactor")) { if (!inherits(x@numerator[[1]], c("BFoneSample", "BFindepSample"))) { stop("'x' is not a t-test!", call. = FALSE) } return(effectsize(x, ci = ci, verbose = verbose, ...)) } alternative <- match.arg(alternative, c("two.sided", "less", "greater")) out <- .get_data_2_samples(x, y, data, verbose) x <- out$x y <- out$y if (is.null(y)) { if (type == "delta") { stop("For Glass' Delta, please provide data from two samples.", call. = FALSE) } y <- rep(0, length.out = length(x)) paired <- TRUE } # Compute index if (paired) { o <- stats::complete.cases(x, y) x <- x[o] y <- y[o] d <- mean(x - y) n <- length(x) s <- stats::sd(x - y) hn <- 1 / (n - 1) se <- s / sqrt(n) df <- n - 1 pooled_sd <- NULL } else { x <- stats::na.omit(x) y <- stats::na.omit(y) d <- mean(x) - mean(y) s1 <- stats::sd(x) s2 <- stats::sd(y) n1 <- length(x) n2 <- length(y) n <- n1 + n2 if (type %in% c("d", "g")) { if (pooled_sd) { s <- suppressWarnings(sd_pooled(x, y)) hn <- (1 / n1 + 1 / n2) se <- s * sqrt(1 / n1 + 1 / n2) df <- n - 2 } else { s <- sqrt((s1^2 + s2^2) / 2) hn <- (2 * (n2 * s1^2 + n1 * s2^2)) / (n1 * n2 * (s1^2 + s2^2)) se1 <- sqrt(s1^2 / n1) se2 <- sqrt(s2^2 / n2) se <- sqrt(se1^2 + se2^2) df <- se^4 / (se1^4 / (n1 - 1) + se2^4 / (n2 - 1)) } } else if (type == "delta") { pooled_sd <- NULL s <- s2 hn <- 1 / n2 + s1^2 / (n1 * s2^2) se <- (s2 * sqrt(1 / n2 + s1^2 / (n1 * s2^2))) df <- n2 - 1 } } out <- data.frame(d = (d - mu) / s) types <- c("d" = "Cohens_d", "g" = "Hedges_g", "delta" = "Glass_delta") colnames(out) <- types[type] ci_method <- NULL if (is.numeric(ci)) { stopifnot(length(ci) == 1, ci < 1, ci > 0) # Add cis out$CI <- ci ci.level <- if (alternative == "two.sided") ci else 2 * ci - 1 t <- (d - mu) / se ts <- .get_ncp_t(t, df, ci.level) out$CI_low <- ts[1] * sqrt(hn) out$CI_high <- ts[2] * sqrt(hn) ci_method <- list(method = "ncp", distribution = "t") if (alternative == "less") { out$CI_low <- -Inf } else if (alternative == "greater") { out$CI_high <- Inf } } else { alternative <- NULL } if (type == "g") { J <- exp(lgamma(df / 2) - log(sqrt(df / 2)) - lgamma((df - 1) / 2)) # exact method out[, colnames(out) %in% c("Hedges_g", "CI_low", "CI_high")] <- out[, colnames(out) %in% c("Hedges_g", "CI_low", "CI_high")] * J } class(out) <- c("effectsize_difference", "effectsize_table", "see_effectsize_table", class(out)) attr(out, "paired") <- paired attr(out, "correction") <- type == "g" # Don't actually need this attr(out, "pooled_sd") <- pooled_sd attr(out, "mu") <- mu attr(out, "ci") <- ci attr(out, "ci_method") <- ci_method attr(out, "approximate") <- FALSE attr(out, "alternative") <- alternative return(out) }effectsize/R/print.rules.R0000644000175000017500000000240214170065645015352 0ustar nileshnilesh#' @importFrom utils head tail #' @export print.rules <- function(x, ...) { orig_x <- x name <- attr(x, "rule_name") if (length(x$values) == length(x$labels)) { title_type <- "Values" df <- data.frame( Labels = x$labels, Values = x$values ) out <- insight::export_table(df, align = "rl", sep = " ~ ") } else { title_type <- "Thresholds" if (isTRUE(attr(x, "right"))) { gLeft <- " < " gRight <- " <= " } else { gLeft <- " <= " gRight <- " < " } df <- data.frame( " " = c("", x$values), " " = c("", rep(gLeft, length(x$values))), Label = x$labels, " " = c(rep(gRight, length(x$values)), ""), " " = c(x$values, ""), check.names = FALSE ) out <- insight::export_table(df, align = "rcccl", sep = " ") } insight::print_color(sprintf("# Reference %s (%s)\n\n", title_type, name), "blue") cat(out) invisible(orig_x) } #' @export print.effectsize_interpret <- function(x, ...) { orig_x <- x name <- attr(x, "rule_name") attr(x, "rule_name") <- NULL class(x) <- class(x)[-1] print(x, ...) insight::print_color(paste0("(Rules: ", name, ")\n"), "blue") invisible(orig_x) } effectsize/R/format_standardize.R0000644000175000017500000000441014132466117016743 0ustar nileshnilesh#' Transform a standardized vector into character #' #' Transform a standardized vector into character, e.g., `c("-1 SD", "Mean", "+1 SD")`. #' #' @param x A standardized numeric vector. #' @param reference The reference vector from which to compute the mean and SD. #' @inheritParams standardize.default #' @inheritParams insight::format_value #' @param ... Other arguments to pass to \code{\link[insight:format_value]{insight::format_value()}} such as \code{digits}, etc. #' #' @examples #' format_standardize(c(-1, 0, 1)) #' format_standardize(c(-1, 0, 1, 2), reference = rnorm(1000)) #' format_standardize(c(-1, 0, 1, 2), reference = rnorm(1000), robust = TRUE) #' #' format_standardize(standardize(mtcars$wt), digits = 1) #' format_standardize(standardize(mtcars$wt, robust = TRUE), digits = 1) #' @importFrom stats median mad sd #' @importFrom insight format_value #' @export format_standardize <- function(x, reference = x, robust = FALSE, digits = 1, protect_integers = TRUE, ...) { # Check if robust info stored in attributes if ("robust" %in% names(attributes(reference))) { robust <- attributes(reference)$robust } # Find parameters and their names if (robust) { central <- stats::median(reference, na.rm = TRUE) central_name <- "Median" deviation <- stats::mad(reference, na.rm = TRUE) deviation_name <- "MAD" } else { central <- mean(reference, na.rm = TRUE) central_name <- "Mean" deviation <- stats::sd(reference, na.rm = TRUE) deviation_name <- "SD" } # See if they are not stored as attributes if ("center" %in% names(attributes(reference))) { central <- attributes(reference)$center } if ("scale" %in% names(attributes(reference))) { deviation <- attributes(reference)$scale } # Express in deviations if (length(x) != length(reference) || any(x != reference)) { x <- (x - central) / deviation } # Round x <- round(x, digits = digits) # Format vector as character L <- insight::format_value(x, digits = digits, ...) # Complete L[!grepl("-", L)] <- paste0("+", L[!grepl("-", L)]) L <- paste(L, deviation_name) L[x == 0] <- central_name # Order idx <- L[order(x, decreasing = TRUE)] factor(L, levels = unique(idx)) } effectsize/R/utils_validate_input_data.R0000644000175000017500000001061614170065645020314 0ustar nileshnilesh #' @keywords internal #' @importFrom stats terms #' @importFrom stats delete.response .get_data_2_samples <- function(x, y = NULL, data = NULL, verbose = TRUE) { # Sanity checks if (((is.character(x) && length(x) == 1) || (is.character(y) && length(y) == 1)) && is.null(data)) { stop("Please provide data argument.") } ## Pull columns ---- ### Formula ---- if (inherits(x, "formula")) { if (length(x) != 3) { stop("Formula must be two sided.", call. = FALSE) } mf <- stats::model.frame(formula = x, data = data) x <- mf[[1]] if (ncol(mf) == 1) { y <- NULL } else if (ncol(mf) == 2) { y <- mf[[2]] } else { stop("Formula must have only one term on the RHS.", call. = FALSE) } if (!is.null(y) && !is.factor(y)) y <- factor(y) } ### Character ---- if (is.character(x)) { if (!x %in% names(data)) { stop("Column ", x, " missing from data.", call. = FALSE) } x <- data[[x]] } if (is.character(y) && length(y) == 1) { if (!y %in% names(data)) { stop("Column ", y, " missing from data.", call. = FALSE) } y <- data[[y]] } ## Validate x,y ---- if (!is.numeric(x)) { stop("Cannot compute effect size for a non-numeric vector.", call. = FALSE) } else if (inherits(x, "Pair")) { x <- -apply(x, 1, diff) } # If y is a factor if (!is.null(y)) { if (!is.numeric(y)) { if (length(unique(y)) > 2) { stop("Grouping variable y has more that 2 levels.", call. = FALSE ) } if (ifelse(inherits(x, "Pair"), nrow(x), length(x)) != length(y)) { stop("Grouping variable must be the same length.", call. = FALSE) } data <- split(x, y) data <- Filter(length, data) x <- data[[1]] y <- data[[2]] } else { # Only relevant when y is not a factor if (verbose && length(unique(y)) == 2) { warning( "'y' is numeric but has only 2 unique values. If this is a grouping variable, convert it to a factor.", call. = FALSE ) } } } list(x = x, y = y) } #' @keywords internal #' @importFrom stats model.frame .get_data_multi_group <- function(x, groups, data) { if (inherits(frm <- x, "formula")) { mf <- stats::model.frame(formula = frm, data = data) if (length(frm) != 3 | ncol(mf) != 2) { stop("Formula must have the form of 'outcome ~ group'.", call. = FALSE) } x <- mf[[1]] groups <- factor(mf[[2]]) } else if (inherits(x, "list")) { groups <- rep(seq_along(x), sapply(x, length)) x <- unsplit(x, groups) } else if (is.character(x)) { x <- data[[x]] groups <- data[[groups]] } else if (length(x) != length(groups)) { stop("x and g must be of the same length.", call. = FALSE) } data.frame(x, groups) } #' @keywords internal #' @importFrom stats model.frame reshape .get_data_nested_groups <- function(x, groups, blocks, data = NULL) { if (inherits(frm <- x, "formula")) { if ((length(frm) != 3L) || (length(frm[[3L]]) != 3L) || (frm[[3L]][[1L]] != as.name("|"))) { stop("Formula must have the 'x ~ groups | blocks'.", call. = FALSE) } frm[[3L]][[1L]] <- as.name("+") mf <- stats::model.frame(formula = frm, data = data) if (ncol(mf) != 3) { stop("Formula must have only two terms on the RHS.", call. = FALSE) } x <- mf[[1]] groups <- mf[[2]] blocks <- mf[[3]] } else if (inherits(x, c("table", "matrix", "array", "data.frame"))) { data <- data.frame( x = c(x), groups = rep(factor(seq_len(ncol(x))), each = nrow(x) ), blocks = rep( factor(seq_len(nrow(x))), ncol(x) ) ) x <- data[[1]] groups <- data[[2]] blocks <- data[[3]] } else if (is.character(x)) { x <- data[[x]] groups <- data[[groups]] blocks <- data[[blocks]] } else if (length(x) != length(groups) || length(x) != length(blocks)) { stop("x, groups and blocks must be of the same length.", call. = FALSE) } data <- data.frame(x, groups, blocks, stringsAsFactors = FALSE) data <- stats::reshape( data, direction = "wide", v.names = "x", timevar = "groups", idvar = "blocks" ) as.matrix(data[, -1]) } effectsize/R/interpret_kendalls_w.R0000644000175000017500000000214614132466117017306 0ustar nileshnilesh#' Interpret Kendall's coefficient of concordance #' #' @param w Value or vector of Kendall's coefficient of concordance. #' @param rules Can be `"landis1977"` (default) or a custom set of [rules()]. #' #' @section Rules: #' #' - Landis & Koch (1977) (`"landis1977"`; default) #' - **0.00 <= w < 0.20** - Slight agreement #' - **0.20 <= w < 0.40** - Fair agreement #' - **0.40 <= w < 0.60** - Moderate agreement #' - **0.60 <= w < 0.80** - Substantial agreement #' - **w >= 0.80** - Almost perfect agreement #' #' @references #' - Landis, J. R., & Koch G. G. (1977). The measurement of observer agreement #' for categorical data. Biometrics, 33:159-74. #' #' @export #' interpret_kendalls_w <- function(w, rules = "landis1977") { rules <- .match.rules( rules, list( landis1977 = rules(c(0.2, 0.4, 0.6, 0.8), c( "slight agreement", "fair agreement", "moderate agreement", "substantial agreement", "almost perfect agreement" ), name = "landis1977", right = FALSE ) ) ) interpret(w, rules) } effectsize/R/convert_between_common_language.R0000644000175000017500000001010714170065645021472 0ustar nileshnilesh#' Convert Standardized Mean Difference to Common Language Effect Sizes #' #' @param d,rb A numeric value of Cohen's d / rank-biserial correlation *or* #' the output from [cohens_d()] / [rank_biserial()]. #' #' @details #' This function use the following formulae for Cohen's *d*: #' \deqn{Pr(superiority) = \Phi(d/\sqrt{2})}{Pr(superiority) = pnorm(d / sqrt(2))} #' \cr #' \deqn{Cohen's U_3 = \Phi(d)}{U3 = pnorm(d)} #' \cr #' \deqn{Overlap = 2 \times \Phi(-|d|/2)}{Overlap = 2 * pnorm(-abs(d) / 2)} #' \cr #' And the following for the rank-biserial correlation: #' \deqn{Pr(superiority) = (r_{rb} + 1)/2}{Pr(superiority) = (rb + 1)/2} #' #' @return A list of `Cohen's U3`, `Overlap`, `Pr(superiority)`, a #' numeric vector of `Pr(superiority)`, or a data frame, depending #' on the input. #' #' @note #' These calculations assume that the populations have equal variance and are #' normally distributed. #' #' @seealso [cohens_d()], [rank_biserial()] #' @family convert between effect sizes #' #' @references #' - Cohen, J. (1977). Statistical power analysis for the behavioral sciences. #' New York: Routledge. #' #' - Reiser, B., & Faraggi, D. (1999). Confidence intervals for the overlapping #' coefficient: the normal equal variance case. Journal of the Royal Statistical #' Society, 48(3), 413-418. #' #' - Ruscio, J. (2008). A probability-based measure of effect size: robustness #' to base rates and other factors. Psychological methods, 13(1), 19–30. #' #' @export #' @aliases convert_d_to_common_language d_to_common_language #' @importFrom stats pnorm d_to_cles <- function(d) { UseMethod("d_to_cles") } #' @export d_to_cles.numeric <- function(d) { list( "Pr(superiority)" = stats::pnorm(d / sqrt(2)), "Cohen's U3" = stats::pnorm(d), Overlap = 2 * stats::pnorm(-abs(d) / 2) ) } #' @export d_to_cles.effectsize_difference <- function(d) { if (!any(colnames(d) %in% c("Cohens_d", "Hedges_g")) || attr(d, "paired") || !attr(d, "pooled_sd")) { stop("Common language effect size only applicable to 2-sample Cohen's d with pooled SD.") } out <- lapply(d[,colnames(d) %in% c("Cohens_d", "Hedges_g", "CI_low", "CI_high")], function(x) unlist(d_to_cles(x))) out <- as.data.frame(out) out$Parameter <- rownames(out) rownames(out) <- NULL colnames(out)[1] <- "Coefficient" if ("CI" %in% colnames(d)) { out$CI <- d$CI out <- out[c("Parameter", "Coefficient", "CI", "CI_low", "CI_high")] if (d[[1]] > 0) { out[3, 4:5] <- out[3, 5:4] } if (sign(d$CI_low) != sign(d$CI_high)) { out$CI_high[3] <- 1 } } else { out <- out[c("Parameter", "Coefficient")] } class(out) <- c("effectsize_table", class(out)) out } #' @export #' @aliases rb_to_common_language convert_rb_to_common_language #' @rdname d_to_cles rb_to_cles <- function(rb) { UseMethod("rb_to_cles") } #' @export rb_to_cles.numeric <- function(rb) { (rb + 1)/2 } #' @export rb_to_cles.effectsize_difference <- function(rb) { if (!any(colnames(rb) == "r_rank_biserial") || attr(rb, "paired")) { stop("Common language effect size only applicable to 2-sample rank-biserial correlation.") } out <- lapply(rb[,colnames(rb) %in% c("r_rank_biserial", "CI_low", "CI_high")], rb_to_cles) out <- as.data.frame(out) out$Parameter <- "Pr(superiority)" rownames(out) <- NULL colnames(out)[1] <- "Coefficient" if ("CI" %in% colnames(rb)) { out$CI <- rb$CI out <- out[c("Parameter", "Coefficient", "CI", "CI_low", "CI_high")] } else { out <- out[c("Parameter", "Coefficient")] } class(out) <- c("effectsize_table", class(out)) attr(out, "table_footer") <- c("\n- Non-parametric CLES", "cyan") out } # Aliases ----------------------------------------------------------------- #' @export convert_d_to_common_language <- d_to_cles #' @export convert_rb_to_common_language <- rb_to_cles #' @export d_to_common_language <- d_to_cles #' @export rb_to_common_language <- rb_to_cles effectsize/inst/0000755000175000017500000000000014174212105013505 5ustar nileshnilesheffectsize/inst/CITATION0000644000175000017500000000136214170261271014650 0ustar nileshnileshbibentry( mheader = "To cite effectsize in publications use:", textVersion = paste( "Ben-Shachar M, Lüdecke D, Makowski D (2020). effectsize: Estimation of Effect Size Indices and Standardized Parameters.", "Journal of Open Source Software, 5(56), 2815. doi: 10.21105/joss.02815" ), # BibTeX info: bibtype="Article", title = "{e}ffectsize: Estimation of Effect Size Indices and Standardized Parameters", author = "Mattan S. Ben-Shachar and Daniel Lüdecke and Dominique Makowski", year = "2020", journal = "Journal of Open Source Software", volume = "5", number = "56", pages = "2815", publisher = "The Open Journal", doi = "10.21105/joss.02815", url = "https://doi.org/10.21105/joss.02815" )effectsize/inst/WORDLIST0000644000175000017500000000323714132466117014714 0ustar nileshnileshADF AGFI ANOVA's APA's Agadullina Albers Algina Awang Azen BESD Baptista BayestestR's Behaviour Bergsma Biometrics Bmj Bollen's Borenstein Byrne Bürkner CFA CFI Cesarini Chacón Chisq Codecov Cramer's Cramér's Cureton DOI Delacre EQS ESS Erlbaum Falk Faraggi Funder GFI GLMM Gelman Gignac Gignac's Glass’ Guilford Gustafson HDI HLM Hedges’ Hoekstra IFI IRR IRRs Jarosz Jeffreys Johannesson Katz Kerby Keselman Kruschke Kutner LMM LMMs Lakens Landis Ley Leys Liu Lomax Lovakov MADs MLM Mahwah Martínez Marín McNemar McNemar's Meca Minium Mordkoff Morey Moscoso NCP NFI NNFI Neter Noncentrality Nordholm Normed Nosek Olejnik Olkin Ozer PLOS PNFI PPD Pearon's Penfield Psychometrika Psychonomic RFI RMR RMSEA Raftery Reiser Rescale Rhat Ringle Rosenthal Rothstein Rouder Routledge Ruscio SDs SEM SEs SRMR Sarstedt Sawilowsky Schumacker Shachar Shmekels Steiger Szodorai Szumilas Sánchez TLI TOST Tomczak Tschuprow's Un Vehtari Verkuilen Verkuilen's Wagenmakers Waggoner Wasserman Welch's Wilcoxon's al anova arXiv arcsin automagically biserial brglm brms brussels cbu cfa cgam cglm complmrob cplm df dichotomize doi dynamicfit easystats effectSize et fixest frac friedman github glmmadmb http https ifelse infty interpretability io joss kruskal lm mis mixor modelling mrc ncp ncps nd noncentral osf partialization partialled partilled pb pkgdown posthoc ppd pre preprint reproducibility rescale rescaled rescaling rmANOVA sd statswiki th tu uk un unitless varialbe visualise wilcox effectsize/inst/doc/0000755000175000017500000000000014174212105014252 5ustar nileshnilesheffectsize/inst/doc/simple_htests.Rmd0000644000175000017500000002256714170072533017622 0ustar nileshnilesh--- title: "Effect Sizes for Simple Hypothesis Tests" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, conversion] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Effect Sizes for Simple Hypothesis Tests} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r setup, include=FALSE} library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 3) pkgs <- c("effectsize", "BayesFactor") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(7) ``` This vignette provides a short review of effect sizes for common hypothesis tests (in **`R`** these are usually achieved with various `*.test()` functions). ```{r} library(effectsize) library(BayesFactor) ``` In most cases, the effect sizes can be automagically extracted from the `htest` object via the `effectsize()` function. ## Standardized Differences For *t*-tests, it is common to report an effect size representing a standardized difference between the two compared samples' means. These measures range from $-\infty$ to $+\infty$, with negative values indicating the second group's mean is larger (and vice versa). ### Two Independent Samples For two independent samples, the difference between the means is standardized based on the pooled standard deviation of both samples (assumed to be equal in the population): ```{r} t.test(mpg ~ am, data = mtcars, var.equal = TRUE) cohens_d(mpg ~ am, data = mtcars) ``` Hedges' *g* provides a bias correction for small sample sizes ($N < 20$). ```{r} hedges_g(mpg ~ am, data = mtcars) ``` If variances cannot be assumed to be equal, it is possible to get estimates that are not based on the pooled standard deviation: ```{r} t.test(mpg ~ am, data = mtcars, var.equal = FALSE) cohens_d(mpg ~ am, data = mtcars, pooled_sd = FALSE) hedges_g(mpg ~ am, data = mtcars, pooled_sd = FALSE) ``` In cases where the differences between the variances are substantial, it is also common to standardize the difference based only on the standard deviation of one of the groups (usually the "control" group); this effect size is known as Glass' $\Delta$ (delta) (Note that the standard deviation is taken from the *second* sample). ```{r} glass_delta(mpg ~ am, data = mtcars) ``` For a one-sided hypothesis, it is also possible to construct one-sided confidence intervals: ```{r} t.test(mpg ~ am, data = mtcars, var.equal = TRUE, alternative = "less") cohens_d(mpg ~ am, data = mtcars, pooled_sd = TRUE, alternative = "less") ``` #### Common Language Effect Sizes Related effect sizes are the *common language effect sizes* which present information about group differences in terms of probability. ```{r} cles(mpg ~ am, data = mtcars) ``` ### One Sample and Paired Samples In the case of a one-sample test, the effect size represents the standardized distance of the mean of the sample from the null value. For paired-samples, the difference between the paired samples is used: ```{r} t.test(extra ~ group, data = sleep, paired = TRUE) cohens_d(extra ~ group, data = sleep, paired = TRUE) hedges_g(extra ~ group, data = sleep, paired = TRUE) ``` ### For a Bayesian *t*-test ```{r} (BFt <- ttestBF(mtcars$mpg[mtcars$am == 0], mtcars$mpg[mtcars$am == 1])) effectsize(BFt, test = NULL) ``` ## One way ANOVA For more details, see [ANOVA vignette](https://easystats.github.io/effectsize/articles/anovaES.html). ```{r, message=FALSE} onew <- oneway.test(mpg ~ gear, data = mtcars, var.equal = TRUE) eta_squared(onew) ``` ## Contingency Tables and Proportions For contingency tables Cramér's *V*, $\phi$ (Phi, also known as Cohen's *w*) and Pearson's contingency coefficient indicate the strength of association with 0 indicating no association between the variables. While Cramér's *V* and Pearson's *C* are capped at 1 (perfect association), $\phi$ can be larger than 1. ```{r} (Music <- matrix( c( 150, 130, 35, 55, 100, 50, 10, 40, 165, 65, 2, 25 ), byrow = TRUE, nrow = 3, dimnames = list( Study = c("Psych", "Econ", "Law"), Music = c("Pop", "Rock", "Jazz", "Classic") ) )) chisq.test(Music) cramers_v(Music) phi(Music) pearsons_c(Music) ``` Pearson's *C* and $\phi$ are also applicable to tests of goodness-of-fit, where small values indicate no deviation from the hypothetical probabilities and large values indicate... large deviation from the hypothetical probabilities. ```{r} O <- c(89, 37, 130, 28, 2) # observed group sizes E <- c(.40, .20, .20, .15, .05) # expected group freq chisq.test(O, p = E, rescale.p = TRUE) pearsons_c(O, p = E, rescale.p = TRUE) phi(O, p = E, rescale.p = TRUE) ``` These can also be extracted from the equivalent Bayesian test: ```{r} (BFX <- contingencyTableBF(Music, sampleType = "jointMulti")) effectsize(BFX, type = "cramers_v", test = NULL) effectsize(BFX, type = "phi", test = NULL) effectsize(BFX, type = "pearsons_c", test = NULL) ``` ### Comparing Two Proportions (2x2 tables) For $2\times 2$ tables, in addition to Cramér's *V*, $\phi$ and Pearson's *C*, we can also compute the Odds-ratio (OR), where each column represents a different group. Values larger than 1 indicate that the odds are higher in the first group (and vice versa). ```{r} (RCT <- matrix( c( 71, 30, 50, 100 ), nrow = 2, byrow = TRUE, dimnames = list( Diagnosis = c("Sick", "Recovered"), Group = c("Treatment", "Control") ) )) chisq.test(RCT) # or fisher.test(RCT) oddsratio(RCT) ``` We can also compute the Risk-ratio (RR), which is the ratio between the proportions of the two groups - a measure which some claim is more intuitive. ```{r} riskratio(RCT) ``` Additionally, Cohen's *h* can also be computed, which uses the *arcsin* transformation. Negative values indicate smaller proportion in the first group (and vice versa). ```{r} cohens_h(RCT) ``` ### Paired Contingency Tables For dependent (paired) contingency tables, Cohen's *g* represents the symmetry of the table, ranging between 0 (perfect symmetry) and 0.5 (perfect asymmetry). ```{r} (Performance <- matrix( c( 794, 86, 150, 570 ), nrow = 2, byrow = TRUE, dimnames = list( "1st Survey" = c("Approve", "Disapprove"), "2nd Survey" = c("Approve", "Disapprove") ) )) mcnemar.test(Performance) cohens_g(Performance) ``` ## Rank Based tests Rank based tests get rank based effect sizes! ### Difference in Ranks For two independent samples, the rank-biserial correlation ($r_{rb}$) is a measure of relative superiority - i.e., larger values indicate a higher probability of a randomly selected observation from *X* being larger than randomly selected observation from *Y*. A value of $(-1)$ indicates that all observations in the second group are larger than the first, and a value of $(+1)$ indicates that all observations in the first group are larger than the second. ```{r, warning=FALSE} A <- c(48, 48, 77, 86, 85, 85) B <- c(14, 34, 34, 77) wilcox.test(A, B) # aka Mann–Whitney U test rank_biserial(A, B) ``` Here too we have a *common language effect size*: ```{r} cles(A, B, rank = TRUE) ``` For one sample, $r_{rb}$ measures the symmetry around $\mu$ (mu; the null value), with 0 indicating perfect symmetry, $(-1)$ indicates that all observations fall below $\mu$, and $(+1)$ indicates that all observations fall above $\mu$. For paired samples the difference between the paired samples is used: ```{r} x <- c(1.15, 0.88, 0.90, 0.74, 1.21, 1.36, 0.89) wilcox.test(x, mu = 1) # aka Signed-Rank test rank_biserial(x, mu = 1) x <- c(1.83, 0.50, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.30) y <- c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29) wilcox.test(x, y, paired = TRUE) # aka Signed-Rank test rank_biserial(x, y, paired = TRUE) ``` ### Rank One way ANOVA The Rank-Epsilon-Squared ($\varepsilon^2$) is a measure of association for the rank based one-way ANOVA. Values range between 0 (no relative superiority between any of the groups) to 1 (complete separation - with no overlap in ranks between the groups). ```{r} group_data <- list( g1 = c(2.9, 3.0, 2.5, 2.6, 3.2), # normal subjects g2 = c(3.8, 2.7, 4.0, 2.4), # with obstructive airway disease g3 = c(2.8, 3.4, 3.7, 2.2, 2.0) # with asbestosis ) kruskal.test(group_data) rank_epsilon_squared(group_data) ``` ### Rank One way Repeated-Measures ANOVA For a rank based repeated measures one-way ANOVA, Kendall's *W* is a measure of agreement on the effect of condition between various "blocks" (the subjects), or more often conceptualized as a measure of reliability of the rating / scores of observations (or "groups") between "raters" ("blocks"). ```{r} # Subjects are COLUMNS (ReactionTimes <- matrix( c(398, 338, 520, 325, 388, 555, 393, 363, 561, 367, 433, 470, 286, 492, 536, 362, 475, 496, 253, 334, 610), nrow = 7, byrow = TRUE, dimnames = list( paste0("Subject", 1:7), c("Congruent", "Neutral", "Incongruent") ) )) friedman.test(ReactionTimes) kendalls_w(ReactionTimes) ``` # References effectsize/inst/doc/effectsize.Rmd0000644000175000017500000002560514170302654017062 0ustar nileshnilesh--- title: "Effect Sizes: Getting Started" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, conversion] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{effectsize} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` # Aims of the Package In both theoretical and applied research, it is often of interest to assess the strength of an observed association. This is typically done to allow the judgment of the magnitude of an effect [especially when units of measurement are not meaningful, e.g., in the use of estimated latent variables; @bollen1989structural], to facilitate comparing between predictors' importance within a given model, or both. Though some indices of effect size, such as the correlation coefficient (itself a standardized covariance coefficient) are readily available, other measures are often harder to obtain. **effectsize** is an R package [@rcore] that fills this important gap, providing utilities for easily estimating a wide variety of standardized effect sizes (i.e., effect sizes that are not tied to the units of measurement of the variables of interest) and their confidence intervals (CIs), from a variety of statistical models. **effectsize** provides easy-to-use functions, with full documentation and explanation of the various effect sizes offered, and is also used by developers of other R packages as the back-end for effect size computation, such as **parameters** [@ludecke2020extracting], **ggstatsplot** [@patil2020ggstatsplot], **gtsummary** [@sjoberg2020gtsummary] and more. # Comparison to Other Packages **effectsize**'s functionality is in part comparable to packages like **lm.beta** [@behrendt2014lmbeta], **MOTE** [@buchanan2019MOTE], and **MBESS** [@kelley2020MBESS]. Yet, there are some notable differences, e.g.: - **lm.beta** provides standardized regression coefficients for linear models, based on post-hoc model matrix standardization. However, the functionality is available only for a limited number of models (models inheriting from the `lm` class), whereas **effectsize** provides support for many types of models, including (generalized) linear mixed models, Bayesian models, and more. Additionally, in additional to post-hoc model matrix standardization, **effectsize** offers other methods of standardization (see below). - Both **MOTE** and **MBESS** provide functions for computing effect sizes such as Cohen's *d* and effect sizes for ANOVAs [@cohen1988statistical], and their confidence intervals. However, both require manual input of *F*- or *t*-statistics, *degrees of freedom*, and *sums of squares* for the computation the effect sizes, whereas **effectsize** can automatically extract this information from the provided models, thus allowing for better ease-of-use as well as reducing any potential for error. # Examples of Features **effectsize** provides various functions for extracting and estimating effect sizes and their confidence intervals [estimated using the noncentrality parameter method; @steiger2004beyond]. In this article, we provide basic usage examples for estimating some of the most common effect size. A comprehensive overview, including in-depth examples and [a full list of features and functions](https://easystats.github.io/effectsize/reference/index.html), are accessible via a dedicated website (https://easystats.github.io/effectsize/). ## Indices of Effect Size ### Standardized Differences **effectsize** provides functions for estimating the common indices of standardized differences such as Cohen's *d* (`cohens_d()`), Hedges' *g* (`hedges_g()`) for both paired and independent samples [@cohen1988statistical; @hedges1985statistical], and Glass' $\Delta$ (`glass_delta()`) for independent samples with different variances [@hedges1985statistical]. ```{r} library(effectsize) cohens_d(mpg ~ am, data = mtcars) ``` ### Contingency Tables Pearson's $\phi$ (`phi()`) and Cramér's *V* (`cramers_v()`) can be used to estimate the strength of association between two categorical variables [@cramer1946mathematical], while Cohen's *g* (`cohens_g()`) estimates the deviance between paired categorical variables [@cohen1988statistical]. ```{r} M <- rbind(c(150, 130, 35, 55), c(100, 50, 10, 40), c(165, 65, 2, 25)) cramers_v(M) ``` ## Parameter and Model Standardization Standardizing parameters (i.e., coefficients) can allow for their comparison within and between models, variables and studies. To this end, two functions are available: `standardize()`, which returns an updated model, re-fit with standardized data, and `standardize_parameters()`, which returns a table of standardized coefficients from a provided model [for a list of supported models, see the *insight* package; @luedecke2019insight]. ```{r} model <- lm(mpg ~ cyl * am, data = mtcars) standardize(model) standardize_parameters(model) ``` Standardized parameters can also be produced for generalized linear models (GLMs; where only the predictors are standardized): ```{r} model <- glm(am ~ cyl + hp, family = "binomial", data = mtcars) standardize_parameters(model, exponentiate = TRUE) ``` `standardize_parameters()` provides several standardization methods, such as robust standardization, or *pseudo*-standardized coefficients for (generalized) linear mixed models [@hoffman2015longitudinal]. A full review of these methods can be found in the [*Parameter and Model Standardization* vignette](https://easystats.github.io/effectsize/articles/standardize_parameters.html). ## Effect Sizes for ANOVAs Unlike standardized parameters, the effect sizes reported in the context of ANOVAs (analysis of variance) or ANOVA-like tables represent the amount of variance explained by each of the model's terms, where each term can be represented by one or more parameters. `eta_squared()` can produce such popular effect sizes as Eta-squared ($\eta^2$), its partial version ($\eta^2_p$), as well as the generalized $\eta^2_G$ [@cohen1988statistical; @olejnik2003generalized]: ```{r} options(contrasts = c('contr.sum', 'contr.poly')) data("ChickWeight") # keep only complete cases and convert `Time` to a factor ChickWeight <- subset(ChickWeight, ave(weight, Chick, FUN = length) == 12) ChickWeight$Time <- factor(ChickWeight$Time) model <- aov(weight ~ Diet * Time + Error(Chick / Time), data = ChickWeight) eta_squared(model, partial = TRUE) eta_squared(model, generalized = "Time") ``` **effectsize** also offers $\epsilon^2_p$ (`epsilon_squared()`) and $\omega^2_p$ (`omega_squared()`), which are less biased estimates of the variance explained in the population [@kelley1935unbiased; @olejnik2003generalized]. For more details about the various effect size measures and their applications, see the [*Effect sizes for ANOVAs* vignette](https://easystats.github.io/effectsize/articles/anovaES.html). ## Effect Size Conversion ### From Test Statistics In many real world applications there are no straightforward ways of obtaining standardized effect sizes. However, it is possible to get approximations of most of the effect size indices (*d*, *r*, $\eta^2_p$...) with the use of test statistics [@friedman1982simplified]. These conversions are based on the idea that test statistics are a function of effect size and sample size (or more often of degrees of freedom). Thus it is possible to reverse-engineer indices of effect size from test statistics (*F*, *t*, $\chi^2$, and *z*). ```{r} F_to_eta2(f = c(40.72, 33.77), df = c(2, 1), df_error = c(18, 9)) t_to_d(t = -5.14, df_error = 22) t_to_r(t = -5.14, df_error = 22) ``` These functions also power the `effectsize()` convenience function for estimating effect sizes from R's `htest`-type objects. For example: ```{r} data(hardlyworking, package = "effectsize") aov1 <- oneway.test(salary ~ n_comps, data = hardlyworking, var.equal = TRUE) effectsize(aov1) xtab <- rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477)) Xsq <- chisq.test(xtab) effectsize(Xsq) ``` These functions also power our *Effect Sizes From Test Statistics* shiny app (https://easystats4u.shinyapps.io/statistic2effectsize/). ### Between Effect Sizes For comparisons between different types of designs and analyses, it is useful to be able to convert between different types of effect sizes [*d*, *r*, Odds ratios and Risk ratios; @borenstein2009converting; @grant2014converting]. ```{r} r_to_d(0.7) d_to_oddsratio(1.96) oddsratio_to_riskratio(34.99, p0 = 0.4) oddsratio_to_r(34.99) ``` ## Effect Size Interpretation Finally, **effectsize** provides convenience functions to apply existing or custom interpretation rules of thumb, such as for instance Cohen's (1988). Although we strongly advocate for the cautious and parsimonious use of such judgment-replacing tools, we provide these functions to allow users and developers to explore and hopefully gain a deeper understanding of the relationship between data values and their interpretation. More information is available in the [*Automated Interpretation of Indices of Effect Size* vignette](https://easystats.github.io/effectsize/articles/interpret.html). ```{r} interpret_cohens_d(c(0.02, 0.52, 0.86), rules = "cohen1988") ``` # Licensing and Availability **effectsize** is licensed under the GNU General Public License (v3.0), with all source code stored at GitHub (https://github.com/easystats/effectsize), and with a corresponding issue tracker for bug reporting and feature enhancements. In the spirit of honest and open science, we encourage requests/tips for fixes, feature updates, as well as general questions and concerns via direct interaction with contributors and developers, by [filing an issue](https://github.com/easystats/effectsize/issues/). See the package's [*Contribution Guidelines*](https://github.com/easystats/effectsize/blob/main/.github/CONTRIBUTING.md/). # Acknowledgments **effectsize** is part of the [*easystats*](https://github.com/easystats/easystats/) ecosystem, a collaborative project created to facilitate the usage of R for statistical analyses. Thus, we would like to thank the [members of easystats](https://github.com/orgs/easystats/people/) as well as the users. # References effectsize/inst/doc/effectsize.R0000644000175000017500000000447614174212062016541 0ustar nileshnilesh## ---- include = FALSE--------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- library(effectsize) cohens_d(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- M <- rbind(c(150, 130, 35, 55), c(100, 50, 10, 40), c(165, 65, 2, 25)) cramers_v(M) ## ----------------------------------------------------------------------------- model <- lm(mpg ~ cyl * am, data = mtcars) standardize(model) standardize_parameters(model) ## ----------------------------------------------------------------------------- model <- glm(am ~ cyl + hp, family = "binomial", data = mtcars) standardize_parameters(model, exponentiate = TRUE) ## ----------------------------------------------------------------------------- options(contrasts = c('contr.sum', 'contr.poly')) data("ChickWeight") # keep only complete cases and convert `Time` to a factor ChickWeight <- subset(ChickWeight, ave(weight, Chick, FUN = length) == 12) ChickWeight$Time <- factor(ChickWeight$Time) model <- aov(weight ~ Diet * Time + Error(Chick / Time), data = ChickWeight) eta_squared(model, partial = TRUE) eta_squared(model, generalized = "Time") ## ----------------------------------------------------------------------------- F_to_eta2(f = c(40.72, 33.77), df = c(2, 1), df_error = c(18, 9)) t_to_d(t = -5.14, df_error = 22) t_to_r(t = -5.14, df_error = 22) ## ----------------------------------------------------------------------------- data(hardlyworking, package = "effectsize") aov1 <- oneway.test(salary ~ n_comps, data = hardlyworking, var.equal = TRUE) effectsize(aov1) xtab <- rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477)) Xsq <- chisq.test(xtab) effectsize(Xsq) ## ----------------------------------------------------------------------------- r_to_d(0.7) d_to_oddsratio(1.96) oddsratio_to_riskratio(34.99, p0 = 0.4) oddsratio_to_r(34.99) ## ----------------------------------------------------------------------------- interpret_cohens_d(c(0.02, 0.52, 0.86), rules = "cohen1988") effectsize/inst/doc/simple_htests.html0000644000175000017500000015316114174212075020040 0ustar nileshnilesh Effect Sizes for Simple Hypothesis Tests

Effect Sizes for Simple Hypothesis Tests

This vignette provides a short review of effect sizes for common hypothesis tests (in R these are usually achieved with various *.test() functions).

library(effectsize)
library(BayesFactor)

In most cases, the effect sizes can be automagically extracted from the htest object via the effectsize() function.

Standardized Differences

For t-tests, it is common to report an effect size representing a standardized difference between the two compared samples’ means. These measures range from \(-\infty\) to \(+\infty\), with negative values indicating the second group’s mean is larger (and vice versa).

Two Independent Samples

For two independent samples, the difference between the means is standardized based on the pooled standard deviation of both samples (assumed to be equal in the population):

t.test(mpg ~ am, data = mtcars, var.equal = TRUE)
> 
>   Two Sample t-test
> 
> data:  mpg by am
> t = -4, df = 30, p-value = 3e-04
> alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
> 95 percent confidence interval:
>  -10.85  -3.64
> sample estimates:
> mean in group 0 mean in group 1 
>            17.1            24.4
cohens_d(mpg ~ am, data = mtcars)
> Cohen's d |         95% CI
> --------------------------
> -1.48     | [-2.27, -0.67]
> 
> - Estimated using pooled SD.

Hedges’ g provides a bias correction for small sample sizes (\(N < 20\)).

hedges_g(mpg ~ am, data = mtcars)
> Hedges' g |         95% CI
> --------------------------
> -1.44     | [-2.21, -0.65]
> 
> - Estimated using pooled SD.

If variances cannot be assumed to be equal, it is possible to get estimates that are not based on the pooled standard deviation:

t.test(mpg ~ am, data = mtcars, var.equal = FALSE)
> 
>   Welch Two Sample t-test
> 
> data:  mpg by am
> t = -4, df = 18, p-value = 0.001
> alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
> 95 percent confidence interval:
>  -11.28  -3.21
> sample estimates:
> mean in group 0 mean in group 1 
>            17.1            24.4
cohens_d(mpg ~ am, data = mtcars, pooled_sd = FALSE)
> Cohen's d |         95% CI
> --------------------------
> -1.41     | [-2.26, -0.53]
> 
> - Estimated using un-pooled SD.
hedges_g(mpg ~ am, data = mtcars, pooled_sd = FALSE)
> Hedges' g |         95% CI
> --------------------------
> -1.35     | [-2.17, -0.51]
> 
> - Estimated using un-pooled SD.

In cases where the differences between the variances are substantial, it is also common to standardize the difference based only on the standard deviation of one of the groups (usually the “control” group); this effect size is known as Glass’ \(\Delta\) (delta) (Note that the standard deviation is taken from the second sample).

glass_delta(mpg ~ am, data = mtcars)
> Glass' delta |         95% CI
> -----------------------------
> -1.17        | [-1.93, -0.39]

For a one-sided hypothesis, it is also possible to construct one-sided confidence intervals:

t.test(mpg ~ am, data = mtcars, var.equal = TRUE, alternative = "less")
> 
>   Two Sample t-test
> 
> data:  mpg by am
> t = -4, df = 30, p-value = 1e-04
> alternative hypothesis: true difference in means between group 0 and group 1 is less than 0
> 95 percent confidence interval:
>   -Inf -4.25
> sample estimates:
> mean in group 0 mean in group 1 
>            17.1            24.4
cohens_d(mpg ~ am, data = mtcars, pooled_sd = TRUE, alternative = "less")
> Cohen's d |        95% CI
> -------------------------
> -1.48     | [-Inf, -0.80]
> 
> - Estimated using pooled SD.
> - One-sided CIs: lower bound fixed at (-Inf).

Common Language Effect Sizes

Related effect sizes are the common language effect sizes which present information about group differences in terms of probability.

cles(mpg ~ am, data = mtcars)
> Parameter       | Coefficient |       95% CI
> --------------------------------------------
> Pr(superiority) |        0.15 | [0.05, 0.32]
> Cohen's U3      |        0.07 | [0.01, 0.25]
> Overlap         |        0.46 | [0.26, 0.74]

One Sample and Paired Samples

In the case of a one-sample test, the effect size represents the standardized distance of the mean of the sample from the null value. For paired-samples, the difference between the paired samples is used:

t.test(extra ~ group, data = sleep, paired = TRUE)
> 
>   Paired t-test
> 
> data:  extra by group
> t = -4, df = 9, p-value = 0.003
> alternative hypothesis: true difference in means is not equal to 0
> 95 percent confidence interval:
>  -2.46 -0.70
> sample estimates:
> mean of the differences 
>                   -1.58
cohens_d(extra ~ group, data = sleep, paired = TRUE)
> Cohen's d |         95% CI
> --------------------------
> -1.28     | [-2.23, -0.44]
hedges_g(extra ~ group, data = sleep, paired = TRUE)
> Hedges' g |         95% CI
> --------------------------
> -1.17     | [-2.04, -0.40]

For a Bayesian t-test

(BFt <- ttestBF(mtcars$mpg[mtcars$am == 0], mtcars$mpg[mtcars$am == 1]))
> Bayes factor analysis
> --------------
> [1] Alt., r=0.707 : 86.6 ±0%
> 
> Against denominator:
>   Null, mu1-mu2 = 0 
> ---
> Bayes factor type: BFindepSample, JZS
effectsize(BFt, test = NULL)
> Cohen's d |         95% CI
> --------------------------
> -1.29     | [-2.09, -0.51]

One way ANOVA

For more details, see ANOVA vignette.

onew <- oneway.test(mpg ~ gear, data = mtcars, var.equal = TRUE)

eta_squared(onew)
> # Effect Size for ANOVA
> 
> Eta2 |       95% CI
> -------------------
> 0.43 | [0.18, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Contingency Tables and Proportions

For contingency tables Cramér’s V, \(\phi\) (Phi, also known as Cohen’s w) and Pearson’s contingency coefficient indicate the strength of association with 0 indicating no association between the variables. While Cramér’s V and Pearson’s C are capped at 1 (perfect association), \(\phi\) can be larger than 1.

(Music <- matrix(
  c(
    150, 130, 35, 55,
    100, 50, 10, 40,
    165, 65, 2, 25
  ),
  byrow = TRUE, nrow = 3,
  dimnames = list(
    Study = c("Psych", "Econ", "Law"),
    Music = c("Pop", "Rock", "Jazz", "Classic")
  )
))
>        Music
> Study   Pop Rock Jazz Classic
>   Psych 150  130   35      55
>   Econ  100   50   10      40
>   Law   165   65    2      25
chisq.test(Music)
> 
>   Pearson's Chi-squared test
> 
> data:  Music
> X-squared = 52, df = 6, p-value = 2e-09
cramers_v(Music)
> Cramer's V |       95% CI
> -------------------------
> 0.18       | [0.13, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
phi(Music)
> Phi  |       95% CI
> -------------------
> 0.25 | [0.18, 1.41]
> 
> - One-sided CIs: upper bound fixed at (1.4142135623731).
pearsons_c(Music)
> Pearson's C |       95% CI
> --------------------------
> 0.24        | [0.18, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Pearson’s C and \(\phi\) are also applicable to tests of goodness-of-fit, where small values indicate no deviation from the hypothetical probabilities and large values indicate… large deviation from the hypothetical probabilities.

O <- c(89,  37,  130, 28,  2) # observed group sizes
E <- c(.40, .20, .20, .15, .05) # expected group freq

chisq.test(O, p = E, rescale.p = TRUE)
> 
>   Chi-squared test for given probabilities
> 
> data:  O
> X-squared = 121, df = 4, p-value <2e-16
pearsons_c(O, p = E, rescale.p = TRUE)
> Pearson's C |       95% CI
> --------------------------
> 0.55        | [0.48, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
phi(O, p = E, rescale.p = TRUE)
> Phi  |           95% CI
> -----------------------
> 0.65 | [0.54,      Inf]
> 
> - One-sided CIs: upper bound fixed at (Inf).

These can also be extracted from the equivalent Bayesian test:

(BFX <- contingencyTableBF(Music, sampleType = "jointMulti"))
> Bayes factor analysis
> --------------
> [1] Non-indep. (a=1) : 10053377 ±0%
> 
> Against denominator:
>   Null, independence, a = 1 
> ---
> Bayes factor type: BFcontingencyTable, joint multinomial
effectsize(BFX, type = "cramers_v", test = NULL)
> Cramer's V |       95% CI
> -------------------------
> 0.18       | [0.14, 0.22]
effectsize(BFX, type = "phi", test = NULL)
> Phi  |       95% CI
> -------------------
> 0.26 | [0.19, 0.31]
effectsize(BFX, type = "pearsons_c", test = NULL)
> Pearson's C |       95% CI
> --------------------------
> 0.25        | [0.19, 0.30]

Comparing Two Proportions (2x2 tables)

For \(2\times 2\) tables, in addition to Cramér’s V, \(\phi\) and Pearson’s C, we can also compute the Odds-ratio (OR), where each column represents a different group. Values larger than 1 indicate that the odds are higher in the first group (and vice versa).

(RCT <- matrix(
  c(
    71, 30,
    50, 100
  ),
  nrow = 2, byrow = TRUE,
  dimnames = list(
    Diagnosis = c("Sick", "Recovered"),
    Group = c("Treatment", "Control")
  )
))
>            Group
> Diagnosis   Treatment Control
>   Sick             71      30
>   Recovered        50     100
chisq.test(RCT) # or fisher.test(RCT)
> 
>   Pearson's Chi-squared test with Yates' continuity correction
> 
> data:  RCT
> X-squared = 32, df = 1, p-value = 2e-08
oddsratio(RCT)
> Odds ratio |       95% CI
> -------------------------
> 4.73       | [2.74, 8.17]

We can also compute the Risk-ratio (RR), which is the ratio between the proportions of the two groups - a measure which some claim is more intuitive.

riskratio(RCT)
> Risk ratio |       95% CI
> -------------------------
> 2.54       | [1.87, 3.45]

Additionally, Cohen’s h can also be computed, which uses the arcsin transformation. Negative values indicate smaller proportion in the first group (and vice versa).

cohens_h(RCT)
> Cohen's h |       95% CI
> ------------------------
> 0.74      | [0.50, 0.99]

Paired Contingency Tables

For dependent (paired) contingency tables, Cohen’s g represents the symmetry of the table, ranging between 0 (perfect symmetry) and 0.5 (perfect asymmetry).

(Performance <- matrix(
  c(
    794, 86,
    150, 570
  ),
  nrow = 2, byrow = TRUE,
  dimnames = list(
    "1st Survey" = c("Approve", "Disapprove"),
    "2nd Survey" = c("Approve", "Disapprove")
  )
))
>             2nd Survey
> 1st Survey   Approve Disapprove
>   Approve        794         86
>   Disapprove     150        570
mcnemar.test(Performance)
> 
>   McNemar's Chi-squared test with continuity correction
> 
> data:  Performance
> McNemar's chi-squared = 17, df = 1, p-value = 4e-05
cohens_g(Performance)
> Cohen's g |       95% CI
> ------------------------
> 0.14      | [0.07, 0.19]

Rank Based tests

Rank based tests get rank based effect sizes!

Difference in Ranks

For two independent samples, the rank-biserial correlation (\(r_{rb}\)) is a measure of relative superiority - i.e., larger values indicate a higher probability of a randomly selected observation from X being larger than randomly selected observation from Y. A value of \((-1)\) indicates that all observations in the second group are larger than the first, and a value of \((+1)\) indicates that all observations in the first group are larger than the second.

A <- c(48, 48, 77, 86, 85, 85)
B <- c(14, 34, 34, 77)

wilcox.test(A, B) # aka Mann–Whitney U test
> 
>   Wilcoxon rank sum test with continuity correction
> 
> data:  A and B
> W = 22, p-value = 0.05
> alternative hypothesis: true location shift is not equal to 0
rank_biserial(A, B)
> r (rank biserial) |       95% CI
> --------------------------------
> 0.79              | [0.30, 0.95]

Here too we have a common language effect size:

cles(A, B, rank = TRUE)
> Parameter       | Coefficient |       95% CI
> --------------------------------------------
> Pr(superiority) |        0.85 | [0.49, 0.98]
> Cohen's U3      |        0.93 | [0.49, 1.00]
> Overlap         |        0.47 | [0.15, 1.00]

For one sample, \(r_{rb}\) measures the symmetry around \(\mu\) (mu; the null value), with 0 indicating perfect symmetry, \((-1)\) indicates that all observations fall below \(\mu\), and \((+1)\) indicates that all observations fall above \(\mu\). For paired samples the difference between the paired samples is used:

x <- c(1.15, 0.88, 0.90, 0.74, 1.21, 1.36, 0.89)

wilcox.test(x, mu = 1) # aka Signed-Rank test
> 
>   Wilcoxon signed rank exact test
> 
> data:  x
> V = 16, p-value = 0.8
> alternative hypothesis: true location is not equal to 1
rank_biserial(x, mu = 1)
> r (rank biserial) |        95% CI
> ---------------------------------
> 0.14              | [-0.59, 0.75]
> 
> - Deviation from a difference of 1.
x <- c(1.83, 0.50, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.30)
y <- c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29)

wilcox.test(x, y, paired = TRUE) # aka Signed-Rank test
> 
>   Wilcoxon signed rank exact test
> 
> data:  x and y
> V = 40, p-value = 0.04
> alternative hypothesis: true location shift is not equal to 0
rank_biserial(x, y, paired = TRUE)
> r (rank biserial) |       95% CI
> --------------------------------
> 0.78              | [0.30, 0.94]

Rank One way ANOVA

The Rank-Epsilon-Squared (\(\varepsilon^2\)) is a measure of association for the rank based one-way ANOVA. Values range between 0 (no relative superiority between any of the groups) to 1 (complete separation - with no overlap in ranks between the groups).

group_data <- list(
  g1 = c(2.9, 3.0, 2.5, 2.6, 3.2), # normal subjects
  g2 = c(3.8, 2.7, 4.0, 2.4), # with obstructive airway disease
  g3 = c(2.8, 3.4, 3.7, 2.2, 2.0) # with asbestosis
)

kruskal.test(group_data)
> 
>   Kruskal-Wallis rank sum test
> 
> data:  group_data
> Kruskal-Wallis chi-squared = 0.8, df = 2, p-value = 0.7
rank_epsilon_squared(group_data)
> Epsilon2 (rank) |       95% CI
> ------------------------------
> 0.06            | [0.01, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Rank One way Repeated-Measures ANOVA

For a rank based repeated measures one-way ANOVA, Kendall’s W is a measure of agreement on the effect of condition between various “blocks” (the subjects), or more often conceptualized as a measure of reliability of the rating / scores of observations (or “groups”) between “raters” (“blocks”).

# Subjects are COLUMNS
(ReactionTimes <- matrix(
  c(398, 338, 520,
    325, 388, 555,
    393, 363, 561,
    367, 433, 470,
    286, 492, 536,
    362, 475, 496,
    253, 334, 610),
  nrow = 7, byrow = TRUE,
  dimnames = list(
    paste0("Subject", 1:7),
    c("Congruent", "Neutral", "Incongruent")
  )
))
>          Congruent Neutral Incongruent
> Subject1       398     338         520
> Subject2       325     388         555
> Subject3       393     363         561
> Subject4       367     433         470
> Subject5       286     492         536
> Subject6       362     475         496
> Subject7       253     334         610
friedman.test(ReactionTimes)
> 
>   Friedman rank sum test
> 
> data:  ReactionTimes
> Friedman chi-squared = 11, df = 2, p-value = 0.004
kendalls_w(ReactionTimes)
> Kendall's W |       95% CI
> --------------------------
> 0.80        | [0.76, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

References

effectsize/inst/doc/effectsize_API.R0000644000175000017500000000542514174212063017226 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) knitr::opts_chunk$set(comment = ">", warning = FALSE, message = FALSE) options(digits = 2) options(knitr.kable.NA = '') pkgs <- c("effectsize") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(333) ## ----------------------------------------------------------------------------- library(effectsize) ## ----------------------------------------------------------------------------- min_aov <- data.frame( Parameter = c("(Intercept)", "A", "B", "Residuals"), Sum_Squares = c(30, 40, 10, 100), df = c(1, 1, 2, 50) ) ## ----------------------------------------------------------------------------- .es_aov_simple( min_aov, type = "eta", partial = TRUE, generalized = FALSE, include_intercept = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE ) ## ----------------------------------------------------------------------------- min_aovlist <- data.frame( Group = c("S", "S", "S:A", "S:A"), Parameter = c("(Intercept)", "Residuals", "A", "Residuals"), Sum_Squares = c(34, 21, 34, 400), df = c(1, 12, 4, 30) ) ## ----------------------------------------------------------------------------- .es_aov_strata( min_aovlist, DV_names = c("S", "A"), type = "omega", partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = TRUE ) ## ----------------------------------------------------------------------------- min_anova <- data.frame( Parameter = c("(Intercept)", "A", "B"), F = c(4, 7, 0.7), df = c(1, 1, 2), df_error = 34 ) ## ----------------------------------------------------------------------------- .es_aov_table( min_anova, type = "eta", partial = TRUE, generalized = FALSE, include_intercept = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE ) ## ----------------------------------------------------------------------------- mod <- lm(mpg ~ factor(cyl) + am, mtcars) class(mod) <- "superMODEL" ## ----------------------------------------------------------------------------- .anova_es.superMODEL <- function(model, ...) { # Get ANOVA table anov <- suppressWarnings(stats:::anova.lm(model)) anov <- as.data.frame(anov) # Clean up anov[["Parameter"]] <- rownames(anov) colnames(anov)[2:1] <- c("Sum_Squares", "df") # Pass out <- .es_aov_simple(anov, ...) # Set attribute attr(out, "anova_type") <- 1 out } ## ----------------------------------------------------------------------------- eta_squared(mod) eta_squared(mod, partial = FALSE) omega_squared(mod) # Etc... effectsize/inst/doc/standardize_parameters.Rmd0000644000175000017500000005220714170302654021464 0ustar nileshnilesh--- title: "Parameter and Model Standardization" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, standardization, effect size, cohen d, standardized coefficients] vignette: > %\VignetteIndexEntry{Parameter and Model Standardization} \usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) knitr::opts_chunk$set(comment = ">", warning = FALSE, message = FALSE) options(digits = 2) options(knitr.kable.NA = '') pkgs <- c("effectsize", "parameters", "correlation") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(333) ``` # Introduction Standardizing parameters (*i.e.*, coefficients) can allow for their comparison within and between models, variables and studies. Moreover, as it returns coefficients expressed in terms of **change of variance** (for instance, coefficients expressed in terms of SD of the response variable), it can allow for the usage of [effect size interpretation guidelines](https://easystats.github.io/effectsize/articles/interpret.html), such as Cohen's (1988) famous rules of thumb. However, standardizing a model's parameters should *not* be automatically and mindlessly done: for some research fields, particular variables or types of studies (*e.g.*, replications), it sometimes makes more sense to keep, use and interpret the original parameters, especially if they are well known or easily understood. Critically, **parameters standardization is not a trivial process**. Different techniques exist, that can lead to drastically different results. Thus, it is critical that the standardization method is explicitly documented and detailed. ## Standardizing Parameters of Simple Models ### Standardized Associations ```{r} library(effectsize) m <- lm(rating ~ complaints, data = attitude) standardize_parameters(m) ``` Standardizing the coefficient of this *simple* linear regression gives a value of `0.87`, but did you know that for a simple regression this is actually the **same as a correlation**? Thus, you can eventually apply some (*in*)famous interpretation guidelines (e.g., Cohen's rules of thumb). ```{r} correlation::correlation(attitude, select = c("rating", "complaints")) ``` ### Standardized Differences How does it work in the case of differences, when **factors** are entered and differences between a given level and a reference level? You might have heard that it is similar to a **Cohen's *d***. Well, let's see. ```{r include=FALSE} mtcars <- datasets::mtcars ``` ```{r} # Select portion of data containing the two levels of interest mtcars$am <- factor(mtcars$am, labels = c("Manual", "Automatic")) m <- lm(mpg ~ am, data = mtcars) standardize_parameters(m) ``` This linear model suggests that the *standardized* difference between *Manual* (the reference level - the model's intercept) and *Automatic* is of 1.20 standard deviation of `mpg` (because the response variable was standardized, right?). Let's compute the **Cohen's *d*** between these two levels: ```{r} cohens_d(mpg ~ am, data = mtcars) ``` ***It is larger!*** Why? How? Both differences should be expressed in units of SD! But which SDs? Different SDs! When looking at the difference between groups as a **slope**, the standardized parameter is the difference between the means in $SD_{mpg}$. That is, the *slope* between `Manual` and `Automatic` is a change of 1.20 $SD_{mpg}$s. However, when looking a the difference as a **distance between two populations**, Cohen's d is the distance between the means in units of [**pooled SDs**](https://easystats.github.io/effectsize/reference/sd_pooled.html). That is, the *distance* between `Manual` and `Automatic` is of 1.48 SDs of *each of the groups* (here assumed to be equal). In this simple model, the pooled SD is the residual SD, so we can also estimate Cohen's *d* as: ```{r} coef(m)[2] / sigma(m) ``` And we can also get an approximation of Cohen's *d* by converting the $t$-statistic from the regression model via `t_to_d()`: ```{r} parameters::model_parameters(m) t_to_d(4.11, df_error = 30) ``` It is also interesting to note that using the `smart` method (explained in detail below) when standardizing parameters will give you indices equivalent to **Glass' *delta***, which is a standardized difference expressed in terms of SD of the reference group. ```{r} m <- lm(mpg ~ am, data = mtcars) standardize_parameters(m, method = "smart") glass_delta(mpg ~ am, data = mtcars) ``` ***... So note that some standardized differences are different than others! :)*** ## Standardizing Parameters of Linear Models As mentioned above, standardization of parameters can also be used to compare among parameters within the same model. Essentially, what prevents us from normally being able to compare among different parameters is that their underlying variables are on different scales.[^But also as noted above, this is not always an issue. For example, when the variables scale is important for the interpretation of results, standardization might in fact hinder interpretation!] For example, in the following example, we use a liner regression model to predict a worker's salary (in Shmekels) from their age (years), seniority (years), overtime (`xtra_hours`) and how many compliments they give their boss (`n_comps`). Let us explore the different parameter standardization methods provided by `effectsize`. ### Standardized Slopes are Not (Always) Correlations We saw that in simple linear models, the standardized slope is equal to the correlation between the outcome and predictor - does this hold for **multiple regression** as well? As in each effect in a regression model is "adjusted" for the other ones, we might expect coefficients to be somewhat alike to **partial correlations**. Let's first start by computing the partial correlation between numeric predictors and the outcome. ```{r} data("hardlyworking", package = "effectsize") head(hardlyworking) correlation::correlation( hardlyworking, select = "salary", select2 = c("xtra_hours", "n_comps", "age", "seniority"), partial = TRUE # get partial correlations ) ``` Let's compare these to the standardized slopes: ```{r} mod <- lm(salary ~ xtra_hours + n_comps + age + seniority, data = hardlyworking) standardize_parameters(mod) ``` They are quite different! It seems then that ***standardized slopes in multiple linear regressions are not the same a correlations or partial correlations*** :( However, not all hope is lost yet - we can still try and recover the partial correlations from our model, in another way: by converting the *t*-statistics (and their degrees of freedom, *df*) into a partial correlation coefficient *r*. ```{r} params <- parameters::model_parameters(mod) t_to_r(params$t[-1], df_error = params$df_error[-1]) ``` Wow, the retrieved correlations coefficients from the regression model are **exactly** the same as the partial correlations we estimated above! So these "*r*" effect sizes can also be used. ### Methods of Standardizing Parameters Let's convert `age` into a 3-level factor: ```{r} hardlyworking$age_g <- cut(hardlyworking$age, breaks = c(25,30,35,45)) mod <- lm(salary ~ xtra_hours + n_comps + age_g + seniority, data = hardlyworking) parameters::model_parameters(mod) ``` It seems like the best or most important predictor is `n_comps` as it has the coefficient. However, it is hard to compare among predictors, as they are on different scales. To address this issue, we must have all the predictors on the same scale - usually in the arbitrary unit of *standard deviations*. #### **`"refit"`**: Re-fitting the model with standardized data **This method is based on a complete model re-fit with a standardized version of data**. Hence, this method is equal to standardizing the variables *before* fitting the model. It is the "purest" and the most accurate [@neter1989applied], but it is also the most computationally costly and long (especially for heavy models such as Bayesian models, or complex mixed models). This method is particularly recommended for models that include interactions or transformations (e.g., exponentiation, log, polynomial or spline terms). ```{r} standardize_parameters(mod, method = "refit") ``` `standardize_parameters` also has a `robust` argument (default to `FALSE`), which enables a **robust standardization of the data**, *i.e.*, based on the **median** and **MAD** instead of the **mean** and **SD**: ```{r} standardize_parameters(mod, method = "refit", robust = TRUE) ``` Note that since `age_g` is a factor, it is not numerically standardized, and so it standardized parameter is still not directly comparable to those of numeric variables. To address this, we can set `two_sd = TRUE`, thereby scaling parameters on 2 SDs (or MADs) of the predictors [@gelman2008scaling]. ```{r} standardize_parameters(mod, method = "refit", two_sd = TRUE) ``` `effectsize` also comes with a helper function that returns the re-fit model, without summarizing it, which can then be used as the original model would: ```{r} mod_z <- standardize(mod, two_sd = FALSE, robust = FALSE) mod_z parameters::model_parameters(mod_z) ``` #### **`"posthoc"`**: Refit without refitting Post-hoc standardization of the parameters aims at emulating the results obtained by `"refit"` without refitting the model. The coefficients are divided by the standard deviation (or MAD if `robust`) of the outcome (which becomes their expression 'unit'). Then, the coefficients related to numeric variables are additionally multiplied by the standard deviation (or MAD if `robust`) of the related terms, so that they correspond to changes of 1 SD of the predictor (e.g., "A change in 1 SD of *x* is related to a change of 0.24 of the SD of *y*). This does not apply to binary variables or factors, so the coefficients are still related to changes in levels. This method is not accurate and tend to give aberrant results when interactions are specified. ```{r} standardize_parameters(mod, method = "posthoc") ``` #### **`"smart"`**: Standardization of Model's parameters with Adjustment, Reconnaissance and Transformation > Experimental Similar to `method = "posthoc"` in that it does not involve model refitting. The difference is that the SD of the response is computed on the relevant section of the data. For instance, if a factor with 3 levels A (the intercept), B and C is entered as a predictor, the effect corresponding to B vs. A will be scaled by the variance of the response at the intercept only. As a results, the coefficients for effects of factors are similar to a Glass' *delta*. ```{r} standardize_parameters(mod, method = "smart") ``` #### **`"basic"`**: Raw scaling of the model frame This method is similar to `method = "posthoc"`, but treats all variables as continuous: it scales the coefficient by the standard deviation of model's matrix' parameter of factors levels (transformed to integers) or binary predictors. Although it can be argued that this might be inappropriate for these cases, this method allows for easier importance judgment across all predictor type (numeric, factor, interactions...). It is also the type of standardization implemented by default in other software packages (also `lm.beta::lm.beta()`), and, such as can be used for reproducibility and replication purposes. ```{r} standardize_parameters(mod, method = "basic") ``` ### Standardizing Parameters In Mixed Models Linear mixed models (LMM/HLM/MLM) offer an additional conundrum to standardization - how does one even calculate the SDs of the various predictors? Or of the response - is it the deviations within each group? Or perhaps between them? The solution: standardize according to level of the predictor [@hoffman2015longitudinal, page 342]! Level 1 parameters are standardized according to variance *within* groups, while level 2 parameters are standardized according to variance *between* groups. The resulting standardized coefficient are also called *pseudo*-standardized coefficients.[^Note that like method `"basic"`, these are based on the model matrix.] ```{r, eval=knitr::opts_chunk$get("eval") && require(lme4) && require(lmerTest), warning=FALSE} m <- lme4::lmer(Reaction ~ Days + (Days|Subject), data = lme4::sleepstudy) standardize_parameters(m, method = "pseudo", ci_method = "satterthwaite") # compare to: standardize_parameters(m, method = "basic", ci_method = "satterthwaite") ``` ### Standardizing Parameters In Generalized Linear Models Unlike linear (/mixed) models, in generalized linear (/mixed) models (GLMs) there is *less* of a need for standardization. Why? Because in many GLMs the estimated coefficients are themselves measures of effect size, such as *odds-ratios* (OR) in logistic regression, or *incidence rate ratios* (IRR) in Poisson regressions. This is because in such model the outcome is **not** on an arbitrary scale - that is, the meaning of rates and probabilities are changed by arbitrary linear transformations. But still, some standardization is sometimes needed, for the predictors. Luckily, `standardize_parameters()` (and `standardize()`) are smart enough to know when GLMs are passed so as to only standardize according to the predictors: ```{r} mod_b <- glm(am ~ mpg + factor(cyl), data = mtcars, family = binomial()) standardize_parameters(mod_b, method = "refit", two_sd = TRUE) # standardize_parameters(mod_b, method = "posthoc", two_sd = TRUE) # standardize_parameters(mod_b, method = "basic") ``` These can then be converted to OR (with `exp()`) and discussed as the "*change in Odds as a function of a change in one SD of x*". ```{r} std <- standardize_parameters(mod_b, method = "refit", two_sd = TRUE) exp(std$Std_Coefficient) ``` Or we can directly ask for the coefficients to be exponentiated: ```{r} standardize_parameters(mod_b, method = "refit", two_sd = TRUE, exponentiate = TRUE) ``` ## Cohen's *f* Cohen's $f$ (of [ANOVA fame](https://easystats.github.io/effectsize/articles/anovaES.html)) can be used as a measure of effect size in the context of sequential multiple regression (i.e., [**nested models**](https://easystats.github.io/performance/reference/test_performance.html)). That is, when comparing two models, we can examine the ratio between the increase in $R^2$ and the unexplained variance: $$ f^{2}={R_{AB}^{2}-R_{A}^{2} \over 1-R_{AB}^{2}} $$ ```{r} m1 <- lm(salary ~ xtra_hours, data = hardlyworking) m2 <- lm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking) cohens_f_squared(m1, model2 = m2) ``` # References effectsize/inst/doc/standardize_parameters.R0000644000175000017500000001200214174212104021122 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) knitr::opts_chunk$set(comment = ">", warning = FALSE, message = FALSE) options(digits = 2) options(knitr.kable.NA = '') pkgs <- c("effectsize", "parameters", "correlation") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(333) ## ----------------------------------------------------------------------------- library(effectsize) m <- lm(rating ~ complaints, data = attitude) standardize_parameters(m) ## ----------------------------------------------------------------------------- correlation::correlation(attitude, select = c("rating", "complaints")) ## ----include=FALSE------------------------------------------------------------ mtcars <- datasets::mtcars ## ----------------------------------------------------------------------------- # Select portion of data containing the two levels of interest mtcars$am <- factor(mtcars$am, labels = c("Manual", "Automatic")) m <- lm(mpg ~ am, data = mtcars) standardize_parameters(m) ## ----------------------------------------------------------------------------- cohens_d(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- coef(m)[2] / sigma(m) ## ----------------------------------------------------------------------------- parameters::model_parameters(m) t_to_d(4.11, df_error = 30) ## ----------------------------------------------------------------------------- m <- lm(mpg ~ am, data = mtcars) standardize_parameters(m, method = "smart") glass_delta(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- data("hardlyworking", package = "effectsize") head(hardlyworking) correlation::correlation( hardlyworking, select = "salary", select2 = c("xtra_hours", "n_comps", "age", "seniority"), partial = TRUE # get partial correlations ) ## ----------------------------------------------------------------------------- mod <- lm(salary ~ xtra_hours + n_comps + age + seniority, data = hardlyworking) standardize_parameters(mod) ## ----------------------------------------------------------------------------- params <- parameters::model_parameters(mod) t_to_r(params$t[-1], df_error = params$df_error[-1]) ## ----------------------------------------------------------------------------- hardlyworking$age_g <- cut(hardlyworking$age, breaks = c(25,30,35,45)) mod <- lm(salary ~ xtra_hours + n_comps + age_g + seniority, data = hardlyworking) parameters::model_parameters(mod) ## ----------------------------------------------------------------------------- standardize_parameters(mod, method = "refit") ## ----------------------------------------------------------------------------- standardize_parameters(mod, method = "refit", robust = TRUE) ## ----------------------------------------------------------------------------- standardize_parameters(mod, method = "refit", two_sd = TRUE) ## ----------------------------------------------------------------------------- mod_z <- standardize(mod, two_sd = FALSE, robust = FALSE) mod_z parameters::model_parameters(mod_z) ## ----------------------------------------------------------------------------- standardize_parameters(mod, method = "posthoc") ## ----------------------------------------------------------------------------- standardize_parameters(mod, method = "smart") ## ----------------------------------------------------------------------------- standardize_parameters(mod, method = "basic") ## ---- eval=knitr::opts_chunk$get("eval") && require(lme4) && require(lmerTest), warning=FALSE---- m <- lme4::lmer(Reaction ~ Days + (Days|Subject), data = lme4::sleepstudy) standardize_parameters(m, method = "pseudo", ci_method = "satterthwaite") # compare to: standardize_parameters(m, method = "basic", ci_method = "satterthwaite") ## ----------------------------------------------------------------------------- mod_b <- glm(am ~ mpg + factor(cyl), data = mtcars, family = binomial()) standardize_parameters(mod_b, method = "refit", two_sd = TRUE) # standardize_parameters(mod_b, method = "posthoc", two_sd = TRUE) # standardize_parameters(mod_b, method = "basic") ## ----------------------------------------------------------------------------- std <- standardize_parameters(mod_b, method = "refit", two_sd = TRUE) exp(std$Std_Coefficient) ## ----------------------------------------------------------------------------- standardize_parameters(mod_b, method = "refit", two_sd = TRUE, exponentiate = TRUE) ## ----------------------------------------------------------------------------- m1 <- lm(salary ~ xtra_hours, data = hardlyworking) m2 <- lm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking) cohens_f_squared(m1, model2 = m2) effectsize/inst/doc/convert.R0000644000175000017500000000434214174212061016061 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 3) pkgs <- c("effectsize", "ggplot2", "correlation", "parameters", "bayestestR") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ## ----------------------------------------------------------------------------- set.seed(1) data <- bayestestR::simulate_difference( n = 10, d = 0.2, names = c("Group", "Outcome") ) ## ---- echo=FALSE-------------------------------------------------------------- print(data, digits = 3) ## ----------------------------------------------------------------------------- cohens_d(Outcome ~ Group, data = data) ## ---- warning=FALSE----------------------------------------------------------- correlation::correlation(data, include_factors = TRUE)[2, ] ## ----------------------------------------------------------------------------- d_to_r(-0.31) ## ----------------------------------------------------------------------------- fit <- lm(mpg ~ am + hp, data = mtcars) parameters::model_parameters(fit) # A couple of ways to get partial-d: 5.28 / sigma(fit) t_to_d(4.89, df_error = 29)[[1]] ## ----------------------------------------------------------------------------- t_to_r(4.89, df_error = 29) correlation::correlation(mtcars[, c("mpg", "am", "hp")], partial = TRUE)[1, ] # all close to: d_to_r(1.81) ## ----------------------------------------------------------------------------- # 1. Set a threshold thresh <- 0 # 2. dichotomize the outcome data$Outcome_binom <- data$Outcome < thresh # 3. Fit a logistic regression: fit <- glm(Outcome_binom ~ Group, data = data, family = binomial() ) parameters::model_parameters(fit) # Convert log(OR) (the coefficient) to d oddsratio_to_d(-0.81, log = TRUE) ## ----------------------------------------------------------------------------- OR <- 3.5 baserate <- 0.85 oddsratio_to_riskratio(OR, baserate) ## ----------------------------------------------------------------------------- OR <- 3.5 baserate <- 0.04 oddsratio_to_riskratio(OR, baserate) effectsize/inst/doc/anovaES.Rmd0000644000175000017500000002300514170072537016263 0ustar nileshnilesh--- title: "Effect sizes for ANOVAs" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, ANOVA] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Effect sizes for ANOVAs} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") options(digits = 2) knitr::opts_chunk$set(comment = ">", warning = FALSE) set.seed(1) pkgs <- c("effectsize", "parameters", "car", "afex") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` ## Eta2 In the context of ANOVA-like tests, it is common to report ANOVA-like effect sizes. Unlike [standardized parameters](https://easystats.github.io/effectsize/articles/standardize_parameters.html), these effect sizes represent the amount of variance explained by each of the model's terms, where each term can be represented by 1 *or more* parameters. For example, in the following case, the parameters for the `treatment` term represent specific contrasts between the factor's levels (treatment groups) - the difference between each level and the reference level (`obk.long == 'control'`). ```{r} data(obk.long, package = "afex") # modify the data slightly for the demonstration: obk.long <- obk.long[1:240 %% 3 == 0, ] obk.long$id <- seq_len(nrow(obk.long)) m <- lm(value ~ treatment, data = obk.long) parameters::model_parameters(m) ``` But we can also ask about the overall effect of `treatment` - how much of the variation in our dependent variable `value` can be predicted by (or explained by) the variation between the `treatment` groups. Such a question can be answered with an ANOVA test: ```{r} parameters::model_parameters(anova(m)) ``` As we can see, the variance in `value` (the *sums-of-squares*, or *SS*) has been split into pieces: - The part associated with `treatment`. - The unexplained part (The Residual-*SS*). We can now ask what is the percent of the total variance in `value` that is associated with `treatment`. This measure is called Eta-squared (written as $\eta^2$): $$ \eta^2 = \frac{SS_{effect}}{SS_{total}} = \frac{72.23}{72.23 + 250.96} = 0.22 $$ and can be accessed via the `eta_squared()` function: ```{r} library(effectsize) eta_squared(m, partial = FALSE) ``` ### Adding More Terms When we add more terms to our model, we can ask two different questions about the percent of variance explained by a predictor - how much variance is accounted by the predictor in *total*, and how much is accounted when *controlling* for any other predictors. The latter questions is answered by the *partial*-Eta squared ($\eta^2_p$), which is the percent of the **partial** variance (after accounting for other predictors in the model) associated with a term: $$ \eta^2_p = \frac{SS_{effect}}{SS_{effect} + SS_{error}} $$ which can also be accessed via the `eta_squared()` function: ```{r} m <- lm(value ~ gender + phase + treatment, data = obk.long) eta_squared(m, partial = FALSE) eta_squared(m) # partial = TRUE by default ``` *(`phase` is a repeated-measures variable, but for simplicity it is not modeled as such.)* In the calculation above, the *SS*s were computed sequentially - that is the *SS* for `phase` is computed after controlling for `gender`, and the *SS* for `treatment` is computed after controlling for both `gender` and `phase`. This method of sequential *SS* is called also *type-I* test. If this is what you want, that's great - however in many fields (and other statistical programs) it is common to use "simultaneous" sums of squares (*type-II* or *type-III* tests), where each *SS* is computed controlling for all other predictors, regardless of order. This can be done with `car::Anova(type = ...)`: ```{r} eta_squared(car::Anova(m, type = 2), partial = FALSE) eta_squared(car::Anova(m, type = 3)) # partial = TRUE by default ``` $\eta^2_p$ will always be larger than $\eta^2$. The idea is to simulate the effect size in a design where only the term of interest was manipulated. This terminology assumes some causal relationship between the predictor and the outcome, which reflects the experimental world from which these analyses and measures hail; However, $\eta^2_p$ can also simply be seen as a **signal-to-noise- ratio**, as it only uses the term's *SS* and the error-term's *SS*.[^in repeated-measure designs the term-specific residual-*SS* is used for the computation of the effect size]. (Note that in a one-way fixed-effect designs $\eta^2 = \eta^2_p$.) ### Adding Interactions Type II and type III treat interaction differently. Without going into the weeds here, keep in mind that **when using type III SS, it is important to center all of the predictors**; for numeric variables this can be done by mean-centering the predictors; for factors this can be done by using orthogonal coding (such as `contr.sum` for *effects-coding*) for the dummy variables (and *NOT* treatment coding, which is the default in R). This unfortunately makes parameter interpretation harder, but *only* when this is does do the *SS*s associated with each lower-order term (or lower-order interaction) represent the ***SS*** of the **main effect** (with treatment coding they represent the *SS* of the simple effects). ```{r} # compare m_interaction1 <- lm(value ~ treatment * gender, data = obk.long) # to: m_interaction2 <- lm( value ~ treatment * gender, data = obk.long, contrasts = list( treatment = "contr.sum", gender = "contr.sum" ) ) eta_squared(car::Anova(m_interaction1, type = 3)) eta_squared(car::Anova(m_interaction2, type = 3)) ``` If all of this type-III-effects-coding seems like a hassle, you can use the `afex` package, which takes care of all of this behind the scenes: ```{r} library(afex) m_afex <- aov_car(value ~ treatment * gender + Error(id), data = obk.long) eta_squared(m_afex) ``` ## Other Measures of Effect Size ### Unbiased Effect Sizes These effect sizes are unbiased estimators of the population's $\eta^2$: - **Omega Squared** ($\omega^2$) - **Epsilon Squared** ($\epsilon^2$), also referred to as *Adjusted Eta Squared*. ```{r} omega_squared(m_afex) epsilon_squared(m_afex) ``` Both $\omega^2$ and $\epsilon^2$ (and their partial counterparts, $\omega^2_p$ & $\epsilon^2_p$) are unbiased estimators of the population's $\eta^2$ (or $\eta^2_p$, respectively), which is especially important is small samples. Though $\omega^2$ is the more popular choice [@albers2018power], $\epsilon^2$ is analogous to adjusted-$R^2$ [@allen2017statistics, p. 382], and has been found to be less biased [@carroll1975sampling]. ### Generalized Eta2 *Partial* Eta squared aims at estimating the effect size in a design where only the term of interest was manipulated, assuming all other terms are have also manipulated. However, not all predictors are always manipulated - some can only be observed. For such cases, we can use *generalized* Eta squared ($\eta^2_G$), which like $\eta^2_p$ estimating the effect size in a design where only the term of interest was manipulated, accounting for the fact that some terms cannot be manipulated (and so their variance would be present in such a design). ```{r} eta_squared(m_afex, generalized = "gender") ``` $\eta^2_G$ is useful in repeated-measures designs, as it can estimate what a *within-subject* effect size would have been had that predictor been manipulated *between-subjects* [@olejnik2003generalized]. ### Cohen's *f* Finally, we have the forgotten child - Cohen's $f$. Cohen's $f$ is a transformation of $\eta^2_p$, and is the ratio between the term-*SS* and the error-*SS*. $$\text{Cohen's} f_p = \sqrt{\frac{\eta^2_p}{1-\eta^2_p}} = \sqrt{\frac{SS_{effect}}{SS_{error}}}$$ It can take on values between zero, when the population means are all equal, and an indefinitely large number as the means are further and further apart. It is analogous to Cohen's $d$ when there are only two groups. ```{r} cohens_f(m_afex) ``` ## When Sum-of-Squares are Hard to Come By Until now we've discusses effect sizes in fixed-effect linear model and repeated-measures ANOVA's - cases where the *SS*s are readily available, and so the various effect sized presented can easily be estimated. How ever this is not always the case. For example, in linear mixed models (LMM/HLM/MLM), the estimation of all required *SS*s is not straightforward. However, we can still *approximate* these effect sizes (only their partial versions) based on the **test-statistic approximation method** (learn more in the [*Effect Size from Test Statistics* vignette](https://easystats.github.io/effectsize/articles/from_test_statistics.html)). ```{r, eval=require(lmerTest)} library(lmerTest) fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) anova(fit_lmm) # note the type-3 errors F_to_eta2(45.8, df = 1, df_error = 17) ``` Or directly with `eta_squared() and co.: ```{r, eval=require(lmerTest)} eta_squared(fit_lmm) epsilon_squared(fit_lmm) omega_squared(fit_lmm) ``` Another case where *SS*s are not available is when use Bayesian models. `effectsize` has Bayesian solutions for Bayesian models, about which you can read in the [*Effect Sizes for Bayesian Models* vignette](https://easystats.github.io/effectsize/articles/bayesian_models.html). # References effectsize/inst/doc/convert.Rmd0000644000175000017500000001416714170065645016422 0ustar nileshnilesh--- title: "Converting Between Indices of Effect Size" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, conversion] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Converting Between Indices of Effect Size} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 3) pkgs <- c("effectsize", "ggplot2", "correlation", "parameters", "bayestestR") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` The `effectsize` package contains function to convert among indices of effect size. This can be useful for meta-analyses, or any comparison between different types of statistical analyses. ## Converting Between *d*, *r*, and *OR* The most basic conversion is between *r* values, a measure of standardized association between two continuous measures, and *d* values (such as Cohen's *d*), a measure of standardized differences between two groups / conditions. Let's simulate some data: ```{r} set.seed(1) data <- bayestestR::simulate_difference( n = 10, d = 0.2, names = c("Group", "Outcome") ) ``` ```{r, echo=FALSE} print(data, digits = 3) ``` We can compute Cohen's *d* between the two groups: ```{r} cohens_d(Outcome ~ Group, data = data) ``` But we can also treat the 2-level `group` variable as a numeric variable, and compute Pearson's *r*: ```{r, warning=FALSE} correlation::correlation(data, include_factors = TRUE)[2, ] ``` But what if we only have summary statistics? Say, we only have $d=-0.37$ and we want to know what the *r* would have been? We can approximate *r* using the following formula [@borenstein2009converting]: $$ r \approx \frac{d}{\sqrt{d^2 + 4}} $$ And indeed, if we use `d_to_r()`, we get a pretty decent approximation: ```{r} d_to_r(-0.31) ``` (Which also works in the other way, with `r_to_d(0.17)` gives `r round(r_to_d(-0.17),3)`) As we can see, these are rough approximations, but they can be useful when we don't have the raw data on hand. ### In multiple regression Although not exactly a classic Cohen's d, we can also approximate a partial-*d* value (that is, the standardized difference between two groups / conditions, with variance from other predictors partilled out). For example: ```{r} fit <- lm(mpg ~ am + hp, data = mtcars) parameters::model_parameters(fit) # A couple of ways to get partial-d: 5.28 / sigma(fit) t_to_d(4.89, df_error = 29)[[1]] ``` We can convert these semi-*d* values to *r* values, but in this case these represent the *partial* correlation: ```{r} t_to_r(4.89, df_error = 29) correlation::correlation(mtcars[, c("mpg", "am", "hp")], partial = TRUE)[1, ] # all close to: d_to_r(1.81) ``` ## From Odds ratios In binomial regression (more specifically in logistic regression), Odds ratios (OR) are themselves measures of effect size; they indicate the expected change in the odds of a some event. In some fields, it is common to dichotomize outcomes in order to be able to analyze them with logistic models. For example, if the outcome is the count of white blood cells, it can be more useful (medically) to predict the crossing of the threshold rather than the raw count itself. And so, where some scientists would maybe analyze the above data with a *t*-test and present Cohen's *d*, others might analyze it with a logistic regression model on the dichotomized outcome, and present OR. So the question can be asked: given such a OR, what would Cohen's *d* have been? Fortunately, there is a formula to approximate this [@sanchez2003effect]: $$ d = log(OR) \times \frac{\sqrt{3}}{\pi} $$ which is implemented in the `oddsratio_to_d()` function. Let's give it a try: ```{r} # 1. Set a threshold thresh <- 0 # 2. dichotomize the outcome data$Outcome_binom <- data$Outcome < thresh # 3. Fit a logistic regression: fit <- glm(Outcome_binom ~ Group, data = data, family = binomial() ) parameters::model_parameters(fit) # Convert log(OR) (the coefficient) to d oddsratio_to_d(-0.81, log = TRUE) ``` ### Odds ratios to Risk Ratios Odds ratio, although popular, are not very intuitive in their interpretations. We don't often think about the chances of catching a disease in terms of *odds*, instead we instead tend to think in terms of *probability* or some event - or the *risk*. Talking about *risks* we can also talk about the *change in risk*, knows as the *risk ratio* (*RR*). For example, if we find that for individual suffering from a migraine, for every bowl of brussels sprouts they eat, they're odds of reducing the migraine increase by an $OR = 3.5$ over a period of an hour. So, should people eat brussels sprouts to effectively reduce pain? Well, hard to say... Maybe if we look at *RR* we'll get a clue. We can convert between *OR* and *RR* for the following formula [@grant2014converting]: $$ RR = \frac{OR}{(1 - p0 + (p0 \times OR))} $$ Where $p0$ is the base-rate risk - the probability of the event without the intervention (e.g., what is the probability of the migraine subsiding within an hour without eating any brussels sprouts). If it the base-rate risk is, say, 85%, we get a *RR* of: ```{r} OR <- 3.5 baserate <- 0.85 oddsratio_to_riskratio(OR, baserate) ``` That is - for every bowl of brussels sprouts, we increase the chances of reducing the migraine by a mere 12%! Is if worth it? Depends on you affinity to brussels sprouts... Note that the base-rate risk is crucial here. If instead of 85% it was only 4%, then the *RR* would be: ```{r} OR <- 3.5 baserate <- 0.04 oddsratio_to_riskratio(OR, baserate) ``` That is - for every bowl of brussels sprouts, we increase the chances of reducing the migraine by a whopping 318%! Is if worth it? I guess that still depends on your affinity to brussels sprouts... # References effectsize/inst/doc/effectsize_API.Rmd0000644000175000017500000002201314170065645017547 0ustar nileshnilesh--- title: "Support functions for model extensions" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, ANOVA, standardization, standardized coefficients] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Support functions for model extensions} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) knitr::opts_chunk$set(comment = ">", warning = FALSE, message = FALSE) options(digits = 2) options(knitr.kable.NA = '') pkgs <- c("effectsize") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(333) ``` ```{r} library(effectsize) ``` ## Supporting ANOVA Effect Sizes To add support for you model, create a new `.anova_es()` method function. This functions should generally do 3 things: 1. Build a data frame with all the required information. 2. Pass the data frame to one of the 3 functions. 3. Set some attributes to the output. ### Simple ANOVA tables The input data frame must have these columns: - `Parameter` (char) - The name of the parameter or, more often, the term. - `Sum_Squares` (num) - The sum of squares. - `df` (num) - The degrees of freedom associated with the `Sum_Squares`. - `Mean_Square_residuals` (num; *optional*) - if *not* present, is calculated as `Sum_Squares / df`. (Any other column is ignored.) And exactly *1* row Where `Parameter` is `Residual`. Optionally, one of the rows can have a `(Intercept)` value for `Parameter`. An example of a minimally valid data frame: ```{r} min_aov <- data.frame( Parameter = c("(Intercept)", "A", "B", "Residuals"), Sum_Squares = c(30, 40, 10, 100), df = c(1, 1, 2, 50) ) ``` Pass the data frame to `.es_aov_simple()`: ```{r} .es_aov_simple( min_aov, type = "eta", partial = TRUE, generalized = FALSE, include_intercept = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE ) ``` The output is a data frame with the columns: `Parameter`, the effect size, and (optionally) `CI` + `CI_low` + `CI_high`, And with the following attributes: `partial`, `generalized`, `ci`, `alternative`, `anova_type` (`NA` or `NULL`), `approximate`. You can then set the `anova_type` attribute to {1, 2, 3, or `NA`} and return the output. ### ANOVA Tables with Multiple Error Strata (e.g., `aovlist` models.) The input data frame must have these columns: - `Group` (char) - The strata - `Parameter` (char) - `Sum_Squares` (num) - `df` (num) - `Mean_Square_residuals` (num; *optional*) And exactly *1* row ***per `Group`*** Where `Parameter` is `Residual`. Optionally, one of the rows can have a `(Intercept)` value for `Parameter`. An example of a minimally valid data frame: ```{r} min_aovlist <- data.frame( Group = c("S", "S", "S:A", "S:A"), Parameter = c("(Intercept)", "Residuals", "A", "Residuals"), Sum_Squares = c(34, 21, 34, 400), df = c(1, 12, 4, 30) ) ``` Pass the data frame to `.es_aov_strata()`, along with a list of predictors (including the stratifying variables) to the `DV_names` argument: ```{r} .es_aov_strata( min_aovlist, DV_names = c("S", "A"), type = "omega", partial = TRUE, generalized = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE, include_intercept = TRUE ) ``` The output is a data frame with the columns: `Group`, `Parameter`, the effect size, and (optionally) `CI` + `CI_low` + `CI_high`, And with the following attributes: `partial`, `generalized`, `ci`, `alternative`, `approximate`. You can then set the `anova_type` attribute to {1, 2, 3, or `NA`} and return the output. ### Approximate Effect sizes When *sums of squares* cannot be extracted, we can still get *approximate* effect sizes based on the `F_to_eta2()` family of functions. The input data frame must have these columns: - `Parameter` (char) - `F` (num) - The *F* test statistic. - `df` (num) - effect degrees of freedom. - (Can also have a `t` col instead, in which case `df` is set to 1, and `F` is `t^2`). - `df_error` (num) - error degrees of freedom. Optionally, one of the rows can have `(Intercept)` as the `Parameter`. An example of a minimally valid data frame: ```{r} min_anova <- data.frame( Parameter = c("(Intercept)", "A", "B"), F = c(4, 7, 0.7), df = c(1, 1, 2), df_error = 34 ) ``` Pass the table to `.es_aov_table()`: ```{r} .es_aov_table( min_anova, type = "eta", partial = TRUE, generalized = FALSE, include_intercept = FALSE, ci = 0.95, alternative = "greater", verbose = TRUE ) ``` The output is a data frame with the columns: `Parameter`, the effect size, and (optionally) `CI` + `CI_low` + `CI_high`, And with the following attributes: `partial`, `generalized`, `ci`, `alternative`, `approximate`. You can then set the `anova_type` attribute to {1, 2, 3, or `NA`} and return the output, and optionally the `approximate` attribute, and return the output. ### *Example* Let's fit a simple linear model and change its class: ```{r} mod <- lm(mpg ~ factor(cyl) + am, mtcars) class(mod) <- "superMODEL" ``` We now need a new `.anova_es.superMODEL` function: ```{r} .anova_es.superMODEL <- function(model, ...) { # Get ANOVA table anov <- suppressWarnings(stats:::anova.lm(model)) anov <- as.data.frame(anov) # Clean up anov[["Parameter"]] <- rownames(anov) colnames(anov)[2:1] <- c("Sum_Squares", "df") # Pass out <- .es_aov_simple(anov, ...) # Set attribute attr(out, "anova_type") <- 1 out } ``` And... that's it! Our new `superMODEL` class of models is fully supported! ```{r} eta_squared(mod) eta_squared(mod, partial = FALSE) omega_squared(mod) # Etc... ``` ## Supporting Model Re-Fitting with Standardized Data `effectsize::standardize.default()` should support your model if you have methods for: 1. `{insight}` functions. 2. An `update()` method that can take the model and a data frame via the `data = ` argument. Or you can make your own `standardize.my_class()` function, DIY-style (possibly using `datawizard::standardize.data.frame()` or `datawizard::standardize.numeric()`). This function should return a fiffed model of the same class as the input model. ## Supporting Standardized Parameters `standardize_parameters.default()` offers a few methods of parameter standardization: - For `method = "refit"` all you need is to have `effectsize::standardize()` support (see above) as well as `parameters::model_parameters()`. - ***API for post-hoc methods coming soon...*** # References effectsize/inst/doc/bayesian_models.R0000644000175000017500000000472114174212060017537 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) options(knitr.kable.NA = "") options(digits = 2) knitr::opts_chunk$set(comment = ">") set.seed(1) pkgs <- c("effectsize", "parameters", "rstanarm", "bayestestR", "car") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ## ---- warning=FALSE----------------------------------------------------------- library(rstanarm) data("hardlyworking", package = "effectsize") head(hardlyworking) mod <- stan_glm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking, prior = normal(0, scale = c(1, 0.5, 0.5), autoscale = TRUE), # set some priors refresh = 0 ) parameters::model_parameters(mod, test = NULL) ## ----------------------------------------------------------------------------- library(effectsize) standardize_parameters(mod, method = "refit", ci = 0.89) ## ----------------------------------------------------------------------------- library(effectsize) standardize_parameters(mod, method = "basic", ci = 0.89, centrality = "MAP", ci_method = "eti" ) ## ----------------------------------------------------------------------------- hardlyworking$age_f <- cut(hardlyworking$age, breaks = c(25, 35, 45), right = FALSE, labels = c("Young", "Less_young") ) hardlyworking$comps_f <- cut(hardlyworking$n_comps, breaks = c(0, 1, 2, 3), include.lowest = TRUE, right = FALSE ) table(hardlyworking$age_f, hardlyworking$comps_f) ## ----------------------------------------------------------------------------- # use (special) effects coding contrasts(hardlyworking$age_f) <- bayestestR::contr.bayes contrasts(hardlyworking$comps_f) <- bayestestR::contr.bayes modAOV <- stan_glm(salary ~ age_f * comps_f, data = hardlyworking, family = gaussian(), refresh = 0 ) ## ----------------------------------------------------------------------------- pes_posterior <- eta_squared_posterior(modAOV, draws = 500, # how many samples from the PPD? partial = TRUE, # partial eta squared # type 3 SS ss_function = car::Anova, type = 3 ) head(pes_posterior) bayestestR::describe_posterior(pes_posterior, rope_range = c(0, 0.1), test = "rope") ## ----------------------------------------------------------------------------- modAOV_f <- lm(salary ~ age_f * comps_f, data = hardlyworking ) eta_squared(car::Anova(modAOV_f, type = 3)) effectsize/inst/doc/standardize_parameters.html0000644000175000017500000016562114174212105021706 0ustar nileshnilesh Parameter and Model Standardization

Parameter and Model Standardization

Introduction

Standardizing parameters (i.e., coefficients) can allow for their comparison within and between models, variables and studies. Moreover, as it returns coefficients expressed in terms of change of variance (for instance, coefficients expressed in terms of SD of the response variable), it can allow for the usage of effect size interpretation guidelines, such as Cohen’s (1988) famous rules of thumb.

However, standardizing a model’s parameters should not be automatically and mindlessly done: for some research fields, particular variables or types of studies (e.g., replications), it sometimes makes more sense to keep, use and interpret the original parameters, especially if they are well known or easily understood.

Critically, parameters standardization is not a trivial process. Different techniques exist, that can lead to drastically different results. Thus, it is critical that the standardization method is explicitly documented and detailed.

Standardizing Parameters of Simple Models

Standardized Associations

library(effectsize)

m <- lm(rating ~ complaints, data = attitude)

standardize_parameters(m)
> # Standardization method: refit
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |          -9.57e-16 | [-0.21, 0.21]
> complaints  |               0.83 | [ 0.61, 1.04]

Standardizing the coefficient of this simple linear regression gives a value of 0.87, but did you know that for a simple regression this is actually the same as a correlation? Thus, you can eventually apply some (in)famous interpretation guidelines (e.g., Cohen’s rules of thumb).

correlation::correlation(attitude, select = c("rating", "complaints"))
> # Correlation Matrix (pearson-method)
> 
> Parameter1 | Parameter2 |    r |       95% CI | t(28) |         p
> -----------------------------------------------------------------
> rating     | complaints | 0.83 | [0.66, 0.91] |  7.74 | < .001***
> 
> p-value adjustment method: Holm (1979)
> Observations: 30

Standardized Differences

How does it work in the case of differences, when factors are entered and differences between a given level and a reference level? You might have heard that it is similar to a Cohen’s d. Well, let’s see.

# Select portion of data containing the two levels of interest
mtcars$am <- factor(mtcars$am, labels = c("Manual", "Automatic"))

m <- lm(mpg ~ am, data = mtcars)
standardize_parameters(m)
> # Standardization method: refit
> 
> Parameter   | Coefficient (std.) |         95% CI
> -------------------------------------------------
> (Intercept) |               0.11 | [-0.19,  0.41]
> am1         |              -0.60 | [-0.90, -0.30]

This linear model suggests that the standardized difference between Manual (the reference level - the model’s intercept) and Automatic is of 1.20 standard deviation of mpg (because the response variable was standardized, right?). Let’s compute the Cohen’s d between these two levels:

cohens_d(mpg ~ am, data = mtcars) 
> Cohen's d |         95% CI
> --------------------------
> -1.48     | [-2.27, -0.67]
> 
> - Estimated using pooled SD.

It is larger! Why? How? Both differences should be expressed in units of SD! But which SDs? Different SDs!

When looking at the difference between groups as a slope, the standardized parameter is the difference between the means in \(SD_{mpg}\). That is, the slope between Manual and Automatic is a change of 1.20 \(SD_{mpg}\)s.

However, when looking a the difference as a distance between two populations, Cohen’s d is the distance between the means in units of pooled SDs. That is, the distance between Manual and Automatic is of 1.48 SDs of each of the groups (here assumed to be equal).

In this simple model, the pooled SD is the residual SD, so we can also estimate Cohen’s d as:

coef(m)[2] / sigma(m)
>   am1 
> -0.74

And we can also get an approximation of Cohen’s d by converting the \(t\)-statistic from the regression model via t_to_d():

parameters::model_parameters(m)
> Parameter   | Coefficient |   SE |         95% CI | t(30) |      p
> ------------------------------------------------------------------
> (Intercept) |       20.77 | 0.88 | [18.97, 22.57] | 23.54 | < .001
> am [1]      |       -3.62 | 0.88 | [-5.42, -1.82] | -4.11 | < .001
t_to_d(4.11, df_error = 30)
> d    |       95% CI
> -------------------
> 1.50 | [0.68, 2.30]

It is also interesting to note that using the smart method (explained in detail below) when standardizing parameters will give you indices equivalent to Glass’ delta, which is a standardized difference expressed in terms of SD of the reference group.

m <- lm(mpg ~ am, data = mtcars)

standardize_parameters(m, method = "smart")
> # Standardization method: smart
> 
> Parameter   | Coefficient (std.) |         95% CI
> -------------------------------------------------
> (Intercept) |               0.00 | [ 0.00,  0.00]
> am1         |              -0.59 | [-0.88, -0.30]
glass_delta(mpg ~ am, data = mtcars)
> Glass' delta |         95% CI
> -----------------------------
> -1.17        | [-1.93, -0.39]

… So note that some standardized differences are different than others! :)

Standardizing Parameters of Linear Models

As mentioned above, standardization of parameters can also be used to compare among parameters within the same model. Essentially, what prevents us from normally being able to compare among different parameters is that their underlying variables are on different scales.[^But also as noted above, this is not always an issue. For example, when the variables scale is important for the interpretation of results, standardization might in fact hinder interpretation!]

For example, in the following example, we use a liner regression model to predict a worker’s salary (in Shmekels) from their age (years), seniority (years), overtime (xtra_hours) and how many compliments they give their boss (n_comps).

Let us explore the different parameter standardization methods provided by effectsize.

Standardized Slopes are Not (Always) Correlations

We saw that in simple linear models, the standardized slope is equal to the correlation between the outcome and predictor - does this hold for multiple regression as well? As in each effect in a regression model is “adjusted” for the other ones, we might expect coefficients to be somewhat alike to partial correlations. Let’s first start by computing the partial correlation between numeric predictors and the outcome.

data("hardlyworking", package = "effectsize")

head(hardlyworking)
>   salary xtra_hours n_comps age seniority
> 1  19745        4.2       1  32         3
> 2  11302        1.6       0  34         3
> 3  20636        1.2       3  33         5
> 4  23047        7.2       1  35         3
> 5  27342       11.3       0  33         4
> 6  25657        3.6       2  30         5
correlation::correlation(
  hardlyworking,
  select = "salary",
  select2 = c("xtra_hours", "n_comps", "age", "seniority"),
  partial = TRUE # get partial correlations
) 
> # Correlation Matrix (pearson-method)
> 
> Parameter1 | Parameter2 |    r |       95% CI | t(498) |         p
> ------------------------------------------------------------------
> salary     | xtra_hours | 0.87 | [0.85, 0.89] |  39.79 | < .001***
> salary     |    n_comps | 0.71 | [0.66, 0.75] |  22.40 | < .001***
> salary     |        age | 0.09 | [0.01, 0.18] |   2.09 | 0.037*   
> salary     |  seniority | 0.19 | [0.10, 0.27] |   4.30 | < .001***
> 
> p-value adjustment method: Holm (1979)
> Observations: 500

Let’s compare these to the standardized slopes:

mod <- lm(salary ~ xtra_hours + n_comps + age + seniority,
          data = hardlyworking)

standardize_parameters(mod)
> # Standardization method: refit
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |          -4.52e-17 | [-0.03, 0.03]
> xtra_hours  |               0.77 | [ 0.73, 0.81]
> n_comps     |               0.39 | [ 0.36, 0.42]
> age         |               0.04 | [ 0.00, 0.07]
> seniority   |               0.08 | [ 0.04, 0.12]

They are quite different! It seems then that standardized slopes in multiple linear regressions are not the same a correlations or partial correlations :(

However, not all hope is lost yet - we can still try and recover the partial correlations from our model, in another way: by converting the t-statistics (and their degrees of freedom, df) into a partial correlation coefficient r.

params <- parameters::model_parameters(mod)

t_to_r(params$t[-1], df_error = params$df_error[-1])
> r    |       95% CI
> -------------------
> 0.87 | [0.85, 0.89]
> 0.71 | [0.67, 0.74]
> 0.09 | [0.01, 0.18]
> 0.19 | [0.10, 0.27]

Wow, the retrieved correlations coefficients from the regression model are exactly the same as the partial correlations we estimated above! So these “r” effect sizes can also be used.

Methods of Standardizing Parameters

Let’s convert age into a 3-level factor:

hardlyworking$age_g <- cut(hardlyworking$age,
                           breaks = c(25,30,35,45))

mod <- lm(salary ~ xtra_hours + n_comps + age_g + seniority,
          data = hardlyworking)

parameters::model_parameters(mod)
> Parameter   | Coefficient |     SE |              95% CI | t(494) |      p
> --------------------------------------------------------------------------
> (Intercept) |    10135.07 | 442.88 | [9264.91, 11005.24] |  22.88 | < .001
> xtra hours  |     1221.39 |  30.72 | [1161.03,  1281.75] |  39.76 | < .001
> n comps     |     2944.95 | 131.12 | [2687.32,  3202.57] |  22.46 | < .001
> age g [1]   |     -329.90 | 189.55 | [-702.32,    42.51] |  -1.74 | 0.082 
> age g [2]   |       63.50 | 166.92 | [-264.47,   391.46] |   0.38 | 0.704 
> seniority   |      443.92 | 102.38 | [ 242.77,   645.08] |   4.34 | < .001

It seems like the best or most important predictor is n_comps as it has the coefficient. However, it is hard to compare among predictors, as they are on different scales. To address this issue, we must have all the predictors on the same scale - usually in the arbitrary unit of standard deviations.

"refit": Re-fitting the model with standardized data

This method is based on a complete model re-fit with a standardized version of data. Hence, this method is equal to standardizing the variables before fitting the model. It is the “purest” and the most accurate (Neter, Wasserman, and Kutner 1989), but it is also the most computationally costly and long (especially for heavy models such as Bayesian models, or complex mixed models). This method is particularly recommended for models that include interactions or transformations (e.g., exponentiation, log, polynomial or spline terms).

standardize_parameters(mod, method = "refit")
> # Standardization method: refit
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |           7.45e-03 | [-0.04, 0.05]
> xtra_hours  |               0.77 | [ 0.73, 0.81]
> n_comps     |               0.39 | [ 0.36, 0.43]
> age_g1      |              -0.05 | [-0.11, 0.01]
> age_g2      |               0.01 | [-0.04, 0.06]
> seniority   |               0.08 | [ 0.04, 0.12]

standardize_parameters also has a robust argument (default to FALSE), which enables a robust standardization of the data, i.e., based on the median and MAD instead of the mean and SD:

standardize_parameters(mod, method = "refit", robust = TRUE)
> # Standardization method: refit
> 
> Parameter   | Coefficient (std.) |         95% CI
> -------------------------------------------------
> (Intercept) |              -0.14 | [-0.20, -0.08]
> xtra_hours  |               0.65 | [ 0.62,  0.68]
> n_comps     |               0.82 | [ 0.74,  0.89]
> age_g1      |              -0.06 | [-0.13,  0.01]
> age_g2      |               0.01 | [-0.05,  0.07]
> seniority   |               0.12 | [ 0.07,  0.18]
> 
> - Scaled by one MAD(s) from the median.

Note that since age_g is a factor, it is not numerically standardized, and so it standardized parameter is still not directly comparable to those of numeric variables. To address this, we can set two_sd = TRUE, thereby scaling parameters on 2 SDs (or MADs) of the predictors (Gelman 2008).

standardize_parameters(mod, method = "refit", two_sd = TRUE)
> # Standardization method: refit
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |           7.45e-03 | [-0.04, 0.05]
> xtra_hours  |               1.54 | [ 1.46, 1.61]
> n_comps     |               0.78 | [ 0.72, 0.85]
> age_g1      |              -0.05 | [-0.11, 0.01]
> age_g2      |               0.01 | [-0.04, 0.06]
> seniority   |               0.16 | [ 0.09, 0.24]
> 
> - Scaled by two SD(s) from the mean.

effectsize also comes with a helper function that returns the re-fit model, without summarizing it, which can then be used as the original model would:

mod_z <- standardize(mod, two_sd = FALSE, robust = FALSE)
mod_z
> 
> Call:
> lm(formula = salary ~ xtra_hours + n_comps + age_g + seniority, 
>     data = data_std)
> 
> Coefficients:
> (Intercept)   xtra_hours      n_comps       age_g1       age_g2    seniority  
>     0.00745      0.76923      0.39206     -0.05323      0.01024      0.08212
parameters::model_parameters(mod_z)
> Parameter   | Coefficient |   SE |        95% CI | t(494) |      p
> ------------------------------------------------------------------
> (Intercept) |    7.45e-03 | 0.02 | [-0.04, 0.05] |   0.31 | 0.753 
> xtra hours  |        0.77 | 0.02 | [ 0.73, 0.81] |  39.76 | < .001
> n comps     |        0.39 | 0.02 | [ 0.36, 0.43] |  22.46 | < .001
> age g [1]   |       -0.05 | 0.03 | [-0.11, 0.01] |  -1.74 | 0.082 
> age g [2]   |        0.01 | 0.03 | [-0.04, 0.06] |   0.38 | 0.704 
> seniority   |        0.08 | 0.02 | [ 0.04, 0.12] |   4.34 | < .001

"posthoc": Refit without refitting

Post-hoc standardization of the parameters aims at emulating the results obtained by "refit" without refitting the model. The coefficients are divided by the standard deviation (or MAD if robust) of the outcome (which becomes their expression ‘unit’). Then, the coefficients related to numeric variables are additionally multiplied by the standard deviation (or MAD if robust) of the related terms, so that they correspond to changes of 1 SD of the predictor (e.g., “A change in 1 SD of x is related to a change of 0.24 of the SD of y). This does not apply to binary variables or factors, so the coefficients are still related to changes in levels. This method is not accurate and tend to give aberrant results when interactions are specified.

standardize_parameters(mod, method = "posthoc")
> # Standardization method: posthoc
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |               0.00 | [ 0.00, 0.00]
> xtra_hours  |               0.77 | [ 0.73, 0.81]
> n_comps     |               0.39 | [ 0.36, 0.43]
> age_g1      |              -0.05 | [-0.11, 0.01]
> age_g2      |               0.01 | [-0.04, 0.06]
> seniority   |               0.08 | [ 0.04, 0.12]

"smart": Standardization of Model’s parameters with Adjustment, Reconnaissance and Transformation

Experimental

Similar to method = "posthoc" in that it does not involve model refitting. The difference is that the SD of the response is computed on the relevant section of the data. For instance, if a factor with 3 levels A (the intercept), B and C is entered as a predictor, the effect corresponding to B vs. A will be scaled by the variance of the response at the intercept only. As a results, the coefficients for effects of factors are similar to a Glass’ delta.

standardize_parameters(mod, method = "smart")
> # Standardization method: smart
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |               0.00 | [ 0.00, 0.00]
> xtra_hours  |               0.77 | [ 0.73, 0.81]
> n_comps     |               0.39 | [ 0.36, 0.43]
> age_g1      |              -0.05 | [-0.11, 0.01]
> age_g2      |               0.01 | [-0.04, 0.06]
> seniority   |               0.08 | [ 0.04, 0.12]

"basic": Raw scaling of the model frame

This method is similar to method = "posthoc", but treats all variables as continuous: it scales the coefficient by the standard deviation of model’s matrix’ parameter of factors levels (transformed to integers) or binary predictors. Although it can be argued that this might be inappropriate for these cases, this method allows for easier importance judgment across all predictor type (numeric, factor, interactions…). It is also the type of standardization implemented by default in other software packages (also lm.beta::lm.beta()), and, such as can be used for reproducibility and replication purposes.

standardize_parameters(mod, method = "basic")
> # Standardization method: basic
> 
> Parameter   | Coefficient (std.) |        95% CI
> ------------------------------------------------
> (Intercept) |               0.00 | [ 0.00, 0.00]
> xtra_hours  |               0.77 | [ 0.73, 0.81]
> n_comps     |               0.39 | [ 0.36, 0.43]
> age_g1      |              -0.03 | [-0.07, 0.00]
> age_g2      |           6.56e-03 | [-0.03, 0.04]
> seniority   |               0.08 | [ 0.04, 0.12]

Standardizing Parameters In Mixed Models

Linear mixed models (LMM/HLM/MLM) offer an additional conundrum to standardization - how does one even calculate the SDs of the various predictors? Or of the response - is it the deviations within each group? Or perhaps between them?

The solution: standardize according to level of the predictor (Hoffman 2015, 342)! Level 1 parameters are standardized according to variance within groups, while level 2 parameters are standardized according to variance between groups. The resulting standardized coefficient are also called pseudo-standardized coefficients.[^Note that like method "basic", these are based on the model matrix.]

m <- lme4::lmer(Reaction ~ Days + (Days|Subject), data = lme4::sleepstudy)

standardize_parameters(m, method = "pseudo", ci_method = "satterthwaite")
> # Standardization method: pseudo
> 
> Parameter   | Coefficient (std.) |       95% CI
> -----------------------------------------------
> (Intercept) |               0.00 | [0.00, 0.00]
> Days        |               0.68 | [0.47, 0.89]
# compare to:
standardize_parameters(m, method = "basic", ci_method = "satterthwaite")
> # Standardization method: basic
> 
> Parameter   | Coefficient (std.) |       95% CI
> -----------------------------------------------
> (Intercept) |               0.00 | [0.00, 0.00]
> Days        |               0.54 | [0.37, 0.70]

Standardizing Parameters In Generalized Linear Models

Unlike linear (/mixed) models, in generalized linear (/mixed) models (GLMs) there is less of a need for standardization. Why? Because in many GLMs the estimated coefficients are themselves measures of effect size, such as odds-ratios (OR) in logistic regression, or incidence rate ratios (IRR) in Poisson regressions. This is because in such model the outcome is not on an arbitrary scale - that is, the meaning of rates and probabilities are changed by arbitrary linear transformations.

But still, some standardization is sometimes needed, for the predictors. Luckily, standardize_parameters() (and standardize()) are smart enough to know when GLMs are passed so as to only standardize according to the predictors:

mod_b <- glm(am ~ mpg + factor(cyl),
             data = mtcars,
             family = binomial())

standardize_parameters(mod_b, method = "refit", two_sd = TRUE)
> # Standardization method: refit
> 
> Parameter    | Coefficient (std.) |         95% CI
> --------------------------------------------------
> (Intercept)  |              -0.43 | [-1.37,  0.46]
> mpg          |               4.46 | [ 0.30, 10.54]
> factor(cyl)1 |              -0.48 | [-2.67,  1.56]
> factor(cyl)2 |               0.25 | [-1.05,  1.55]
> 
> - Scaled by two SD(s) from the mean.
> (Response is unstandardized)
# standardize_parameters(mod_b, method = "posthoc", two_sd = TRUE)
# standardize_parameters(mod_b, method = "basic")

These can then be converted to OR (with exp()) and discussed as the “change in Odds as a function of a change in one SD of x.”

std <- standardize_parameters(mod_b, method = "refit", two_sd = TRUE)
exp(std$Std_Coefficient)
> [1]  0.65 86.40  0.62  1.29

Or we can directly ask for the coefficients to be exponentiated:

standardize_parameters(mod_b, method = "refit", two_sd = TRUE, exponentiate = TRUE)
> # Standardization method: refit
> 
> Parameter    | Odds Ratio (std.) |           95% CI
> ---------------------------------------------------
> (Intercept)  |              0.65 | [0.25,     1.58]
> mpg          |             86.40 | [1.36, 37955.41]
> factor(cyl)1 |              0.62 | [0.07,     4.77]
> factor(cyl)2 |              1.29 | [0.35,     4.69]
> 
> - Scaled by two SD(s) from the mean.
> (Response is unstandardized)

Cohen’s f

Cohen’s \(f\) (of ANOVA fame) can be used as a measure of effect size in the context of sequential multiple regression (i.e., nested models). That is, when comparing two models, we can examine the ratio between the increase in \(R^2\) and the unexplained variance:

\[ f^{2}={R_{AB}^{2}-R_{A}^{2} \over 1-R_{AB}^{2}} \]

m1 <- lm(salary ~ xtra_hours, data = hardlyworking)
m2 <- lm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking)

cohens_f_squared(m1, model2 = m2)
> Cohen's f2 (partial) |           95% CI | R2_delta
> --------------------------------------------------
> 1.19                 | [0.99,      Inf] |     0.17
> 
> - One-sided CIs: upper bound fixed at (Inf).

References

Gelman, Andrew. 2008. “Scaling Regression Inputs by Dividing by Two Standard Deviations.” Statistics in Medicine 27 (15): 2865–73.
Hoffman, Lesa. 2015. Longitudinal Analysis: Modeling Within-Person Fluctuation and Change. Routledge.
Neter, John, William Wasserman, and Michael H Kutner. 1989. “Applied Linear Regression Models.”
effectsize/inst/doc/from_test_statistics.Rmd0000644000175000017500000001574514170065645021221 0ustar nileshnilesh--- title: "Effect Size from Test Statistics" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, standardization, cohen d] vignette: > %\VignetteIndexEntry{Effect Size from Test Statistics} \usepackage[utf8]{inputenc} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) library(effectsize) knitr::opts_chunk$set(comment = ">") options(digits = 2) options(knitr.kable.NA = "") pkgs <- c("effectsize", "afex", "lmerTest", "emmeans", "parameters") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } else { library(afex) library(lmerTest) library(emmeans) library(parameters) } set.seed(747) ``` # Introduction In many real world applications there are no straightforward ways of obtaining standardized effect sizes. However, it is possible to get approximations of most of the effect size indices ($d$, $r$, $\eta^2_p$...) with the use of test statistics. These conversions are based on the idea that **test statistics are a function of effect size and sample size**. Thus information about samples size (or more often of degrees of freedom) is used to reverse-engineer indices of effect size from test statistics. This idea and these functions also power our [***Effect Sizes From Test Statistics*** *shiny app*](https://easystats4u.shinyapps.io/statistic2effectsize/). The measures discussed here are, in one way or another, ***signal to noise ratios***, with the "noise" representing the unaccounted variance in the outcome variable^[Note that for generalized linear models (Poisson, Logistic...), where the outcome is never on an arbitrary scale, estimates themselves **are** indices of effect size! Thus this vignette is relevant only to general linear models.]. The indices are: - Percent variance explained ($\eta^2_p$, $\omega^2_p$, $\epsilon^2_p$). - Measure of association ($r$). - Measure of difference ($d$). ## (Partial) Percent Variance Explained These measures represent the ratio of $Signal^2 / (Signal^2 + Noise^2)$, with the "noise" having all other "signals" partial-ed out (be they of other fixed or random effects). The most popular of these indices is $\eta^2_p$ (Eta; which is equivalent to $R^2$). The conversion of the $F$- or $t$-statistic is based on @friedman1982simplified. Let's look at an example: ```{r} library(afex) data(md_12.1) aov_fit <- aov_car(rt ~ angle * noise + Error(id / (angle * noise)), data = md_12.1, anova_table = list(correction = "none", es = "pes") ) aov_fit ``` Let's compare the $\eta^2_p$ (the `pes` column) obtained here with ones recovered from `F_to_eta2()`: ```{r} library(effectsize) F_to_eta2( f = c(40.72, 33.77, 45.31), df = c(2, 1, 2), df_error = c(18, 9, 18) ) ``` **They are identical!**^[Note that these are *partial* percent variance explained, and so their sum can be larger than 1.] (except for the fact that `F_to_eta2()` also provides confidence intervals^[Confidence intervals for all indices are estimated using the non-centrality parameter method; These methods search for a the best non-central parameter of the non-central $F$/$t$ distribution for the desired tail-probabilities, and then convert these ncps to the corresponding effect sizes.] :) In this case we were able to easily obtain the effect size (thanks to `afex`!), but in other cases it might not be as easy, and using estimates based on test statistic offers a good approximation. For example: ### In Simple Effect and Contrast Analysis ```{r} library(emmeans) joint_tests(aov_fit, by = "noise") F_to_eta2( f = c(5, 79), df = 2, df_error = 29 ) ``` We can also use `t_to_eta2()` for contrast analysis: ```{r} pairs(emmeans(aov_fit, ~angle)) t_to_eta2( t = c(-5.7, -8.9, -3.2), df_error = 18 ) ``` ### In Linear Mixed Models ```{r} library(lmerTest) fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) anova(fit_lmm) F_to_eta2(45.8, 1, 17) ``` We can also use `t_to_eta2()` for the slope of `Days` (which in this case gives the same result). ```{r} parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite") t_to_eta2(6.77, df_error = 17) ``` ### Bias-Corrected Indices Alongside $\eta^2_p$ there are also the less biased $\omega_p^2$ (Omega) and $\epsilon^2_p$ (Epsilon; sometimes called $\text{Adj. }\eta^2_p$, which is equivalent to $R^2_{adj}$; @albers2018power, @mordkoff2019simple). ```{r} F_to_eta2(45.8, 1, 17) F_to_epsilon2(45.8, 1, 17) F_to_omega2(45.8, 1, 17) ``` ## Measure of Association Similar to $\eta^2_p$, $r$ is a signal to noise ratio, and is in fact equal to $\sqrt{\eta^2_p}$ (so it's really a *partial* $r$). It is often used instead of $\eta^2_p$ when discussing the *strength* of association (but I suspect people use it instead of $\eta^2_p$ because it gives a bigger number, which looks better). ### For Slopes ```{r} parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite") t_to_r(6.77, df_error = 17) ``` In a fixed-effect linear model, this returns the **partial** correlation. Compare: ```{r} fit_lm <- lm(rating ~ complaints + critical, data = attitude) parameters::model_parameters(fit_lm) t_to_r( t = c(7.46, 0.01), df_error = 27 ) ``` to: ```{r, eval=require(correlation, quietly = TRUE)} correlation::correlation(attitude[, c(1, 2, 6)], partial = TRUE)[1:2, c(2, 3, 7, 8)] ``` ### In Contrast Analysis This measure is also sometimes used in contrast analysis, where it is called the point bi-serial correlation - $r_{pb}$ [@cohen1965some; @rosnow2000contrasts]: ```{r} pairs(emmeans(aov_fit, ~angle)) t_to_r( t = c(-5.7, -8.9, -3.2), df_error = 18 ) ``` ## Measures of Difference These indices represent $Signal/Noise$ with the "signal" representing the difference between two means. This is akin to Cohen's $d$, and is a close approximation when comparing two groups of equal size [@wolf1986meta; @rosnow2000contrasts]. These can be useful in contrast analyses. ### Between-Subject Contrasts ```{r} m <- lm(breaks ~ tension, data = warpbreaks) em_tension <- emmeans(m, ~tension) pairs(em_tension) t_to_d( t = c(2.53, 3.72, 1.20), df_error = 51 ) ``` However, these are merely approximations of a *true* Cohen's *d*. It is advised to directly estimate Cohen's *d*, whenever possible. For example, here with `emmeans::eff_size()`: ```{r} eff_size(em_tension, sigma = sigma(m), edf = df.residual(m)) ``` ### Within-Subject Contrasts ```{r} pairs(emmeans(aov_fit, ~angle)) t_to_d( t = c(-5.7, -5.9, -3.2), df_error = 18, paired = TRUE ) ``` (Note set `paired = TRUE` to not over estimate the size of the effect; @rosenthal1991meta; @rosnow2000contrasts) # References effectsize/inst/doc/interpret.Rmd0000644000175000017500000002554514170072534016753 0ustar nileshnilesh--- title: "Automated Interpretation of Indices of Effect Size" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, effect size, rules of thumb, guidelines, interpretation] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Automated Interpretation of Indices of Effect Size} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 2) pkgs <- c("effectsize") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` ## Why? The metrics used in statistics (indices of fit, model performance, or parameter estimates) can be very abstract. A long experience is required to intuitively ***feel*** the meaning of their values. In order to facilitate the understanding of the results they are facing, many scientists use (often implicitly) some set of **rules of thumb**. Some of these rules of thumb have been standardize and validated and subsequently published as guidelines. Understandably then, such rules of thumb are just suggestions and there is nothing universal about them. The interpretation of **any** effect size measures is always going to be relative to the discipline, the specific data, and the aims of the analyst. This is important because what might be considered a small effect in psychology might be large for some other field like public health. One of the most famous interpretation grids was proposed by **Cohen (1988)** for a series of widely used indices, such as the correlation **r** (*r* = .20, small; *r* = .40, moderate and *r* = .60, large) or the **standardized difference** (*Cohen's d*). However, there is now a clear evidence that Cohen's guidelines (which he himself later disavowed; Funder, 2019) are much too stringent and not particularly meaningful taken out of context [@funder2019evaluating]. This led to the emergence of a literature discussing and creating new sets of rules of thumb. Although **everybody** agrees on the fact that effect size interpretation in a study should be justified with a rationale (and depend on the context, the field, the literature, the hypothesis, etc.), these pre-baked rules can nevertheless be useful to give a rough idea or frame of reference to understand scientific results. The package **`effectsize`** catalogs such sets of rules of thumb for a variety of indices in a flexible and explicit fashion, helping you understand and report your results in a scientific yet meaningful way. Again, readers should keep in mind that these thresholds, as ubiquitous as they may be, **remain arbitrary**. Thus, their use should be discussed on a case-by-case basis depending on the field, hypotheses, prior results, and so on, to avoid their crystallization, as for the infamous $p < .05$ criterion of hypothesis testing. Moreover, some authors suggest the counter-intuitive idea that *very large effects*, especially in the context of psychological research, is likely to be a "gross overestimate that will rarely be found in a large sample or in a replication" [@funder2019evaluating]. They suggest that smaller effect size are worth taking seriously (as they can be potentially consequential), as well as more believable. ## Correlation *r* #### @funder2019evaluating ```r interpret_r(x, rules = "funder2019") ``` - **r < 0.05** - Tiny - **0.05 <= r < 0.1** - Very small - **0.1 <= r < 0.2** - Small - **0.2 <= r < 0.3** - Medium - **0.3 <= r < 0.4** - Large - **r >= 0.4** - Very large #### @gignac2016effect Gignac's rules of thumb are actually one of few interpretation grid justified and based on actual data, in this case on the distribution of effect magnitudes in the literature. ```r interpret_r(x, rules = "gignac2016") ``` - **r < 0.1** - Very small - **0.1 <= r < 0.2** - Small - **0.2 <= r < 0.3** - Moderate - **r >= 0.3** - Large #### @cohen1988statistical ```r interpret_r(x, rules = "cohen1988") ``` - **r < 0.1** - Very small - **0.1 <= r < 0.3** - Small - **0.3 <= r < 0.5** - Moderate - **r >= 0.5** - Large #### @evans1996straightforward ```r interpret_r(x, rules = "evans1996") ``` - **r < 0.2** - Very weak - **0.2 <= r < 0.4** - Weak - **0.4 <= r < 0.6** - Moderate - **0.6 <= r < 0.8** - Strong - **r >= 0.8** - Very strong #### @lovakov2021empirically ```r interpret_r(x, rules = "lovakov2021") ``` - **r < 0.12** - Very small - **0.12 <= r < 0.24** - Small - **0.24 <= r < 0.41** - Moderate - **r >= 0.41** - Large ## Standardized Difference *d* (Cohen's *d*) The standardized difference can be obtained through the standardization of linear model's parameters or data, in which they can be used as indices of effect size. #### @cohen1988statistical ```r interpret_cohens_d(x, rules = "cohen1988") ``` - **d < 0.2** - Very small - **0.2 <= d < 0.5** - Small - **0.5 <= d < 0.8** - Medium - **d >= 0.8** - Large #### @sawilowsky2009new ```r interpret_cohens_d(x, rules = "sawilowsky2009") ``` - **d < 0.1** - Tiny - **0.1 <= d < 0.2** - Very small - **0.2 <= d < 0.5** - Small - **0.5 <= d < 0.8** - Medium - **0.8 <= d < 1.2** - Large - **1.2 <= d < 2** - Very large - **d >= 2** - Huge #### @gignac2016effect Gignac's rules of thumb are actually one of few interpretation grid justified and based on actual data, in this case on the distribution of effect magnitudes in the literature. These is in fact the same grid used for *r*, based on the conversion of *r* to *d*: ```r interpret_cohens_d(x, rules = "gignac2016") ``` - **d < 0.2** - Very small - **0.2 <= d < 0.41** - Small - **0.41 <= d < 0.63** - Moderate - **d >= 0.63** - Large #### @lovakov2021empirically ```r interpret_cohens_d(x, rules = "lovakov2021") ``` - **r < 0.15** - Very small - **0.15 <= r < 0.36** - Small - **0.36 <= r < 0.65** - Moderate - **r >= 0.65** - Large ## Odds Ratio (OR) Odds ratio, and *log* odds ratio, are often found in epidemiological studies. However, they are also the parameters of ***logistic*** regressions, where they can be used as indices of effect size. Note that the (log) odds ratio from logistic regression coefficients are *unstandardized*, as they depend on the scale of the predictor. In order to apply the following guidelines, make sure you [*standardize*](https://easystats.github.io/effectsize/articles/standardize_parameters.html) your predictors! Keep in mind that these apply to Odds *ratios*, so Odds ratio of 10 is as extreme as a Odds ratio of 0.1 (1/10). #### @chen2010big ```r interpret_oddsratio(x, rules = "chen2010") ``` - **OR < 1.68** - Very small - **1.68 <= OR < 3.47** - Small - **3.47 <= OR < 6.71** - Medium - **OR >= 6.71 ** - Large #### @cohen1988statistical ```r interpret_oddsratio(x, rules = "cohen1988") ``` - **OR < 1.44** - Very small - **1.44 <= OR < 2.48** - Small - **2.48 <= OR < 4.27** - Medium - **OR >= 4.27 ** - Large This converts (log) odds ratio to standardized difference *d* using the following formula [@cohen1988statistical;@sanchez2003effect]: $$ d = log(OR) \times \frac{\sqrt{3}}{\pi} $$ ## Coefficient of determination (R2) ### For Linear Regression #### @cohen1988statistical ```r interpret_r2(x, rules = "cohen1988") ``` - **R2 < 0.02** - Very weak - **0.02 <= R2 < 0.13** - Weak - **0.13 <= R2 < 0.26** - Moderate - **R2 >= 0.26** - Substantial #### @falk1992primer ```r interpret_r2(x, rules = "falk1992") ``` - **R2 < 0.1** - Negligible - **R2 >= 0.1** - Adequate ### For PLS / SEM R-Squared of *latent* variables #### @chin1998partial ```r interpret_r2(x, rules = "chin1998") ``` - **R2 < 0.19** - Very weak - **0.19 <= R2 < 0.33** - Weak - **0.33 <= R2 < 0.67** - Moderate - **R2 >= 0.67** - Substantial #### @hair2011pls ```r interpret_r2(x, rules = "hair2011") ``` - **R2 < 0.25** - Very weak - **0.25 <= R2 < 0.50** - Weak - **0.50 <= R2 < 0.75** - Moderate - **R2 >= 0.75** - Substantial ## Omega / Eta / Epsilon Squared The Omega squared is a measure of effect size used in ANOVAs. It is an estimate of how much variance in the response variables are accounted for by the explanatory variables. Omega squared is widely viewed as a lesser biased alternative to eta-squared, especially when sample sizes are small. #### @field2013discovering ```r interpret_omega_squared(x, rules = "field2013") ``` - **ES < 0.01** - Very small - **0.01 <= ES < 0.06** - Small - **0.16 <= ES < 0.14** - Medium - **ES >= 0.14 ** - Large #### @cohen1992power These are applicable to one-way ANOVAs, or to *partial* Eta / Omega / Epsilon Squared in a multi-way ANOVA. ```r interpret_omega_squared(x, rules = "cohen1992") ``` - **ES < 0.02** - Very small - **0.02 <= ES < 0.13** - Small - **0.13 <= ES < 0.26** - Medium - **ES >= 0.26** - Large ## Kendall's coefficient of concordance The interpretation of Kendall's coefficient of concordance (*w*) is a measure of effect size used in non-parametric ANOVAs (the Friedman rank sum test). It is an estimate of agreement among multiple raters. #### @landis1977measurement ```r interpret_omega_squared(w, rules = "landis1977") ``` - **0.00 <= w < 0.20** - Slight agreement - **0.20 <= w < 0.40** - Fair agreement - **0.40 <= w < 0.60** - Moderate agreement - **0.60 <= w < 0.80** - Substantial agreement - **w >= 0.80** - Almost perfect agreement ## Cohen's *g* Cohen's *g* is a measure of effect size used for McNemar's test of agreement in selection - when repeating a multiple chose selection, is the percent of matches (first response is equal to the second response) different than 50%? #### @cohen1988statistical ```r interpret_cohens_g(x, rules = "cohen1988") ``` - **d < 0.05** - Very small - **0.05 <= d < 0.15** - Small - **0.15 <= d < 0.25** - Medium - **d >= 0.25** - Large ## Interpretation of other Indices `effectsize` also offers functions for interpreting other statistical indices: - `interpret_gfi()`, `interpret_agfi()`, `interpret_nfi()`, `interpret_nnfi()`, `interpret_cfi()`, `interpret_rmsea()`, `interpret_srmr()`, `interpret_rfi()`, `interpret_ifi()`, and `interpret_pnfi()` for interpretation CFA / SEM goodness of fit. - `interpret_p()` for interpretation of *p*-values. - `interpret_direction()` for interpretation of direction. - `interpret_bf()` for interpretation of Bayes factors. - `interpret_rope()` for interpretation of Bayesian ROPE tests. - `interpret_ess()` and `interpret_rhat()` for interpretation of Bayesian diagnostic indices. # References effectsize/inst/doc/effectsize_API.html0000644000175000017500000010246214174212063017770 0ustar nileshnilesh Support functions for model extensions

Support functions for model extensions

library(effectsize)

Supporting ANOVA Effect Sizes

To add support for you model, create a new .anova_es() method function. This functions should generally do 3 things:

  1. Build a data frame with all the required information.
  2. Pass the data frame to one of the 3 functions.
  3. Set some attributes to the output.

Simple ANOVA tables

The input data frame must have these columns: - Parameter (char) - The name of the parameter or, more often, the term. - Sum_Squares (num) - The sum of squares. - df (num) - The degrees of freedom associated with the Sum_Squares. - Mean_Square_residuals (num; optional) - if not present, is calculated as Sum_Squares / df. (Any other column is ignored.)

And exactly 1 row Where Parameter is Residual.

Optionally, one of the rows can have a (Intercept) value for Parameter.

An example of a minimally valid data frame:

min_aov <- data.frame(
  Parameter = c("(Intercept)", "A", "B", "Residuals"),
  Sum_Squares = c(30, 40, 10, 100),
  df = c(1, 1, 2, 50)
)

Pass the data frame to .es_aov_simple():

.es_aov_simple(
  min_aov,
  type = "eta", partial = TRUE, generalized = FALSE,
  include_intercept = FALSE,
  ci = 0.95, alternative = "greater",
  verbose = TRUE
)
>   Parameter Eta2_partial   CI CI_low CI_high
> 1         A        0.286 0.95   0.12       1
> 2         B        0.091 0.95   0.00       1

The output is a data frame with the columns: Parameter, the effect size, and (optionally) CI + CI_low + CI_high,

And with the following attributes: partial, generalized, ci, alternative, anova_type (NA or NULL), approximate.

You can then set the anova_type attribute to {1, 2, 3, or NA} and return the output.

ANOVA Tables with Multiple Error Strata

(e.g., aovlist models.)

The input data frame must have these columns:

  • Group (char) - The strata
  • Parameter (char)
  • Sum_Squares (num)
  • df (num)
  • Mean_Square_residuals (num; optional)

And exactly 1 row per Group Where Parameter is Residual.

Optionally, one of the rows can have a (Intercept) value for Parameter.

An example of a minimally valid data frame:

min_aovlist <- data.frame(
  Group = c("S", "S", "S:A", "S:A"),
  Parameter = c("(Intercept)", "Residuals", "A", "Residuals"),
  Sum_Squares = c(34, 21, 34, 400),
  df = c(1, 12, 4, 30)
)

Pass the data frame to .es_aov_strata(), along with a list of predictors (including the stratifying variables) to the DV_names argument:

.es_aov_strata(
  min_aovlist, DV_names = c("S", "A"),
  type = "omega", partial = TRUE, generalized = FALSE,
  ci = 0.95, alternative = "greater",
  verbose = TRUE,
  include_intercept = TRUE
)
>   Group   Parameter Omega2_partial   CI CI_low CI_high
> 1     S (Intercept)          0.568 0.95   0.21       1
> 2   S:A           A         -0.042 0.95   0.00       1

The output is a data frame with the columns: Group, Parameter, the effect size, and (optionally) CI + CI_low + CI_high,

And with the following attributes: partial, generalized, ci, alternative, approximate.

You can then set the anova_type attribute to {1, 2, 3, or NA} and return the output.

Approximate Effect sizes

When sums of squares cannot be extracted, we can still get approximate effect sizes based on the F_to_eta2() family of functions.

The input data frame must have these columns:

  • Parameter (char)
  • F (num) - The F test statistic.
  • df (num) - effect degrees of freedom.
  • (Can also have a t col instead, in which case df is set to 1, and F is t^2).
  • df_error (num) - error degrees of freedom.

Optionally, one of the rows can have (Intercept) as the Parameter.

An example of a minimally valid data frame:

min_anova <- data.frame(
  Parameter = c("(Intercept)", "A", "B"),
  F = c(4, 7, 0.7),
  df = c(1, 1, 2),
  df_error = 34
)

Pass the table to .es_aov_table():

.es_aov_table(
  min_anova,
  type = "eta", partial = TRUE, generalized = FALSE,
  include_intercept = FALSE,
  ci = 0.95, alternative = "greater",
  verbose = TRUE
)
>   Parameter Eta2_partial   CI CI_low CI_high
> 1         A         0.17 0.95  0.023       1
> 2         B         0.04 0.95  0.000       1

The output is a data frame with the columns: Parameter, the effect size, and (optionally) CI + CI_low + CI_high,

And with the following attributes: partial, generalized, ci, alternative, approximate.

You can then set the anova_type attribute to {1, 2, 3, or NA} and return the output, and optionally the approximate attribute, and return the output.

Example

Let’s fit a simple linear model and change its class:

mod <- lm(mpg ~ factor(cyl) + am, mtcars)

class(mod) <- "superMODEL"

We now need a new .anova_es.superMODEL function:

.anova_es.superMODEL <- function(model, ...) {
  # Get ANOVA table
  anov <- suppressWarnings(stats:::anova.lm(model))
  anov <- as.data.frame(anov)
  
  # Clean up
  anov[["Parameter"]] <- rownames(anov)
  colnames(anov)[2:1] <- c("Sum_Squares", "df")
  
  # Pass
  out <- .es_aov_simple(anov, ...)
  
  # Set attribute
  attr(out, "anova_type") <- 1
  
  out
}

And… that’s it! Our new superMODEL class of models is fully supported!

eta_squared(mod)
> # Effect Size for ANOVA (Type I)
> 
> Parameter   | Eta2 (partial) |       95% CI
> -------------------------------------------
> factor(cyl) |           0.76 | [0.61, 1.00]
> am          |           0.12 | [0.00, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
eta_squared(mod, partial = FALSE)
> # Effect Size for ANOVA (Type I)
> 
> Parameter   | Eta2 |       95% CI
> ---------------------------------
> factor(cyl) | 0.73 | [0.57, 1.00]
> am          | 0.03 | [0.00, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
omega_squared(mod)
> # Effect Size for ANOVA (Type I)
> 
> Parameter   | Omega2 (partial) |       95% CI
> ---------------------------------------------
> factor(cyl) |             0.73 | [0.56, 1.00]
> am          |             0.08 | [0.00, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
# Etc...

Supporting Model Re-Fitting with Standardized Data

effectsize::standardize.default() should support your model if you have methods for:

  1. {insight} functions.
  2. An update() method that can take the model and a data frame via the data = argument.

Or you can make your own standardize.my_class() function, DIY-style (possibly using datawizard::standardize.data.frame() or datawizard::standardize.numeric()). This function should return a fiffed model of the same class as the input model.

Supporting Standardized Parameters

standardize_parameters.default() offers a few methods of parameter standardization:

  • For method = "refit" all you need is to have effectsize::standardize() support (see above) as well as parameters::model_parameters().
  • API for post-hoc methods coming soon…

References

effectsize/inst/doc/anovaES.R0000644000175000017500000000541614174212042015737 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) options(knitr.kable.NA = "") options(digits = 2) knitr::opts_chunk$set(comment = ">", warning = FALSE) set.seed(1) pkgs <- c("effectsize", "parameters", "car", "afex") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ## ----------------------------------------------------------------------------- data(obk.long, package = "afex") # modify the data slightly for the demonstration: obk.long <- obk.long[1:240 %% 3 == 0, ] obk.long$id <- seq_len(nrow(obk.long)) m <- lm(value ~ treatment, data = obk.long) parameters::model_parameters(m) ## ----------------------------------------------------------------------------- parameters::model_parameters(anova(m)) ## ----------------------------------------------------------------------------- library(effectsize) eta_squared(m, partial = FALSE) ## ----------------------------------------------------------------------------- m <- lm(value ~ gender + phase + treatment, data = obk.long) eta_squared(m, partial = FALSE) eta_squared(m) # partial = TRUE by default ## ----------------------------------------------------------------------------- eta_squared(car::Anova(m, type = 2), partial = FALSE) eta_squared(car::Anova(m, type = 3)) # partial = TRUE by default ## ----------------------------------------------------------------------------- # compare m_interaction1 <- lm(value ~ treatment * gender, data = obk.long) # to: m_interaction2 <- lm( value ~ treatment * gender, data = obk.long, contrasts = list( treatment = "contr.sum", gender = "contr.sum" ) ) eta_squared(car::Anova(m_interaction1, type = 3)) eta_squared(car::Anova(m_interaction2, type = 3)) ## ----------------------------------------------------------------------------- library(afex) m_afex <- aov_car(value ~ treatment * gender + Error(id), data = obk.long) eta_squared(m_afex) ## ----------------------------------------------------------------------------- omega_squared(m_afex) epsilon_squared(m_afex) ## ----------------------------------------------------------------------------- eta_squared(m_afex, generalized = "gender") ## ----------------------------------------------------------------------------- cohens_f(m_afex) ## ---- eval=require(lmerTest)-------------------------------------------------- library(lmerTest) fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) anova(fit_lmm) # note the type-3 errors F_to_eta2(45.8, df = 1, df_error = 17) ## ---- eval=require(lmerTest)-------------------------------------------------- eta_squared(fit_lmm) epsilon_squared(fit_lmm) omega_squared(fit_lmm) effectsize/inst/doc/convert.html0000644000175000017500000007015514174212061016631 0ustar nileshnilesh Converting Between Indices of Effect Size

Converting Between Indices of Effect Size

The effectsize package contains function to convert among indices of effect size. This can be useful for meta-analyses, or any comparison between different types of statistical analyses.

Converting Between d, r, and OR

The most basic conversion is between r values, a measure of standardized association between two continuous measures, and d values (such as Cohen’s d), a measure of standardized differences between two groups / conditions.

Let’s simulate some data:

set.seed(1)
data <- bayestestR::simulate_difference(
  n = 10,
  d = 0.2,
  names = c("Group", "Outcome")
)
>    Group Outcome
> 1      0  -0.942
> 2      0  -0.485
> 3      0  -0.100
> 4      0   0.285
> 5      0   0.742
> 6      1  -0.742
> 7      1  -0.285
> 8      1   0.100
> 9      1   0.485
> 10     1   0.942

We can compute Cohen’s d between the two groups:

cohens_d(Outcome ~ Group, data = data)
> Cohen's d |        95% CI
> -------------------------
> -0.31     | [-1.54, 0.95]
> 
> - Estimated using pooled SD.

But we can also treat the 2-level group variable as a numeric variable, and compute Pearson’s r:

correlation::correlation(data, include_factors = TRUE)[2, ]
> # Correlation Matrix (pearson-method)
> 
> Parameter1 | Parameter2 |     r |        95% CI |  t(8) |      p
> ----------------------------------------------------------------
> Group.0    |    Outcome | -0.17 | [-0.72, 0.52] | -0.48 | > .999
> 
> p-value adjustment method: Holm (1979)
> Observations: 10

But what if we only have summary statistics? Say, we only have \(d=-0.37\) and we want to know what the r would have been? We can approximate r using the following formula (Borenstein et al. 2009):

\[ r \approx \frac{d}{\sqrt{d^2 + 4}} \] And indeed, if we use d_to_r(), we get a pretty decent approximation:

d_to_r(-0.31)
> [1] -0.153

(Which also works in the other way, with r_to_d(0.17) gives r round(r_to_d(-0.17),3))

As we can see, these are rough approximations, but they can be useful when we don’t have the raw data on hand.

In multiple regression

Although not exactly a classic Cohen’s d, we can also approximate a partial-d value (that is, the standardized difference between two groups / conditions, with variance from other predictors partilled out). For example:

fit <- lm(mpg ~ am + hp, data = mtcars)

parameters::model_parameters(fit)
> Parameter   | Coefficient |       SE |         95% CI | t(29) |      p
> ----------------------------------------------------------------------
> (Intercept) |       26.58 |     1.43 | [23.67, 29.50] | 18.65 | < .001
> am          |        5.28 |     1.08 | [ 3.07,  7.48] |  4.89 | < .001
> hp          |       -0.06 | 7.86e-03 | [-0.07, -0.04] | -7.50 | < .001
> 
> Uncertainty intervals (equal-tailed) and p values (two-tailed) computed using a
>   Wald t-distribution approximation.
# A couple of ways to get partial-d:
5.28 / sigma(fit)
> [1] 1.81
t_to_d(4.89, df_error = 29)[[1]]
> [1] 1.82

We can convert these semi-d values to r values, but in this case these represent the partial correlation:

t_to_r(4.89, df_error = 29)
> r    |       95% CI
> -------------------
> 0.67 | [0.43, 0.80]
correlation::correlation(mtcars[, c("mpg", "am", "hp")], partial = TRUE)[1, ]
> # Correlation Matrix (pearson-method)
> 
> Parameter1 | Parameter2 |    r |       95% CI | t(30) |         p
> -----------------------------------------------------------------
> mpg        |         am | 0.67 | [0.42, 0.83] |  4.97 | < .001***
> 
> p-value adjustment method: Holm (1979)
> Observations: 32
# all close to:
d_to_r(1.81)
> [1] 0.671

From Odds ratios

In binomial regression (more specifically in logistic regression), Odds ratios (OR) are themselves measures of effect size; they indicate the expected change in the odds of a some event.

In some fields, it is common to dichotomize outcomes in order to be able to analyze them with logistic models. For example, if the outcome is the count of white blood cells, it can be more useful (medically) to predict the crossing of the threshold rather than the raw count itself. And so, where some scientists would maybe analyze the above data with a t-test and present Cohen’s d, others might analyze it with a logistic regression model on the dichotomized outcome, and present OR. So the question can be asked: given such a OR, what would Cohen’s d have been?

Fortunately, there is a formula to approximate this (Sánchez-Meca, Marı́n-Martı́nez, and Chacón-Moscoso 2003):

\[ d = log(OR) \times \frac{\sqrt{3}}{\pi} \]

which is implemented in the oddsratio_to_d() function.

Let’s give it a try:

# 1. Set a threshold
thresh <- 0

# 2. dichotomize the outcome
data$Outcome_binom <- data$Outcome < thresh

# 3. Fit a logistic regression:
fit <- glm(Outcome_binom ~ Group,
  data = data,
  family = binomial()
)

parameters::model_parameters(fit)
> Parameter   | Log-Odds |   SE |        95% CI |     z |     p
> -------------------------------------------------------------
> (Intercept) |     0.41 | 0.91 | [-1.39, 2.43] |  0.44 | 0.657
> Group [1]   |    -0.81 | 1.29 | [-3.54, 1.70] | -0.63 | 0.530
> 
> Uncertainty intervals (profile-likelihood) and p values (two-tailed) computed
>   using a Wald z-distribution approximation.
# Convert log(OR) (the coefficient) to d
oddsratio_to_d(-0.81, log = TRUE)
> [1] -0.447

Odds ratios to Risk Ratios

Odds ratio, although popular, are not very intuitive in their interpretations. We don’t often think about the chances of catching a disease in terms of odds, instead we instead tend to think in terms of probability or some event - or the risk. Talking about risks we can also talk about the change in risk, knows as the risk ratio (RR).

For example, if we find that for individual suffering from a migraine, for every bowl of brussels sprouts they eat, they’re odds of reducing the migraine increase by an \(OR = 3.5\) over a period of an hour. So, should people eat brussels sprouts to effectively reduce pain? Well, hard to say… Maybe if we look at RR we’ll get a clue.

We can convert between OR and RR for the following formula (Grant 2014):

\[ RR = \frac{OR}{(1 - p0 + (p0 \times OR))} \]

Where \(p0\) is the base-rate risk - the probability of the event without the intervention (e.g., what is the probability of the migraine subsiding within an hour without eating any brussels sprouts). If it the base-rate risk is, say, 85%, we get a RR of:

OR <- 3.5
baserate <- 0.85

oddsratio_to_riskratio(OR, baserate)
> [1] 1.12

That is - for every bowl of brussels sprouts, we increase the chances of reducing the migraine by a mere 12%! Is if worth it? Depends on you affinity to brussels sprouts…

Note that the base-rate risk is crucial here. If instead of 85% it was only 4%, then the RR would be:

OR <- 3.5
baserate <- 0.04

oddsratio_to_riskratio(OR, baserate)
> [1] 3.18

That is - for every bowl of brussels sprouts, we increase the chances of reducing the migraine by a whopping 318%! Is if worth it? I guess that still depends on your affinity to brussels sprouts…

References

Borenstein, Michael, Larry V Hedges, JPT Higgins, and Hannah R Rothstein. 2009. “Converting Among Effect Sizes.” Introduction to Meta-Analysis, 45–49.
Grant, Robert L. 2014. “Converting an Odds Ratio to a Range of Plausible Relative Risks for Better Communication of Research Findings.” Bmj 348: f7450.
Sánchez-Meca, Julio, Fulgencio Marı́n-Martı́nez, and Salvador Chacón-Moscoso. 2003. “Effect-Size Indices for Dichotomized Outcomes in Meta-Analysis.” Psychological Methods 8 (4): 448.
effectsize/inst/doc/effectsize.html0000644000175000017500000014477214174212063017311 0ustar nileshnilesh Effect Sizes: Getting Started

Effect Sizes: Getting Started

Aims of the Package

In both theoretical and applied research, it is often of interest to assess the strength of an observed association. This is typically done to allow the judgment of the magnitude of an effect [especially when units of measurement are not meaningful, e.g., in the use of estimated latent variables; Bollen (1989)], to facilitate comparing between predictors’ importance within a given model, or both. Though some indices of effect size, such as the correlation coefficient (itself a standardized covariance coefficient) are readily available, other measures are often harder to obtain. effectsize is an R package (R Core Team 2020) that fills this important gap, providing utilities for easily estimating a wide variety of standardized effect sizes (i.e., effect sizes that are not tied to the units of measurement of the variables of interest) and their confidence intervals (CIs), from a variety of statistical models. effectsize provides easy-to-use functions, with full documentation and explanation of the various effect sizes offered, and is also used by developers of other R packages as the back-end for effect size computation, such as parameters (Lüdecke et al. 2020), ggstatsplot (Patil 2018), gtsummary (Sjoberg et al. 2020) and more.

Comparison to Other Packages

effectsize’s functionality is in part comparable to packages like lm.beta (Behrendt 2014), MOTE (Buchanan et al. 2019), and MBESS (K. Kelley 2020). Yet, there are some notable differences, e.g.:

  • lm.beta provides standardized regression coefficients for linear models, based on post-hoc model matrix standardization. However, the functionality is available only for a limited number of models (models inheriting from the lm class), whereas effectsize provides support for many types of models, including (generalized) linear mixed models, Bayesian models, and more. Additionally, in additional to post-hoc model matrix standardization, effectsize offers other methods of standardization (see below).
  • Both MOTE and MBESS provide functions for computing effect sizes such as Cohen’s d and effect sizes for ANOVAs (Cohen 1988), and their confidence intervals. However, both require manual input of F- or t-statistics, degrees of freedom, and sums of squares for the computation the effect sizes, whereas effectsize can automatically extract this information from the provided models, thus allowing for better ease-of-use as well as reducing any potential for error.

Examples of Features

effectsize provides various functions for extracting and estimating effect sizes and their confidence intervals [estimated using the noncentrality parameter method; Steiger (2004)]. In this article, we provide basic usage examples for estimating some of the most common effect size. A comprehensive overview, including in-depth examples and a full list of features and functions, are accessible via a dedicated website (https://easystats.github.io/effectsize/).

Indices of Effect Size

Standardized Differences

effectsize provides functions for estimating the common indices of standardized differences such as Cohen’s d (cohens_d()), Hedges’ g (hedges_g()) for both paired and independent samples (Cohen 1988; Hedges and Olkin 1985), and Glass’ \(\Delta\) (glass_delta()) for independent samples with different variances (Hedges and Olkin 1985).

library(effectsize)

cohens_d(mpg ~ am, data = mtcars)
#> Cohen's d |         95% CI
#> --------------------------
#> -1.48     | [-2.27, -0.67]
#> 
#> - Estimated using pooled SD.

Contingency Tables

Pearson’s \(\phi\) (phi()) and Cramér’s V (cramers_v()) can be used to estimate the strength of association between two categorical variables (Cramér 1946), while Cohen’s g (cohens_g()) estimates the deviance between paired categorical variables (Cohen 1988).

M <- rbind(c(150, 130, 35, 55),
           c(100, 50,  10, 40),
           c(165, 65,  2,  25))

cramers_v(M)
#> Cramer's V |       95% CI
#> -------------------------
#> 0.18       | [0.13, 1.00]
#> 
#> - One-sided CIs: upper bound fixed at (1).

Parameter and Model Standardization

Standardizing parameters (i.e., coefficients) can allow for their comparison within and between models, variables and studies. To this end, two functions are available: standardize(), which returns an updated model, re-fit with standardized data, and standardize_parameters(), which returns a table of standardized coefficients from a provided model [for a list of supported models, see the insight package; Lüdecke, Waggoner, and Makowski (2019)].

model <- lm(mpg ~ cyl * am, 
            data = mtcars)

standardize(model)
#> 
#> Call:
#> lm(formula = mpg ~ cyl * am, data = data_std)
#> 
#> Coefficients:
#> (Intercept)          cyl           am       cyl:am  
#>     -0.0977      -0.7426       0.1739      -0.1930

standardize_parameters(model)
#> # Standardization method: refit
#> 
#> Parameter   | Coefficient (std.) |         95% CI
#> -------------------------------------------------
#> (Intercept) |              -0.10 | [-0.30,  0.11]
#> cyl         |              -0.74 | [-0.95, -0.53]
#> am          |               0.17 | [-0.04,  0.39]
#> cyl:am      |              -0.19 | [-0.41,  0.02]

Standardized parameters can also be produced for generalized linear models (GLMs; where only the predictors are standardized):

model <- glm(am ~ cyl + hp,
             family = "binomial",
             data = mtcars)

standardize_parameters(model, exponentiate = TRUE)
#> # Standardization method: refit
#> 
#> Parameter   | Odds Ratio (std.) |        95% CI
#> -----------------------------------------------
#> (Intercept) |              0.53 | [0.18,  1.32]
#> cyl         |              0.05 | [0.00,  0.29]
#> hp          |              6.70 | [1.32, 61.54]
#> 
#> (Response is unstandardized)

standardize_parameters() provides several standardization methods, such as robust standardization, or pseudo-standardized coefficients for (generalized) linear mixed models (Hoffman 2015). A full review of these methods can be found in the Parameter and Model Standardization vignette.

Effect Sizes for ANOVAs

Unlike standardized parameters, the effect sizes reported in the context of ANOVAs (analysis of variance) or ANOVA-like tables represent the amount of variance explained by each of the model’s terms, where each term can be represented by one or more parameters. eta_squared() can produce such popular effect sizes as Eta-squared (\(\eta^2\)), its partial version (\(\eta^2_p\)), as well as the generalized \(\eta^2_G\) (Cohen 1988; Olejnik and Algina 2003):

options(contrasts = c('contr.sum', 'contr.poly'))

data("ChickWeight")
# keep only complete cases and convert `Time` to a factor
ChickWeight <- subset(ChickWeight, ave(weight, Chick, FUN = length) == 12)
ChickWeight$Time <- factor(ChickWeight$Time)

model <- aov(weight ~ Diet * Time + Error(Chick / Time),
             data = ChickWeight) 

eta_squared(model, partial = TRUE)
#> # Effect Size for ANOVA (Type I)
#> 
#> Group      | Parameter | Eta2 (partial) |       95% CI
#> ------------------------------------------------------
#> Chick      |      Diet |           0.27 | [0.06, 1.00]
#> Chick:Time |      Time |           0.87 | [0.85, 1.00]
#> Chick:Time | Diet:Time |           0.22 | [0.11, 1.00]
#> 
#> - One-sided CIs: upper bound fixed at (1).

eta_squared(model, generalized = "Time")
#> # Effect Size for ANOVA (Type I)
#> 
#> Group      | Parameter | Eta2 (generalized) |       95% CI
#> ----------------------------------------------------------
#> Chick      |      Diet |               0.04 | [0.00, 1.00]
#> Chick:Time |      Time |               0.74 | [0.71, 1.00]
#> Chick:Time | Diet:Time |               0.03 | [0.00, 1.00]
#> 
#> - Observed variables: Time
#> - One-sided CIs: upper bound fixed at (1).

effectsize also offers \(\epsilon^2_p\) (epsilon_squared()) and \(\omega^2_p\) (omega_squared()), which are less biased estimates of the variance explained in the population (T. L. Kelley 1935; Olejnik and Algina 2003). For more details about the various effect size measures and their applications, see the Effect sizes for ANOVAs vignette.

Effect Size Conversion

From Test Statistics

In many real world applications there are no straightforward ways of obtaining standardized effect sizes. However, it is possible to get approximations of most of the effect size indices (d, r, \(\eta^2_p\)…) with the use of test statistics (Friedman 1982). These conversions are based on the idea that test statistics are a function of effect size and sample size (or more often of degrees of freedom). Thus it is possible to reverse-engineer indices of effect size from test statistics (F, t, \(\chi^2\), and z).

F_to_eta2(f = c(40.72, 33.77),
          df = c(2, 1), df_error = c(18, 9))
#> Eta2 (partial) |       95% CI
#> -----------------------------
#> 0.82           | [0.66, 1.00]
#> 0.79           | [0.49, 1.00]
#> 
#> - One-sided CIs: upper bound fixed at (1).

t_to_d(t = -5.14, df_error = 22)
#> d     |         95% CI
#> ----------------------
#> -2.19 | [-3.23, -1.12]

t_to_r(t = -5.14, df_error = 22)
#> r     |         95% CI
#> ----------------------
#> -0.74 | [-0.85, -0.49]

These functions also power the effectsize() convenience function for estimating effect sizes from R’s htest-type objects. For example:

data(hardlyworking, package = "effectsize")

aov1 <- oneway.test(salary ~ n_comps, 
                    data = hardlyworking, var.equal = TRUE)
effectsize(aov1)
#> Eta2 |       95% CI
#> -------------------
#> 0.20 | [0.14, 1.00]
#> 
#> - One-sided CIs: upper bound fixed at (1).

xtab <- rbind(c(762, 327, 468), c(484, 239, 477), c(484, 239, 477))
Xsq <- chisq.test(xtab)
effectsize(Xsq)
#> Cramer's V |       95% CI
#> -------------------------
#> 0.07       | [0.05, 1.00]
#> 
#> - One-sided CIs: upper bound fixed at (1).

These functions also power our Effect Sizes From Test Statistics shiny app (https://easystats4u.shinyapps.io/statistic2effectsize/).

Between Effect Sizes

For comparisons between different types of designs and analyses, it is useful to be able to convert between different types of effect sizes [d, r, Odds ratios and Risk ratios; Borenstein et al. (2009); Grant (2014)].

r_to_d(0.7)
#> [1] 1.96

d_to_oddsratio(1.96)
#> [1] 35

oddsratio_to_riskratio(34.99, p0 = 0.4)
#> [1] 2.4

oddsratio_to_r(34.99)
#> [1] 0.7

Effect Size Interpretation

Finally, effectsize provides convenience functions to apply existing or custom interpretation rules of thumb, such as for instance Cohen’s (1988). Although we strongly advocate for the cautious and parsimonious use of such judgment-replacing tools, we provide these functions to allow users and developers to explore and hopefully gain a deeper understanding of the relationship between data values and their interpretation. More information is available in the Automated Interpretation of Indices of Effect Size vignette.

interpret_cohens_d(c(0.02, 0.52, 0.86), rules = "cohen1988")
#> [1] "very small" "medium"     "large"     
#> (Rules: cohen1988)

Licensing and Availability

effectsize is licensed under the GNU General Public License (v3.0), with all source code stored at GitHub (https://github.com/easystats/effectsize), and with a corresponding issue tracker for bug reporting and feature enhancements. In the spirit of honest and open science, we encourage requests/tips for fixes, feature updates, as well as general questions and concerns via direct interaction with contributors and developers, by filing an issue. See the package’s Contribution Guidelines.

Acknowledgments

effectsize is part of the easystats ecosystem, a collaborative project created to facilitate the usage of R for statistical analyses. Thus, we would like to thank the members of easystats as well as the users.

References

Behrendt, Stefan. 2014. lm.beta: Add Standardized Regression Coefficients to Lm-Objects. https://CRAN.R-project.org/package=lm.beta/.
Bollen, Kenneth A. 1989. Structural Equations with Latent Variables. New York: John Wiley & Sons. https://doi.org/10.1002/9781118619179.
Borenstein, Michael, Larry V Hedges, JPT Higgins, and Hannah R Rothstein. 2009. “Converting Among Effect Sizes.” Introduction to Meta-Analysis, 45–49.
Buchanan, Erin M., Amber Gillenwaters, John E. Scofield, and K. D. Valentine. 2019. MOTE: Measure of the Effect: Package to Assist in Effect Size Calculations and Their Confidence Intervals. https://github.com/doomlab/MOTE/.
Cohen, J. 1988. Statistical Power Analysis for the Behavioral Sciences, 2nd Ed. New York: Routledge.
Cramér, Harald. 1946. Mathematical Methods of Statistics. Princeton: Princeton University Press.
Friedman, Herbert. 1982. “Simplified Determinations of Statistical Power, Magnitude of Effect and Research Sample Sizes.” Educational and Psychological Measurement 42 (2): 521–26.
Grant, Robert L. 2014. “Converting an Odds Ratio to a Range of Plausible Relative Risks for Better Communication of Research Findings.” Bmj 348: f7450.
Hedges, L, and I Olkin. 1985. Statistical Methods for Meta-Analysis. San Diego: Academic Press.
Hoffman, Lesa. 2015. Longitudinal Analysis: Modeling Within-Person Fluctuation and Change. Routledge.
Kelley, Ken. 2020. MBESS: The MBESS R Package. https://CRAN.R-project.org/package=MBESS/.
Kelley, Truman L. 1935. “An Unbiased Correlation Ratio Measure.” Proceedings of the National Academy of Sciences of the United States of America 21 (9): 554–59. https://doi.org/10.1073/pnas.21.9.554.
Lüdecke, Daniel, Mattan S Ben-Shachar, Indrajeet Patil, and Dominique Makowski. 2020. “Extracting, Computing and Exploring the Parameters of Statistical Models Using R.” Journal of Open Source Software 5 (53): 2445. https://doi.org/10.21105/joss.02445.
Lüdecke, Daniel, Philip Waggoner, and Dominique Makowski. 2019. insight: A Unified Interface to Access Information from Model Objects in R.” Journal of Open Source Software 4 (38): 1412. https://doi.org/10.21105/joss.01412.
Olejnik, Stephen, and James Algina. 2003. “Generalized Eta and Omega Squared Statistics: Measures of Effect Size for Some Common Research Designs.” Psychological Methods 8 (4): 434.
Patil, Indrajeet. 2018. ggstatsplot: ’Ggplot2’ Based Plots with Statistical Details.” CRAN. https://doi.org/10.5281/zenodo.2074621.
R Core Team. 2020. R: A Language and Environment for Statistical Computing. Vienna, Austria: R Foundation for Statistical Computing. https://www.R-project.org/.
Sjoberg, Daniel D., Michael Curry, Margie Hannum, Karissa Whiting, and Emily C. Zabor. 2020. gtsummary: Presentation-Ready Data Summary and Analytic Result Tables. https://CRAN.R-project.org/package=gtsummary/.
Steiger, James H. 2004. “Beyond the F Test: Effect Size Confidence Intervals and Tests of Close Fit in the Analysis of Variance and Contrast Analysis.” Psychological Methods 9 (2): 164–82. https://doi.org/10.1037/1082-989X.9.2.164.
effectsize/inst/doc/from_test_statistics.R0000644000175000017500000000635514174212065020667 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) library(effectsize) knitr::opts_chunk$set(comment = ">") options(digits = 2) options(knitr.kable.NA = "") pkgs <- c("effectsize", "afex", "lmerTest", "emmeans", "parameters") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } else { library(afex) library(lmerTest) library(emmeans) library(parameters) } set.seed(747) ## ----------------------------------------------------------------------------- library(afex) data(md_12.1) aov_fit <- aov_car(rt ~ angle * noise + Error(id / (angle * noise)), data = md_12.1, anova_table = list(correction = "none", es = "pes") ) aov_fit ## ----------------------------------------------------------------------------- library(effectsize) F_to_eta2( f = c(40.72, 33.77, 45.31), df = c(2, 1, 2), df_error = c(18, 9, 18) ) ## ----------------------------------------------------------------------------- library(emmeans) joint_tests(aov_fit, by = "noise") F_to_eta2( f = c(5, 79), df = 2, df_error = 29 ) ## ----------------------------------------------------------------------------- pairs(emmeans(aov_fit, ~angle)) t_to_eta2( t = c(-5.7, -8.9, -3.2), df_error = 18 ) ## ----------------------------------------------------------------------------- library(lmerTest) fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) anova(fit_lmm) F_to_eta2(45.8, 1, 17) ## ----------------------------------------------------------------------------- parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite") t_to_eta2(6.77, df_error = 17) ## ----------------------------------------------------------------------------- F_to_eta2(45.8, 1, 17) F_to_epsilon2(45.8, 1, 17) F_to_omega2(45.8, 1, 17) ## ----------------------------------------------------------------------------- parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite") t_to_r(6.77, df_error = 17) ## ----------------------------------------------------------------------------- fit_lm <- lm(rating ~ complaints + critical, data = attitude) parameters::model_parameters(fit_lm) t_to_r( t = c(7.46, 0.01), df_error = 27 ) ## ---- eval=require(correlation, quietly = TRUE)------------------------------- correlation::correlation(attitude[, c(1, 2, 6)], partial = TRUE)[1:2, c(2, 3, 7, 8)] ## ----------------------------------------------------------------------------- pairs(emmeans(aov_fit, ~angle)) t_to_r( t = c(-5.7, -8.9, -3.2), df_error = 18 ) ## ----------------------------------------------------------------------------- m <- lm(breaks ~ tension, data = warpbreaks) em_tension <- emmeans(m, ~tension) pairs(em_tension) t_to_d( t = c(2.53, 3.72, 1.20), df_error = 51 ) ## ----------------------------------------------------------------------------- eff_size(em_tension, sigma = sigma(m), edf = df.residual(m)) ## ----------------------------------------------------------------------------- pairs(emmeans(aov_fit, ~angle)) t_to_d( t = c(-5.7, -5.9, -3.2), df_error = 18, paired = TRUE ) effectsize/inst/doc/interpret.R0000644000175000017500000000047714174212065016426 0ustar nileshnilesh## ----message=FALSE, warning=FALSE, include=FALSE------------------------------ library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 2) pkgs <- c("effectsize") if (!all(sapply(pkgs, requireNamespace, quietly = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } effectsize/inst/doc/bayesian_models.html0000644000175000017500000056616414174212060020320 0ustar nileshnilesh Effect Sizes for Bayesian Models

Effect Sizes for Bayesian Models

Standardized Parameters

Introduction

Like in OLS / ML or other frequentists methods of model parameter estimation, standardizing the parameters of Bayesian (generalized) linear regression models can allow for the comparison of so-called “effects” within and between models, variables and studies.

As with frequentists methods, standardizing parameters should not be the only method of examining the role different predictors play in a particular Bayesian model, and this vignette generally assumes that the issues of model convergence, goodness of fit and model selection have already been taken care of. (Learn more about how to become a Bayesian master with the bayestestR package.)

Setup

We will examine the predictive role of overtime (xtra_hours), number of compliments given to the boss (n_comps) and seniority in predicting workers salaries. Let’s fit the model:

library(rstanarm)

data("hardlyworking", package = "effectsize")

head(hardlyworking)
>   salary xtra_hours n_comps age seniority
> 1  19745        4.2       1  32         3
> 2  11302        1.6       0  34         3
> 3  20636        1.2       3  33         5
> 4  23047        7.2       1  35         3
> 5  27342       11.3       0  33         4
> 6  25657        3.6       2  30         5
mod <- stan_glm(salary ~ xtra_hours + n_comps + seniority,
  data = hardlyworking,
  prior = normal(0, scale = c(1, 0.5, 0.5), autoscale = TRUE), # set some priors
  refresh = 0
)

parameters::model_parameters(mod, test = NULL)
> Parameter   |   Median |              95% CI |  Rhat |     ESS |                      Prior
> -------------------------------------------------------------------------------------------
> (Intercept) | 10023.20 | [9212.29, 10889.54] | 0.999 | 5405.00 | Normal (20000 +- 15495.02)
> xtra_hours  |  1236.25 | [1177.38,  1294.31] | 1.000 | 4756.00 |      Normal (0 +- 1587.80)
> n_comps     |  2964.35 | [2701.67,  3207.82] | 1.000 | 5032.00 |      Normal (0 +- 3755.71)
> seniority   |   439.46 | [ 248.63,   636.66] | 0.999 | 4283.00 |      Normal (0 +- 2702.93)
> 
> Uncertainty intervals (highest-density) and p values (two-tailed) computed
>   using a MCMC distribution approximation.

Looking at the un-standardized (“raw”) parameters, it looks like all predictors positively predict workers’ salaries, but which has the highest predictive power? Unfortunately, the predictors are not on the same scale (hours, compliments, years), so comparing them is hard when looking at the raw data. This is where standardization comes in.

Like with frequentists models we can choose from the same standardization methods. Let’s use the (slow) "refit" method.

library(effectsize)

standardize_parameters(mod, method = "refit", ci = 0.89)
> Warning: Standardizing variables without adjusting priors may lead to bogus
> results unless priors are auto-scaled.
> # Standardization method: refit
> 
> Parameter   | Median (std.) |        89% CI
> -------------------------------------------
> (Intercept) |     -2.63e-04 | [-0.03, 0.03]
> xtra_hours  |          0.78 | [ 0.75, 0.81]
> n_comps     |          0.39 | [ 0.37, 0.42]
> seniority   |          0.08 | [ 0.05, 0.11]

Note that the central tendency of the posterior distribution is still the median - the median of the standardized posterior distribution. We can easily change this, of the type of credible interval used:

library(effectsize)

standardize_parameters(mod,
  method = "basic", ci = 0.89,
  centrality = "MAP", ci_method = "eti"
)
> # Standardization method: basic
> 
> Parameter   | MAP (std.) |       89% CI
> ---------------------------------------
> (Intercept) |       0.00 | [0.00, 0.00]
> xtra_hours  |       0.78 | [0.75, 0.81]
> n_comps     |       0.39 | [0.37, 0.42]
> seniority   |       0.08 | [0.05, 0.11]

As we can see, working harder (or at least for longer hours) has stronger predictive power than complementing or seniority. (Do note, however, that this does not mean that if you wish to have a higher salary you should work overtime - the raw parameters seem to suggest that complementing your boss is the way to go, with one compliment worth almost 3.5 times more than a full hours’ work!)

Eta2

Introduction

In classical frequentists models, the computation of \(\eta^2\) or \(\eta^2_p\) is straightforward: based on the right combinations of sums-of-squares (SSs), we get the correct proportion of variance accounted for by some predictor term. However such a computation is not as straightforward for Bayesian models, for various reasons (e.g., the model-SS and the residual-SS don’t necessarily sum to the total-SS). Although some have proposed Bayesian methods of estimating explained variance in ANOVA designs (Marsman et al. 2019), these are not yet easy to implement with stan-based models.

An alternative route to obtaining effect sizes of explained variance, is via the use of the posterior predictive distribution (PPD). The PPD is the Bayesian expected distribution of possible unobserved values. Thus, after observing some data, we can estimate not just the expected mean values (the conditional marginal means), but also the full distribution of data around these values (Gelman et al. 2014, chap. 7).

By sampling from the PPD, we can decompose the sample to the various SSs needed for the computation of explained variance measures. By repeatedly sampling from the PPD, we can generate a posterior distribution of explained variance estimates. But note that these estimates are conditioned not only on the location-parameters of the model, but also on the scale-parameters of the model! So it is vital to validate the PPD before using it to estimate explained variance measures.

Setup

Let’s factorize out data from above:

hardlyworking$age_f <- cut(hardlyworking$age,
  breaks = c(25, 35, 45), right = FALSE,
  labels = c("Young", "Less_young")
)
hardlyworking$comps_f <- cut(hardlyworking$n_comps,
  breaks = c(0, 1, 2, 3),
  include.lowest = TRUE,
  right = FALSE
)

table(hardlyworking$age_f, hardlyworking$comps_f)
>             
>              [0,1) [1,2) [2,3]
>   Young        124   188   114
>   Less_young    13    34    27

And fit our model:

# use (special) effects coding
contrasts(hardlyworking$age_f) <- bayestestR::contr.bayes
> Warning: 'contr.bayes' is deprecated.
> Use 'contr.orthonorm' instead.
> See help("Deprecated")
contrasts(hardlyworking$comps_f) <- bayestestR::contr.bayes
> Warning: 'contr.bayes' is deprecated.
> Use 'contr.orthonorm' instead.
> See help("Deprecated")
modAOV <- stan_glm(salary ~ age_f * comps_f,
  data = hardlyworking, family = gaussian(),
  refresh = 0
)

We can use eta_squared_posterior() to get the posterior distribution of \(eta^2\) or \(eta^2_p\) for each effect. Like an ANOVA table, we must make sure to use the right effects-coding and SS-type:

pes_posterior <- eta_squared_posterior(modAOV,
  draws = 500, # how many samples from the PPD?
  partial = TRUE, # partial eta squared
  # type 3 SS
  ss_function = car::Anova, type = 3
)
> Simulating effect size... This can take a while...
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
head(pes_posterior)
>   age_f comps_f age_f:comps_f
> 1 0.028   0.161        0.0024
> 2 0.030   0.117        0.0252
> 3 0.014   0.091        0.0078
> 4 0.035   0.112        0.0118
> 5 0.017   0.100        0.0054
> 6 0.126   0.058        0.0013
bayestestR::describe_posterior(pes_posterior, rope_range = c(0, 0.1), test = "rope")
> Summary of Posterior Distribution
> 
> Parameter     | Median |       95% CI |         ROPE | % in ROPE
> ----------------------------------------------------------------
> age_f         |   0.03 | [0.00, 0.07] | [0.00, 0.10] |      100%
> comps_f       |   0.14 | [0.06, 0.22] | [0.00, 0.10] |    14.08%
> age_f:comps_f |   0.01 | [0.00, 0.04] | [0.00, 0.10] |      100%

Compare to:

modAOV_f <- lm(salary ~ age_f * comps_f,
  data = hardlyworking
)

eta_squared(car::Anova(modAOV_f, type = 3))
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> # Effect Size for ANOVA (Type III)
> 
> Parameter     | Eta2 (partial) |       95% CI
> ---------------------------------------------
> age_f         |           0.03 | [0.01, 1.00]
> comps_f       |           0.13 | [0.09, 1.00]
> age_f:comps_f |       6.65e-03 | [0.00, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

References

Gelman, Andrew, John B Carlin, Hal S Stern, and Donald B Rubin. 2014. “Bayesian Data Analysis (Vol. 2).” Boca Raton, FL: Chapman.
Marsman, Maarten, Lourens Waldorp, Fabian Dablander, and Eric-Jan Wagenmakers. 2019. “Bayesian Estimation of Explained Variance in ANOVA Designs.” Statistica Neerlandica 73 (3): 351–72.
effectsize/inst/doc/anovaES.html0000644000175000017500000012004214174212042016473 0ustar nileshnilesh Effect sizes for ANOVAs

Effect sizes for ANOVAs

Eta2

In the context of ANOVA-like tests, it is common to report ANOVA-like effect sizes. Unlike standardized parameters, these effect sizes represent the amount of variance explained by each of the model’s terms, where each term can be represented by 1 or more parameters.

For example, in the following case, the parameters for the treatment term represent specific contrasts between the factor’s levels (treatment groups) - the difference between each level and the reference level (obk.long == 'control').

data(obk.long, package = "afex")
# modify the data slightly for the demonstration:
obk.long <- obk.long[1:240 %% 3 == 0, ]
obk.long$id <- seq_len(nrow(obk.long))

m <- lm(value ~ treatment, data = obk.long)

parameters::model_parameters(m)
> Parameter     | Coefficient |   SE |       95% CI | t(77) |      p
> ------------------------------------------------------------------
> (Intercept)   |        4.28 | 0.36 | [3.56, 5.00] | 11.85 | < .001
> treatment [A] |        1.97 | 0.54 | [0.89, 3.05] |  3.64 | < .001
> treatment [B] |        2.09 | 0.47 | [1.15, 3.03] |  4.42 | < .001
> 
> Uncertainty intervals (equal-tailed) and p values (two-tailed) computed using a
>   Wald t-distribution approximation.

But we can also ask about the overall effect of treatment - how much of the variation in our dependent variable value can be predicted by (or explained by) the variation between the treatment groups. Such a question can be answered with an ANOVA test:

parameters::model_parameters(anova(m))
> Parameter | Sum_Squares | df | Mean_Square |     F |      p
> -----------------------------------------------------------
> treatment |       72.23 |  2 |       36.11 | 11.08 | < .001
> Residuals |      250.96 | 77 |        3.26 |       |       
> 
> Anova Table (Type 1 tests)

As we can see, the variance in value (the sums-of-squares, or SS) has been split into pieces:

  • The part associated with treatment.
  • The unexplained part (The Residual-SS).

We can now ask what is the percent of the total variance in value that is associated with treatment. This measure is called Eta-squared (written as \(\eta^2\)):

\[ \eta^2 = \frac{SS_{effect}}{SS_{total}} = \frac{72.23}{72.23 + 250.96} = 0.22 \]

and can be accessed via the eta_squared() function:

library(effectsize)

eta_squared(m, partial = FALSE)
> # Effect Size for ANOVA (Type I)
> 
> Parameter | Eta2 |       95% CI
> -------------------------------
> treatment | 0.22 | [0.09, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Adding More Terms

When we add more terms to our model, we can ask two different questions about the percent of variance explained by a predictor - how much variance is accounted by the predictor in total, and how much is accounted when controlling for any other predictors. The latter questions is answered by the partial-Eta squared (\(\eta^2_p\)), which is the percent of the partial variance (after accounting for other predictors in the model) associated with a term:

\[ \eta^2_p = \frac{SS_{effect}}{SS_{effect} + SS_{error}} \] which can also be accessed via the eta_squared() function:

m <- lm(value ~ gender + phase + treatment, data = obk.long)

eta_squared(m, partial = FALSE)
> # Effect Size for ANOVA (Type I)
> 
> Parameter |     Eta2 |       95% CI
> -----------------------------------
> gender    |     0.03 | [0.00, 1.00]
> phase     | 9.48e-03 | [0.00, 1.00]
> treatment |     0.25 | [0.11, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
eta_squared(m) # partial = TRUE by default
> # Effect Size for ANOVA (Type I)
> 
> Parameter | Eta2 (partial) |       95% CI
> -----------------------------------------
> gender    |           0.04 | [0.00, 1.00]
> phase     |           0.01 | [0.00, 1.00]
> treatment |           0.26 | [0.12, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

(phase is a repeated-measures variable, but for simplicity it is not modeled as such.)

In the calculation above, the SSs were computed sequentially - that is the SS for phase is computed after controlling for gender, and the SS for treatment is computed after controlling for both gender and phase. This method of sequential SS is called also type-I test. If this is what you want, that’s great - however in many fields (and other statistical programs) it is common to use “simultaneous” sums of squares (type-II or type-III tests), where each SS is computed controlling for all other predictors, regardless of order. This can be done with car::Anova(type = ...):

eta_squared(car::Anova(m, type = 2), partial = FALSE)
> # Effect Size for ANOVA (Type II)
> 
> Parameter |     Eta2 |       95% CI
> -----------------------------------
> gender    |     0.05 | [0.00, 1.00]
> phase     | 9.22e-03 | [0.00, 1.00]
> treatment |     0.24 | [0.11, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
eta_squared(car::Anova(m, type = 3)) # partial = TRUE by default
> # Effect Size for ANOVA (Type III)
> 
> Parameter | Eta2 (partial) |       95% CI
> -----------------------------------------
> gender    |           0.07 | [0.01, 1.00]
> phase     |           0.01 | [0.00, 1.00]
> treatment |           0.26 | [0.12, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

\(\eta^2_p\) will always be larger than \(\eta^2\). The idea is to simulate the effect size in a design where only the term of interest was manipulated. This terminology assumes some causal relationship between the predictor and the outcome, which reflects the experimental world from which these analyses and measures hail; However, \(\eta^2_p\) can also simply be seen as a signal-to-noise- ratio, as it only uses the term’s SS and the error-term’s SS.[^in repeated-measure designs the term-specific residual-SS is used for the computation of the effect size].

(Note that in a one-way fixed-effect designs \(\eta^2 = \eta^2_p\).)

Adding Interactions

Type II and type III treat interaction differently. Without going into the weeds here, keep in mind that when using type III SS, it is important to center all of the predictors; for numeric variables this can be done by mean-centering the predictors; for factors this can be done by using orthogonal coding (such as contr.sum for effects-coding) for the dummy variables (and NOT treatment coding, which is the default in R). This unfortunately makes parameter interpretation harder, but only when this is does do the SSs associated with each lower-order term (or lower-order interaction) represent the SS of the main effect (with treatment coding they represent the SS of the simple effects).

# compare
m_interaction1 <- lm(value ~ treatment * gender, data = obk.long)

# to:
m_interaction2 <- lm(
  value ~ treatment * gender,
  data = obk.long,
  contrasts = list(
    treatment = "contr.sum",
    gender = "contr.sum"
  )
)

eta_squared(car::Anova(m_interaction1, type = 3))
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Eta2 (partial) |       95% CI
> ------------------------------------------------
> treatment        |           0.12 | [0.02, 1.00]
> gender           |       9.11e-03 | [0.00, 1.00]
> treatment:gender |           0.20 | [0.07, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
eta_squared(car::Anova(m_interaction2, type = 3))
> Type 3 ANOVAs only give sensible and informative results when covariates are
>   mean-centered and factors are coded with orthogonal contrasts (such as those
>   produced by 'contr.sum', 'contr.poly', or 'contr.helmert', but *not* by the
>   default 'contr.treatment').
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Eta2 (partial) |       95% CI
> ------------------------------------------------
> treatment        |           0.27 | [0.13, 1.00]
> gender           |           0.12 | [0.03, 1.00]
> treatment:gender |           0.20 | [0.07, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

If all of this type-III-effects-coding seems like a hassle, you can use the afex package, which takes care of all of this behind the scenes:

library(afex)
m_afex <- aov_car(value ~ treatment * gender + Error(id), data = obk.long)
> Contrasts set to contr.sum for the following variables: treatment, gender
eta_squared(m_afex)
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Eta2 (partial) |       95% CI
> ------------------------------------------------
> treatment        |           0.27 | [0.13, 1.00]
> gender           |           0.12 | [0.03, 1.00]
> treatment:gender |           0.20 | [0.07, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Other Measures of Effect Size

Unbiased Effect Sizes

These effect sizes are unbiased estimators of the population’s \(\eta^2\):

  • Omega Squared (\(\omega^2\))
  • Epsilon Squared (\(\epsilon^2\)), also referred to as Adjusted Eta Squared.
omega_squared(m_afex)
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Omega2 (partial) |       95% CI
> --------------------------------------------------
> treatment        |             0.24 | [0.10, 1.00]
> gender           |             0.10 | [0.02, 1.00]
> treatment:gender |             0.17 | [0.05, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
epsilon_squared(m_afex)
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Epsilon2 (partial) |       95% CI
> ----------------------------------------------------
> treatment        |               0.25 | [0.11, 1.00]
> gender           |               0.11 | [0.02, 1.00]
> treatment:gender |               0.18 | [0.06, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Both \(\omega^2\) and \(\epsilon^2\) (and their partial counterparts, \(\omega^2_p\) & \(\epsilon^2_p\)) are unbiased estimators of the population’s \(\eta^2\) (or \(\eta^2_p\), respectively), which is especially important is small samples. Though \(\omega^2\) is the more popular choice (Albers and Lakens 2018), \(\epsilon^2\) is analogous to adjusted-\(R^2\) (Allen 2017, 382), and has been found to be less biased (Carroll and Nordholm 1975).

Generalized Eta2

Partial Eta squared aims at estimating the effect size in a design where only the term of interest was manipulated, assuming all other terms are have also manipulated. However, not all predictors are always manipulated - some can only be observed. For such cases, we can use generalized Eta squared (\(\eta^2_G\)), which like \(\eta^2_p\) estimating the effect size in a design where only the term of interest was manipulated, accounting for the fact that some terms cannot be manipulated (and so their variance would be present in such a design).

eta_squared(m_afex, generalized = "gender")
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Eta2 (generalized) |       95% CI
> ----------------------------------------------------
> treatment        |               0.21 | [0.08, 1.00]
> gender           |               0.10 | [0.02, 1.00]
> treatment:gender |               0.18 | [0.06, 1.00]
> 
> - Observed variables: gender
> - One-sided CIs: upper bound fixed at (1).

\(\eta^2_G\) is useful in repeated-measures designs, as it can estimate what a within-subject effect size would have been had that predictor been manipulated between-subjects (Olejnik and Algina 2003).

Cohen’s f

Finally, we have the forgotten child - Cohen’s \(f\). Cohen’s \(f\) is a transformation of \(\eta^2_p\), and is the ratio between the term-SS and the error-SS.

\[\text{Cohen's} f_p = \sqrt{\frac{\eta^2_p}{1-\eta^2_p}} = \sqrt{\frac{SS_{effect}}{SS_{error}}}\]

It can take on values between zero, when the population means are all equal, and an indefinitely large number as the means are further and further apart. It is analogous to Cohen’s \(d\) when there are only two groups.

cohens_f(m_afex)
> # Effect Size for ANOVA (Type III)
> 
> Parameter        | Cohen's f (partial) |           95% CI
> ---------------------------------------------------------
> treatment        |                0.61 | [0.38,      Inf]
> gender           |                0.37 | [0.17,      Inf]
> treatment:gender |                0.50 | [0.28,      Inf]
> 
> - One-sided CIs: upper bound fixed at (Inf).

When Sum-of-Squares are Hard to Come By

Until now we’ve discusses effect sizes in fixed-effect linear model and repeated-measures ANOVA’s - cases where the SSs are readily available, and so the various effect sized presented can easily be estimated. How ever this is not always the case.

For example, in linear mixed models (LMM/HLM/MLM), the estimation of all required SSs is not straightforward. However, we can still approximate these effect sizes (only their partial versions) based on the test-statistic approximation method (learn more in the Effect Size from Test Statistics vignette).

library(lmerTest)

fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)

anova(fit_lmm) # note the type-3 errors
> Type III Analysis of Variance Table with Satterthwaite's method
>      Sum Sq Mean Sq NumDF DenDF F value  Pr(>F)    
> Days  30031   30031     1    17    45.9 3.3e-06 ***
> ---
> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
F_to_eta2(45.8, df = 1, df_error = 17)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.73           | [0.51, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Or directly with `eta_squared() and co.:

eta_squared(fit_lmm)
> # Effect Size for ANOVA (Type III)
> 
> Parameter | Eta2 (partial) |       95% CI
> -----------------------------------------
> Days      |           0.73 | [0.51, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
epsilon_squared(fit_lmm)
> # Effect Size for ANOVA (Type III)
> 
> Parameter | Epsilon2 (partial) |       95% CI
> ---------------------------------------------
> Days      |               0.71 | [0.48, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
omega_squared(fit_lmm)
> # Effect Size for ANOVA (Type III)
> 
> Parameter | Omega2 (partial) |       95% CI
> -------------------------------------------
> Days      |             0.70 | [0.47, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Another case where SSs are not available is when use Bayesian models. effectsize has Bayesian solutions for Bayesian models, about which you can read in the Effect Sizes for Bayesian Models vignette.

References

Albers, Casper, and Daniël Lakens. 2018. “When Power Analyses Based on Pilot Data Are Biased: Inaccurate Effect Size Estimators and Follow-up Bias.” Journal of Experimental Social Psychology 74: 187–95.
Allen, Rory. 2017. Statistics and Experimental Design for Psychologists: A Model Comparison Approach. World Scientific Publishing Company.
Carroll, Robert M, and Lena A Nordholm. 1975. “Sampling Characteristics of Kelley’s Epsilon and Hays’ Omega.” Educational and Psychological Measurement 35 (3): 541–54.
Olejnik, Stephen, and James Algina. 2003. “Generalized Eta and Omega Squared Statistics: Measures of Effect Size for Some Common Research Designs.” Psychological Methods 8 (4): 434.
effectsize/inst/doc/bayesian_models.Rmd0000644000175000017500000001460314170302654020065 0ustar nileshnilesh--- title: "Effect Sizes for Bayesian Models" output: rmarkdown::html_vignette: toc: true fig_width: 10.08 fig_height: 6 tags: [r, bayesian, effect size] vignette: > \usepackage[utf8]{inputenc} %\VignetteIndexEntry{Effect Sizes for Bayesian Models} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console bibliography: bibliography.bib --- ```{r message=FALSE, warning=FALSE, include=FALSE} library(knitr) options(knitr.kable.NA = "") options(digits = 2) knitr::opts_chunk$set(comment = ">") set.seed(1) pkgs <- c("effectsize", "parameters", "rstanarm", "bayestestR", "car") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } ``` ## Standardized Parameters ### Introduction Like in OLS / ML or other frequentists methods of model parameter estimation, standardizing the parameters of Bayesian (generalized) linear regression models can allow for the comparison of so-called "effects" within and between models, variables and studies. As with frequentists methods, standardizing parameters should not be the only method of examining the role different predictors play in a particular Bayesian model, and this vignette generally assumes that the issues of model convergence, goodness of fit and model selection have already been taken care of. (Learn more about how to become a Bayesian master with [the `bayestestR` package](https://easystats.github.io/bayestestR/).) ### Setup We will examine the predictive role of overtime (`xtra_hours`), number of compliments given to the boss (`n_comps`) and seniority in predicting workers salaries. Let's fit the model: ```{r, warning=FALSE} library(rstanarm) data("hardlyworking", package = "effectsize") head(hardlyworking) mod <- stan_glm(salary ~ xtra_hours + n_comps + seniority, data = hardlyworking, prior = normal(0, scale = c(1, 0.5, 0.5), autoscale = TRUE), # set some priors refresh = 0 ) parameters::model_parameters(mod, test = NULL) ``` Looking at the un-standardized ("raw") parameters, it looks like all predictors positively predict workers' salaries, but which has the highest predictive power? Unfortunately, the predictors are not on the same scale (hours, compliments, years), so comparing them is hard when looking at the raw data. This is where standardization comes in. Like with [frequentists models](https://easystats.github.io/effectsize/articles/standardize_parameters.html) we can choose from the same standardization methods. Let's use the (slow) `"refit"` method. ```{r} library(effectsize) standardize_parameters(mod, method = "refit", ci = 0.89) ``` Note that the central tendency of the posterior distribution is still the *median* - the median of the standardized posterior distribution. We can easily change this, of the type of credible interval used: ```{r} library(effectsize) standardize_parameters(mod, method = "basic", ci = 0.89, centrality = "MAP", ci_method = "eti" ) ``` As we can see, working harder (or at least for longer hours) has stronger predictive power than complementing or seniority. (Do note, however, that this does not mean that if you wish to have a higher salary you should work overtime - the raw parameters seem to suggest that complementing your boss is the way to go, with one compliment worth almost 3.5 times **more** than a full hours' work!) ## Eta2 ### Introduction In classical frequentists models, the computation of $\eta^2$ or $\eta^2_p$ is straightforward: based on the right combinations of sums-of-squares (*SS*s), we get the correct proportion of variance accounted for by some predictor term. However such a computation is not as straightforward for Bayesian models, for various reasons (e.g., the model-*SS* and the residual-*SS* don't necessarily sum to the total-*SS*). Although some have proposed Bayesian methods of estimating explained variance in ANOVA designs [@marsman2019bayesian], these are not yet easy to implement with `stan`-based models. An alternative route to obtaining effect sizes of explained variance, is via the use of the ***posterior predictive distribution*** (*PPD*). The PPD is the Bayesian expected distribution of possible unobserved values. Thus, after observing some data, we can estimate not just the expected mean values (the conditional marginal means), but also the full *distribution* of data around these values [@gelman2014bayesian, chapter 7]. By sampling from the PPD, we can decompose the sample to the various *SS*s needed for the computation of explained variance measures. By repeatedly sampling from the PPD, we can generate a posterior distribution of explained variance estimates. But note that **these estimates are conditioned not only on the location-parameters of the model, but also on the scale-parameters of the model!** So it is vital to [validate the PPD](https://mc-stan.org/docs/2_23/stan-users-guide/meta-models-part.html#meta-models.part/) before using it to estimate explained variance measures. ### Setup Let's factorize out data from above: ```{r} hardlyworking$age_f <- cut(hardlyworking$age, breaks = c(25, 35, 45), right = FALSE, labels = c("Young", "Less_young") ) hardlyworking$comps_f <- cut(hardlyworking$n_comps, breaks = c(0, 1, 2, 3), include.lowest = TRUE, right = FALSE ) table(hardlyworking$age_f, hardlyworking$comps_f) ``` And fit our model: ```{r} # use (special) effects coding contrasts(hardlyworking$age_f) <- bayestestR::contr.bayes contrasts(hardlyworking$comps_f) <- bayestestR::contr.bayes modAOV <- stan_glm(salary ~ age_f * comps_f, data = hardlyworking, family = gaussian(), refresh = 0 ) ``` We can use `eta_squared_posterior()` to get the posterior distribution of $eta^2$ or $eta^2_p$ for each effect. Like an ANOVA table, we must make sure to use the right effects-coding and *SS*-type: ```{r} pes_posterior <- eta_squared_posterior(modAOV, draws = 500, # how many samples from the PPD? partial = TRUE, # partial eta squared # type 3 SS ss_function = car::Anova, type = 3 ) head(pes_posterior) bayestestR::describe_posterior(pes_posterior, rope_range = c(0, 0.1), test = "rope") ``` Compare to: ```{r} modAOV_f <- lm(salary ~ age_f * comps_f, data = hardlyworking ) eta_squared(car::Anova(modAOV_f, type = 3)) ``` # References effectsize/inst/doc/from_test_statistics.html0000644000175000017500000012375414174212065021435 0ustar nileshnilesh Effect Size from Test Statistics

Effect Size from Test Statistics

Introduction

In many real world applications there are no straightforward ways of obtaining standardized effect sizes. However, it is possible to get approximations of most of the effect size indices (\(d\), \(r\), \(\eta^2_p\)…) with the use of test statistics. These conversions are based on the idea that test statistics are a function of effect size and sample size. Thus information about samples size (or more often of degrees of freedom) is used to reverse-engineer indices of effect size from test statistics. This idea and these functions also power our Effect Sizes From Test Statistics shiny app.

The measures discussed here are, in one way or another, signal to noise ratios, with the “noise” representing the unaccounted variance in the outcome variable1.

The indices are:

  • Percent variance explained (\(\eta^2_p\), \(\omega^2_p\), \(\epsilon^2_p\)).

  • Measure of association (\(r\)).

  • Measure of difference (\(d\)).

(Partial) Percent Variance Explained

These measures represent the ratio of \(Signal^2 / (Signal^2 + Noise^2)\), with the “noise” having all other “signals” partial-ed out (be they of other fixed or random effects). The most popular of these indices is \(\eta^2_p\) (Eta; which is equivalent to \(R^2\)).

The conversion of the \(F\)- or \(t\)-statistic is based on Friedman (1982).

Let’s look at an example:

library(afex)

data(md_12.1)

aov_fit <- aov_car(rt ~ angle * noise + Error(id / (angle * noise)),
  data = md_12.1,
  anova_table = list(correction = "none", es = "pes")
)
aov_fit
> Anova Table (Type 3 tests)
> 
> Response: rt
>        Effect    df     MSE         F  pes p.value
> 1       angle 2, 18 3560.00 40.72 *** .819   <.001
> 2       noise  1, 9 8460.00 33.77 *** .790   <.001
> 3 angle:noise 2, 18 1160.00 45.31 *** .834   <.001
> ---
> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1

Let’s compare the \(\eta^2_p\) (the pes column) obtained here with ones recovered from F_to_eta2():

library(effectsize)

F_to_eta2(
  f = c(40.72, 33.77, 45.31),
  df = c(2, 1, 2),
  df_error = c(18, 9, 18)
)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.82           | [0.66, 1.00]
> 0.79           | [0.49, 1.00]
> 0.83           | [0.69, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

They are identical!2 (except for the fact that F_to_eta2() also provides confidence intervals3 :)

In this case we were able to easily obtain the effect size (thanks to afex!), but in other cases it might not be as easy, and using estimates based on test statistic offers a good approximation.

For example:

In Simple Effect and Contrast Analysis

library(emmeans)

joint_tests(aov_fit, by = "noise")
> noise = absent:
>  model term df1 df2 F.ratio p.value
>  angle        2   9   8.000  0.0096
> 
> noise = present:
>  model term df1 df2 F.ratio p.value
>  angle        2   9  51.000  <.0001
F_to_eta2(
  f = c(5, 79),
  df = 2,
  df_error = 29
)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.26           | [0.04, 1.00]
> 0.84           | [0.75, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

We can also use t_to_eta2() for contrast analysis:

pairs(emmeans(aov_fit, ~angle))
>  contrast estimate   SE df t.ratio p.value
>  X0 - X4      -108 17.4  9  -6.200  0.0004
>  X0 - X8      -168 20.6  9  -8.200  0.0001
>  X4 - X8       -60 18.4  9  -3.300  0.0244
> 
> Results are averaged over the levels of: noise 
> P value adjustment: tukey method for comparing a family of 3 estimates
t_to_eta2(
  t = c(-5.7, -8.9, -3.2),
  df_error = 18
)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.64           | [0.39, 1.00]
> 0.81           | [0.66, 1.00]
> 0.36           | [0.09, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

In Linear Mixed Models

library(lmerTest)

fit_lmm <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)

anova(fit_lmm)
> Type III Analysis of Variance Table with Satterthwaite's method
>      Sum Sq Mean Sq NumDF DenDF F value  Pr(>F)    
> Days  30031   30031     1    17    45.9 3.3e-06 ***
> ---
> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
F_to_eta2(45.8, 1, 17)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.73           | [0.51, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

We can also use t_to_eta2() for the slope of Days (which in this case gives the same result).

parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite")
> # Fixed Effects
> 
> Parameter   | Coefficient |   SE |           95% CI | t(17.00) |      p
> -----------------------------------------------------------------------
> (Intercept) |      251.41 | 6.82 | [237.01, 265.80] |    36.84 | < .001
> Days        |       10.47 | 1.55 | [  7.21,  13.73] |     6.77 | < .001
> 
> Uncertainty intervals (equal-tailed) and p values (two-tailed) computed using a
>   Wald t-distribution with Satterthwaite approximation.
t_to_eta2(6.77, df_error = 17)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.73           | [0.51, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Bias-Corrected Indices

Alongside \(\eta^2_p\) there are also the less biased \(\omega_p^2\) (Omega) and \(\epsilon^2_p\) (Epsilon; sometimes called \(\text{Adj. }\eta^2_p\), which is equivalent to \(R^2_{adj}\); Albers and Lakens (2018), Mordkoff (2019)).

F_to_eta2(45.8, 1, 17)
> Eta2 (partial) |       95% CI
> -----------------------------
> 0.73           | [0.51, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
F_to_epsilon2(45.8, 1, 17)
> Epsilon2 (partial) |       95% CI
> ---------------------------------
> 0.71               | [0.48, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).
F_to_omega2(45.8, 1, 17)
> Omega2 (partial) |       95% CI
> -------------------------------
> 0.70             | [0.47, 1.00]
> 
> - One-sided CIs: upper bound fixed at (1).

Measure of Association

Similar to \(\eta^2_p\), \(r\) is a signal to noise ratio, and is in fact equal to \(\sqrt{\eta^2_p}\) (so it’s really a partial \(r\)). It is often used instead of \(\eta^2_p\) when discussing the strength of association (but I suspect people use it instead of \(\eta^2_p\) because it gives a bigger number, which looks better).

For Slopes

parameters::model_parameters(fit_lmm, effects = "fixed", ci_method = "satterthwaite")
> # Fixed Effects
> 
> Parameter   | Coefficient |   SE |           95% CI | t(17.00) |      p
> -----------------------------------------------------------------------
> (Intercept) |      251.41 | 6.82 | [237.01, 265.80] |    36.84 | < .001
> Days        |       10.47 | 1.55 | [  7.21,  13.73] |     6.77 | < .001
> 
> Uncertainty intervals (equal-tailed) and p values (two-tailed) computed using a
>   Wald t-distribution with Satterthwaite approximation.
t_to_r(6.77, df_error = 17)
> r    |       95% CI
> -------------------
> 0.85 | [0.67, 0.92]

In a fixed-effect linear model, this returns the partial correlation. Compare:

fit_lm <- lm(rating ~ complaints + critical, data = attitude)

parameters::model_parameters(fit_lm)
> Parameter   | Coefficient |    SE |         95% CI | t(27) |      p
> -------------------------------------------------------------------
> (Intercept) |       14.25 | 11.17 | [-8.67, 37.18] |  1.28 | 0.213 
> complaints  |        0.75 |  0.10 | [ 0.55,  0.96] |  7.46 | < .001
> critical    |    1.91e-03 |  0.14 | [-0.28,  0.28] |  0.01 | 0.989
> 
> Uncertainty intervals (equal-tailed) and p values (two-tailed) computed using a
>   Wald t-distribution approximation.
t_to_r(
  t = c(7.46, 0.01),
  df_error = 27
)
> r        |        95% CI
> ------------------------
> 0.82     | [ 0.67, 0.89]
> 1.92e-03 | [-0.35, 0.35]

to:

correlation::correlation(attitude[, c(1, 2, 6)], partial = TRUE)[1:2, c(2, 3, 7, 8)]
> Parameter2 |        r | t(28)
> -----------------------------
> complaints |     0.82 |  7.60
> critical   | 2.70e-03 |  0.01

In Contrast Analysis

This measure is also sometimes used in contrast analysis, where it is called the point bi-serial correlation - \(r_{pb}\) (Cohen et al. 1965; Rosnow, Rosenthal, and Rubin 2000):

pairs(emmeans(aov_fit, ~angle))
>  contrast estimate   SE df t.ratio p.value
>  X0 - X4      -108 17.4  9  -6.200  0.0004
>  X0 - X8      -168 20.6  9  -8.200  0.0001
>  X4 - X8       -60 18.4  9  -3.300  0.0244
> 
> Results are averaged over the levels of: noise 
> P value adjustment: tukey method for comparing a family of 3 estimates
t_to_r(
  t = c(-5.7, -8.9, -3.2),
  df_error = 18
)
> r     |         95% CI
> ----------------------
> -0.80 | [-0.89, -0.57]
> -0.90 | [-0.95, -0.78]
> -0.60 | [-0.79, -0.22]

Measures of Difference

These indices represent \(Signal/Noise\) with the “signal” representing the difference between two means. This is akin to Cohen’s \(d\), and is a close approximation when comparing two groups of equal size (Wolf 1986; Rosnow, Rosenthal, and Rubin 2000).

These can be useful in contrast analyses.

Between-Subject Contrasts

m <- lm(breaks ~ tension, data = warpbreaks)

em_tension <- emmeans(m, ~tension)
pairs(em_tension)
>  contrast estimate SE df t.ratio p.value
>  L - M        10.0  4 51   2.500  0.0400
>  L - H        14.7  4 51   3.700  <.0001
>  M - H         4.7  4 51   1.200  0.4600
> 
> P value adjustment: tukey method for comparing a family of 3 estimates
t_to_d(
  t = c(2.53, 3.72, 1.20),
  df_error = 51
)
> d    |        95% CI
> --------------------
> 0.71 | [ 0.14, 1.27]
> 1.04 | [ 0.45, 1.62]
> 0.34 | [-0.22, 0.89]

However, these are merely approximations of a true Cohen’s d. It is advised to directly estimate Cohen’s d, whenever possible. For example, here with emmeans::eff_size():

eff_size(em_tension, sigma = sigma(m), edf = df.residual(m))
>  contrast effect.size   SE df lower.CL upper.CL
>  L - M           0.84 0.34 51     0.15     1.53
>  L - H           1.24 0.36 51     0.53     1.95
>  M - H           0.40 0.34 51    -0.28     1.07
> 
> sigma used for effect sizes: 11.88 
> Confidence level used: 0.95

Within-Subject Contrasts

pairs(emmeans(aov_fit, ~angle))
>  contrast estimate   SE df t.ratio p.value
>  X0 - X4      -108 17.4  9  -6.200  0.0004
>  X0 - X8      -168 20.6  9  -8.200  0.0001
>  X4 - X8       -60 18.4  9  -3.300  0.0244
> 
> Results are averaged over the levels of: noise 
> P value adjustment: tukey method for comparing a family of 3 estimates
t_to_d(
  t = c(-5.7, -5.9, -3.2),
  df_error = 18,
  paired = TRUE
)
> d     |         95% CI
> ----------------------
> -1.34 | [-1.97, -0.70]
> -1.39 | [-2.03, -0.74]
> -0.75 | [-1.27, -0.22]

(Note set paired = TRUE to not over estimate the size of the effect; Rosenthal (1991); Rosnow, Rosenthal, and Rubin (2000))

References

Albers, Casper, and Daniël Lakens. 2018. “When Power Analyses Based on Pilot Data Are Biased: Inaccurate Effect Size Estimators and Follow-up Bias.” Journal of Experimental Social Psychology 74: 187–95.
Cohen, Jacob et al. 1965. “Some Statistical Issues in Psychological Research.” Handbook of Clinical Psychology, 95–121.
Friedman, Herbert. 1982. “Simplified Determinations of Statistical Power, Magnitude of Effect and Research Sample Sizes.” Educational and Psychological Measurement 42 (2): 521–26.
Mordkoff, J Toby. 2019. “A Simple Method for Removing Bias from a Popular Measure of Standardized Effect Size: Adjusted Partial Eta Squared.” Advances in Methods and Practices in Psychological Science 2 (3): 228–32.
Rosenthal, Robert. 1991. “Meta-Analytic Procedures for Social Sciences.” Newbury Park, CA: Sage 10: 9781412984997.
Rosnow, Ralph L, Robert Rosenthal, and Donald B Rubin. 2000. “Contrasts and Correlations in Effect-Size Estimation.” Psychological Science 11 (6): 446–53.
Wolf, Fredric M. 1986. Meta-Analysis: Quantitative Methods for Research Synthesis. Vol. 59. Sage.

  1. Note that for generalized linear models (Poisson, Logistic…), where the outcome is never on an arbitrary scale, estimates themselves are indices of effect size! Thus this vignette is relevant only to general linear models.↩︎

  2. Note that these are partial percent variance explained, and so their sum can be larger than 1.↩︎

  3. Confidence intervals for all indices are estimated using the non-centrality parameter method; These methods search for a the best non-central parameter of the non-central \(F\)/\(t\) distribution for the desired tail-probabilities, and then convert these ncps to the corresponding effect sizes.↩︎

effectsize/inst/doc/interpret.html0000644000175000017500000011016514174212066017166 0ustar nileshnilesh Automated Interpretation of Indices of Effect Size

Automated Interpretation of Indices of Effect Size

Why?

The metrics used in statistics (indices of fit, model performance, or parameter estimates) can be very abstract. A long experience is required to intuitively feel the meaning of their values. In order to facilitate the understanding of the results they are facing, many scientists use (often implicitly) some set of rules of thumb. Some of these rules of thumb have been standardize and validated and subsequently published as guidelines. Understandably then, such rules of thumb are just suggestions and there is nothing universal about them. The interpretation of any effect size measures is always going to be relative to the discipline, the specific data, and the aims of the analyst. This is important because what might be considered a small effect in psychology might be large for some other field like public health.

One of the most famous interpretation grids was proposed by Cohen (1988) for a series of widely used indices, such as the correlation r (r = .20, small; r = .40, moderate and r = .60, large) or the standardized difference (Cohen’s d). However, there is now a clear evidence that Cohen’s guidelines (which he himself later disavowed; Funder, 2019) are much too stringent and not particularly meaningful taken out of context (Funder and Ozer 2019). This led to the emergence of a literature discussing and creating new sets of rules of thumb.

Although everybody agrees on the fact that effect size interpretation in a study should be justified with a rationale (and depend on the context, the field, the literature, the hypothesis, etc.), these pre-baked rules can nevertheless be useful to give a rough idea or frame of reference to understand scientific results.

The package effectsize catalogs such sets of rules of thumb for a variety of indices in a flexible and explicit fashion, helping you understand and report your results in a scientific yet meaningful way. Again, readers should keep in mind that these thresholds, as ubiquitous as they may be, remain arbitrary. Thus, their use should be discussed on a case-by-case basis depending on the field, hypotheses, prior results, and so on, to avoid their crystallization, as for the infamous \(p < .05\) criterion of hypothesis testing.

Moreover, some authors suggest the counter-intuitive idea that very large effects, especially in the context of psychological research, is likely to be a “gross overestimate that will rarely be found in a large sample or in a replication” (Funder and Ozer 2019). They suggest that smaller effect size are worth taking seriously (as they can be potentially consequential), as well as more believable.

Correlation r

Funder and Ozer (2019)

interpret_r(x, rules = "funder2019")
  • r < 0.05 - Tiny

  • 0.05 <= r < 0.1 - Very small

  • 0.1 <= r < 0.2 - Small

  • 0.2 <= r < 0.3 - Medium

  • 0.3 <= r < 0.4 - Large

  • r >= 0.4 - Very large

Gignac and Szodorai (2016)

Gignac’s rules of thumb are actually one of few interpretation grid justified and based on actual data, in this case on the distribution of effect magnitudes in the literature.

interpret_r(x, rules = "gignac2016")
  • r < 0.1 - Very small

  • 0.1 <= r < 0.2 - Small

  • 0.2 <= r < 0.3 - Moderate

  • r >= 0.3 - Large

J. Cohen (1988)

interpret_r(x, rules = "cohen1988")
  • r < 0.1 - Very small

  • 0.1 <= r < 0.3 - Small

  • 0.3 <= r < 0.5 - Moderate

  • r >= 0.5 - Large

Evans (1996)

interpret_r(x, rules = "evans1996")
  • r < 0.2 - Very weak

  • 0.2 <= r < 0.4 - Weak

  • 0.4 <= r < 0.6 - Moderate

  • 0.6 <= r < 0.8 - Strong

  • r >= 0.8 - Very strong

Lovakov and Agadullina (2021)

interpret_r(x, rules = "lovakov2021")
  • r < 0.12 - Very small

  • 0.12 <= r < 0.24 - Small

  • 0.24 <= r < 0.41 - Moderate

  • r >= 0.41 - Large

Standardized Difference d (Cohen’s d)

The standardized difference can be obtained through the standardization of linear model’s parameters or data, in which they can be used as indices of effect size.

J. Cohen (1988)

interpret_cohens_d(x, rules = "cohen1988")
  • d < 0.2 - Very small

  • 0.2 <= d < 0.5 - Small

  • 0.5 <= d < 0.8 - Medium

  • d >= 0.8 - Large

Sawilowsky (2009)

interpret_cohens_d(x, rules = "sawilowsky2009")
  • d < 0.1 - Tiny

  • 0.1 <= d < 0.2 - Very small

  • 0.2 <= d < 0.5 - Small

  • 0.5 <= d < 0.8 - Medium

  • 0.8 <= d < 1.2 - Large

  • 1.2 <= d < 2 - Very large

  • d >= 2 - Huge

Gignac and Szodorai (2016)

Gignac’s rules of thumb are actually one of few interpretation grid justified and based on actual data, in this case on the distribution of effect magnitudes in the literature. These is in fact the same grid used for r, based on the conversion of r to d:

interpret_cohens_d(x, rules = "gignac2016")
  • d < 0.2 - Very small

  • 0.2 <= d < 0.41 - Small

  • 0.41 <= d < 0.63 - Moderate

  • d >= 0.63 - Large

Lovakov and Agadullina (2021)

interpret_cohens_d(x, rules = "lovakov2021")
  • r < 0.15 - Very small

  • 0.15 <= r < 0.36 - Small

  • 0.36 <= r < 0.65 - Moderate

  • r >= 0.65 - Large

Odds Ratio (OR)

Odds ratio, and log odds ratio, are often found in epidemiological studies. However, they are also the parameters of logistic regressions, where they can be used as indices of effect size. Note that the (log) odds ratio from logistic regression coefficients are unstandardized, as they depend on the scale of the predictor. In order to apply the following guidelines, make sure you standardize your predictors!

Keep in mind that these apply to Odds ratios, so Odds ratio of 10 is as extreme as a Odds ratio of 0.1 (1/10).

Chen, Cohen, and Chen (2010)

interpret_oddsratio(x, rules = "chen2010")
  • OR < 1.68 - Very small

  • 1.68 <= OR < 3.47 - Small

  • 3.47 <= OR < 6.71 - Medium

  • OR >= 6.71 - Large

J. Cohen (1988)

interpret_oddsratio(x, rules = "cohen1988")
  • OR < 1.44 - Very small

  • 1.44 <= OR < 2.48 - Small

  • 2.48 <= OR < 4.27 - Medium

  • OR >= 4.27 - Large

This converts (log) odds ratio to standardized difference d using the following formula (J. Cohen 1988; Sánchez-Meca, Marı́n-Martı́nez, and Chacón-Moscoso 2003):

\[ d = log(OR) \times \frac{\sqrt{3}}{\pi} \]

Coefficient of determination (R2)

For Linear Regression

J. Cohen (1988)

interpret_r2(x, rules = "cohen1988")
  • R2 < 0.02 - Very weak

  • 0.02 <= R2 < 0.13 - Weak

  • 0.13 <= R2 < 0.26 - Moderate

  • R2 >= 0.26 - Substantial

Falk and Miller (1992)

interpret_r2(x, rules = "falk1992")
  • R2 < 0.1 - Negligible

  • R2 >= 0.1 - Adequate

For PLS / SEM R-Squared of latent variables

Chin et al. (1998)

interpret_r2(x, rules = "chin1998")
  • R2 < 0.19 - Very weak

  • 0.19 <= R2 < 0.33 - Weak

  • 0.33 <= R2 < 0.67 - Moderate

  • R2 >= 0.67 - Substantial

Hair, Ringle, and Sarstedt (2011)

interpret_r2(x, rules = "hair2011")
  • R2 < 0.25 - Very weak

  • 0.25 <= R2 < 0.50 - Weak

  • 0.50 <= R2 < 0.75 - Moderate

  • R2 >= 0.75 - Substantial

Omega / Eta / Epsilon Squared

The Omega squared is a measure of effect size used in ANOVAs. It is an estimate of how much variance in the response variables are accounted for by the explanatory variables. Omega squared is widely viewed as a lesser biased alternative to eta-squared, especially when sample sizes are small.

Field (2013)

interpret_omega_squared(x, rules = "field2013")
  • ES < 0.01 - Very small

  • 0.01 <= ES < 0.06 - Small

  • 0.16 <= ES < 0.14 - Medium

  • ES >= 0.14 - Large

Jacob Cohen (1992)

These are applicable to one-way ANOVAs, or to partial Eta / Omega / Epsilon Squared in a multi-way ANOVA.

interpret_omega_squared(x, rules = "cohen1992")
  • ES < 0.02 - Very small

  • 0.02 <= ES < 0.13 - Small

  • 0.13 <= ES < 0.26 - Medium

  • ES >= 0.26 - Large

Kendall’s coefficient of concordance

The interpretation of Kendall’s coefficient of concordance (w) is a measure of effect size used in non-parametric ANOVAs (the Friedman rank sum test). It is an estimate of agreement among multiple raters.

Landis and Koch (1977)

interpret_omega_squared(w, rules = "landis1977")
  • 0.00 <= w < 0.20 - Slight agreement
  • 0.20 <= w < 0.40 - Fair agreement
  • 0.40 <= w < 0.60 - Moderate agreement
  • 0.60 <= w < 0.80 - Substantial agreement
  • w >= 0.80 - Almost perfect agreement

Cohen’s g

Cohen’s g is a measure of effect size used for McNemar’s test of agreement in selection - when repeating a multiple chose selection, is the percent of matches (first response is equal to the second response) different than 50%?

J. Cohen (1988)

interpret_cohens_g(x, rules = "cohen1988")
  • d < 0.05 - Very small

  • 0.05 <= d < 0.15 - Small

  • 0.15 <= d < 0.25 - Medium

  • d >= 0.25 - Large

Interpretation of other Indices

effectsize also offers functions for interpreting other statistical indices:

  • interpret_gfi(), interpret_agfi(), interpret_nfi(), interpret_nnfi(), interpret_cfi(), interpret_rmsea(), interpret_srmr(), interpret_rfi(), interpret_ifi(), and interpret_pnfi() for interpretation CFA / SEM goodness of fit.

  • interpret_p() for interpretation of p-values.

  • interpret_direction() for interpretation of direction.

  • interpret_bf() for interpretation of Bayes factors.

  • interpret_rope() for interpretation of Bayesian ROPE tests.

  • interpret_ess() and interpret_rhat() for interpretation of Bayesian diagnostic indices.

References

Chen, Henian, Patricia Cohen, and Sophie Chen. 2010. “How Big Is a Big Odds Ratio? Interpreting the Magnitudes of Odds Ratios in Epidemiological Studies.” Communications in Statistics—Simulation and Computation 39 (4): 860–64.
Chin, Wynne W et al. 1998. “The Partial Least Squares Approach to Structural Equation Modeling.” Modern Methods for Business Research 295 (2): 295–336.
Cohen, J. 1988. Statistical Power Analysis for the Behavioral Sciences, 2nd Ed. New York: Routledge.
Cohen, Jacob. 1992. “A Power Primer.” Psychological Bulletin 112 (1): 155.
Evans, James D. 1996. Straightforward Statistics for the Behavioral Sciences. Thomson Brooks/Cole Publishing Co.
Falk, R Frank, and Nancy B Miller. 1992. A Primer for Soft Modeling. University of Akron Press.
Field, Andy. 2013. Discovering Statistics Using IBM SPSS Statistics. sage.
Funder, David C, and Daniel J Ozer. 2019. “Evaluating Effect Size in Psychological Research: Sense and Nonsense.” Advances in Methods and Practices in Psychological Science, 2515245919847202.
Gignac, Gilles E, and Eva T Szodorai. 2016. “Effect Size Guidelines for Individual Differences Researchers.” Personality and Individual Differences 102: 74–78.
Hair, Joe F, Christian M Ringle, and Marko Sarstedt. 2011. “PLS-SEM: Indeed a Silver Bullet.” Journal of Marketing Theory and Practice 19 (2): 139–52.
Landis, J Richard, and Gary G Koch. 1977. “The Measurement of Observer Agreement for Categorical Data.” Biometrics, 159–74.
Lovakov, Andrey, and Elena R Agadullina. 2021. “Empirically Derived Guidelines for Effect Size Interpretation in Social Psychology.” European Journal of Social Psychology.
Sánchez-Meca, Julio, Fulgencio Marı́n-Martı́nez, and Salvador Chacón-Moscoso. 2003. “Effect-Size Indices for Dichotomized Outcomes in Meta-Analysis.” Psychological Methods 8 (4): 448.
Sawilowsky, Shlomo S. 2009. “New Effect Size Rules of Thumb.”
effectsize/inst/doc/simple_htests.R0000644000175000017500000001271114174212075017270 0ustar nileshnilesh## ----setup, include=FALSE----------------------------------------------------- library(knitr) options(knitr.kable.NA = "") knitr::opts_chunk$set(comment = ">") options(digits = 3) pkgs <- c("effectsize", "BayesFactor") if (!all(sapply(pkgs, require, quietly = TRUE, character.only = TRUE))) { knitr::opts_chunk$set(eval = FALSE) } set.seed(7) ## ----------------------------------------------------------------------------- library(effectsize) library(BayesFactor) ## ----------------------------------------------------------------------------- t.test(mpg ~ am, data = mtcars, var.equal = TRUE) cohens_d(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- hedges_g(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- t.test(mpg ~ am, data = mtcars, var.equal = FALSE) cohens_d(mpg ~ am, data = mtcars, pooled_sd = FALSE) hedges_g(mpg ~ am, data = mtcars, pooled_sd = FALSE) ## ----------------------------------------------------------------------------- glass_delta(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- t.test(mpg ~ am, data = mtcars, var.equal = TRUE, alternative = "less") cohens_d(mpg ~ am, data = mtcars, pooled_sd = TRUE, alternative = "less") ## ----------------------------------------------------------------------------- cles(mpg ~ am, data = mtcars) ## ----------------------------------------------------------------------------- t.test(extra ~ group, data = sleep, paired = TRUE) cohens_d(extra ~ group, data = sleep, paired = TRUE) hedges_g(extra ~ group, data = sleep, paired = TRUE) ## ----------------------------------------------------------------------------- (BFt <- ttestBF(mtcars$mpg[mtcars$am == 0], mtcars$mpg[mtcars$am == 1])) effectsize(BFt, test = NULL) ## ---- message=FALSE----------------------------------------------------------- onew <- oneway.test(mpg ~ gear, data = mtcars, var.equal = TRUE) eta_squared(onew) ## ----------------------------------------------------------------------------- (Music <- matrix( c( 150, 130, 35, 55, 100, 50, 10, 40, 165, 65, 2, 25 ), byrow = TRUE, nrow = 3, dimnames = list( Study = c("Psych", "Econ", "Law"), Music = c("Pop", "Rock", "Jazz", "Classic") ) )) chisq.test(Music) cramers_v(Music) phi(Music) pearsons_c(Music) ## ----------------------------------------------------------------------------- O <- c(89, 37, 130, 28, 2) # observed group sizes E <- c(.40, .20, .20, .15, .05) # expected group freq chisq.test(O, p = E, rescale.p = TRUE) pearsons_c(O, p = E, rescale.p = TRUE) phi(O, p = E, rescale.p = TRUE) ## ----------------------------------------------------------------------------- (BFX <- contingencyTableBF(Music, sampleType = "jointMulti")) effectsize(BFX, type = "cramers_v", test = NULL) effectsize(BFX, type = "phi", test = NULL) effectsize(BFX, type = "pearsons_c", test = NULL) ## ----------------------------------------------------------------------------- (RCT <- matrix( c( 71, 30, 50, 100 ), nrow = 2, byrow = TRUE, dimnames = list( Diagnosis = c("Sick", "Recovered"), Group = c("Treatment", "Control") ) )) chisq.test(RCT) # or fisher.test(RCT) oddsratio(RCT) ## ----------------------------------------------------------------------------- riskratio(RCT) ## ----------------------------------------------------------------------------- cohens_h(RCT) ## ----------------------------------------------------------------------------- (Performance <- matrix( c( 794, 86, 150, 570 ), nrow = 2, byrow = TRUE, dimnames = list( "1st Survey" = c("Approve", "Disapprove"), "2nd Survey" = c("Approve", "Disapprove") ) )) mcnemar.test(Performance) cohens_g(Performance) ## ---- warning=FALSE----------------------------------------------------------- A <- c(48, 48, 77, 86, 85, 85) B <- c(14, 34, 34, 77) wilcox.test(A, B) # aka Mann–Whitney U test rank_biserial(A, B) ## ----------------------------------------------------------------------------- cles(A, B, rank = TRUE) ## ----------------------------------------------------------------------------- x <- c(1.15, 0.88, 0.90, 0.74, 1.21, 1.36, 0.89) wilcox.test(x, mu = 1) # aka Signed-Rank test rank_biserial(x, mu = 1) x <- c(1.83, 0.50, 1.62, 2.48, 1.68, 1.88, 1.55, 3.06, 1.30) y <- c(0.878, 0.647, 0.598, 2.05, 1.06, 1.29, 1.06, 3.14, 1.29) wilcox.test(x, y, paired = TRUE) # aka Signed-Rank test rank_biserial(x, y, paired = TRUE) ## ----------------------------------------------------------------------------- group_data <- list( g1 = c(2.9, 3.0, 2.5, 2.6, 3.2), # normal subjects g2 = c(3.8, 2.7, 4.0, 2.4), # with obstructive airway disease g3 = c(2.8, 3.4, 3.7, 2.2, 2.0) # with asbestosis ) kruskal.test(group_data) rank_epsilon_squared(group_data) ## ----------------------------------------------------------------------------- # Subjects are COLUMNS (ReactionTimes <- matrix( c(398, 338, 520, 325, 388, 555, 393, 363, 561, 367, 433, 470, 286, 492, 536, 362, 475, 496, 253, 334, 610), nrow = 7, byrow = TRUE, dimnames = list( paste0("Subject", 1:7), c("Congruent", "Neutral", "Incongruent") ) )) friedman.test(ReactionTimes) kendalls_w(ReactionTimes) effectsize/data/0000755000175000017500000000000014132466117013451 5ustar nileshnilesheffectsize/data/hardlyworking.rda0000644000175000017500000002177314132466117017037 0ustar nileshnileshBZh91AY&SYM90 nguuSk+]vۻg7sv软_ w+˶t0@ &F= 'bih6z<f0Bb=jОj2IOC@ 4i4LiT:A #LM0516F)O0OS1aLJ~F`ҧ= OIBi=byM&QSMSi0SɓM&e?CD='2=&#dLohiMɡA~MɣMh#A)=166$=SM2OFyC)MѢ`zh@4iLLOM'4=BzMCOSLI<yz y6&6(<'O"2djzAO4M3IhfyF##iMIM4i=@hdOSC@`AAh&žQ$FCFyM='fQ63Sh3I12d⍤mOSzdy53S2mOHjzP(a- Bbb @HB$Hm&CM ҖLbB!2 hm!6@@! @! CplB+VSED0mm&7 66EK[yz'd(ɖP0Le%`%46I$D$UC&RMTRG(%-"I:P*&jM)H`Ք Ptrb$%QQL7ZMG,Vt)6r؆Ԉd)&\Sp&LtyhJ!iPɗ2 t utcT&7[4E+GJ41` V0H&)bfjt-aB3 ӰO s4SlQa=&  ٜ." 1Dt/Rp3!P$p< HF(HPa1"i(DQ č`QA\GJ=\ ,JȠэlfr9BQ! 4B$ jjo@Aʈ18EĢBqg=P ""dF6Fdb"yPʀnVF*q(XLUDV{ NyʼoXc!8@#9ĥe]Rʆo !`DDD|1(uҒI% $$Ir~WnX+BQ #,Qc#0(%1qyhw}VJW9#kt|G LBL HDIC !B@KMfRLH(bbA |?zc1g6?y{E/LLQi"HbB "GJJ 4ā)l%fakk<}]yvI_/mDBoʳ&:n˫ 4}қW>w5n0V%koW;-NJuQ&zFoYr:@du7*~ &8[{q<39d,Ssc@Y $ͦJw}lOAݙq$x5|2 @)U( wf-|MxCI؄A }dLW2B*Sx꺥D"w`:Pk(`<]?Lz:jQݐʧ=&'LzTprT8]eҷdY1n9mjmNXʲ"jyZ'B{YF墈.CL#.ޘ0}E@l%=ߟa]'=Ե8~ƾd{J.ǟ4/vZu _\zg Lr~mfx/{94gvWhLXUv OK,$J 4U.hsp-O ?S*Wju (9vĻ s/uB*5RIolN [Jg2'M]v'w5J,!'`5$ f Y>+f 5jܦZkq&춢u5 qn,}-y}#o-4z0keC:jTܵktζihʙn&5m 8Bd 2y֥yk_,\#93b=@g\6F|֛ѭ}Ӭi!m^?{&З`@ԚeaEgہ R.mw Nb W_"/kjr DᦱM`, "&_*uXK< 88 Ν&z Dt ]IBjH@! T[O" s. ̒Z;wu҂wBeu#QA1X;}{bj}@ jzMAa;EgG󉘻bcnۡmߓj7`%t^#V,_(e)Mc. ^y3OPiA2xB1"piBI_ gZ.%v!펜f*btMO>Xͅ '}Es|g вkeNcBr-0|qbҗ͢4zEA)^eKe\vm i&6Ui`#5v2FLOaӍu|◢wYԓʠN]hL J~!R y-׷htЖ[ߞ9iPpgXZe7fΪ*&g)O扌VѫH"e.CaLgyrXy}X*q赺̀& TXU(.T]$+ 8s>B0s 1ڲWm#ꩄHӝ)\+޴ը@#Rt!f! p\z-o 3^U߁ضbsrK/ҢLb4lr3m)1I3M1!;ؾ XUm,t˷9 q1)[}!eFc] YUI㙗XaP~(in xur16ar\ܒw'Oot˹;H>p=y-& FʺbÞ8czCZbbX?}n^5zh]lXgxN)희d QYU:ʧWP 3SϹgHy XY`qd{ aF^ڙu-|%o N˾  ac٠i>򩓷l«f=h5y"ns!h!(A?`zb<{0m2'9$KFDz&rVR @jA᧹:Ո1ysXݬqyUƦppD?5-Au)aIy*)ͦ o^(ˤ|j E*ɇ`Gk+U;g_Zy3&2Ў>%uuفVge~<{;dq'RUNt1/<;L^*cx*aY݊WBrW""`Tw|IqiiL%]ЩHL<;;/Ќq(\ڥ9Ks=ai`ÿ-l<(>M3>2y)SfY~aǽb Û+b3& ERԻp˶¨F(;ԓ5)Y H MƲ Jd/pٛM +_ikkrJ'k=f&zj %DÇgN4#,])߃"IeEC~ӕFsD7͒gQIЙ(qW(gdM?&"hii>,,$ @ϵzJ!3c!ZBJ,ڤ_Χ%y1!`(AVɳ#Z{x5,g';%r-7dgiQ2/JQH"SS";<Ô,I&|;?nZH"<J6Y/L )7Dsia'ƓN xAH$vñ9:ds Qd'1`>&Nn!Epjy\C/Qw/6/ 9l9)QOXʭz7|2MjGs-h wɜ]}6"fM,6cN6#'wCb( Ben!^u(syPyӬڗm(ŕy]v; Ϛw۝[vܹ5_Y\?:{p7u>ule{a\-Fgy]f) dE+aM En: sZpgO++޻]#e x'&o:or-W|~Ή;"L2Ւ8)UI[rcb(yö;}-*) Wu6=S8VPB^}fY,IZ^ew6qSX7z@6=G_Ui]Ĥ]-qYzN<̯ǥ@ϵ}xkjH*{$?VPoo{ Bw9|9}1MlނЫc<w_fa+l։Mvk3g_Ore?-Ui5]Q: l=%ܹʯʓTxjz`O.q0Ȋ[ɚF]+f}},G+ q 3 6%Ad3'rl ]Vi"\/ jK@R )Ep7cO\w}􋾰P[T^E{] vat` AZmEFu[%@yhQ3 hg;Rw;_=i0>xӼҍs<_4$m_ q75] .{Wp&3%74WWԑk2P$ccS͒&=o߲B0%})'62xrא%-3-4)q?ƆӦ tePBMEuɔ2PӍ 2OY*2g/(63>+82[ mfv?m1Lrv[,UJ7UcN_8r:-h=&:7\o&%@Vnb@t=>lu|yMP)Z;^GkdWx:F4Եl}y>Mtʡc|Ux-R3 rU ؛l:Qݍ6^VvDV+G>3,-β؜$m)MIߡu9*0tŴ#jhsJLrtpISVayXL7|6{K"7+vΪG^BBL-%eCs~=ZJ*n;}nv"O}CnoJ/q)gWe{G;quVPS!;$LMT뗰N]R3H);q4f8~')+-_5L[x\=7INa;ލIuu48z&|@$Dd(\@ˏT7.p2S}OhPxL9-gM"q>SXk/;_"* -~X>rƭdظDZOh?Ԗ 4a<˰, 훮K <>fsUuZ#fXWg2Em;[b[Z'.y)U 9:-*` Pm vS)`X2D"|dwNБ>l^*gy]Jz'6f %~l#̣ۨU'J,@5-r8UǷI3ݏչfȊ[{7 ]94>DmKt1 kEa uh^xll3붴ut֫~/-r2x/EW-QDj mdR40F'{eDծo]@RbµMxvcpZOu614ʿPPJA[( mFkD?}hk%8}I@ -: 85Qz W4_hn f֌e)V@1f 5 CIo _kREP@cP' t( jxno}^3ieZ.5D4BF6oġo89،r~MugnxE]s.5G\N\AQ,q9r7|_@,沸kKTq.k %7\;3\mV׿!b{>xW(.N 647uoݼ]u{lc[߉/} \,ۘB/-[qvwKj,ȴ] 0Za}Q8ZR[}Mmd!ٯ(^ąv8J[ ^Yz[ Knh3bH^ DzD`p#]H%KYsڒꨶsV ΋ Ul[-_ /Z/'W#".w/Z.賩Z}˾qɝ[=[RaEy-֛gol-/|wKVao?͈7cj+3s? *9e-9Wa2>Os?gp; g̜\I+IF^5:Qor8{.V7'D 9֡/LYhےSlps a>KWW6/+ڡr60BR=*,BQy/I_E{ֿ7ʖs3J(gfE+#{ !3Jva6G9RY&UxL lDB!tр ְbnQnoԔY3F!{IM!_p6d$rAPIĴ7ZA֒3 VS} ˭nuN ۵;"Jv}2>0$aOӍ2yHXHÏ#Hs3Y9JtmbSxCP!YF}k}*܊d-`7ĝ eQ~9VP xPz7\g00XL~' Ч x8N,.^ڽR>rU.g6\+Ͻ;]~]Znv%I,oo{VSC`deZ3Hڈn#]GU؀|jV N'+ J- N!'TSZJ(ò2:L>X }WT*v(P @*%ݴR@HME[KD_U챉UY-'˶*חgOOJ&m?چAkm2jeqCq. F5i9~JjCu=^AGݱWkj6) . Z ']kG'W I}Hqokomz3]XWw$S effectsize/NAMESPACE0000644000175000017500000001616114174212025013755 0ustar nileshnilesh# Generated by roxygen2: do not edit by hand S3method(.anova_es,model_fit) S3method(d_to_cles,effectsize_difference) S3method(d_to_cles,numeric) S3method(effectsize,BFBayesFactor) S3method(effectsize,afex_aov) S3method(effectsize,anova) S3method(effectsize,aov) S3method(effectsize,aovlist) S3method(effectsize,default) S3method(effectsize,easycorrelation) S3method(effectsize,htest) S3method(equivalence_test,effectsize_table) S3method(eta_squared_posterior,brmsfit) S3method(eta_squared_posterior,stanreg) S3method(format,effectsize_table) S3method(format,equivalence_test_effectsize) S3method(interpret,effectsize_table) S3method(interpret,lavaan) S3method(interpret,numeric) S3method(interpret,performance_lavaan) S3method(odds_to_probs,data.frame) S3method(odds_to_probs,numeric) S3method(oddsratio_to_riskratio,default) S3method(oddsratio_to_riskratio,numeric) S3method(plot,effectsize_table) S3method(plot,equivalence_test_effectsize) S3method(print,effectsize_anova) S3method(print,effectsize_difference) S3method(print,effectsize_interpret) S3method(print,effectsize_std_params) S3method(print,effectsize_table) S3method(print,equivalence_test_effectsize) S3method(print,rules) S3method(probs_to_odds,data.frame) S3method(probs_to_odds,numeric) S3method(rb_to_cles,effectsize_difference) S3method(rb_to_cles,numeric) S3method(standardize,Surv) S3method(standardize,bcplm) S3method(standardize,clm2) S3method(standardize,default) S3method(standardize,mediate) S3method(standardize,wbgee) S3method(standardize,wbm) S3method(standardize_info,default) S3method(standardize_parameters,bootstrap_model) S3method(standardize_parameters,bootstrap_parameters) S3method(standardize_parameters,default) S3method(standardize_parameters,mediate) S3method(standardize_parameters,model_fit) S3method(standardize_parameters,parameters_model) export(.es_aov_simple) export(.es_aov_strata) export(.es_aov_table) export(F_to_d) export(F_to_epsilon2) export(F_to_eta2) export(F_to_eta2_adj) export(F_to_f) export(F_to_f2) export(F_to_omega2) export(F_to_r) export(adjust) export(change_scale) export(chisq_to_cohens_w) export(chisq_to_cramers_v) export(chisq_to_pearsons_c) export(chisq_to_phi) export(cles) export(cliffs_delta) export(cohens_d) export(cohens_f) export(cohens_f_squared) export(cohens_g) export(cohens_h) export(cohens_u3) export(cohens_w) export(common_language) export(convert_d_to_common_language) export(convert_d_to_oddsratio) export(convert_d_to_r) export(convert_logoddsratio_to_d) export(convert_logoddsratio_to_r) export(convert_odds_to_probs) export(convert_oddsratio_to_d) export(convert_oddsratio_to_r) export(convert_probs_to_odds) export(convert_r_to_d) export(convert_r_to_oddsratio) export(convert_rb_to_common_language) export(cramers_v) export(d_to_cles) export(d_to_common_language) export(d_to_oddsratio) export(d_to_r) export(effectsize) export(epsilon_squared) export(equivalence_test) export(eta2_to_f) export(eta2_to_f2) export(eta_squared) export(eta_squared_posterior) export(f2_to_eta2) export(f_to_eta2) export(format_standardize) export(get_effectsize_label) export(get_effectsize_name) export(glass_delta) export(hedges_g) export(interpret) export(interpret_agfi) export(interpret_bf) export(interpret_cfi) export(interpret_cohens_d) export(interpret_cohens_g) export(interpret_cramers_v) export(interpret_d) export(interpret_delta) export(interpret_direction) export(interpret_epsilon_squared) export(interpret_ess) export(interpret_eta_squared) export(interpret_g) export(interpret_gfi) export(interpret_glass_delta) export(interpret_hedges_g) export(interpret_icc) export(interpret_ifi) export(interpret_kendalls_w) export(interpret_nfi) export(interpret_nnfi) export(interpret_oddsratio) export(interpret_omega_squared) export(interpret_p) export(interpret_parameters) export(interpret_pd) export(interpret_phi) export(interpret_pnfi) export(interpret_r) export(interpret_r2) export(interpret_rank_biserial) export(interpret_rfi) export(interpret_rhat) export(interpret_rmsea) export(interpret_rope) export(interpret_srmr) export(interpret_vif) export(is.rules) export(is_effectsize_name) export(kendalls_w) export(logoddsratio_to_d) export(logoddsratio_to_r) export(mad_pooled) export(normalize) export(odds_to_probs) export(oddsratio) export(oddsratio_to_d) export(oddsratio_to_r) export(oddsratio_to_riskratio) export(omega_squared) export(p_overlap) export(p_superiority) export(pearsons_c) export(phi) export(phi_to_chisq) export(probs_to_odds) export(r_to_d) export(r_to_oddsratio) export(rank_biserial) export(rank_epsilon_squared) export(ranktransform) export(rb_to_cles) export(rb_to_common_language) export(riskratio) export(riskratio_to_oddsratio) export(rules) export(sd_pooled) export(standardize) export(standardize_info) export(standardize_parameters) export(standardize_posteriors) export(t_to_d) export(t_to_epsilon2) export(t_to_eta2) export(t_to_eta2_adj) export(t_to_f) export(t_to_f2) export(t_to_omega2) export(t_to_r) export(unstandardize) export(z_to_d) export(z_to_r) importFrom(bayestestR,describe_posterior) importFrom(bayestestR,equivalence_test) importFrom(datawizard,adjust) importFrom(datawizard,change_scale) importFrom(datawizard,demean) importFrom(datawizard,normalize) importFrom(datawizard,ranktransform) importFrom(datawizard,standardize) importFrom(datawizard,unstandardize) importFrom(insight,check_if_installed) importFrom(insight,clean_names) importFrom(insight,find_formula) importFrom(insight,find_predictors) importFrom(insight,find_random) importFrom(insight,find_response) importFrom(insight,find_terms) importFrom(insight,find_weights) importFrom(insight,format_value) importFrom(insight,get_data) importFrom(insight,get_parameters) importFrom(insight,get_predictors) importFrom(insight,get_random) importFrom(insight,get_response) importFrom(insight,get_variance) importFrom(insight,get_weights) importFrom(insight,model_info) importFrom(parameters,model_parameters) importFrom(parameters,parameters_type) importFrom(performance,check_heterogeneity_bias) importFrom(stats,anova) importFrom(stats,aov) importFrom(stats,as.formula) importFrom(stats,ave) importFrom(stats,chisq.test) importFrom(stats,complete.cases) importFrom(stats,contrasts) importFrom(stats,delete.response) importFrom(stats,kruskal.test) importFrom(stats,lm) importFrom(stats,mad) importFrom(stats,median) importFrom(stats,model.frame) importFrom(stats,na.omit) importFrom(stats,optim) importFrom(stats,pchisq) importFrom(stats,pf) importFrom(stats,plogis) importFrom(stats,pnorm) importFrom(stats,prop.test) importFrom(stats,pt) importFrom(stats,qchisq) importFrom(stats,qf) importFrom(stats,qlogis) importFrom(stats,qnorm) importFrom(stats,qt) importFrom(stats,reshape) importFrom(stats,sd) importFrom(stats,setNames) importFrom(stats,terms) importFrom(stats,update) importFrom(stats,var) importFrom(stats,weighted.mean) importFrom(utils,as.roman) importFrom(utils,capture.output) importFrom(utils,head) importFrom(utils,packageVersion) importFrom(utils,tail)