# How to calculate the p.value of an odds ratio in R?

I have following table of values:

25  75
38  162


The odds ratio is 0.7037 and log(OR) is -0.3514. For a contingency table with values a, b, c, and d, the variance of log(OR) is given by

(1/a + 1/b + 1/c + 1/d)


How can I calculate the p.value of log(OR) from this data in R (whether it is significantly different from 0)?

You can use Fisher's exact test, which inputs a contingency table and outputs a p-value, with a null hypothesis that the odds ratio is 1 and an alternative hypothesis that the odds ratio is not equal to 1.

(tab <- matrix(c(38, 25, 162, 75), nrow=2))
#      [,1] [,2]
# [1,]   38  162
# [2,]   25   75
fisher.test(tab)
#
#   Fisher's Exact Test for Count Data
#
# data:  tab
# p-value = 0.2329
# alternative hypothesis: true odds ratio is not equal to 1
# 95 percent confidence interval:
#  0.3827433 1.3116294
# sample estimates:
# odds ratio
#  0.7045301


In this case the p value is 0.23.

• Thanks for a clever way to determine the p value. Chi-square test can also be used in a similar manner. – rnso Jun 13 '15 at 17:09
• @rnso for sure, though Fisher's exact test is preferred over Chi-square when you have small cell sizes in your contingency table. – josliber Jun 13 '15 at 17:16
• This is a longstanding myth but is unfortunately not true. The ordinary Pearson $\chi^2$ provides more accurate $P$-values than Fisher's so-called "exact" test even when expected frequencies are as low as 1.0. – Frank Harrell Jun 14 '15 at 12:14
• could you say a bit more about this @FrankHarrell? I know the $\chi^2$ would be an asymptotic result, whereas the Fisher's exact test relies on the exact distribution, how is the $p$-value more "accurate" using the asymptotic method? – bdeonovic Jun 15 '15 at 11:58
• See extensive comments about this on the site. Briefly, the P-values from Fisher's test are too large. The mean absolute error in P-values from Pearson's test is smaller. Fisher's is only "exact" in the sense that P-values are "guaranteed" not to be too small. – Frank Harrell Jun 16 '15 at 12:23

Another way to do it (other than Fisher's exact test) is to put the values into a binomial GLM:

d <- data.frame(g=factor(1:2),
s=c(25,75),
f=c(38,162))
g <- glm(s/(s+f)~g,weights=s+f,data=d,
family="binomial")
coef(summary(g))["g2",c("Estimate","Pr(>|z|)")]
##   Estimate   Pr(>|z|)
## -0.3513979  0.2303337


To get the likelihood ratio test (slightly more accurate than the Wald $p$-value shown above), do

anova(g,test="Chisq")


which gives

##      Df Deviance Resid. Df Resid. Dev Pr(>Chi)
## NULL                     1     1.4178
## g     1   1.4178         0     0.0000   0.2338


(LRT $p=0.2338 \approx$ Wald $p=0.2303337 \approx$ Fisher $p=0.2329$ in this case because the sample is fairly large)

It's better to generalize the solution and use the likelihood ratio $\chi^2$ test from a statistical model such as the logistic model. The LR test provides fairly accurate $P$-values. This also handles cases where you need to test more than one parameter, e.g., 3-group problems, continuous effects that are nonlinear, etc. The LR test for the overall model (which is all that's needed in this example since there are no adjustment variables) may be easily obtained in base R or using the rms package, e.g.

f <- lrm(y ~ groups, weights=freqs)
f  # prints LR chi-sq, d.f., P, many other quantities


Here the nested models are this model and an intercept-only model.

• I could find that LR test (lrtest) is used for comparing nested models. How can we use it here? Could you write a line of R code for it? – rnso Jun 15 '15 at 2:36
• for what it's worth this is more or less the same statistical approach (although with a better explanation) as in my answer above. lrm() has different defaults, output formats, etc., but the statistical model (IIUC) is the same as glm(...,family="binomial") – Ben Bolker Jun 24 '15 at 21:51