banner

Workshop 6 - Training course in data analysis for genomic surveillance of African malaria vectors


Module 4 - AnoPrimer - Primer Design in Anopheles gambiae#

Theme: Tools & technology

In the previous modules of workshop 6, we learnt how to discover candidate loci for insecticide resistance, using genome-wide selection scans (GWSS). In this module, we introduce a new python package AnoPrimer, which uses the malariagen_data API and primer3-py, to allow us to design primers and probes in Anopheles gambiae s.l. We can use them to design these primers and probes to help us validate putative resistance loci. By integrating with data from MalariaGEN, AnoPrimer allows us to consider and avoid genetic variation in the Ag1000G resource during the design of our primers.

Learning objectives#

At the end of this module you will be able to:

  • Describe the principles of PCR, qPCR, and the role of primers.

  • Describe some of the applications of PCR in vector control.

  • Explain why it is important to consider SNPs when designing primers.

  • Design genomic DNA primers to target the ace1-G280S mutation.

  • Design RT-qPCR primers to measure gene expression of the carboxylesterase, COEAE2F.

Lecture#

English#

Français#

Please note that the code in the cells below might differ from that shown in the video. This can happen because Python packages and their dependencies change due to updates, necessitating tweaks to the code.

Introduction#

What is the polymerase chain reaction (PCR)?#

PCR is a technique used to “amplify” small segments of DNA. Using PCR it is possible to generate thousands to millions of copies of a particular section of DNA from a very small input amount. In the context of vector control, we can use PCR and qPCR to genotype vectors for specific alleles, determine species, identify bloodmeals, measure gene expression, and much more.

Ingredients

  • The DNA template to be copied

  • DNA nucleotide bases (also known as dNTPs). DNA bases (A, C, G and T) are the building blocks of DNA and are needed to construct the new strand of DNA

  • Taq polymerase enzyme to add in the new DNA bases

  • Buffer to ensure the right conditions for the reaction

  • Primers, short stretches of DNA that initiate the PCR reaction, designed to bind to either side of the section of DNA you want to copy. DNA polymerase enzymes which perform DNA replication are only capable of adding nucleotides to the 3’-end of an existing nucleic acid, requiring a primer be bound to the template before DNA polymerase can begin a complementary strand.

pcr

The stages of PCR:

pcr

Denaturation – Heat the reaction to ~95°C to break apart the double stranded DNA template into single strands.

denaturing

Annealing – Lower the temperature to 50-56°C enable the DNA primers to attach to the single-stranded template DNA.

denaturing

Extension – Raise the temperature to 72°C and the new strand of DNA is synthesised by the Taq polymerase enzyme.

denaturing

Repeat entire process for 25-40 cycles. After each cycle, the number of DNA molecules will approximately double.

Standard PCR (Detection / Genotyping / Sequencing)#

In standard PCR, we amplify specific regions of the genome and utilise the PCR product at the end of the reaction. This could be an endpoint PCR, where we run the PCR product on an agarose gel to determine the size of the amplicon. Alternatively, standard PCR can be used to amplify specific regions of the genome prior to next-generation sequencing (amplicon sequencing).

denaturing

Example applications in An. gambiae#

  • The SINE PCR species ID assay for An. gambiae s.l, which can differentiate between the members of the gambiae complex, based on a marker on the X chromosome [1]

  • The 2La PCR assay, which determines the karyotype of the 2la inversion. [2]

  • ANOSPP, an amplicon sequencing panel which determines species within the entire Anopheles genus [3]

Quantitative PCR (Detection / Genotyping / Genotyping)#

In quantitative PCR, the concept is the same as standard PCR, but we measure the amount of DNA in the reaction at each cycle. To do this, we use either a fluorescent dye or fluorescent hybridisation probes, which emit light as the DNA concentration increases. The number of cycles at which each sample passes a given threshold, is called the Cq or Ct value. By measuring fluorescense and determining Cq values, we can determine the amount of DNA template that was in the original sample.

pcr

SYBR green#

SYBR green is a fluorescent dye, which emits a fluorescent signal when bound to double stranded DNA.

sybr

Example applications in An. gambiae#

  • Standard RT-qPCR assays for measuring gene expression

  • The SINE melt curve assay species ID for An. gambiae s.l [4], a high throughput version of the SINE PCR assay.

