2026-10-17

Hardy-Weinberg Equilibrium

Population genetics is the field of study focused on the genetic makeup of a population and the forces that cause it to change over time and space.

One of the most important concepts in population genetics is that of Hardy-Weinberg Equilibrium, discovered in 1908. This states that the allelic and genotypic frequencies of a population will reach equilibrium according to the Hardy-Weinberg Equation under certain assumptions.

These assumptions are

  • All genotypes confer equal fitness in survival and reproduction
  • Mating is random with respect to genotype
  • Effectively infinite (very large) population size
  • Mendelian segregation
  • No evolutionary forces (mutation, migration, population subdivision, etc)

This is significant because it means that in the absence of outside forces, the frequency of each genotype (homozygous dominant, heterozygous, homozygous recessive) and thus the distribution of traits will remain the same in a population from generation to generation.

This disproved the idea that populations would lose genetic diversity as an inherent characteristic of sexual reproduction, which was prevalent at the time.

Hardy-Weinberg Equilibrium also greatly simplifies the modeling of population genetics.

Hardy-Weinberg Equation

\[ p^2 + 2pq + q^2 = 1\] \[p + q = 1\]

Say we are studying a gene in a population that has only two alleles, the dominant allele \(A\) and the recessive allele \(a\).

The first equation includes information about genotype frequency, where \(p^2\) denotes the frequency of the homozygous dominant genotype \(AA\), \(2pq\) denotes the frequency of the heterozygous genotype \(Aa\), and \(q^2\) denotes the frequency of the homozygous recessive genotype \(aa\).

The second equation includes information about allele frequency, where \(p\) is the frequency of \(A\) and \(q\) is the frequency of \(a\).

Hardy-Weinberg Proportions

Why test for Hardy-Weinberg Equilibrium?

Given the idealistic assumptions under which Hardy-Weinberg proportions hold, you may wonder whether H-W is useful at all in the analysis of real populations, or why we would test a gene in a population for Hardy-Weinberg distributions.

Actually, Hardy-Weinberg proportions hold in many populations since the effect of forces such as migration, gene flow, and natural selection too small and gradual to make a statistical difference, especially when we are only studying one gene in a population. For example, the alleles for blood type (ABO) are in Hardy-Weinberg equilibrium for the human population.

Testing for Hardy-Weinberg equilibrium is an important quality control measure in most population genetics studies because data inconsistent with Hardy-Weinberg equilibrium can be indicative of genotyping errors in the lab, poor sampling, or the effect of a large evolutionary force.

Chi-Square Test

A common test for Hardy-Weinberg equilibrium is the Chi-Square test, which checks for goodness-of-fit by using \(\chi^2\) as a measure of a population’s deviance from Hardy-Weinberg proportions.

\[ \chi^2 = \Sigma \frac{(Obs-Exp)^2}{Exp} \]

Obs- Observed count of individuals with \(x\) genotype

Exp- Expected count of individuals with \(x\) genotype according to Hardy-Weinberg equilibrium

For example, a gene with two alleles would have the test statistic:

\[ \chi^2 = \frac{(f_{obs}(AA)-f_{HW}(AA))^2}{f_{HW}(AA)} + \frac{(f_{obs}(Aa)-f_{HW}(Aa))^2}{f_{HW}(Aa)} + \frac{(f_{obs}(aa)-f_{HW}(aa))^2}{f_{HW}(aa)} \]

Chi-Square Test (Ctd)

The test statistic \(\chi^2\) is then used to reject or fail to reject the null hypothesis, i.e. that the population is in Hardy-Weinberg equilibrium. If the test statistic is greater than the critical value corresponding to the degrees of freedom and chosen \(\alpha\), then we say the population is not in Hardy-Weinberg equilibrium.

Example done manually

The Glyoxalase dataset gives the observed genotype frequencies for the biallelic glyoxalase gene for 17 populations from India. We will first test whether the Marathi population in India is in Hardy-Weinberg equilibrium for the glyoxalase gene.

Count of individuals with each genotype

##         AA  AB  BB
## Marathi 29 168 254

Calculating allele frequency from observed counts

## [1] 0.2505543 0.7494457

Expected counts according to HWE

## [1]  28.31264 169.37472 253.31264

