I need to calculate cohen's d to determine the effect size of an experiment. Is there any implementation in a sound library I could use? If not, what would be a good implementation?
24 Answers
The above implementation is correct in the special case that the two groups have equal size. A more general solution based on the formulas found at Wikipedia and in Robert Coe's article is the 2nd method shown below. Be aware that the denominator is the pooled standard deviation which is generally only appropriate if the population standard deviation is equal for both groups:
from numpy import std, mean, sqrt #correct if the population S.D. is expected to be equal for the two groups. def cohen_d(x,y): nx = len(x) ny = len(y) dof = nx + ny - 2 return (mean(x) - mean(y)) / sqrt(((nx-1)*std(x, ddof=1) ** 2 + (ny-1)*std(y, ddof=1) ** 2) / dof) #dummy data x = [2,4,7,3,7,35,8,9] y = [i*2 for i in x] # extra element so that two group sizes are not equal. x.append(10) #correct only if nx=ny d = (mean(x) - mean(y)) / sqrt((std(x, ddof=1) ** 2 + std(y, ddof=1) ** 2) / 2.0) print ("d by the 1st method = " + str(d)) if (len(x) != len(y)): print("The first method is incorrect because nx is not equal to ny.") #correct for more general case including nx !=ny print ("d by the more general 2nd method = " + str(cohen_d(x,y))) Output will be:
d by the 1st method = -0.559662109472 The first method is incorrect because nx is not equal to ny. d by the more general 2nd method = -0.572015604666
5Since Python3.4, you can use the statistics module for calculating spread and average metrics. With that, Cohen's d can be calculated easily:
from statistics import mean, stdev from math import sqrt # test conditions c0 = [2, 4, 7, 3, 7, 35, 8, 9] c1 = [i * 2 for i in c0] cohens_d = (mean(c0) - mean(c1)) / (sqrt((stdev(c0) ** 2 + stdev(c1) ** 2) / 2)) print(cohens_d) Output:
-0.5567679522645598 So we observe a medium effect.
In Python 2.7, you can use numpy with a couple of caveats, as I discovered while adapting Bengt's answer from Python 3.4.
- Ensure division always returns float with:
from __future__ import division - Specify the division argument on the variance with
ddof=1into thestdfunction , i.e.numpy.std(c0, ddof=1). numpy's standard deviation default behaviour is to divide byn, whereas withddof=1it will divide byn-1.
Code
from __future__ import division #Ensure division returns float from numpy import mean, std # version >= 1.7.1 && <= 1.9.1 from math import sqrt import sys def cohen_d(x,y): return (mean(x) - mean(y)) / sqrt((std(x, ddof=1) ** 2 + std(y, ddof=1) ** 2) / 2.0) if __name__ == "__main__": # test conditions c0 = [2, 4, 7, 3, 7, 35, 8, 9] c1 = [i * 2 for i in c0] print(cohen_d(c0,c1)) Output will then be:
-0.556767952265 3If anyone has trouble with any of the above answers, I've written a function for Python 3.7 that takes a Pandas Series and returns Cohen's d:
from numpy import var, mean, sqrt from pandas import Series def cohend(d1: Series, d2: Series) -> float: # calculate the size of samples n1, n2 = len(d1), len(d2) # calculate the variance of the samples s1, s2 = var(d1, ddof=1), var(d2, ddof=1) # calculate the pooled standard deviation s = sqrt(((n1 - 1) * s1 + (n2 - 1) * s2) / (n1 + n2 - 2)) # calculate the means of the samples u1, u2 = mean(d1), mean(d2) # return the effect size return (u1 - u2) / s