TaqMan / LNA Probe#

Hybridisation probes are short sequences, like primers, which bind to the DNA template. These can have flourophores attached, which emit fluorescence when the probe is displaced from the DNA template. By designing multiple probes (with different fluorophores) which are specific to either the wild-type or mutant allele, we can genotype SNPs. TaqMan and Locked nucleic acid probes have modifications which increase the stability of the probe-template duplex and help probes to discriminate between SNPs.

pcr

Example applications in An. gambiae#

  • TaqMan genotyping assays for Ace1, KDR.

  • Kdr LNA assay [5], which simultaneously genotypes the vgsc-995F and vgsc-995S mutations.

What is important to consider when designing primers?#

Suitable primers are crucial to effective PCR reactions and must be designed to be robust, reliable and consistent across experimental conditions. Primers are typically designed with the following characteristics:

  • Size: Size of the primer.

    • Between 17-24 bases long

    • This can vary depending on the application. Often, TaqMan and LNA probes are shorter, as it helps to discriminate between SNPs.

  • Tm: the temperature at which the primer duplex dissociates into single strands.

    • A Melting temperature of 59–64°C, with an ideal temperature of 62°C, which is based on typical PCR conditions and the optimum temperature for PCR enzymes.

  • GC content

    • GC content between 35–70%, with an ideal content of ~50%.

    • Should not contain regions of 4 or more consecutive G residues.

Primers should also be free of strong secondary structures and self-complementarity. Primer design algorithms, such as primer3, will take these considerations into account.

What happens if there are SNPs in primer binding sites?#

Single nucleotide polymorphisms (SNPs) in primer binding sites can affect both the stability and Tm of the primer-template duplex, as well as the efficiency with which DNA polymerases can extend the primer (Figure 2). In some cases, this can completely prevent primer binding and amplification of the template DNA, often referred to as null alleles. Null alleles can become particular troublesome when performing PCR on pooled samples, where we may not observe whether all samples amplified successfully, and so we may not be sampling and observing the full range of alleles.

An equally problematic scenario may occur if primers bind but with unequal efficiency against different genetic variants. In this case, in any quantitative molecular assay such as qPCR for gene expression, SNPs could lead to biases in the estimation of gene expression between genetic variants or strains. Even single SNPs can introduce a variety of effects, ranging from minor to major impacts on Cq values [6]. The effect will depend on the type of SNP (which nucleotides are involved), and on the position of the SNP (3’ or 5’ end), as SNPs within the last 5 nucleotides at the 3’ end can disrupt the nearby polymerase active site, and so these tend to have a much greater impact [7]. SNPs at the terminal 3’ base had the strongest shift of Cq, altering Cq by as much as 5–7 cycles (Figure 2).

mismatches2
Figure 2: Mismatches at the 3’ end of primers substantially reduce qPCR performance [6].

To maximise the accuracy of our data we should therefore aim to either design primers that avoid SNPs completely or that contain a mix of bases (degenerate) at the sites of SNPs. There is a useful article on this topic on the IDT website - Consider SNPs when designing PCR and qPCR assays.

AnoPrimer#

As we have seen in earlier workshops, the Ag1000G resource [8] has revealed extreme amounts of genetic variation in Anopheles gambiae s.l. You can find a SNP in less than every 2 bases of the accessible genome - which makes considering SNPs even more important when designing molecular assays. However, it was not previously straightforward to consider genetic variation when designing primers, and so the vast majority of primers currently in use did not consider SNP variation during their design.

Primer3 is the most widely cited program for primer design, and has been used extensively over the past two decades [9]. It is also the primer design engine behind Primer-BLAST [10], a web-server which designs primers and then blasts the primers to check for specificity. Thanks to the primer3-py python package and the malariagen_data API, it is now possible to design primers in the cloud with google colab, considering SNP variation.

Setup#

Install and import the packages we’ll need.

%pip install -q --no-warn-conflicts malariagen_data AnoPrimer primer3-py kaleido gget seaborn
Note: you may need to restart the kernel to use updated packages.

You might see a warning message looking something like:

