# 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, 2015 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. Jun 13, 2015 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. Jun 14, 2015 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? Jun 15, 2015 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. Jun 16, 2015 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, 2015 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") Jun 24, 2015 at 21:51