Disease alleles, penetrance and population risk

Population Genetics Practical 13: From genotype frequencies to phenotype probabilities

Learning objectives

Distinguish allele frequency, heterozygote frequency and phenotype probability; apply genotype-specific penetrance; examine an inbreeding model; distinguish population averages from known-parent crosses; and identify why phenotype prevalence alone may not identify an allele frequency.

This practical follows population genetics in medicine, building on HWE and structure and inbreeding. Every parameter is hypothetical and describes an invented single-locus phenotype. The examples use base R and are not estimates for a named disease or an individual.

1. Define the allele and the observation window

Let a have frequency \(q\) and A frequency \(p=1-q\) at an autosomal diploid locus. Under random union, genotype probabilities are \(p^2,2pq,q^2\) for AA, Aa and aa.

For this example, call Aa a heterozygous carrier of a. An aa individual has two copies and is reported separately. Keep the counted allele and genotype labels explicit.

Show R code
q <- 0.02
genotype_frequencies <- function(q,F=0) {
  stopifnot(length(q)==1,is.finite(q),q>=0,q<=1,
            length(F)==1,is.finite(F),F>=0,F<=1)
  p <- 1-q
  c(AA=p^2+F*p*q,Aa=2*p*q*(1-F),aa=q^2+F*p*q)
}
freq <- genotype_frequencies(q)
stopifnot(abs(sum(freq)-1)<1e-12,
          max(abs(freq-c(0.9604,0.0392,0.0004)))<1e-12)
data.frame(genotype=names(freq),probability=unname(freq),
           expected_in_100000=100000*unname(freq))
  genotype probability expected_in_100000
1       AA      0.9604              96040
2       Aa      0.0392               3920
3       aa      0.0004                 40

The expected counts are averages over hypothetical samples; they do not require every sample to contain exactly these numbers.

Define \(f_g=P(Y=1\mid G=g)\) as the probability of the specified phenotype by a declared age or observation window. These genotype-specific probabilities are the penetrance inputs. They describe whether the phenotype occurs, rather than its severity. MedlinePlus explains reduced penetrance and variable expressivity.

2. Average genotype-specific phenotype probabilities

The population phenotype probability is

\[K=P(Y=1)=\sum_g P(G=g)f_g.\]

In a fully penetrant, purely recessive model, \(f_{AA}=f_{Aa}=0\) and \(f_{aa}=1\). Only with these assumptions and HWE does \(K=q^2\).

Show R code
full_recessive <- c(AA=0,Aa=0,aa=1)
incomplete <- c(AA=0,Aa=0,aa=0.8)
background <- c(AA=0.001,Aa=0.001,aa=0.8)
phenotype_probability <- function(freq,penetrance) {
  stopifnot(identical(names(freq),names(penetrance)),
            all(is.finite(freq)),all(freq>=0),abs(sum(freq)-1)<1e-12,
            all(is.finite(penetrance)),all(penetrance>=0),all(penetrance<=1))
  sum(freq*penetrance)
}
models <- list(Full_recessive=full_recessive,Incomplete=incomplete,
               Background=background)
K <- vapply(models,function(f) phenotype_probability(freq,f),numeric(1))
stopifnot(max(abs(K-c(0.0004,0.00032,0.0013196)))<1e-12)
K
Full_recessive     Incomplete     Background 
     0.0004000      0.0003200      0.0013196 

The background model allows the same phenotype in people without aa through causes outside this locus. It does not classify those causes or assign a clinical diagnosis. Its parameters must refer to the same observation window and sampling population.

Question. Does an aa genotype guarantee the phenotype in the incomplete model?

No. Its supplied phenotype probability is 0.8. Conversely, in the background model the phenotype can occur without aa. Genotype, phenotype and inferred cause are distinct.

3. Inbreeding changes genotype frequencies

Under the inbreeding model on a specified ancestral base,

\[P(AA)=p^2+Fpq,\quad P(Aa)=2pq(1-F),\quad P(aa)=q^2+Fpq.\]

This preserves allele frequency while shifting heterozygote probability into both homozygote classes. Here \(F\) is a supplied model parameter, not inferred from phenotype prevalence or a heterozygosity deficit.

Show R code
F_values <- c(0,0.01,0.0625)
comparison <- do.call(rbind,lapply(F_values,function(F) {
  g <- genotype_frequencies(q,F)
  stopifnot(abs(g["aa"]+g["Aa"]/2-q)<1e-12)
  data.frame(F=F,heterozygote=unname(g["Aa"]),aa=unname(g["aa"]),
             phenotype=phenotype_probability(g,background))
}))
comparison
       F heterozygote       aa   phenotype
1 0.0000     0.039200 0.000400 0.001319600
2 0.0100     0.038808 0.000596 0.001476204
3 0.0625     0.036750 0.001625 0.002298375
Show R code
F_grid <- seq(0,0.1,length.out=100)
plot(F_grid,vapply(F_grid,function(F) {
  phenotype_probability(genotype_frequencies(q,F),background)
},numeric(1)),type="l",lwd=2,col="#1b9e77",
     xlab="Supplied inbreeding coefficient F",ylab="Population phenotype probability")

For this particular penetrance vector, the phenotype probability increases with F. More generally,

\[K(F)-K(0)=Fpq(f_{AA}-2f_{Aa}+f_{aa}).\]

The direction depends on genotype-specific phenotype probabilities; it is not universally positive. This one-generation model does not simulate selection, purging, demography or changes in q.

Show R code
g_F <- genotype_frequencies(q,0.0625)
change <- phenotype_probability(g_F,background)-K["Background"]
expected_change <- 0.0625*q*(1-q)*
  (background["AA"]-2*background["Aa"]+background["aa"])
stopifnot(abs(change-expected_change)<1e-12)