Tue May 14 04:41:51 2024 WARNING No project ID could be determined. Consider running 'gcloud config set project' or setting the GOOGLE_CLOUD_PROJECT environment variable

This can safely be ignored.

# Import libraries 
import malariagen_data
import pandas as pd
import primer3
import AnoPrimer
import gget

#configure plotting with matplotlib
%matplotlib inline
%config InlineBackend.figure_format = "retina"
Wed May 22 06:52:06 2024 WARNING No project ID could be determined. Consider running `gcloud config set project` or setting the GOOGLE_CLOUD_PROJECT environment variable
Wed May 22 06:52:07 2024 WARNING No project ID could be determined. Consider running `gcloud config set project` or setting the GOOGLE_CLOUD_PROJECT environment variable

Worked example: Designing primers for the ace1-280S mutation#

In the rest of this module, we will design primers and probes for the ace1-280S mutation (previously ace1-119S). This mutation is known to be important in resistance to organophosphates and carbamates, insecticides which are widely used in indoor residual spraying (IRS). The resistance mutation has spread throughout much of west and central Africa, including introgressing from An. gambiae to An. coluzzii, and is often found on the background of large duplications which often pair wild-type and mutant alleles [11]. We see very large signals of selection at this locus in many contemporary populations of An. gambiae.

Selecting primer parameters#

In the below cells, replace the values with those desired for your primers.

#@title **Primer parameters** { run: "auto" }

# N.B., this cell will be rendered as a form when running on colab

assay_type = "gDNA primers + probe"  #@param ["gDNA primers", "gDNA primers + probe", "probe", "cDNA primers"]
assay_name = 'ace1-280s'  #@param {type:"string"}
min_amplicon_size = 60  #@param {type:"integer"}
max_amplicon_size = 120  #@param {type:"integer"}
amplicon_size_range = [[min_amplicon_size, max_amplicon_size]]
n_primer_pairs = 6  #@param {type:"slider", min:1, max:20, step:1}

#@markdown    
#@markdown target_loc is required for gDNA primers and probes, and transcript required for qPCR primers.

contig =  "2R"  #@param ['2L', '2R', '3L', '3R', 'X']
target_loc =  '3492074'  #@param {type:"string"}
target_loc = int(target_loc)
transcript =  ''  #@param {type:"string"} 

if any(item in assay_type for item in ['gDNA', 'probe']):
  assert target_loc > 0, "Target location must be above 0 and less than the contig length"
elif assay_type == 'cDNA primers':
  assert len(transcript) > 2, "Transcript ID is not valid, should be vectorbase ID such as 'AGAP004707-RD'"

Configure access to the MalariaGEN Ag3 data resource. Note that authentication is required to access data through the package, please follow the instructions here.

ag3 = malariagen_data.Ag3()
ag3
Wed May 22 06:52:41 2024 WARNING No project ID could be determined. Consider running `gcloud config set project` or setting the GOOGLE_CLOUD_PROJECT environment variable
MalariaGEN Ag3 API client
Please note that data are subject to terms of use, for more information see the MalariaGEN website or contact support@malariagen.net. See also the Ag3 API docs.
Storage URL gs://vo_agam_release/
Data releases available 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9
Results cache None
Cohorts analysis 20240418
AIM analysis 20220528
Site filters analysis dt_20200416
Software version malariagen_data 9.0.0
Client location unknown

Load sequence data for the chromosomal arm of choice, using the malariagen_data API:

genome_seq = ag3.genome_sequence(region=contig)
print(f"Our genome sequence for {contig} is {genome_seq.shape[0]} bp long")
Our genome sequence for 2R is 61545105 bp long

Now we need to extract the bit of genome sequence we need. We will use functions in the AnoPrimer package. For genomic DNA primers, this is quite simple and we can make direct use of the ag3.genome_sequence() function, but for cDNA qPCR primers, we must only include coding sequence, and so we must concatenate exons together.

With qPCR primers for cDNA, we also must ensure that one primer spans an exon-exon junction, to ensure that any residual genomic DNA in our samples does not get amplified. We must therefore make a note of where the exon junctions are, and we store that as a list in the exon_junctions variable.

