easystats / easystats/effectsize
Discrepancy in `t_to_d`
Nobody has claimed this yet.
- Dominant language
- R
- Stars
- 353
- Forks
- 25
- Avg merge
- 9h 21m
- Merged PRs (30d)
- 1
Description
Describe the bug
I was doing a comparison between the results of emmeans::eff_size() and effectsize::t_to_d(), and come up with very different results
To Reproduce
require(effectsize)
#> Loading required package: effectsize
require(emmeans)
#> Loading required package: emmeans
warp.lm = lm(breaks ~ tension, data = warpbreaks)
emm = emmeans(warp.lm, "tension")
### Using emmeans::eff_size
eff_size(emm, sigma = sigma(warp.lm), edf = df.residual(warp.lm))
#> contrast effect.size SE df lower.CL upper.CL
#> L - M 0.842 0.344 51 0.152 1.53
#> L - H 1.239 0.355 51 0.526 1.95
#> M - H 0.397 0.336 51 -0.276 1.07
#>
#> sigma used for effect sizes: 11.88
#> Confidence level used: 0.95
### Using t_to_d
t = summary(pairs(emm)) $ t.ratio
t_to_d(t, df = df.residual(warp.lm))
#> d | 95% CI
#> --------------------
#> 0.71 | [ 0.14, 1.27]
#> 1.04 | [ 0.45, 1.62]
#> 0.33 | [-0.22, 0.89]
Note that the estimated effect sizes are somewhat smaller than yielded by eff_size().
My own equivalent function
- Since d = delta / sigma (where delta is the difference of means), and t = delta / (sigma * sqrt (2 / n)), we have that d = sqrt(2 / n) * t.
- Meanwhile, df = k * (n - 1) where k is the number of groups, so that n = 1 + df / k.
- Finally, to get k, the number of t ratios is k * (k - 1) / 2, so k = (1 + sqrt(1 + 8 * L)) / 2, where L is the number of t ratios.
Hence, my own version of t_to_d(), implementing these steps backwards:
### My own equivalent (just the point estimates)
rvl_t2d = function(t, df) {
k = (1 + sqrt(1 + 8 * length(t))) / 2
n = 1 + df / k
sqrt(2 / n) * t
}
rvl_t2d(t, df.residual(warp.lm))
#> [1] 0.8417098 1.2391839 0.3974741
These results agree with the emmeans::eff_size() results.
Additional comment
My rvl_t2d() function depends on our having a balanced experiment with independent samples. Anyn such function that depends only on t ratios and df would require the same assumption (or something equally restrictive). The function emmeans::eff_size() does not require such a simplifying assumption, as it actually estimates each pairwise difference and divides it by the estimate of sigma. That's one reason I am quite sure that its results are correct. But I would think that effectsize::t_to_d() should yield the same results when those restrictive assumptions are satisfied, as they are in this illustration. I am not well-versed in the effect-size literature so I cannot comment with any authority on the formulas given in the documentation for t_to_d().
My system
R.Version()
#> $platform
#> [1] "x86_64-w64-mingw32"
#>
#> $arch
#> [1] "x86_64"
#>
#> $os
#> [1] "mingw32"
#>
#> $system
#> [1] "x86_64, mingw32"
#>
#> $status
#> [1] ""
#>
#> $major
#> [1] "4"
#>
#> $minor
#> [1] "0.3"
#>
#> $year
#> [1] "2020"
#>
#> $month
#> [1] "10"
#>
#> $day
#> [1] "10"
#>
#> $`svn rev`
#> [1] "79318"
#>
#> $language
#> [1] "R"
#>
#> $version.string
#> [1] "R version 4.0.3 (2020-10-10)"
#>
#> $nickname
#> [1] "Bunny-Wunnies Freak Out"
packageVersion("effectsize")
#> [1] '0.4.0'
packageVersion("emmeans")
#> [1] '1.5.2.10003'
Created on 2020-11-28 by the reprex package (v0.3.0)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the t_to_d() entry point and run the supplied warpbreaks, emmeans, and effectsize reproduction. Compare its point estimates and confidence intervals with emmeans::eff_size() and the reported balanced-design calculation; done means the discrepancy is explained and the expected behavior is clearly established.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- r
- Domain
- analytics
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100