library(gstudio)
library(dplyr)
library(ggplot2)
data(arapat)4 Allele Frequencies & HWE
This chapter covers estimating allele and genotype frequencies, testing for Hardy-Weinberg Equilibrium, and rarefaction analysis.
4.1 Allele Frequencies
The frequencies() function is an S3 generic that works on both individual locus vectors and entire data.frames.
4.1.1 Single Locus
arapat |>
pull(LTRS) |>
frequencies() Allele Frequency
1 01 0.523416
2 02 0.476584
4.1.2 Entire Data Frame
When applied to a data.frame, frequencies() returns frequencies for all locus columns:
freqs <- arapat |>
frequencies()
freqs |>
head(12) Locus Allele Frequency
17 AML 01 0.001470588
18 AML 02 0.001470588
19 AML 03 0.019117647
20 AML 04 0.126470588
21 AML 05 0.070588235
22 AML 06 0.072058824
23 AML 07 0.235294118
24 AML 08 0.255882353
25 AML 09 0.145588235
26 AML 10 0.014705882
27 AML 11 0.039705882
28 AML 12 0.016176471
4.1.3 By Stratum
Add a stratum argument to partition frequencies by population:
freqs_pop <- arapat |>
frequencies(stratum = "Species")
freqs_pop |>
head(12) Stratum Locus Allele Frequency
1 Cape AML 01 0.0000000
2 Cape AML 02 0.0000000
3 Cape AML 03 0.0915493
4 Cape AML 04 0.6056338
5 Cape AML 05 0.3028169
6 Cape AML 06 0.0000000
7 Cape AML 07 0.0000000
8 Cape AML 08 0.0000000
9 Cape AML 09 0.0000000
10 Cape AML 10 0.0000000
11 Cape AML 11 0.0000000
12 Cape AML 12 0.0000000
Because the result is a tidy data frame, dplyr summaries follow directly, for example the most common allele at each locus in each species:
freqs_pop |>
group_by(Stratum, Locus) |>
slice_max(Frequency, n = 1, with_ties = FALSE) |>
filter(Locus %in% c("LTRS", "WNT"))# A tibble: 6 × 4
# Groups: Stratum, Locus [6]
Stratum Locus Allele Frequency
<chr> <chr> <chr> <dbl>
1 Cape LTRS 02 0.92
2 Cape WNT 02 0.865
3 Mainland LTRS 02 0.736
4 Mainland WNT 01 0.983
5 Peninsula LTRS 01 0.692
6 Peninsula WNT 03 0.609
4.1.4 Selecting Specific Loci
arapat |>
frequencies(loci = c("LTRS", "WNT")) Locus Allele Frequency
1 LTRS 01 0.523415978
2 LTRS 02 0.476584022
3 WNT 01 0.357954545
4 WNT 02 0.181818182
5 WNT 03 0.430397727
6 WNT 04 0.026988636
7 WNT 05 0.002840909
4.2 Frequency Matrix
The frequency_matrix() function produces a wide-format matrix of allele frequencies:
fm <- arapat |>
frequency_matrix(stratum = "Species")
fm[, 1:8] Stratum AML-01 AML-02 AML-03 AML-04 AML-05 AML-06
1 Cape 0.000000000 0.000000000 0.0915493 0.6056338 0.30281690 0.00000000
2 Mainland 0.000000000 0.000000000 0.0000000 0.0000000 0.00000000 0.00000000
3 Peninsula 0.002016129 0.002016129 0.0000000 0.0000000 0.01008065 0.09879032
AML-07
1 0.0000000
2 0.0000000
3 0.3225806
4.3 Genotype Counts and Frequencies
4.3.1 Genotype Counts per Stratum
The genotype_counts() function summarizes non-missing genotype counts per locus, optionally by stratum:
arapat |>
genotype_counts() Stratum N LTRS WNT EN EF ZMP AML ATPS MP20
ALL ALL 363 363 352 360 361 330 340 363 358
arapat |>
genotype_counts(stratum = "Species") Stratum N LTRS WNT EN EF ZMP AML ATPS MP20
Cape Cape 75 75 74 75 75 70 71 75 75
Mainland Mainland 36 36 30 35 34 20 21 36 33
Peninsula Peninsula 252 252 248 250 252 240 248 252 250
4.3.2 Genotype Frequencies
The genotype_frequencies() function returns observed and expected genotype counts for a locus vector:
arapat |>
pull(LTRS) |>
genotype_frequencies() Genotype Observed Expected
1 01:01 147 99.44904
2 01:02 86 181.10193
3 02:02 130 82.44904
4.4 Hardy-Weinberg Equilibrium
The hwe() function tests for HWE using a chi-square approximation:
arapat |>
filter(Species == "Mainland") |>
hwe()Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for
LTRS this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at LTRS consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for WNT
this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at WNT consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for EN
this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at EN consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for EF
this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at EF consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for ZMP
this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at ZMP consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for AML
this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at AML consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for
ATPS this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at ATPS consider collapsing alleles.
Warning in genotype_frequencies(x[[locus]]): Some genotype expectations are <
5, a continuity correction should be applied. See ?hwe
Warning in hwe(filter(arapat, Species == "Mainland")): Under 50 samples for
MP20 this may not be a good approximation.
Warning in hwe(filter(arapat, Species == "Mainland")): Fewer than 5 genotypes
expected at MP20 consider collapsing alleles.
Locus statistic df p.value
1 LTRS 0.9082806 1 3.405710e-01
2 WNT 29.2542373 1 6.347726e-08
3 EN 19.7817078 3 1.883729e-04
4 EF 0.3545808 1 5.515314e-01
5 ZMP 5.0927978 1 2.402540e-02
6 AML 36.8802692 6 1.858093e-06
7 ATPS 43.9698647 6 7.494505e-08
8 MP20 161.5565210 45 4.773959e-15
The output includes the chi-square statistic, degrees of freedom, and p-value for each locus. Significant p-values indicate departures from HWE expectations.
4.4.1 Interpreting Results
Departures from HWE can arise from:
- Non-random mating (inbreeding, assortative mating)
- Population substructure (Wahlund effect)
- Selection at or near the marker locus
- Small population size (genetic drift)
4.5 Rarefaction
The rarefaction() function subsamples the data repeatedly at a smaller sample size to estimate the distribution of a diversity statistic:
rare_vals <- arapat |>
pull(LTRS) |>
rarefaction(mode = "Ae", size = 20, nperm = 199)
data.frame(Ae = rare_vals) |>
ggplot(aes(Ae)) +
geom_histogram(bins = 20, fill = "steelblue") +
labs(title = "Rarefied Ae at LTRS", x = "Effective alleles", y = "Count") +
theme_minimal()
This is useful for comparing diversity across populations with unequal sample sizes.
4.6 Optimal Sampling
The optimal_sampling() function estimates how many individuals are needed to capture a given proportion of allelic diversity:
arapat |>
pull(LTRS) |>
optimal_sampling(nrep = 99)4.7 Without Pipes
frequencies(arapat$LTRS)
frequencies(arapat, stratum = "Species")
frequencies(arapat, loci = c("LTRS", "WNT"))
frequency_matrix(arapat, stratum = "Species")
genotype_counts(arapat, stratum = "Species")
genotype_frequencies(arapat$LTRS)
hwe(arapat[arapat$Species == "Mainland", ])
rare_vals <- rarefaction(arapat$LTRS, mode = "Ae", size = 20, nperm = 199)
hist(rare_vals)
optimal_sampling(arapat$LTRS, nrep = 99)