if any(item in assay_type for item in ['gDNA', 'probe']):
    # genomic DNA
    target_sequence, gdna_pos, seq_parameters = AnoPrimer.prepare_gDNA_sequence(
      target_loc=target_loc, 
      amplicon_size_range=amplicon_size_range, 
      genome_seq=genome_seq, 
      assay_name=assay_name, 
      assay_type=assay_type
    )

elif assay_type == 'qPCR primers':
    # RT-quantitative PCR, cDNA
    target_sequence, exon_junctions, gdna_pos, seq_parameters = AnoPrimer.prepare_cDNA_sequence(
        transcript=transcript, 
        gff=ag3.geneset(), 
        genome_seq=genome_seq, 
        assay_name=assay_name
    )
The target sequence is 239 bases long
the target snp is 119 bp into our target sequence

Now we have our target sequence. Lets take a look…

seq_parameters['SEQUENCE_TEMPLATE']
'CGGGCGCGACCATGTGGAACCCGAACACGCCCCTGTCCGAGGACTGTCTGTACATTAACGTGGTGGCACCGCGACCCCGGCCCAAGAATGCGGCCGTCATGCTGTGGATCTTCGGCGGCGGCTTCTACTCCGGCACCGCCACCCTGGACGTGTACGACCACCGGGCGCTTGCGTCGGAGGAGAACGTGATCGTGGTGTCGCTGCAGTACCGCGTGGCCAGTCTGGGCTTCCTGTTTCTC'
seq_parameters
{'SEQUENCE_ID': 'ace1-280s',
 'SEQUENCE_TEMPLATE': 'CGGGCGCGACCATGTGGAACCCGAACACGCCCCTGTCCGAGGACTGTCTGTACATTAACGTGGTGGCACCGCGACCCCGGCCCAAGAATGCGGCCGTCATGCTGTGGATCTTCGGCGGCGGCTTCTACTCCGGCACCGCCACCCTGGACGTGTACGACCACCGGGCGCTTGCGTCGGAGGAGAACGTGATCGTGGTGTCGCTGCAGTACCGCGTGGCCAGTCTGGGCTTCCTGTTTCTC',
 'SEQUENCE_TARGET': [119, 10],
 'GENOMIC_TARGET': 3492074,
 'SEQUENCE_INTERNAL_EXCLUDED_REGION': [[1, 99], [139, 100]]}

We need to set up a second python dictionary, which will be our input to primer3. This contains our preferred primer parameters. In the below cell, you can modify or add primer3 parameters, such as optimal primer size, TM, GC content etc etc. A full list of possible parameters and their functions can be found in the primer3 2.6.1 manual.

primer_parameters  =  {
    'PRIMER_OPT_SIZE': 20,
    'PRIMER_TASK':'generic',
    'PRIMER_MIN_SIZE': 17,
    'PRIMER_MAX_SIZE': 24,
    'PRIMER_OPT_TM': 60.0,
    'PRIMER_MIN_TM': 55.0,
    'PRIMER_MAX_TM': 64.0,
    'PRIMER_MIN_GC': 30.0,
    'PRIMER_MAX_GC': 75.0,
    'PRIMER_MIN_THREE_PRIME_DISTANCE': 3,  # this parameter is the minimum distance between successive pairs. If 1, it means successive primer pairs could be identical bar one base shift
    'PRIMER_INTERNAL_OPT_SIZE': 16,  # Probe size preferences if selected, otherwise ignored
    'PRIMER_INTERNAL_MIN_SIZE': 10,
    'PRIMER_INTERNAL_MAX_SIZE': 22,
    'PRIMER_INTERNAL_MIN_TM': 45,
    'PRIMER_INTERNAL_MAX_TM': 65,  # Probe considerations are quite relaxed, assumed that LNAs / Taqman will be used later to affect TM
    # Extra primer3 parameters can go here
    # In the same format as above                       
}

# adds some necessary parameters depending on assay type
primer_parameters = AnoPrimer.primer_params(
    assay_type=assay_type, 
    primer_parameters=primer_parameters, 
    n_primer_pairs=n_primer_pairs, 
    amplicon_size_range=amplicon_size_range,
) 

Run the primer3 algorithm!#

primer_dict = primer3.designPrimers(
    seq_args=seq_parameters, 
    global_args=primer_parameters
)