For our hypothesis test we will use the standard significance value of \(\alpha = 0.05\). For a biallelic gene, degrees of freedom will be \[ DOF = # categories - # estimated parameters - 1\] \[= 3 - 1 - 1 \] \[= 1\]

Calculating the test statistic \[ \chi^2 = \frac{(f_{obs}(AA)-f_{HW}(AA))^2}{f_{HW}(AA)} + \frac{(f_{obs}(Aa)-f_{HW}(Aa))^2}{f_{HW}(Aa)} + \frac{(f_{obs}(aa)-f_{HW}(aa))^2}{f_{HW}(aa)} \] \[ = \frac{(29-28.31)^2}{28.31} + \frac{(168-169.4)^2}{169.4} + \frac{(254-253.3)^2}{253.3} \] \[ = 0.0303\]

## Warning: Removed 375 rows containing non-finite outside the scale range
## (`stat_align()`).
## Warning: Removed 375 rows containing missing values or values outside the scale range
## (`geom_line()`).

Comparing our \(\chi^2=0.0303\) to the critical value of 3.841, we fail to reject the null hypothesis and say that there is evidence that the Marathi population is in HWE for the glyoxalase gene.

Example using ‘HardyWeinberg’ package in R

We will use the HWChisqStats function in R’s HardyWeinberg package to test all 17 populations to see whether they are in HWE.

populations = rownames(Glyoxalase)

hwe_results = lapply(seq_len(nrow(Glyoxalase)), function(i){
  counts = as.numeric(Glyoxalase[i,])
  names(counts) = colnames(Glyoxalase)
  
  test = HWChisq(
    counts,
    cc=0,
    verbose = F
  )
  
  data.frame(
    Population = populations[i],
    AA = counts[1],
    AB = counts[2],
    BB = counts[3],
    ChiSquare = test$chisq,
    P_value = test$pval
  )
})

hwe_results = do.call(rbind, hwe_results)

fig <- plot_ly(
  x = sprintf("P%02d", seq_len(nrow(hwe_results))),
  y = "Glyoxalase",
  
  z = matrix(
    hwe_results$P_value,
    nrow = 1
  ),
  
  type = "heatmap",
  
  zmin = 0,
  zmax = 1,
  
  colorscale = "Viridis",
  
  text = matrix(
    paste0(
      "<b>", hwe_results$Population, "</b>",
      "<br>AA: ", hwe_results$AA,
      "<br>AB: ", hwe_results$AB,
      "<br>BB: ", hwe_results$BB,
      "<br>χ²: ", round(hwe_results$ChiSquare, 4),
      "<br>p-value: ", signif(hwe_results$P_value, 4)
    ),
    nrow = 1
  ),
  
  hoverinfo = "text",
  
  colorbar = list(
    title = "p-value"
  )
)

fig

This heatmap shows the \(\chi^2\) and \(p\) values for each of the 17 Indian populations, with darker colors showing lower p-values. Only population 15, Naicker, shows a p value below 0.05, indicating that we should reject the null hypothesis and the gene is not in HWE for that population.

Note on AI usage

All informational slide content was researched and written by me. An AI chatbot was used to clarify some topics before I synthesized and rewrote the information.

The graphics and animation were brainstormed and designed by me, but generative AI was used to create the code for these elements. All other code was written by me.

Bibliography

What is HWE

Population Genetics: https://plato.stanford.edu/entries/population-genetics/#HarWeiPri

Population Genetics: https://www.sciencedirect.com/topics/neuroscience/population-genetics

Why test for HW

The importance of examining the Hardy-Weinberg Equilibrium in genetic association studies: https://pmc.ncbi.nlm.nih.gov/articles/PMC10644312/

Assessing departure from Hardy-Weinberg equilibrium in the presence of disease association: https://pubmed.ncbi.nlm.nih.gov/18449919/

Pop gen R packages

learnPopGen: An R package for population genetic simulation and numerical analysis: https://pmc.ncbi.nlm.nih.gov/articles/PMC6662327/

Exploring Diallelic Genetic Markers: The HardyWeinberg Package: https://www.jstatsoft.org/index.php/jss/article/view/v064i03/844

Package ‘HardyWeinberg’: https://cran.r-project.org/web/packages/HardyWeinberg/HardyWeinberg.pdf

Population genetics and genomics in R: https://grunwaldlab.github.io/Population_Genetics_in_R/Locus_Stats.html

Basic statistics

Degrees of Freedom (Explained Simply): https://medium.com/@dlectus/degrees-of-freedom-simply-explained-a96cafa3b39f

How to use chi-square to test for Hardy-Weinberg equilibrium: https://www.biologysimulations.com/post/how-to-use-chi-squared-to-test-for-hardy-weinberg-equilibrium

Chi-Square Distribution: https://stattrek.com/probability-distributions/chi-square

Chi-Square Test in R: A Complete Guide: https://www.datacamp.com/tutorial/chi-square-test-r

R code stuff

External graphics with knitr: https://www.jumpingrivers.com/blog/knitr-include-graphics-external/

Relative paths: https://www.lenovo.com/us/en/glossary/relative-path/