4. Condition on known parents

A population-average probability does not replace a probability conditional on known parental genotypes. For Aa by Aa, each parent transmits a with probability 1/2. Mendelian transmission gives AA, Aa and aa probabilities 1/4, 1/2 and 1/4.

Show R code
cross_probabilities <- function(sire_dosage,dam_dosage) {
  stopifnot(length(sire_dosage)==1,length(dam_dosage)==1,
            sire_dosage %in% 0:2,dam_dosage %in% 0:2)
  ps <- sire_dosage/2
  pd <- dam_dosage/2
  c(AA=(1-ps)*(1-pd),Aa=ps*(1-pd)+(1-ps)*pd,aa=ps*pd)
}
carrier_cross <- cross_probabilities(1,1)
stopifnot(max(abs(carrier_cross-c(0.25,0.5,0.25)))<1e-12)
data.frame(genotype=names(carrier_cross),probability=unname(carrier_cross))
  genotype probability
1       AA        0.25
2       Aa        0.50
3       aa        0.25
Show R code
c(full_recessive=phenotype_probability(carrier_cross,full_recessive),
  incomplete=phenotype_probability(carrier_cross,incomplete),
  background=phenotype_probability(carrier_cross,background))
full_recessive     incomplete     background 
       0.25000        0.20000        0.20075 

For this cross, aa probability is 0.25 while phenotype probability in the incomplete model is 0.20. These are per-offspring probabilities under the declared model. The cross calculation needs the parental genotypes rather than the population q; no HWE assumption is needed once those genotypes are known.

5. Reverse conditioning is a different calculation

In the background population model, Bayes’ rule gives

\[P(G=g\mid Y=1)=\frac{P(G=g)f_g}{K}.\]

Show R code
posterior_genotype <- freq*background/K["Background"]
stopifnot(abs(sum(posterior_genotype)-1)<1e-12)
data.frame(genotype=names(freq),given_phenotype=unname(posterior_genotype))
  genotype given_phenotype
1       AA      0.72779630
2       Aa      0.02970597
3       aa      0.24249773
Show R code
c(phenotype_given_aa=background["aa"],aa_given_phenotype=posterior_genotype["aa"])
phenotype_given_aa.aa aa_given_phenotype.aa 
            0.8000000             0.2424977 

Although aa has high phenotype probability, many phenotype-positive individuals in this toy population have another genotype because those genotypes are much more common. \(P(Y=1\mid aa)\) and \(P(aa\mid Y=1)\) answer different questions.

This calculation presumes that all probabilities and the population sampling scheme are appropriate. A phenotype-ascertained sample has a different genotype distribution from a random population sample; its allele frequency cannot be treated as the population frequency without accounting for sampling.

6. Can phenotype prevalence identify q?

Only under full recessive penetrance, zero background probability and HWE does \(q=\sqrt K\). If aa penetrance is known to be f with zero background, \(q=\sqrt{K/f}\) instead. If F is nonzero, even the fully penetrant expression becomes \(K=q^2+Fq(1-q)\).

Show R code
observed_K <- 0.0004
penetrances <- c(1,0.8,0.5)
q_scenarios <- sqrt(observed_K/penetrances)
data.frame(supplied_aa_penetrance=penetrances,q=q_scenarios,
           check_K=penetrances*q_scenarios^2)
  supplied_aa_penetrance          q check_K
1                    1.0 0.02000000   4e-04
2                    0.8 0.02236068   4e-04
3                    0.5 0.02828427   4e-04
Show R code
naive_q_background <- sqrt(K["Background"])
c(true_supplied_q=q,naive_sqrt_prevalence=naive_q_background)
                 true_supplied_q naive_sqrt_prevalence.Background 
                       0.0200000                        0.0363263 

Several combinations of q and penetrance produce the same K. The naive square-root inversion in the background example overestimates the supplied q. Unknown background probability or population structure adds further ambiguity. Observing prevalence alone does not identify all these quantities.

Exercises

  1. Recalculate heterozygote and aa frequencies at q=0.01 under HWE.
  2. Compute genotype and phenotype probabilities for AA by Aa using the background model.
  3. Give a penetrance vector for which increasing F lowers phenotype probability.
  4. State the assumptions needed to infer q as the square root of prevalence.
Show R code
genotype_frequencies(0.01)
    AA     Aa     aa 
0.9801 0.0198 0.0001 
Show R code
cross <- cross_probabilities(0,1)
cross
 AA  Aa  aa 
0.5 0.5 0.0 
Show R code
phenotype_probability(cross,background)
[1] 0.001
Show R code
heterozygote_only <- c(AA=0,Aa=1,aa=0)
c(F0=phenotype_probability(genotype_frequencies(q,0),heterozygote_only),
  F01=phenotype_probability(genotype_frequencies(q,0.1),heterozygote_only))
     F0     F01 
0.03920 0.03528 

At q=0.01, heterozygote probability is 0.0198 and aa probability is 0.0001. AA by Aa gives half AA and half Aa; its phenotype probability is 0.001 under the background model. If only heterozygotes express the phenotype, increasing F reduces its population probability. The square-root inversion requires HWE, a purely recessive fully penetrant single-locus phenotype, zero background cases and a prevalence measurement corresponding to the model’s population and observation window.

Package connections and teaching extensions

The examples illustrate genotype-frequency expectations and conditioning relevant to gpop; they do not assume an exported medical-risk API. gsim can extend known crosses to explicit inheritance simulations, while fitness effects and selection policies require separate definitions. Penetrance is a phenotype probability and is not automatically a reproductive fitness weight.

Useful extensions include an explicitly specified age-dependent phenotype model, several alleles at one locus or a sampling model for uncertain allele frequencies. Each requires additional assumptions rather than substituting a new number into q squared.