It should be fast. The output, which we store as ‘primer_dict’, is a python dictionary containing the full results from primer3. We will turn this into a pandas dataframe containing just the necessary bits of information. First, we’ll print some information from the primer3 run.

AnoPrimer.primer3_run_statistics(primer_dict, assay_type)
primer_forward_explain  :  considered 796, GC content failed 84, low tm 72, high tm 347, ok 293 

primer_reverse_explain  :  considered 724, GC content failed 68, low tm 17, high tm 395, high hairpin stability 17, ok 227 

primer_probe_explain  :  considered 524, overlap excluded region 212, GC content failed 20, low tm 100, high tm 2, high hairpin stability 1, ok 189 

primer_pair_explain  :  considered 3107, unacceptable product size 3099, primer in pair overlaps a primer in a better pair 1279, ok 8 

primer_forward_num_returned  :  6 

primer_reverse_num_returned  :  6 

primer_probe_num_returned  :  6 

primer_pair_num_returned  :  6 

primer_pair  :  [{'PENALTY': 0.7107151008710275, 'COMPL_ANY_TH': 0.0, 'COMPL_END_TH': 0.0, 'PRODUCT_SIZE': 95, 'PRODUCT_TM': 88.90156407263927}, {'PENALTY': 1.8268188261876048, 'COMPL_ANY_TH': 0.0, 'COMPL_END_TH': 0.0, 'PRODUCT_SIZE': 66, 'PRODUCT_TM': 86.70188305190561}, {'PENALTY': 3.1582579651147853, 'COMPL_ANY_TH': 0.0, 'COMPL_END_TH': 1.562469314775342, 'PRODUCT_SIZE': 103, 'PRODUCT_TM': 89.70217725608332}, {'PENALTY': 3.3586929277500417, 'COMPL_ANY_TH': 12.973825966488278, 'COMPL_END_TH': 17.231667178159967, 'PRODUCT_SIZE': 112, 'PRODUCT_TM': 90.19160166662422}, {'PENALTY': 3.997840573385986, 'COMPL_ANY_TH': 12.56535604041153, 'COMPL_END_TH': 5.976322718447193, 'PRODUCT_SIZE': 117, 'PRODUCT_TM': 90.31415966418224}, {'PENALTY': 4.920050397896603, 'COMPL_ANY_TH': 0.0, 'COMPL_END_TH': 6.284961991996681, 'PRODUCT_SIZE': 109, 'PRODUCT_TM': 90.03744046085752}] 

primer_forward  :  [{'PENALTY': 0.46258069149581615, 'SEQUENCE': 'TCATGCTGTGGATCTTCGGC', 'COORDS': [96, 20], 'TM': 60.462580691495816, 'GC_PERCENT': 55.0, 'SELF_ANY_TH': 12.259940761643293, 'SELF_END_TH': 12.259940761643293, 'HAIRPIN_TH': 36.59415215911514, 'END_STABILITY': 5.54}, {'PENALTY': 0.7458448421797357, 'SEQUENCE': 'GCCGTCATGCTGTGGATCTT', 'COORDS': [92, 20], 'TM': 60.745844842179736, 'GC_PERCENT': 55.0, 'SELF_ANY_TH': 0.0, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 0.0, 'END_STABILITY': 2.4}, {'PENALTY': 1.0630056029440311, 'SEQUENCE': 'CAAGAATGCGGCCGTCATG', 'COORDS': [82, 19], 'TM': 59.93699439705597, 'GC_PERCENT': 57.89473684210526, 'SELF_ANY_TH': 27.740677587638743, 'SELF_END_TH': 17.565181116897747, 'HAIRPIN_TH': 0.0, 'END_STABILITY': 3.07}, {'PENALTY': 1.2319584737745117, 'SEQUENCE': 'ACATTAACGTGGTGGCACCG', 'COORDS': [51, 20], 'TM': 61.23195847377451, 'GC_PERCENT': 55.0, 'SELF_ANY_TH': 30.138122022371874, 'SELF_END_TH': 21.521828989913672, 'HAIRPIN_TH': 42.57679566489054, 'END_STABILITY': 4.94}, {'PENALTY': 2.1245702891837936, 'SEQUENCE': 'GGCCGTCATGCTGTGGAT', 'COORDS': [91, 18], 'TM': 60.124570289183794, 'GC_PERCENT': 61.111111111111114, 'SELF_ANY_TH': 0.0, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 0.0, 'END_STABILITY': 3.41}, {'PENALTY': 2.8924249804836677, 'SEQUENCE': 'CTGTGGATCTTCGGCGGC', 'COORDS': [101, 18], 'TM': 60.89242498048367, 'GC_PERCENT': 66.66666666666667, 'SELF_ANY_TH': 7.2145116769710285, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 0.0, 'END_STABILITY': 6.53}] 

primer_reverse  :  [{'PENALTY': 0.24813440937521136, 'SEQUENCE': 'GATCACGTTCTCCTCCGACG', 'COORDS': [190, 20], 'TM': 60.24813440937521, 'GC_PERCENT': 60.0, 'SELF_ANY_TH': 0.0, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 42.12619709637721, 'END_STABILITY': 5.12}, {'PENALTY': 1.0809739840078691, 'SEQUENCE': 'GTCGTACACGTCCAGGGTG', 'COORDS': [157, 19], 'TM': 60.08097398400787, 'GC_PERCENT': 63.1578947368421, 'SELF_ANY_TH': 1.1902412874118795, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 36.90501393969038, 'END_STABILITY': 4.61}, {'PENALTY': 2.095252362170754, 'SEQUENCE': 'GTTCTCCTCCGACGCAAGC', 'COORDS': [184, 19], 'TM': 61.095252362170754, 'GC_PERCENT': 63.1578947368421, 'SELF_ANY_TH': 0.0, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 45.62213695404927, 'END_STABILITY': 4.01}, {'PENALTY': 2.12673445397553, 'SEQUENCE': 'CGGTGGTCGTACACGTCC', 'COORDS': [162, 18], 'TM': 60.12673445397553, 'GC_PERCENT': 66.66666666666667, 'SELF_ANY_TH': 10.508612029603285, 'SELF_END_TH': 10.508612029603285, 'HAIRPIN_TH': 45.42514212470502, 'END_STABILITY': 4.79}, {'PENALTY': 1.8732702842021922, 'SEQUENCE': 'TACTGCAGCGACACCACGAT', 'COORDS': [207, 20], 'TM': 61.87327028420219, 'GC_PERCENT': 55.0, 'SELF_ANY_TH': 15.265667197231778, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 35.08910707969903, 'END_STABILITY': 3.73}, {'PENALTY': 2.027625417412935, 'SEQUENCE': 'GGTACTGCAGCGACACCAC', 'COORDS': [209, 19], 'TM': 61.027625417412935, 'GC_PERCENT': 63.1578947368421, 'SELF_ANY_TH': 15.265667197231778, 'SELF_END_TH': 0.405162456403275, 'HAIRPIN_TH': 0.0, 'END_STABILITY': 4.16}] 

primer_probe  :  [{'PENALTY': 4.161169114371262, 'SEQUENCE': 'GGCTTCTACTCCGGCACCGC', 'COORDS': [119, 20], 'TM': 59.83883088562874, 'GC_PERCENT': 70.0, 'SELF_ANY_TH': 0.0, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 36.28074618918981}, {'PENALTY': 3.1023652706057874, 'SEQUENCE': 'GCGGCGGCTTCTACTCCGG', 'COORDS': [114, 19], 'TM': 59.89763472939421, 'GC_PERCENT': 73.6842105263158, 'SELF_ANY_TH': 7.734280912202962, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 42.29746586682808}, {'PENALTY': 2.194535966840135, 'SEQUENCE': 'TGGATCTTCGGCGGCGGC', 'COORDS': [104, 18], 'TM': 59.805464033159865, 'GC_PERCENT': 72.22222222222223, 'SELF_ANY_TH': 28.91330394666676, 'SELF_END_TH': 21.80273259353129, 'HAIRPIN_TH': 38.904208273950076}, {'PENALTY': 2.194535966840135, 'SEQUENCE': 'TGGATCTTCGGCGGCGGC', 'COORDS': [104, 18], 'TM': 59.805464033159865, 'GC_PERCENT': 72.22222222222223, 'SELF_ANY_TH': 28.91330394666676, 'SELF_END_TH': 21.80273259353129, 'HAIRPIN_TH': 38.904208273950076}, {'PENALTY': 3.1023652706057874, 'SEQUENCE': 'GCGGCGGCTTCTACTCCGG', 'COORDS': [114, 19], 'TM': 59.89763472939421, 'GC_PERCENT': 73.6842105263158, 'SELF_ANY_TH': 7.734280912202962, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 42.29746586682808}, {'PENALTY': 4.161169114371262, 'SEQUENCE': 'GGCTTCTACTCCGGCACCGC', 'COORDS': [119, 20], 'TM': 59.83883088562874, 'GC_PERCENT': 70.0, 'SELF_ANY_TH': 0.0, 'SELF_END_TH': 0.0, 'HAIRPIN_TH': 36.28074618918981}] 

Now lets convert this into an easy to read pandas dataframe.

primer_df = AnoPrimer.primer3_to_pandas(primer_dict, assay_type)
primer_df
primer_pair 0 1 2 3 4 5
parameter
primer_forward_sequence TCATGCTGTGGATCTTCGGC GCCGTCATGCTGTGGATCTT CAAGAATGCGGCCGTCATG ACATTAACGTGGTGGCACCG GGCCGTCATGCTGTGGAT CTGTGGATCTTCGGCGGC
primer_reverse_sequence GATCACGTTCTCCTCCGACG GTCGTACACGTCCAGGGTG GTTCTCCTCCGACGCAAGC CGGTGGTCGTACACGTCC TACTGCAGCGACACCACGAT GGTACTGCAGCGACACCAC
primer_probe_sequence GGCTTCTACTCCGGCACCGC GCGGCGGCTTCTACTCCGG TGGATCTTCGGCGGCGGC TGGATCTTCGGCGGCGGC GCGGCGGCTTCTACTCCGG GGCTTCTACTCCGGCACCGC
primer_forward_tm 60.462581 60.745845 59.936994 61.231958 60.12457 60.892425
primer_reverse_tm 60.248134 60.080974 61.095252 60.126734 61.87327 61.027625
primer_probe_tm 59.838831 59.897635 59.805464 59.805464 59.897635 59.838831
primer_forward_gc_percent 55.0 55.0 57.894737 55.0 61.111111 66.666667
primer_reverse_gc_percent 60.0 63.157895 63.157895 66.666667 55.0 63.157895
primer_probe_gc_percent 70.0 73.684211 72.222222 72.222222 73.684211 70.0
primer_forward [96, 20] [92, 20] [82, 19] [51, 20] [91, 18] [101, 18]
primer_reverse [190, 20] [157, 19] [184, 19] [162, 18] [207, 20] [209, 19]
primer_probe [119, 20] [114, 19] [104, 18] [104, 18] [114, 19] [119, 20]
primer_pair_product_size 95 66 103 112 117 109

We can write this to .tsv and excel files, which can be explored in other editors.

primer_df.to_csv(f"{assay_name}.{assay_type}.primers.tsv", sep="\t")
primer_df.to_excel(f"{assay_name}.{assay_type}.primers.xlsx")

Looking for variation using the Ag1000G data resource#

As we’ve seen in earlier workshops, Ag1000G samples are organised into sample sets. Lets look at what each sample set contains, breaking it down by species, year and country.

metadata = ag3.sample_metadata(sample_sets='3.0')

pivot_country_year_taxon = (
    metadata
    .pivot_table(
        index=["sample_set", "country", "year"], 
        columns=["taxon"], 
        values="sample_id",
        aggfunc="count",
        fill_value=0
    )
)

pivot_country_year_taxon
                                     
taxon arabiensis coluzzii gambiae gcx1 gcx3 unassigned
sample_set country year
AG1000G-AO Angola 2009 0 81 0 0 0 0
AG1000G-BF-A Burkina Faso 2012 0 82 99 0 0 0
AG1000G-BF-B Burkina Faso 2014 3 53 46 0 0 0
AG1000G-BF-C Burkina Faso 2004 0 0 13 0 0 0
AG1000G-CD Democratic Republic of the Congo 2015 0 0 76 0 0 0
AG1000G-CF Central African Republic 1993 0 5 2 0 0 0
1994 0 13 53 0 0 0
AG1000G-CI Cote d'Ivoire 2012 0 80 0 0 0 0
AG1000G-CM-A Cameroon 2009 0 0 303 0 0 0
AG1000G-CM-B Cameroon 2005 0 7 90 0 0 0
AG1000G-CM-C Cameroon 2013 2 19 23 0 0 0
AG1000G-FR Mayotte 2011 0 0 23 0 0 0
AG1000G-GA-A Gabon 2000 0 0 69 0 0 0
AG1000G-GH Ghana 2012 0 64 36 0 0 0
AG1000G-GM-A Gambia, The 2011 0 6 0 68 0 0
AG1000G-GM-B Gambia, The 2006 0 22 0 9 0 0
AG1000G-GM-C Gambia, The 2012 0 172 2 0 0 0
AG1000G-GN-A Guinea 2012 0 4 41 0 0 0
AG1000G-GN-B Guinea 2012 0 7 83 0 0 1
Mali 2012 0 27 65 0 0 2
AG1000G-GQ Equatorial Guinea 2002 0 0 10 0 0 0
AG1000G-GW Guinea-Bissau 2010 0 0 7 93 0 1
AG1000G-KE Kenya 2000 0 0 19 0 0 0
2007 3 0 0 0 0 0
2012 10 0 0 0 54 0
AG1000G-ML-A Mali 2014 0 27 33 0 0 0
AG1000G-ML-B Mali 2004 2 36 33 0 0 0
AG1000G-MW Malawi 2015 41 0 0 0 0 0
AG1000G-MZ Mozambique 2003 0 0 3 0 0 0
2004 0 0 71 0 0 0
AG1000G-TZ Tanzania 2012 87 0 0 0 0 0
2013 1 0 32 0 10 0
2015 137 0 32 0 1 0
AG1000G-UG Uganda 2012 82 0 207 0 0 1
AG1000G-X Lab Cross -1 0 0 0 0 0 297

Here, we can see the breakdown by sample set for country, species and year. For the purposes of this notebook, let’s use the Ghana sample set. If we wanted to use all sample sets, we could supply ‘3.0’ instead of a sample set, which will load all samples from the Ag3.0 release.

metadata.columns
Index(['sample_id', 'partner_sample_id', 'contributor', 'country', 'location',
       'year', 'month', 'latitude', 'longitude', 'sex_call', 'sample_set',
       'release', 'quarter', 'study_id', 'study_url',
       'aim_species_fraction_arab', 'aim_species_fraction_colu',
       'aim_species_fraction_colu_no2l', 'aim_species_gambcolu_arabiensis',
       'aim_species_gambiae_coluzzii', 'aim_species', 'country_iso',
       'admin1_name', 'admin1_iso', 'admin2_name', 'taxon',
       'cohort_admin1_year', 'cohort_admin1_month', 'cohort_admin1_quarter',
       'cohort_admin2_year', 'cohort_admin2_month', 'cohort_admin2_quarter'],
      dtype='object')
sample_set = ['AG1000G-BF-A', 'AG1000G-GH', 'AG1000G-GN-A']
# here we could subset to specific values in the metadata e.g.: "taxon == 'gambiae'" , or "taxon == 'arabiensis'"
sample_query = None 

Plot allele frequencies in primers locations#

Now we can plot the primers pairs, and the frequency of any alternate alleles in the Ag1000G sample set of choice. When calculating allele frequencies, we will take the sum of all alternate alleles, as we are interested here in any mutations which are different from the reference genome. We can see the frequencies of specific alleles by hovering over the points of the plot - in some cases it may be preferable to design degenerate primers rather than avoid a primer pair completely.

We will also plot the primer Tm, GC and genomic spans of each primer binding site. We can use this plot to identify primers pairs and probes which may be suitable, particularly trying to avoid SNPs in the 3’ end.

results_dict = AnoPrimer.plot_primer_snp_frequencies(
    species='gambiae_sl',
    primer_df=primer_df,
    gdna_pos=gdna_pos,
    contig=contig,
    sample_sets=sample_set, 
    sample_query=sample_query,
    assay_type=assay_type,
    seq_parameters=seq_parameters,
    out_dir="."
)